1. 程式人生 > >android 系統從驅動到 app 新增一個系統服務

android 系統從驅動到 app 新增一個系統服務

注:整理自某個pdf

android系統從下至上分層:

kernel → HAL -->routime(虛擬機器和一些其他的庫)-->framework-->app

我們要新增的服務為 LedService
app 中直接可以這樣使用
LedManager ledManage = getSystemService(Context.LED_SERVICE);
ledManager.setOn();//
設定手機燈的開關
ledManager.setOff();
要達到這個上面的目的,需要在系統的 Context.java ContextImpl.java 裡面做如下工

Context.java

public static final String LED_SERVICE = "led";//新增該行

contextImpl.java 裡面新增如下程式碼:

public Object getSystemService(String name) {
if (WINDOW_SERVICE.equals(name)) {
return WindowManagerImpl.getDefault();
} else if (LAYOUT_INFLATER_SERVICE.equals(name)) {
synchronized (mSync) {
LayoutInflater inflater = mLayoutInflater;
if (inflater != null) {
return inflater;
}
mLayoutInflater = inflater =
PolicyManager.makeNewLayoutInflater(getOuterContext());
return inflater;
}
} else if (ACTIVITY_SERVICE.equals(name)) {
return getActivityManager();
} else if (INPUT_METHOD_SERVICE.equals(name)) {
return InputMethodManager.getInstance(this);
} else if (ALARM_SERVICE.equals(name)) {
return getAlarmManager();
} else if (ACCOUNT_SERVICE.equals(name)) {
return getAccountManager();
} else if (POWER_SERVICE.equals(name)) {
return getPowerManager();
} else if (CONNECTIVITY_SERVICE.equals(name)) {
return getConnectivityManager();
} else if (THROTTLE_SERVICE.equals(name)) {
return getThrottleManager();
} else if (WIFI_SERVICE.equals(name)) {
return getWifiManager();
} else if (NOTIFICATION_SERVICE.equals(name)) {
return getNotificationManager();
} else if (KEYGUARD_SERVICE.equals(name)) {
return new KeyguardManager();
} else if (ACCESSIBILITY_SERVICE.equals(name)) {
return AccessibilityManager.getInstance(this);
} else if (LOCATION_SERVICE.equals(name)) {
return getLocationManager();
} else if (SEARCH_SERVICE.equals(name)) {
return getSearchManager();
} else if (SENSOR_SERVICE.equals(name)) {
return getSensorManager();} else if (STORAGE_SERVICE.equals(name)) {
return getStorageManager();
} else if (VIBRATOR_SERVICE.equals(name)) {
return getVibrator();
} else if (STATUS_BAR_SERVICE.equals(name)) {
synchronized (mSync) {
if (mStatusBarManager == null) {
mStatusBarManager = new StatusBarManager(getOuterContext());
}
return mStatusBarManager;
}
} else if (AUDIO_SERVICE.equals(name)) {
return getAudioManager();
} else if (TELEPHONY_SERVICE.equals(name)) {
return getTelephonyManager();
} else if (CLIPBOARD_SERVICE.equals(name)) {
return getClipboardManager();
} else if (WALLPAPER_SERVICE.equals(name)) {
return getWallpaperManager();
} else if (DROPBOX_SERVICE.equals(name)) {
return getDropBoxManager();
} else if (DEVICE_POLICY_SERVICE.equals(name)) {
return getDevicePolicyManager();
} else if (UI_MODE_SERVICE.equals(name)) {
return getUiModeManager();
} else if (DOWNLOAD_SERVICE.equals(name)) {
return getDownloadManager();
} else if (NFC_SERVICE.equals(name)) {
return getNfcManager();
}else if(LED_SERVICE.equals(name))
{
Log.d("ContextImpl", "get LedManager success");
return getLedManager();
}
return null; } private LedManager getLedManager() { synchronized (mSync) { if (mLedManager == null) { mLedManager = new LedManager(); } } return mLedManager; }
其中 LedManager 類的定義如下:
package android.app;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
public class LedManager
{
private static final String TAG = "LedManager";
private ILedManager mLedService;public LedManager() {
mLedService =
ILedManager.Stub.asInterface(ServiceManager.getService("led"));
if (mLedService != null) {
Log.i(TAG, "The LedManager object is ready.");
}
}
public boolean setOn(int n) {
boolean result = false;
try {
result = mLedService.setOn(n);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in LedManager.LedOn:", e);
}
return result;
}
public boolean setOff(int n) {
boolean result = false;
try {
result = mLedService.setOff(n);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in LedManager.LedOff:", e);
}
return result;
}
}
由於我們用用LedManager取得一個系統服務LedService,所以此處就用到了一個IPC機制,通過aidl
兩個程序之間進行通訊,這樣當操作
LedManager的時候就如同操作LedService了,在這裡這個aidl檔案
如下:



