最近學習瞭如何在Android 6.0上添加一個系統服務,APP如何通過新增的系統服務訪問底層驅動。在這學習過程中,收穫頗多,並結合學習了《Embeded Android》--Karim Yaghmour 一書中的Appendix B. Adding Support For New Hardware ...
最近學習瞭如何在Android 6.0上添加一個系統服務,APP如何通過新增的系統服務訪問底層驅動。
在這學習過程中,收穫頗多,並結合學習了《Embeded Android》--Karim Yaghmour 一書中的
Appendix B. Adding Support For New Hardware章節,受益匪淺,講述瞭如何添加一個完整的系統
服務(app->framework->kernel)。儘管描述的非常詳細,但是基於Android 2.3.7描述的。現在把
書中的opersys例子移植到Android 6.0上,雖然說不上複雜,但由於版本差異,難免會出現許多奇奇
怪怪的問題,甚至版本差異造成了bug出現。特以此移植記錄分享學習過程。
主要圍繞以下幾個步驟添加一個完整的系統服務:
(A) 添加circular-char驅動
(B) 添加opersyshw_qemu HAL
(C) 添加com_android_server_opersys_OpersysService JNI
(D) 添加IOpersysService介面
(E) 添加OpersysService
(F) 添加OpersysManager
(G) 添加系統服務
(H) 註冊服務
(I) 更新API
(J) 設置許可權
(K) 測試服務
(L) 添加測試APP
(A) 添加circular-char驅動
circular-char是一個簡單的字元設備驅動,其實現的功能就是一個簡單的FIFO,APP可以通過
read、write來進行讀寫操作實驗,即寫數據到FIFO,可以從FIFO讀出寫入的數據。
kernel/drivers/circular-driver/circular-char.c
1 #include <linux/module.h> 2 #include <linux/miscdevice.h> 3 #include <linux/fs.h> 4 #include <asm/uaccess.h> 5 6 #define BUF_SIZE 200 7 8 static char buf[BUF_SIZE]; 9 static char *read_ptr; 10 static char *write_ptr; 11 12 static int device_open(struct inode *inode, struct file *file) 13 { 14 printk("device_open called \n"); 15 16 return 0; 17 } 18 19 static int device_release(struct inode *inode, struct file *file) 20 { 21 printk("device_release called \n"); 22 23 return 0; 24 } 25 26 static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */ 27 char *buffer, /* buffer to fill with data */ 28 size_t length, /* length of the buffer */ 29 loff_t * offset) 30 { 31 int chars_read = 0; 32 33 printk("device_read called \n"); 34 35 while(length && *read_ptr && (read_ptr != write_ptr)) { 36 put_user(*(read_ptr++), buffer++); 37 38 printk("Reading %c \n", *read_ptr); 39 40 if(read_ptr >= buf + BUF_SIZE) 41 read_ptr = buf; 42 43 chars_read++; 44 length--; 45 } 46 47 return chars_read; 48 } 49 50 static ssize_t 51 device_write(struct file *filp, const char *buff, size_t len, loff_t * off) 52 { 53 int i; 54 55 printk("device_write called \n"); 56 57 for(i = 0; i < len; i++) { 58 get_user(*write_ptr, buff++); 59 printk("Writing %c \n", *write_ptr); 60 write_ptr++; 61 if (write_ptr >= buf + BUF_SIZE) 62 write_ptr = buf; 63 } 64 65 return len; 66 } 67 68 static struct file_operations fops = { 69 .open = device_open, 70 .release = device_release, 71 .read = device_read, 72 .write = device_write, 73 }; 74 75 static struct miscdevice circ_char_misc = { 76 .minor = MISC_DYNAMIC_MINOR, 77 .name = "circchar", 78 .fops = &fops, 79 }; 80 81 int circ_char_enter(void) 82 { 83 int retval; 84 85 retval = misc_register(&circ_char_misc); 86 printk("CIRC Driver got retval %d\n", retval); 87 printk("mmap is %08X\n", (int) fops.mmap); 88 89 read_ptr = buf; 90 write_ptr = buf; 91 92 return 0; 93 } 94 95 void circ_char_exit(void) 96 { 97 misc_deregister(&circ_char_misc); 98 } 99 100 module_init(circ_char_enter); 101 module_exit(circ_char_exit);
kernel/drivers/circular-driver/Kconfig
1 menuconfig DRIVER_FOR_TEST 2 bool "Drivers for test" 3 help 4 Drivers for test. 5 If unsure, say no. 6 7 if DRIVER_FOR_TEST 8 9 config CIRCULAR_CHAR 10 tristate "circular-char" 11 help 12 circular-char driver. 13 14 endif
kernel/drivers/circular-driver/Makefile
1 obj-$(CONFIG_CIRCULAR_CHAR) += circular-char.o
kernel/drivers/Kconfig
1 ...... 2 source "drivers/circular-driver/Kconfig" 3 ......
kernel/drivers/Makefile
1 ...... 2 obj-$(CONFIG_DRIVER_FOR_TEST) += circular-driver/ 3 ......
kernel/arch/arm/configs/xxx_defconfig
...... CONFIG_DRIVER_FOR_TEST=y CONFIG_CIRCULAR_CHAR=y ......
驅動已添加到內核,編譯燒錄到目標板看是否載入成功:
# ls dev/circchar ls dev/circchar dev/circchar #echo hello > dev/circchar echo hello > dev/circchar #cat dev/circchar dev/circchar hello
如果執行以上命令,輸出對應得信息,則說明驅動載入成功。
(B) 添加opersyshw_qemu HAL
這裡添加一個opersys的HAL層,使應用和驅動分離,hal主要嚮應用提供open、read、write等幾個
介面。
hardware/libhardware/tests/opersyshw/opersyshw_qemu.c
1 #define LOG_TAG "opersyshw_qemu" 2 #include <cutils/log.h> 3 #include <cutils/sockets.h> 4 #include <sys/types.h> 5 #include <sys/stat.h> 6 #include <fcntl.h> 7 #include <hardware/opersyshw.h> 8 #include <malloc.h> 9 10 #define OPERSYSHW_DEBUG 1 11 12 #if OPERSYSHW_DEBUG 13 # define D(...) ALOGD(__VA_ARGS__) 14 #else 15 # define D(...) ((void)0) 16 #endif 17 18 static int fd = 0; 19 20 static int opersyshw__read(char* buffer, int length) 21 { 22 int retval; 23 24 D("OPERSYS HW - read()for %d bytes called", length); 25 26 retval = read(fd, buffer, length); 27 D("read data from driver: %s", buffer); 28 29 return retval; 30 } 31 32 static int opersyshw__write(char* buffer, int length) 33 { 34 int retval; 35 36 D("OPERSYS HW - write()for %d bytes called", length); 37 38 retval = write(fd, buffer, length); 39 D("write data to driver: %s", buffer); 40 41 return retval; 42 } 43 44 static int opersyshw__close(void) 45 { 46 if (fd != -1) { 47 if (!close(fd)) { 48 return 0; 49 } 50 } 51 52 return -1; 53 } 54 55 static int opersyshw__test(int value) 56 { 57 return value; 58 } 59 60 static int open_opersyshw(const struct hw_module_t* module, char const* name, 61 struct hw_device_t** device) 62 { 63 struct opersyshw_device_t *dev = malloc(sizeof(struct opersyshw_device_t)); 64 if (!dev) { 65 D("OPERSYS HW failed to malloc memory !!!"); 66 return -1; 67 } 68 69 memset(dev, 0, sizeof(*dev)); 70 71 dev->common.tag = HARDWARE_DEVICE_TAG; 72 dev->common.version = 0; 73 dev->common.module = (struct hw_module_t*)module; 74 dev->read = opersyshw__read; 75 dev->write = opersyshw__write; 76 dev->close = opersyshw__close; 77 dev->test = opersyshw__test; 78 79 *device = (struct hw_device_t*) dev; 80 81 fd = open("/dev/circchar", O_RDWR); 82 if (fd < 0) { 83 D("failed to open /dev/circchar!"); 84 return 0; 85 } 86 87 D("OPERSYS HW has been initialized"); 88 89 return 0; 90 } 91 92 static struct hw_module_methods_t opersyshw_module_methods = { 93 .open = open_opersyshw, 94 }; 95 96 struct hw_module_t HAL_MODULE_INFO_SYM = { 97 .tag = HARDWARE_MODULE_TAG, 98 .version_major = 1, 99 .version_minor = 0, 100 .id = OPERSYSHW_HARDWARE_MODULE_ID, 101 .name = "Opersys HW Module", 102 .author = "Opersys inc.", 103 .methods = &opersyshw_module_methods, 104 };
hardware/libhardware/include/hardware/opersyshw.h
1 #ifndef ANDROID_OPERSYSHW_INTERFACE_H 2 #define ANDROID_OPERSYSHW_INTERFACE_H 3 4 #include <stdint.h> 5 #include <sys/cdefs.h> 6 #include <sys/types.h> 7 8 #include <hardware/hardware.h> 9 10 __BEGIN_DECLS 11 12 #define OPERSYSHW_HARDWARE_MODULE_ID "opersyshw" 13 14 struct opersyshw_device_t { 15 struct hw_device_t common; 16 17 int (*read)(char* buffer, int length); 18 int (*write)(char* buffer, int length); 19 int (*close)(void); 20 int (*test)(int value); 21 }; 22 23 __END_DECLS 24 25 #endif // ANDROID_OPERSYSHW_INTERFACE_H
hardware/libhardware/tests/opersyshw/Android.mk
1 LOCAL_PATH := $(call my-dir) 2 3 # HAL module implemenation, not prelinked and stored in 4 # hw/<GPS_HARDWARE_MODULE_ID>.<ro.hardware>.so 5 include $(CLEAR_VARS) 6 LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw 7 LOCAL_CFLAGS += $(common_flags) 8 LOCAL_LDLIBS += -llog 9 LOCAL_C_INCLUDES := hardware/libhardware 10 LOCAL_SHARED_LIBRARIES := liblog libcutils libhardware 11 LOCAL_SRC_FILES := opersyshw_qemu.c 12 LOCAL_MODULE := opersyshw.$(TARGET_BOARD_PLATFORM) 13 LOCAL_MODULE_TAGS := optional 14 include $(BUILD_SHARED_LIBRARY)
編譯之後看看是否錯誤,是否生成.so文件,在源碼根目錄下:
# find ./out/ -name 'opersyshw.*.so' ...... ./out/target/product/<project>/system/lib/hw/opersyshw.sc8830.so ......
註意Android.mk中的$(TARGET_BOARD_PLATFORM),這裡是sc8830,不同的平臺會有差異
(C) 添加com_android_server_opersys_OpersysService JNI
JNI介面主要是為了Java(app)調用C/C++。
frameworks/base/services/core/jni/com_android_server_opersys_OpersysService.cpp
1 #define LOG_TAG "OpersysServiceJNI" 2 3 #include "jni.h" 4 #include "JNIHelp.h" 5 #include "android_runtime/AndroidRuntime.h" 6 7 #include <utils/misc.h> 8 #include <utils/Log.h> 9 #include <hardware/hardware.h> 10 #include <hardware/opersyshw.h> 11 12 #include <stdio.h> 13 14 namespace android 15 { 16 17 opersyshw_device_t* opersyshw_dev; 18 19 static jint init_native(JNIEnv *env, jobject /* clazz */) 20 { 21 int err; 22 hw_module_t* module; 23 opersyshw_device_t* dev = NULL; 24 25 //ALOGI("init_native()"); 26 27 err = hw_get_module(OPERSYSHW_HARDWARE_MODULE_ID, (hw_module_t const**)&module); 28 if (err == 0) { 29 if (module->methods->open(module, "", ((hw_device_t**) &dev)) != 0) { 30 ALOGE("Can't open opersys module!!!"); 31 return 0; 32 } 33 } else { 34 ALOGE("Can't get opersys module!!!"); 35 return 0; 36 } 37 38 return (jint)dev; 39 } 40 41 static void finalize_native(JNIEnv *env, jobject /* clazz */, int ptr) 42 { 43 opersyshw_device_t* dev = (opersyshw_device_t*)ptr; 44 45 //ALOGI("finalize_native()"); 46 47 if (dev == NULL) { 48 return; 49 } 50 51 dev->close(); 52 53 free(dev); 54 } 55 56 static int read_native(JNIEnv *env, jobject /* clazz */, int ptr, jbyteArray buffer) 57 { 58 opersyshw_device_t* dev = (opersyshw_device_t*)ptr; 59 jbyte* real_byte_array; 60 int length; 61 62 //ALOGI("read_native()"); 63 64 real_byte_array = env->GetByteArrayElements(buffer, NULL); 65 66 if (dev == NULL) { 67 return 0; 68 } 69 70 length = dev->read((char*) real_byte_array, env->GetArrayLength(buffer)); 71 72 ALOGI("read data from hal: %s", (char *)real_byte_array); 73 74 env->ReleaseByteArrayElements(buffer, real_byte_array, 0); 75 76 return length; 77 } 78 79 static int write_native(JNIEnv *env, jobject /* clazz */, int ptr, jbyteArray buffer) 80 { 81 opersyshw_device_t* dev = (opersyshw_device_t*)ptr; 82 jbyte* real_byte_array; 83 int length; 84 85 //ALOGI("write_native()"); 86 87 real_byte_array = env->GetByteArrayElements(buffer, NULL); 88 89 if (dev == NULL) { 90 return 0; 91 } 92 93 length = dev->write((char*) real_byte_array, env->GetArrayLength(buffer)); 94 95 ALOGI("write data to hal: %s", (char *)real_byte_array); 96 97 env->ReleaseByteArrayElements(buffer, real_byte_array, 0); 98 99 return length; 100 } 101 102 103 static int test_native(JNIEnv *env, jobject /* clazz */, int ptr, int value) 104 { 105 opersyshw_device_t* dev = (opersyshw_device_t*)ptr; 106 107 if (dev == NULL) { 108 return 0; 109 } 110 111 ALOGI("test_native()"); 112 113 return dev->test(value); 114 } 115 116 static JNINativeMethod method_table[] = { 117 { "init_native", "()I", (void*)init_native }, 118 { "finalize_native", "(I)V", (void*)finalize_native }, 119 { "read_native", "(I[B)I", (void*)read_native }, 120 { "write_native", "(I[B)I", (void*)write_native }, 121 { "test_native", "(II)I", (void*)test_native} 122 }; 123 124 int register_android_server_opersys_OpersysService(JNIEnv *env) 125 { 126 return jniRegisterNativeMethods(env, "com/android/server/opersys/OpersysService", 127 method_table, NELEM(method_table)); 128 129 };
frameworks/base/services/core/jni/onload.cpp
1 ...... 2 namespace android { 3 ...... 4 int register_android_server_opersys_OpersysService(JNIEnv* env); 5 ...... 6 }; 7 8 .... 9 10 extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) 11 { 12 ...... 13 register_android_server_opersys_OpersysService(env); 14 ...... 15 }
frameworks/base/services/core/jni/Android.mk
1 ...... 2 LOCAL_SRC_FILES += \ 3 ...... 4 $(LOCAL_REL_DIR)/com_android_server_opersys_OpersysService.cpp \ 5 ......
(D) 添加IOpersysService介面
IOpersysService主要用於實現一個進程間通信的介面,其內部機制就是通過Binder實現進程間通信的,
即客戶端與服務端(OpersysService)分別處於不同的進程中,客戶端和服務端之間不能夠直接相互訪問,
之間必須通過Binder傳遞。
frameworks/base/core/java/android/opersys/IOpersysService.aidl
1 interface IOpersysService { 2 /** 3 * {@hide} 4 */ 5 String read(int maxLength); 6 int write(String mString); 7 }
frameworks/base/Android.mk
1 ...... 2 LOCAL_SRC_FILES += \ 3 ...... 4 core/java/android/opersys/IOpersysService.aidl \ 5 ......
其中,aidl文件主要用於生成同名的.java文件IOpersysService.java,IOpersysService.java主要實現
了一些Binder相關的設置和相關介面。
編譯後,會在out目錄下生成:
out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/opersys/IOpersysService.java
(E) 添加OpersysService
OpersysService主要充當一個服務端(server),直接調用native如:
private static native int init_native(); private static native void finalize_native(int ptr); private static native int read_native(int ptr, byte[] buffer); private static native int write_native(int ptr, byte[] buffer); private static native int test_native(int ptr, int value);
這些方法對應的是frameworks/base/services/core/jni/com_android_server_opersys_OpersysService.cpp
對應的同名函數,視覺上就像Java直接調用了C/C++一樣。
frameworks/base/services/core/java/com/android/server/opersys/OpersysService.java
1 package com.android.server.opersys; 2 3 import android.content.Context; 4 import android.os.Handler; 5 import android.opersys.IOpersysService; 6 import android.os.Looper; 7 import android.os.Message; 8 import android.os.Process; 9 import android.util.Slog; 10 import android.os.RemoteException; 11 12 public class OpersysService extends IOpersysService.Stub { 13 private static final String TAG = "OpersysService"; 14 private Context mContext; 15 private int mNativePointer; 16 17 public OpersysService(Context context) { 18 super(); 19 mContext = context; 20 Slog.i(TAG, "Opersys Service started"); 21 22 mNativePointer = init_native(); 23 24 Slog.i(TAG, "test() returns " + test_native(mNativePointer, 20)); 25 } 26 27 protected void finalize() throws Throwable { 28 finalize_native(mNativePointer); 29 super.finalize(); 30 } 31 32 public String read(int maxLength) throws RemoteException 33 { 34 int length; 35 byte[] buffer = new byte[maxLength]; 36 37 length = read_native(mNativePointer, buffer); 38 39 try { 40 return new String(buffer, 0, length, "UTF-8"); 41 } catch (Exception e) { 42 Slog.e(TAG, "read buffer error!"); 43 return null; 44 } 45 } 46 47 public int write(String mString) throws RemoteException 48 { 49 byte[] buffer = mString.getBytes(); 50 51 return write_native(mNativePointer, buffer); 52 } 53 54 private static native int init_native(); 55 private static native void finalize_native(