Lights Service of Android System Server Outline

Keywords: Android Java Mobile

Lights Service of Android System Server Outline

Android Flash lamp

Android Led

Preface

Since the function machine, mobile phones and other devices have been equipped with LED flash, when there are no phone calls, unread text messages and notifications, etc., Led will flicker. Similar to the vibrator, it gives users a feedback of human-computer interaction, even if the human-computer interaction is so simple. Since it is a Led, that is, a hardware, and the service that drives the hardware on top of Android system is LightsService, this article also describes a combination of hardware and software. But the upper APP can't drive the Led hardware directly. LightsService is used by the system.

Since it is a combination of hardware and software, in Android system, the architecture of top-down implementation of this function is as follows:

LightsService initialization

Review System Server Outline of Android System > In this article, LightsService's startup process in frameworks/base/services/java/com/android/server/SystemServer.java is as follows:

public final class SystemServer {
        // Manages LEDs and display backlight so we need it to bring up the display.
        mSystemServiceManager.startService(LightsService.class);
}

From the above code, LightsService is started by SystemServiceManager.startService(), in the< System Server Outline of Android System > As we can see from the various service startup modes of Android system mentioned in the article, LightsService inherits System Service and calls back onStart() method when LightsService starts. The code is as follows:

public class LightsService extends SystemService {
    @Override
    public void onStart() {
        publishLocalService(LightsManager.class, mService);
    }
}

The above code is in frameworks/base/services/core/java/com/android/server/lights/LightsService.java

In " System Server Outline of Android System > As mentioned in the article, the role of publishLocalService() is to push mService into Local Services, so when we refer to LightsService externally, we actually get mService as an object. The code for mService is as follows:

public class LightsService extends SystemService {
    private final LightsManager mService = new LightsManager() {
        @Override
        public Light getLight(int id) {
            if (id < LIGHT_ID_COUNT) {
                return mLights[id];
            } else {
                return null;
            }
        }
    };
}

The essence of mService is a LightsManager. LightsManager only provides a method getLight(int id) to match a return value from the array mLights. What type of return value is it? Look at the nature of mLights [], as follows:

public class LightsService extends SystemService {
    final LightImpl mLights[] = new LightImpl[LightsManager.LIGHT_ID_COUNT];
}

mLights [] is essentially an array of LightImpl, so the getLight(int id) of LightsManager returns an instance of LightImpl, which is coded as follows:

public class LightsService extends SystemService {
    private final class LightImpl extends Light {

        ......

        @Override
        public void setFlashing(int color, int mode, int onMS, int offMS) {
            synchronized (this) {
                setLightLocked(color, mode, onMS, offMS, BRIGHTNESS_MODE_USER);
            }
        }

       ......
    }
}

LightImpl is actually a subclass of Light, and Light is essentially an abstraction of LED hardware, so a LED is abstracted into a Light object. Therefore, in order to control led, the driver LED can be realized as long as the abstract object light of LED is obtained.

Looking back at the initialization of LightsService, LightsService is constructed as follows:

public class LightsService extends SystemService {
    public LightsService(Context context) {
        super(context);

        mNativePointer = init_native();

        for (int i = 0; i < LightsManager.LIGHT_ID_COUNT; i++) {
            mLights[i] = new LightImpl(i);
        }
    }
}

The LightImpl array mLights mentioned above is initialized in LightsService, where an instance of LightImpl is saved in the mLights array based on the total number of iterations of LightsManager.LIGHT_ID_COUNT. The value of LightsManager.LIGHT_ID_COUNT here is 8, and the code is as follows:

public abstract class LightsManager {
    public static final int LIGHT_ID_BACKLIGHT = 0;
    public static final int LIGHT_ID_KEYBOARD = 1;
    public static final int LIGHT_ID_BUTTONS = 2;
    public static final int LIGHT_ID_BATTERY = 3;
    public static final int LIGHT_ID_NOTIFICATIONS = 4;
    public static final int LIGHT_ID_ATTENTION = 5;
    public static final int LIGHT_ID_BLUETOOTH = 6;
    public static final int LIGHT_ID_WIFI = 7;
    public static final int LIGHT_ID_COUNT = 8;

    public abstract Light getLight(int id);
}