IledManager.aidl
package android.app;
interface ILedManager
{
boolean setOn(int led);
boolean setOff(int led);
}

frameworks/base/Android.mk 檔案增加:

LOCAL_SRC_FILES += \
core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
core/java/android/accessibilityservice/IEventListener.aidl \
core/java/android/accounts/IAccountManager.aidl \
core/java/android/accounts/IAccountManagerResponse.aidl \
core/java/android/accounts/IAccountAuthenticator.aidl \
core/java/android/accounts/IAccountAuthenticatorResponse.aidl \
core/java/android/app/IActivityController.aidl \
core/java/android/app/IActivityPendingResult.aidl \
core/java/android/app/IActivityWatcher.aidl \
core/java/android/app/ILedManager.aidl \
core/java/android/app/IAlarmManager.aidl \
core/java/android/app/IBackupAgent.aidl \
core/java/android/app/IInstrumentationWatcher.aidl \
core/java/android/app/INotificationManager.aidl \
core/java/android/app/ISearchManager.aidl \core/java/android/app/ISearchManagerCallback.aidl \
core/java/android/app/IServiceConnection.aidl \
core/java/android/app/IThumbnailReceiver.aidl \
core/java/android/app/ITransientNotification.aidl \
core/java/android/app/IUiModeManager.aidl \
frameworks/base/services/java/com/android/server/LedService.java的內容如下
import android.content.Context;
import android.os.ServiceManager;
import android.util.Log;
import android.os.IBinder;
import android.app.ILedManager;
public final class LedService extends ILedManager.Stub {
public LedService(Context context) {
Log.i("LedService", "Go to get LED Stub...");
ServiceManager.addService("led", LedService.this);
_init();
}
/*
* Mokoid LED native methods.
*/
public boolean setOn(int led) {
Log.i("MokoidPlatform", "LED On");
return _setOn(led);
}
public boolean setOff(int led) {
Log.i("MokoidPlatform", "LED Off");
return _setOff(led);
}private static native boolean _init();
private static native boolean _setOn(int led);
private static native boolean _setOff(int led);
}

到此為止 我們就可以使用LedManager來使用LedService的方法了
由於
LedManager是在我們的app裡面,是屬於我們程式的程序的,但是LedService是系統啟動的時候就啟
動的服務程式,是屬於
systemServer程序,不同的程序是不能通訊的,android採用了aidl的方式,底層
是採用
binder驅動,通過共享記憶體來達到通訊目的的。
ledService.java裡面有幾個native方法,這幾個方法在java層呼叫,但是實現是在c++層的,這裡是
通過
jni呼叫來達到java程式碼呼叫c++程式碼的
這裡的
jni檔案如下
frameworks/base/services/jni/com_android_server_LedServic
e.cpp
這個檔名不能隨便命名,要遵循 包名_類名.cpp的原則
#define LOG_TAG "Dragon"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <hardware/led.h>
#define LOG_NDDEBUG 0 // 定義後,LOGD能夠輸出
#define LOG_NIDEBUG 0 // 定義後,LOGI能夠輸出
#define LOG_NDEBUG 0 // 定義後,LOGV能夠輸出
namespace android
{
struct led_control_device_t *sLedDevice = NULL;
static jboolean setOn(JNIEnv * env ,jobject clazz,jint led)
{
LOGI("LedService JNI: setOn() is invoked");
if(sLedDevice == NULL)
{
return -1;
}else
{
return sLedDevice->set_led_on(sLedDevice,led);
}
}
static jboolean setOff(JNIEnv * env ,jobject clazz,jint led)
{
LOGI("LedService JNI: setOff() is invoked.");
if (sLedDevice == NULL) {LOGI("LedService JNI: sLedDevice was not fetched correctly.");
return -1;
} else {
return sLedDevice->set_led_off(sLedDevice, led);
}
}
/** helper APIs */
static inline int led_control_open(const struct hw_module_t* module,
struct led_control_device_t** device) {
return module->methods->open(module,
LED_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
}
static jboolean init(JNIEnv * evn ,jclass clazz)
{
led_module_t * module;
if(hw_get_module(LED_HARDWARE_MODULE_ID,(const hw_module_t **)&module) == 0)
{
LOGI("LedService JNI: LED Stub found.");
if (led_control_open(&module->common, &sLedDevice) == 0) {
LOGI("LedService JNI: Got Stub operations.");
return 0;
}
}
LOGE("LedService JNI: Get Stub operations failed.");
return -1;
}
//傳統的jni呼叫,函式名也是有一定規則的,但是android改變了這種方式,通過一下這種方式就可以把
java層和c++層的程式碼對映起來
//第一個引數是java層要呼叫的方法,第二個引數是第一個引數(也就是java層呼叫的函式)的引數和返回
,第三個引數是c++層要呼叫的函式
static const JNINativeMethod gMethods[] = {
{ "_init", "()Z", (void *)init },
{ "_setOn", "(I)Z", (void *)setOn },
{ "_setOff", "(I)Z", (void *)setOff },
};
//這個函式很重要,這裡是註冊我們上面定義的那些方法的
int register_android_server_LedService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/LedService",
gMethods, NELEM(gMethods));
}}
這裡要注意的是,
必須在下面這個函式裡面呼叫我們上面的那個註冊函式,不然編譯能過,最後手機也跑不起來

