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平台中控制硬體已有初步的瞭解。

2010年7月21日 星期三

實作IPC COM控制程式


以下程式用於DMA-6410L 開發平台,其步驟如下:
1. 新增新的activity元件。
2. 修改activityMain.java,更換執行元件。
3. 新增AIDL檔案。
4. 新增COM Service程式。
5. 修改activity程式。
6. 修改Manifest檔案。
7. 安裝及測試














UartIPCactivity.java程式列表:

package tw.com.dmatek.dma6410xp.ipc;

import java.util.Timer;
import java.util.TimerTask;

import tw.com.dmatek.dma6410xp.R;
import tw.com.dmatek.dma6410xp.uart.Linuxc;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;

public class UartIPCactivity extends Activity {
TextView view1,view2,view;

Spinner spinner1;

String receiveMsg;
String Msg,str="liwei",ReStr;
EditText editmsg;
Button myButton0,myButton1,myButton2,myButton3,myButton4;
public int num=1;
public int fd,set,i=-1,ss=0;
int p;
private static final String[] countriesStr =
{ "B1200", "B2400", "B4800", "B9600","B19200","B38400","B57600","B115200","B230400","B921600"};
private Spinner mySpinner;
private ArrayAdapter adapter;
private IUartService uartService=null;

private Button.OnClickListener myButton0_listener=new Button.OnClickListener()
{
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub

try {
if(uartService.openUart(1)>=0)
setTitle("ipc open device sucess!");
else
setTitle("ipc open device false!");
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
view1.setText("The current COM1");
}

};


private Button.OnClickListener myButton1_listener=new Button.OnClickListener()
{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
try {
if(uartService.openUart(2)>=0)
setTitle("ipc open device sucess!");
else
setTitle("ipc open device false!");
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
view1.setText("The current COM2");
}
};

private Button.OnClickListener myButton2_listener=new Button.OnClickListener()
{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
view.setText(null);

}
};

private Button.OnClickListener myButton3_listener=new Button.OnClickListener()
{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
// timer.cancel();
try {
uartService.closeUart(0);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finish();
}
};

private Button.OnClickListener myButton4_listener=new Button.OnClickListener()
{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Msg=editmsg.getText().toString();
try {
uartService.sendMsgUart(Msg);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};

private ServiceConnection mConnection = new ServiceConnection(){

@Override
public void onServiceConnected(ComponentName arg0, IBinder arg1) {
// TODO Auto-generated method stub
uartService = IUartService.Stub.asInterface(arg1);
}

@Override
public void onServiceDisconnected(ComponentName arg0) {
// TODO Auto-generated method stub
uartService = null;

}

};

Timer timer = new Timer();

TimerTask task = new TimerTask() {
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (uartService!=null) {

try {
ReStr=uartService.receiveMsgUart();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(ReStr!=null)
{

view.append(ReStr);
view.append("\n");
ReStr=null;
}

}

}
});
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.uart);
timer.schedule(task, 1000, 1000);
setTitle("IPC Testing");
bindService(new Intent(UartIPCactivity.this, UartService.class), mConnection , Context.BIND_AUTO_CREATE);

view1=(TextView)findViewById(R.id.uart_textview);
view=(TextView)findViewById(R.id.uart_view);
editmsg=(EditText)findViewById(R.id.uart_edit);
editmsg.setWidth(200);
mySpinner=(Spinner)findViewById(R.id.uart_mode);
myButton0=(Button)findViewById(R.id.uart_button0);
myButton1=(Button)findViewById(R.id.uart_button1);
myButton2=(Button)findViewById(R.id.uart_button2);
myButton3=(Button)findViewById(R.id.uart_button3);
myButton4=(Button)findViewById(R.id.uart_button4);

myButton0.setOnClickListener(myButton0_listener);
myButton1.setOnClickListener(myButton1_listener);
myButton2.setOnClickListener(myButton2_listener);
myButton3.setOnClickListener(myButton3_listener);
myButton4.setOnClickListener(myButton4_listener);

adapter = new ArrayAdapter(this,
android.R.layout.simple_spinner_item, countriesStr);

adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);


mySpinner.setAdapter(adapter);

mySpinner.setOnItemSelectedListener(new Spinner.OnItemSelectedListener()
{

@Override
public void onItemSelected(AdapterView arg0, View arg1, int arg2,long arg3)
{
try {
if(uartService!=null) uartService.setUart(arg2);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
arg0.setVisibility(View.VISIBLE);
}

@Override
public void onNothingSelected(AdapterView arg0)
{
// TODO Auto-generated method stub
}

});
}

}

UartService.java程式列表:

package tw.com.dmatek.dma6410xp.ipc;

