2010年8月23日 星期一

Android Proting 首部曲--- 環境建置與原始碼下載

最近針對Android移植部分做了點練習稍有心得,分享最近 Porting 心得看是否可以增加部落格瀏覽人氣 。  ^_^

Android Porting的方法在網路上已經有很多的教學,本系列文章是針對ARM S3C6410的開發版進行Android 2.1移植,移植流程如下圖:

2010年8月16日 星期一

Android 燈光控制軟體追蹤

Google Android提供完整的開發智慧型手機的原始程式,該程式實際上是由Java及C++或C組合而成。本文我們將說明燈光控制相關的軟體,程式說明如下
1. 燈光硬體抽象層(HAL):

有關LED HAL的定義在android_source\hardware\libhardware\include\hardware\lights.h的檔案中,在該檔案中可以看到定義struct light_state_t的資料結構,來儲存燈光的狀態。另一個資料結構struct light_device_t則定義set_light的函式,其完整的定如下:


struct light_device_t {
struct hw_device_t common;

/**
* Set the provided lights to the provided values.
*
* Returns: 0 on succes, error code on failure.
*/
int (*set_light)(struct light_device_t* dev,
struct light_state_t const* state);
};



2. 實作燈號系統函式庫(.so):在android_source\hardware\msm7k\liblights\目錄下可以看到lights.c及Android.mk兩個檔案,前者是實現硬體抽象層的系統檔案,而後者則提供編譯資訊。我們以處理追蹤球燈號為例,其原始程式如下:

static int
handle_trackball_light_locked(struct light_device_t* dev)
{
int mode = g_attention;

if (mode == 7 && g_backlight) {
mode = 0;
}
LOGV("%s g_backlight = %d, mode = %d, g_attention = %d\n",
__func__, g_backlight, mode, g_attention);

// If the value isn't changing, don't set it, because this
// can reset the timer on the breathing mode, which looks bad.
if (g_trackball == mode) {
return 0;
}

return write_int(TRACKBALL_FILE, mode);
}

上述程式中會呼叫write_int函式來和底層驅動溝通,利用open函式來開啟驅動並用write函式來進行控制。

static int
write_int(char const* path, int value)
{
int fd;
static int already_warned = 0;

fd = open(path, O_RDWR);
if (fd >= 0) {
char buffer[20];
int bytes = sprintf(buffer, "%d\n", value);
int amt = write(fd, buffer, bytes);
close(fd);
return amt == -1 ? -errno : 0;
} else {
if (already_warned == 0) {
LOGE("write_int failed to open %s\n", path);
already_warned = 1;
}
return -errno;
}
}


何時會點亮追蹤球燈號呢?當手機有通知事件要通知使用者時,我們可以用set_light_notifications函式來設定追蹤球燈號。

static int
set_light_notifications(struct light_device_t* dev,
struct light_state_t const* state)
{
pthread_mutex_lock(&g_lock);
g_notification = *state;
LOGV("set_light_notifications g_trackball=%d color=0x%08x",
g_trackball, state->color);
if (g_haveTrackballLight) {
handle_trackball_light_locked(dev);
}
handle_speaker_battery_locked(dev);
pthread_mutex_unlock(&g_lock);
return 0;
}

以上的程式是說明定義在硬體抽象層中的set_light如何和系統函式庫相互連結。

static int open_lights(const struct hw_module_t* module, char const* name,
struct hw_device_t** device)
{
int (*set_light)(struct light_device_t* dev,
struct light_state_t const* state);

if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) {
set_light = set_light_backlight;
}
else if (0 == strcmp(LIGHT_ID_KEYBOARD, name)) {
set_light = set_light_keyboard;
}
else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) {
set_light = set_light_buttons;
}
else if (0 == strcmp(LIGHT_ID_BATTERY, name)) {
set_light = set_light_battery;
}
else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {
set_light = set_light_notifications;
}
else if (0 == strcmp(LIGHT_ID_ATTENTION, name)) {
set_light = set_light_attention;
}
else {
return -EINVAL;
}

pthread_once(&g_init, init_globals);

struct light_device_t *dev = malloc(sizeof(struct light_device_t));
memset(dev, 0, sizeof(*dev));

dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (struct hw_module_t*)module;
dev->common.close = (int (*)(struct hw_device_t*))close_lights;
dev->set_light = set_light;

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

3. Java/C JNI介面函式:在android_source\frameworks\base\services\jni目錄下,可以找到com_android_server_HardwareService.cpp檔案,您可以發現JNINativeMethod method_table陣列及register_android_server_HardwareService函式,利用此兩項資訊來註冊硬體相關服務。

static JNINativeMethod method_table[] = {
{ "init_native", "()I", (void*)init_native },
{ "finalize_native", "(I)V", (void*)finalize_native },
{ "setLight_native", "(IIIIIII)V", (void*)setLight_native },
{ "vibratorOn", "(J)V", (void*)vibratorOn },
{ "vibratorOff", "()V", (void*)vibratorOff }
};

int register_android_server_HardwareService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/HardwareService",
method_table, NELEM(method_table));
}

在上述資訊中,我們可以看到setLight_native函式,其程式列表如下:

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

if (light < 0 || light >= LIGHT_COUNT || devices->lights[light] == NULL) {
return ;
}

memset(&state, 0, sizeof(light_state_t));
state.color = colorARGB;
state.flashMode = flashMode;
state.flashOnMS = onMS;
state.flashOffMS = offMS;
state.brightnessMode = brightnessMode;

devices->lights[light]->set_light(devices->lights[light], &state);
}

上述的set_light就定義在硬體抽象層中。

4. Java服務軟體實作:在android_source\frameworks\base\services\java\com\android\server目錄下,HardwareService.java實作硬體服務的程式,該程式定義HardwareService並繼承IHardwareService.Stub介面,在該程式中可以呼叫setLight_native函式來連結JNI層的軟體。

public class HardwareService extends IHardwareService.Stub {
private static final String TAG = "HardwareService";
:
:
private static native int init_native();
private static native void finalize_native(int ptr);

private static native void setLight_native(int ptr, int light, int color, int mode,
int onMS, int offMS, int brightnessMode);

}

以上簡略說明從底層硬體抽象層、系統函式庫層、JNI介面層、及Java服務層,相信您對如何在Google Android平台中控制硬體已有初步的瞭解。