frameworks/base/services/jni/onload.cpp
using namespace android;
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved){
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("GetEnv failed!");
return result;
}
LOG_ASSERT(env, "Could not retrieve the env!");
register_android_server_PowerManagerService(env);
register_android_server_InputManager(env);
register_android_server_LightsService(env);
register_android_server_AlarmManagerService(env);
register_android_server_BatteryService(env);
register_android_server_VibratorService(env);
register_android_server_SystemServer(env);
register_android_server_location_GpsLocationProvider(env);
register_android_server_LedService(env);
//加上這句就好了
return JNI_VERSION_1_4;
}
到這裡我們就從java層徹底到了c/c++層了,在上面那個cpp檔案裡面的標頭檔案有個叫做
hardware/led.h的檔案,這個檔案的路徑是
hardware/libhardware/include/hardware/led.h
對應的led.c路徑是
hardware/msm7k/libdragon-led/led.c檔案
這兩個檔案就是屬於
androidHAL層了
HAL層主要是為了讓硬體廠商不許要公開起驅動原始碼
led.c 檔案的內容如下:
#define LOG_TAG "LedStub"
#include <hardware/hardware.h>
#include <fcntl.h>
#include <errno.h>
#include <cutils/log.h>
#include <cutils/atomic.h>
#include <hardware/led.h>
#define GPG3DAT2_ON 0x4800;
#define GPG3DAT2_OFF 0x4801;
#define LOG_NDDEBUG 0 // 定義後,LOGD 能夠輸出
#define LOG_NIDEBUG 0 // 定義後,LOGI 能夠輸出
#define LOG_NDEBUG 0 // 定義後,LOGV 能夠輸出
int fd;
int led_device_close(struct hw_device_t * device)
{
struct led_control_device_t * ctx = (struct led_control_device_t *)device;
if(ctx)
{
free(ctx);
}
close(fd);
return 0;
}
int led_on(struct led_control_device_t *dev ,int32_t led)
{
LOGI("LED Stub: set *d off.",led);
return 0;
}
int led_off(struct led_control_device_t *dev ,int32_t led)
{
char buf[32] ;
int n ;
LOGI("LED Stub: set %d off.",led);
//這裡就呼叫了led驅動程式
if((fd = open("/dev/led",O_RDWR))== -1)
{
LOGI("LED open error");
}
else
{
LOGI("LED open ok");
n = read(fd,buf,32);
LOGI("LED Stub: read %s data from led driver",buf);
close(fd);
}
return 0;
}
static int led_device_open(const struct hw_module_t * module,const char*
name ,struct hw_device_t ** device)
{
//下面是填充 led_control_device_t 結構體
struct led_control_device_t *dev;
dev = (struct led_control_device_t *)malloc(sizeof(*dev));
memset(dev,0,sizeof(*dev));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = module;
dev->common.close = led_device_close;
dev->set_led_on = led_on;
dev->set_led_off = led_off;
*device = &dev->common;
success:
return 0;
}
static struct hw_module_methods_t led_module_methods =
{
open: led_device_open
};
const struct led_module_t HAL_MODULE_INFO_SYM =
{
common:
{
tag:HARDWARE_MODULE_TAG,
version_major:1,
version_minor:0,
id:LED_HARDWARE_MODULE_ID,
name:"Sample LED Stud",
author:"Open Source Project",
methods:&led_module_methods,
}
}; 



