Android System Server大綱之LightsService

Android System Server大綱之LightsService

Android閃光燈

Android Led

前言

從功能機以來,手機等設備就配備了Led閃光燈,當有未接電話、未讀短信和通知等等,Led就會閃爍。和振動器類似,都是給用戶一種人機交互的反饋,哪怕這種人機交互是那麼的簡單。既然它是一個Led,也就是一個硬件,Android系統上層驅動這個硬件的服務就是LightsService,所以,這個文章也是描述一個軟硬件結合的功能。但是上層APP不能直接驅動Led硬件,LightsService是系統所使用。

既然是軟硬結合的一個功能,那麼在Android系統裏,從上到下實現這個功能的架構如下:

20170215151346265.png

LightsService初始化

回顧《Android系統之System Server大綱 》一文,LightsService在frameworks/base/services/java/com/android/server/SystemServer.java中的啓動過程是:

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

從上面的代碼中,LightsService是通過SystemServiceManager.startService()的方式啓動,在《Android系統之System Server大綱 》一文中提到的Android系統的各種服務的啓動方式可知,LightsService是繼承了SystemService,LightsService啓動後會回調onStart()方法。代碼如下:

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

上述代碼在frameworks/base/services/core/java/com/android/server/lights/LightsService.java中

在《Android系統之System Server大綱 》一文中提到,publishLocalService()的作用,這裏是把mService推進LocalServices中,所以外部引用LightsService時,實際是拿到mService這個對象。mService的代碼如下:

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;
            }
        }
    };
}

mService的實質是一個LightsManager,LightsManager只提供一個方法getLight(int id),從數組mLights中匹配一個返回值,返回值是什麼類型呢?看mLights[]的本質,如下:

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

mLights[]的實質一個LightImpl的數組,所以LightsManager的getLight(int id)返回的是一個LightImpl的實例,LightImpl的代碼如下:

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實際是Light的子類,而Light實質是一個led硬件的抽象,那麼一個led就被抽象成一個Light對象。因此,要控制led,只要拿到led的抽象對象Light即可實現驅動led。

回頭再看看LightsService的初始化,LightsService的構造方法如下:

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);
        }
    }
}

上文提到的LightImpl數組mLights就是在LightsService中被初始化了,這裏會根據LightsManager.LIGHT_ID_COUNT的總數循環生成LightImpl的實例保存在mLights數組中。這裏的LightsManager.LIGHT_ID_COUNT的值是8,代碼如下:

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);
}

從LIGHT_ID_BACKLIGHT到LIGHT_ID_WIFI共8個,這裏是否表示8個led呢,後文在論述清楚。回到LightsService的構造方法,調用了init_native()方法,當然,熟悉Android架構的都清楚,其它硬件的服務和LightsService一樣,都有這麼一個過程。目的就是LightsService啓動時,調用init_native()初始化硬件,也就是軟硬件的通道在init_native()這個方法中打通。init_native()是一個native方法,對應的JNI接口是:

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;
}

這個函數定義在文件frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp中。

init_native()中首先調用了hw_get_module()函數,這個函數就是打開硬件設備,這個是函數的實現過程在hardware/libhardware/hardware.c中,對Android HAL熟悉的讀者,應該很熟悉這個過程,本文也不往下贅述這個過程了。然後通過調用get_device()函數取得返回值賦值Devices的變量lights,get_device()的一個參數是LIGHT_ID_NOTIFICATIONS,這和上文中LightsManager的8個led id是對應的。Devices的變量lights的實質是:

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

light_device_t定義在hardware/libhardware/include/hardware/lights.h文件中。回到init_native()函數,接着看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;
    }
}

通過module->methods->open()打開硬件設備,這個過程本文就不再贅述了。打開設備通道後,取得light_device_t,保存在Devices的數組lights中,這和LightsService上文中的LightImpl的數組mLights也是對應的關係。

通過LightsService啓動led

APP雖然不能直接驅動led,但是led一般也是爲Android的Notification所使用,所以APP發起一個通知,led也會閃爍。本文就從Android的notification入手,分析驅動led的過程。

APP創建Notification時,可以通過如下方法指定這個notification需要led:

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;
}

本文暫時不對這個方法進行過多的闡述,會在以後的文章中再詳細介紹Android的notification機制。當然APP如果不調用這個方法,那麼Android系統會自動爲APP的notification選取默認值。

APP的notification通知到Android系統以後,就會驅動led閃爍起來。在上文LightsService的初始化中提到,led硬件已經抽象成一個Light對象,這個對象便是LightImpl,獲取到LightImpl的實例,便可驅動led。Android的notification獲取LightImpl的代碼如下:

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

這個類定義在文件frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java中

在上文LightsService的初始化中可知,LightsService被推進到LocalService中,所以是通過getLocalService()獲取到LightsManager,然後再通過getLight()方法,獲取到LightImpl的實例,這個過程在上文LightsService的初始化中已經闡述的很明白。獲取到了LightImpl,通過什麼接口驅動led呢?先看看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();
}

這個類定義在文件frameworks/base/services/core/java/com/android/server/lights/Light.java中

如上面的代碼,LightImpl提供setBrightness()設置亮度、setColor設置顏色、setFlashing啓動led等7個方法。因此,Anroid notification驅動led的代碼如下:

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

這方法定義在文件個frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java中

setFlashing()接收四個參數,第一個當然是顏色,類型是rgb;第二參數是mode,數值分別是可以是:

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

這些變量定義在文件frameworks/base/services/core/java/com/android/server/lights/Light.java中

第三個和第四個是一對相反的參數,分別表示led閃爍時亮的時間,led閃爍時滅的時間。接着看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);
        }
    }
}

這個方法定義在文件frameworks/base/services/core/java/com/android/server/lights/LightsService.java中

直接調用了setLightLocked(),代碼如下:

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);
        }
    }
}

這個方法定義在文件frameworks/base/services/core/java/com/android/server/lights/LightsService.java中

對參數整理一遍後,調用了JNI方法setLight_native(),這裏的第二個參數便是LightsManager.LIGHT_ID_NOTIFICATIONS = 4;繼續往下看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);
    }
}

這個函數定義在文件frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp中

set_light()的定義是在文件hardware/libhardware/include/hardware/lights.h中,具體實現,就因led的硬件廠商不同而不同了,這裏以某個led的廠商爲例:

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;
}

在led通道初始化過程中,set_light()函數實際調用的是set_light_backlight函數,set_light_backlight函數如下:

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;
}

跟蹤到這裏,本文也就不往下跟蹤了,這些代碼都會因led廠商不同而不同。驅動led的過程就到此結束。

總結

本文闡述了LightsService的作用,從打開硬件通道和驅動硬件led閃爍起來的過程,雖然APP是不可以直接驅動led,但是led幾乎都是爲android notification所用,所以,APP發佈的notification也可以使用led,通知用戶有新的狀態。對於上文提到的led顏色,對於很多設備,是不支持設置顏色了的,設置了也只能是一種顏色,所以在這些設備上,可以說是大多數設備,這個功能基本就沒有任何用處。另外,因led硬件的不同,文章中提到的8個led id,可能有,可能沒有。

發佈了50 篇原創文章 · 獲贊 70 · 訪問量 60萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章