Le composant LightsService au sein du framework Android orchestre la gestion des éclairages matériels. Son cycle de vie débute par le constructeur principal, qui transfère la configuration à un constructeur interne nécessitant un fournisseur de service HAL (Hardware Abstraction Layer) ainsi qu'une boucle de messages (Looper).
public LightsService(Context appContext) {
this(appContext, new VintfHalCache(), Looper.myLooper());
Log.d("LightsService", "Initializing LightsService");
}
@VisibleForTesting
LightsService(Context appContext, Supplier<ILights> halProvider, Looper threadLooper) {
super(appContext);
mHandler = new Handler(threadLooper);
mAidlLights = (halProvider.get() != null) ? halProvider : null;
discoverAvailableLights(appContext);
mBinderService = new LightsManagerBinderService();
}
Une fois l'instance créée, le système doit recenser les composants d'éclairage physiques présents sur l'appareil. Cette opération de découverte évalue la disponibilité de l'interface AIDL pour choisir la méthode d'initialisation appropriée, en privilégiant AIDL par rapport à l'ancienne interface HIDL.
private void discoverAvailableLights(Context appContext) {
if (mAidlLights != null) {
loadLightsViaAidl(appContext);
} else {
loadLightsViaHidl(appContext);
}
for (int idx = mLightRegistry.size() - 1; idx >= 0; idx--) {
final int lightType = mLightRegistry.keyAt(idx);
if (lightType >= 0 && lightType < mLightTypeArray.length) {
mLightTypeArray[lightType] = mLightRegistry.valueAt(idx);
}
}
}
Lorsque l'interface AIDL est active, le service interroge la couche d'abstraction matérielle pour récupérer la liste complète des lumières. Chaque élément matériel (HwLight) est ensuite instancié dans une classe d'implémentation interne et indexé dans un registre.
private void loadLightsViaAidl(Context appContext) {
try {
for (HwLight hardwareLight : mAidlLights.get().getLights()) {
mLightRegistry.put(hardwareLight.id, new LightImpl(appContext, hardwareLight));
}
} catch (RemoteException exception) {
Slog.e(TAG, "Failed to retrieve lights from HAL", exception);
}
}
Le fournisseur mAidlLights s'appuie sur l'interface ILights, dont le contrat est défini dans le ficheir AIDL du système (android.hardware.light.ILights). Pour des architectures matérielles spécifiques comme Rockchip, l'implémentation native de ce contrat réside dans un fichier C++ dédié (par exemple, Lights.cpp).
Au niveau natif, le pilote doit communiquer avec le système de fichiers sysfs du noyau Linux pour ajuster l'état des LED et du rétroéclairage. Une fonction de validation vérifie au préalable l'accessibilité du nœud système correspondant au rétroéclairage.
static int verifyBacklightAccess() {
std::string sysfsPath(resolveSysfsPath(LightType::BACKLIGHT));
ALOGV("Checking backlight path: %s", sysfsPath.c_str());
if (access(sysfsPath.c_str(), F_OK) != 0) {
ALOGE("Access failed: %s", strerror(errno));
return -errno;
}
return 0;
}
La résolution des chemins sysfs s'effectue dynamiquement en fonction de la catégorie de lumière sollicitée. Une structure de contrôle redirige vers le répertoire système adéquat pour chaque type de périphérique lumineux.
const char* resolveSysfsPath(LightType category) {
switch (category) {
case LightType::BACKLIGHT:
return "/sys/class/backlight/backlight/brightness";
case LightType::BUTTONS:
return "/sys/class/leds/button-backlight/brightness";
case LightType::BATTERY:
case LightType::NOTIFICATIONS:
case LightType::ATTENTION:
return "/sys/class/leds";
case LightType::BLUETOOTH:
case LightType::WIFI:
case LightType::MICROPHONE:
case LightType::KEYBOARD:
default:
return "/sys/class/unsupported";
}
}