下面是 led 驅動程式的實現程式碼 led_driver.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_from/to_user */
#define DRIVER_AUTHOR "xxxxx"
#define DRIVER_DESC "HELLO WORLD DRIVER"
#define DEVICE_NAME "led"
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
int led_open(struct inode *inode, struct file *filp);
int led_release(struct inode *inode, struct file*filp);
ssize_t led_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
ssize_t led_write(struct file *filp, __user char *buf,size_t count, loff_t *f_pos);
void led_exit(void);
int led_init(void);
struct file_operations led_fops = {
read: led_read,
write: led_write,
open: led_open,
release: led_release
};
module_init(led_init);
module_exit(led_exit);
int led_major = 60;
char * led_buffer;
static struct class *led_class;
int led_init()
{
int result;
result = register_chrdev(led_major,DEVICE_NAME,&led_fops);
if(result<0)
{
printk("<1> : LED cannot obtain major number %d \n",led_major);
return result;
}
led_buffer = kmalloc(2,GFP_KERNEL);
if(!led_buffer)
{
result = -ENOMEM;
goto fail;
}
memset(led_buffer,0,2);
printk("<1>Inserting module\n");
//註冊一個類,使 mdev 可以在"/dev/"目錄下面建立裝置節點
led_class = class_create(THIS_MODULE, DEVICE_NAME);
if(IS_ERR(led_class))
{
printk("Err: failed in EmbedSky-leds class. \n");
return -1;
}
//建立一個裝置節點,節點名為 DEVICE_NAME
device_create(led_class, NULL, MKDEV(led_major, 0), NULL,
DEVICE_NAME);
//官方文件為 class_device_create(led_class, NULL, MKDEV(LED_MAJOR, 0),
NULL, DEVICE_NAME);
printk(DEVICE_NAME " initialized\n");
return result;
}
void led_exit()
{
unregister_chrdev(led_major,"");
if(led_buffer)
{
kfree(led_buffer);
}
printk("<1>LED Removing module \n");
}
//在 HAL 層呼叫 open()函式就相當與呼叫下面這個 led_open 函式
int led_open(struct inode *inode,struct file *filp)
{
printk("<1>Open device successfully\n");
return 0;
}
int led_release(struct inode * inode,struct file * filp)
{
printk("<1>Release device successfully\n");
return 0;
}
//在 HAL 層呼叫 read()函式就相當與呼叫下面這個 led_read 函式
ssize_t led_read(struct file *filp,char * buf,size_t count,loff_t * f_pos)
{
int n = 0;
n = copy_to_user(buf,led_buffer,sizeof(led_buffer)/sizeof(char);
printk("<1>read from user char is %s\n",led_buffer);
if(*f_pos ==0)
{
*f_pos += n;
return n;
}
else
{
return *f_pos + n;
}
}
ssize_t led_write(struct file *filp,char * buf,size_t count,loff_t * f_pos)
{
int n = 0;
n = copy_from_user(led_buffer,buf,sizeof(buf)/sizeof(char));
printk("<1>write from user char is %s\n",led_buffer);
return 1;
} 
通過以上步驟就完成了從 kernel 層到 app 層新增一個服務的功能,對於上面的驅動模組的
話,是通過單獨編譯成
.ko 檔案,然後 push 到手機裡面通過 insmod 動態載入到核心裡面
,要看核心 log 資訊可以通過 adb shell dmesg 檢視,上層 log 資訊通過 adb shell
logcat
檢視,當然驅動程式也可以放在 kernel/driver/新建 led 資料夾/led.c 下面直接編譯
kernel 裡面,這樣就可以每次啟動系統的時候自動執行驅動了,不過這樣的話需要修改
原始碼的配置檔案
 。