import tw.com.dmatek.dma6410xp.uart.Linuxc;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class UartService extends Service {
int fd = -1;
int ss = 3;

private final IUartService.Stub mBinder = new IUartService.Stub() {

@Override
public int setUart(int buad) throws RemoteException {
// TODO Auto-generated method stub
ss=buad;
return Linuxc.setUart(ss);
}

@Override
public int openUart(int port) throws RemoteException {
// TODO Auto-generated method stub
if(fd>=0) Linuxc.closeUart(0);
fd=Linuxc.openUart(port);

if(fd>=0)
{
Linuxc.setUart(ss);
}

return fd;
}

@Override
public void closeUart(int port) throws RemoteException {
// TODO Auto-generated method stub
Linuxc.closeUart(port);
}

@Override
public int sendMsgUart(String message) throws RemoteException {
// TODO Auto-generated method stub
return Linuxc.sendMsgUart(message);

}

@Override
public String receiveMsgUart() throws RemoteException {
// TODO Auto-generated method stub
return Linuxc.receiveMsgUart();
}
};
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return mBinder;
}

}

IUartService.aidl程式列表如下:

package tw.com.dmatek.dma6410xp.ipc;

interface IUartService{
int openUart(int buad);
void closeUart(int port);
int setUart(int port);
int sendMsgUart(String message);
String receiveMsgUart();
}

實作IPC LED控制程式


以下程式用於DMA-6410L 開發平台,其步驟如下:
1.新增新的activity元件。
2.修改activityMain.java,更換執行元件。
3.新增AIDL檔案。
4. 新增LED Service程式。
5. 修改LED Activity程式。
6. 修改Manifest檔案。
7. 安裝與測試













LedIPCactivity.java程式列表

package tw.com.dmatek.dma6410xp.ipc;

import tw.com.dmatek.dma6410xp.R;
import tw.com.dmatek.dma6410xp.led.Linuxc;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;

public class LedIPCactivity extends Activity {
/** Called when the activity is first created. */

private Button mButton1;
private Button mButton2;
private Button mButton3;
private Button mButton4;
private Button mButton5;

public int state1=0,state2=0;
public int led_on = 1;
public int led_off = 2;
public int fd = 0;


private Button.OnClickListener button1_listener= new Button.OnClickListener()
{
public void onClick(View v)
{



//Linuxc.send(2, led_on);
try {
ledService.TurnOn(2);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


}
};

private Button.OnClickListener button4_listener= new Button.OnClickListener()
{
public void onClick(View v)
{


// Linuxc.send(2, led_off);
try {
ledService.TurnOff(2);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
};

private Button.OnClickListener button5_listener= new Button.OnClickListener()
{
public void onClick(View v)
{


//Linuxc.send(1, led_off);
try {
ledService.TurnOff(1);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};



private Button.OnClickListener button2_listener= new Button.OnClickListener(){
public void onClick(View v)
{

//Linuxc.send(1, led_on);
try {
ledService.TurnOn(1);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
};
private Button.OnClickListener button3_listener= new Button.OnClickListener(){
public void onClick(View v)
{

finish();
}
};
private ILedService ledService=null;
private ServiceConnection mConnection = new ServiceConnection(){

@Override
public void onServiceConnected(ComponentName arg0, IBinder arg1) {
// TODO Auto-generated method stub
ledService=ILedService.Stub.asInterface(arg1);
}

@Override
public void onServiceDisconnected(ComponentName arg0) {
// TODO Auto-generated method stub

}

};

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.led);

mButton1=(Button)findViewById(R.id.led_myButton1);
mButton2 =(Button) findViewById(R.id.led_myButton2);
mButton3 =(Button) findViewById(R.id.led_myButton3);
mButton4 =(Button) findViewById(R.id.led_myButton1_1);
mButton5 =(Button) findViewById(R.id.led_myButton2_1);


bindService(new Intent(LedIPCactivity.this, LedService.class), mConnection , Context.BIND_AUTO_CREATE);


mButton1.setOnClickListener(button1_listener);

mButton2.setOnClickListener(button2_listener);


mButton3.setOnClickListener(button3_listener);
mButton4.setOnClickListener(button4_listener);
mButton5.setOnClickListener(button5_listener);

}
}

LedService.java列表

package tw.com.dmatek.dma6410xp.ipc;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import tw.com.dmatek.dma6410xp.led.Linuxc;;

public class LedService extends Service {
public int led_on = 1;
public int led_off = 2;
public int fd = -1;
private ILedService.Stub mbinder=new ILedService.Stub(){

@Override
public void TurnOff(int port) throws RemoteException {
// TODO Auto-generated method stub
if(fd>=0) Linuxc.send(port, led_off);
}

@Override
public void TurnOn(int port) throws RemoteException {
// TODO Auto-generated method stub
if(fd>=0) Linuxc.send(port, led_on);
}

};
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
fd=Linuxc.openled();
return mbinder;
}
@Override
public boolean onUnbind(Intent intent) {
// TODO Auto-generated method stub
Linuxc.closeled();
return super.onUnbind(intent);

}

}

2010年7月20日 星期二

虛擬Android Market

Android Market可以使用在模擬器上了,教學網址連結在下面有興趣可以試試看喔。

2010年7月9日 星期五

優化Eclipse 開發工具

一般我們進行Android程式的開發所使用的Eclipse工具,
如果使用在多核心的系統上,可以在eclipse.ini中加入如下的一行讓Eclipse支援雙核心。
-XX:+UseParallelGC