From LIGHT_ID_BACKLIGHT to LIGHT_ID_WIFI, there are eight leds. Does this mean eight leds? Back to LightsService's construction method, the init_native() method is called. Of course, it's clear to everyone familiar with Android architecture that other hardware services, like LightsService, have such a process. The purpose is to call init_native() to initialize the hardware when LightsService starts up, that is, the channel between software and hardware is opened in the method of init_native(). Init_native() is a native method, and the corresponding JNI interface is:

static jlong init_native(JNIEnv* /* env */, jobject /* clazz */)
{
    int err;
    hw_module_t* module;
    Devices* devices;

    devices = (Devices*)malloc(sizeof(Devices));

    err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
    if (err == 0) {
        ......
        devices->lights[LIGHT_INDEX_NOTIFICATIONS]
                = get_device(module, LIGHT_ID_NOTIFICATIONS);
        ......
    } else {
        memset(devices, 0, sizeof(Devices));
    }

    return (jlong)devices;
}

This function is defined in the file frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp.

Initit_native() first calls the hw_get_module() function, which is to turn on the hardware device. This is the implementation process of the function in hardware/libhardware/hardware.c. Readers familiar with Android HAL should be familiar with this process, and this process will not be repeated in this article. Then, by calling the get_device() function, the variable lights of the return value assignment Devices are obtained. One parameter of get_device() is LIGHT_ID_NOTIFICATIONS, which corresponds to the eight LED IDs of LightsManager mentioned above. The essence of Devices variable light is:

struct Devices {
    light_device_t* lights[LIGHT_COUNT];
};

light_device_t is defined in the hardware/libhardware/include/hardware/lights.h file. Back to the init_native() function, and then look at get_device():

static light_device_t* get_device(hw_module_t* module, char const* name)
{
    int err;
    hw_device_t* device;
    err = module->methods->open(module, name, &device);
    if (err == 0) {
        return (light_device_t*)device;
    } else {
        return NULL;
    }
}

Open the hardware device through module - > methods - > open (), and this process will not be repeated in this article. When the device channel is opened, light_device_t is obtained and stored in the array lights of Devices, which corresponds to the array mLights of LightImpl in LightsService above.

Start led through LightsService

APP can't drive led directly, but led is usually used by Android's Notification, so Apple initiates a notification and led flickers. Starting with Android notification, this paper analyses the process of driving led.

When APP creates Notification, you can specify that the notification needs led by:

public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
    mN.ledARGB = argb;
    mN.ledOnMS = onMs;
    mN.ledOffMS = offMs;
    if (onMs != 0 || offMs != 0) {
        mN.flags |= FLAG_SHOW_LIGHTS;
    }
    return this;
}

This article will not elaborate on this method for the time being, but will introduce Android's notification mechanism in detail in future articles. Of course, if APP does not call this method, then the Android system will automatically select the default value for APP notification.

When Apple's notification is notified to the Android system, it drives the led to flicker. As mentioned in the initialization of LightsService above, led hardware has been abstracted into a Light object, which is LightImpl. If you get an instance of LightImpl, you can drive led. Android notification gets LightImpl code as follows:

public class NotificationManagerService extends SystemService {
    final LightsManager lights = getLocalService(LightsManager.class);
    mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
}

This class is defined in document frameworks/base/services/core/java/com/android/server/notification/Notification ManagerService.java

As you can see from the initialization of LightsService above, LightsService is pushed into LocalService, so LightsManager is obtained by getLocalService(), and then LightImpl instance is obtained by getLight() method, which is very clear in the initialization of LightsService above. Get LightImpl, through which interface drive led? First look at the structure of LightImpl:

public abstract class Light {
    ......
    public abstract void setBrightness(int brightness);
    public abstract void setBrightness(int brightness, int brightnessMode);
    public abstract void setColor(int color);
    public abstract void setFlashing(int color, int mode, int onMS, int offMS);
    public abstract void pulse();
    public abstract void pulse(int color, int onMS);
    public abstract void turnOff();
}

This class is defined in file frameworks/base/services/core/java/com/android/server/lights/Light.java

Like the above code, LightImpl provides seven methods for setBrightness() setting brightness, setColor setting color, setFlashing starting led, and so on. Therefore, the code for driving an Anroid notification LED is as follows:

mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,ledOnMS, ledOffMS);

This method is defined in the file frameworks/base/services/core/java/com/android/server/notification/Notification Manager Service.java

setFlashing() receives four parameters, the first of which is, of course, the color, the type rgb, and the second is the mode, the values can be:

public static final int LIGHT_FLASH_NONE = 0;
public static final int LIGHT_FLASH_TIMED = 1;
public static final int LIGHT_FLASH_HARDWARE = 2;

These variables are defined in document frameworks/base/services/core/java/com/android/server/lights/Light.java

The third and fourth are a pair of opposite parameters, representing the time when the led flickers and the time when the led flickers. Then look at the implementation of setFlashing():

private final class LightImpl extends Light {
    public void setFlashing(int color, int mode, int onMS, int offMS) {
        synchronized (this) {
            setLightLocked(color, mode, onMS, offMS, BRIGHTNESS_MODE_USER);
        }
    }
}

This method is defined in document frameworks/base/services/core/java/com/android/server/lights/LightsService.java

setLightLocked() is called directly, and the code is as follows:

private final class LightImpl extends Light {
    private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {
    if (!mLocked && (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS ||
            mBrightnessMode != brightnessMode)) {
        if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"
                + Integer.toHexString(color) + ": brightnessMode=" + brightnessMode);
        mLastColor = mColor;
        mColor = color;
        mMode = mode;
        mOnMS = onMS;
        mOffMS = offMS;
        mLastBrightnessMode = mBrightnessMode;
        mBrightnessMode = brightnessMode;
        Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", 0x"
                + Integer.toHexString(color) + ")");
        try {
            setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_POWER);
        }
    }
}

This method is defined in document frameworks/base/services/core/java/com/android/server/lights/LightsService.java

After sorting out the parameters, we call the JNI method setLight_native(), where the second parameter is LightsManager.LIGHT_ID_NOTIFICATIONS = 4; continue to look at setLight_native():

static void setLight_native(JNIEnv* /* env */, jobject /* clazz */, jlong ptr,
        jint light, jint colorARGB, jint flashMode, jint onMS, jint offMS, jint brightnessMode)
{
    Devices* devices = (Devices*)ptr;
    light_state_t state;
    ......
    state.brightnessMode = brightnessMode;

    {
        ALOGD_IF_SLOW(50, "Excessive delay setting light");
        devices->lights[light]->set_light(devices->lights[light], &state);
    }
}

This function is defined in the file frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp

Set_light() is defined in the file hardware/libhardware/include/hardware/lights.h. The implementation of set_light() varies according to the hardware manufacturer of the led. Here, we take the manufacturer of a LED as an example:

static int open_lights(const struct hw_module_t *module, char const *name,
               struct hw_device_t **device)
{
    struct dragon_lights *lights;
    ......
    lights->base.set_light = set_light_backlight;

    *device = (struct hw_device_t *)lights;
    return 0;
}

During the initialization of led channel, the set_light() function actually calls the set_light_backlight function. The set_light_backlight function is as follows:

static int set_light_backlight(struct light_device_t *dev,
                   struct light_state_t const *state)
{
    struct dragon_lights *lights = to_dragon_lights(dev);
    int err, brightness_idx;
    int brightness = rgb_to_brightness(state);

    if (brightness > 0) {
        // Get the bin number for brightness (0 to kNumBrightnessLevels - 1)
        brightness_idx = (brightness - 1) * kNumBrightnessLevels / 0xff;

        // Get brightness level
        brightness = kBrightnessLevels[brightness_idx];
    }

    pthread_mutex_lock(&lights->lock);
    err = write_brightness(lights, brightness);
    pthread_mutex_unlock(&lights->lock);

    return err;
}

Tracking down here, this article will not follow down, these codes will vary according to the different manufacturers of led. This is the end of driving led.

summary

This paper describes the role of LightsService, from the process of opening hardware channels and driving hardware led blinking. Although APP can not directly drive led, but led is almost used for android notification. Therefore, the notification issued by APP can also use led to inform users of new status. For the LED color mentioned above, for many devices, it is not supported to set the color, which can only be a color, so on these devices, it can be said that most devices, this function is basically useless. In addition, due to the different hardware of led, the eight LED IDS mentioned in this article may or may not be.

Posted by benracer on Mon, 01 Apr 2019 11:06:29 -0700