最近学习了如何在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 #include2 #include 3 #include 4 #include 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 #include3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 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 #include5 #include 6 #include 7 8 #include 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/. .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//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 #include8 #include 9 #include 10 #include 11 12 #include 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(int ptr); 56 private static native int read_native(int ptr, byte[] buffer); 57 private static native int write_native(int ptr, byte[] buffer); 58 private static native int test_native(int ptr, int value); 59 }
(F) 添加OpersysManager
OpersysManager主要用于管理OpersysService,实例化了IOpersysService,在注册服务的
时候就是实例化了一个OpersysManager,APP(客户端)获取服务getService时也是获得这个对象,通过这个对象,APP就
可以调用该服务的相关接口(API)了
frameworks/base/core/java/android/opersys/OpersysManager.java
1 package android.opersys; 2 3 import android.content.Context; 4 import android.os.RemoteException; 5 import android.opersys.IOpersysService; 6 import android.util.Slog; 7 8 public class OpersysManager 9 { 10 private static final String TAG = "OpersysManager"; 11 12 public String read(int maxLength) { 13 try { 14 return mService.read(maxLength); 15 } catch (RemoteException e) { 16 Slog.e(TAG, "read error!"); 17 return null; 18 } 19 } 20 21 public int write(String mString) { 22 try { 23 return mService.write(mString); 24 } catch (RemoteException e) { 25 Slog.e(TAG, "write error!"); 26 return 0; 27 } 28 } 29 30 public OpersysManager(Context context, IOpersysService service) { 31 mService = service; 32 } 33 34 IOpersysService mService; 35 }
(G) 添加系统服务addService
实现了OpersysService的各种接口之后,需要向SystemServer添加服务
frameworks/base/services/java/com/android/server/SystemServer.java
1 ...... 2 import com.android.server.opersys.OpersysService; 3 ...... 4 private void startOtherServices() { 5 ...... 6 OpersysService opersys = null; 7 ...... 8 if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) { 9 ...... 10 try { 11 Slog.i(TAG, "Opersys Service"); 12 opersys = new OpersysService(context); 13 Slog.i(TAG, "Add Opersys Service"); 14 ServiceManager.addService(Context.OPERSYS_SERVICE, opersys); 15 Slog.i(TAG, "Opersys Service Succeed!"); 16 } catch (Throwable e) { 17 Slog.e(TAG, "Failure starting OpersysService Service", e); 18 } 19 ...... 20 } 21 }
frameworks/base/core/java/android/content/Context.java
1 ...... 2 @StringDef({ 3 ...... 4 OPERSYS_SERVICE, 5 ...... 6 }) 7 ...... 8 /** 9 * Use with {@link #getSystemService} to retrieve a 10 * {@link android.opersys.OpersysManager} for using Opersys Service. 11 * 12 * @see #getSystemService 13 */ 14 public static final String OPERSYS_SERVICE = "opersys"; 15 ......
(H) 注册服务
frameworks/base/core/java/android/app/SystemServiceRegistry.java
1 ...... 2 import android.opersys.OpersysManager; 3 import android.opersys.IOpersysService; 4 ...... 5 final class SystemServiceRegistry { 6 ...... 7 static { 8 ...... 9 registerService(Context.OPERSYS_SERVICE, OpersysManager.class, 10 new CachedServiceFetcher() { 11 @Override 12 public OpersysManager createService(ContextImpl ctx) { 13 IBinder b = ServiceManager.getService(Context.OPERSYS_SERVICE); 14 IOpersysService service = IOpersysService.Stub.asInterface(b); 15 if (service == null) { 16 return null; 17 } 18 return new OpersysManager(ctx, service); 19 }}); 20 ...... 21 } 22 ...... 23 } 24 ......
到这里,主要工作都要完成了,貌似可以直接可以编译进行测试了。但是实际上还有些东西需要
设置,如编译设置,SeLinux安全设置等。
(I) 更新API
在前面添加和注册了服务之后,需要更新一下API才能正常编译。
frameworks/data-binding/compiler/src/main/resources/api-versions.xml
1 ...... 2"OPERSYS_SERVICE" /> 3 ......
在源码目录下:
# make update-api -j16
编译通过后,会自动更新以下两个文件
frameworks/base/api/current.txt
...... field public static final java.lang.String OPERSYS_SERVICE = "opersys"; ...... package android.opersys { public abstract interface IOpersysService implements android.os.IInterface { method public abstract int write(java.lang.String) throws android.os.RemoteException; } public static abstract class IOpersysService.Stub extends android.os.Binder implements android.opersys.IOpersysService { ctor public IOpersysService.Stub(); method public android.os.IBinder asBinder(); method public static android.opersys.IOpersysService asInterface(android.os.IBinder); method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException; } public class OpersysManager { ctor public OpersysManager(android.content.Context, android.opersys.IOpersysService); method public java.lang.String read(int); method public int write(java.lang.String); } } ......
frameworks/base/api/system-current.txt更新同上
(J) 设置权限
external/sepolicy/service.te
1 ...... 2 type opersys_service, app_api_service, system_server_service, service_manager_type; 3 ......
external/sepolicy/service_contexts
1 ...... 2 opersys u:object_r:opersys_service:s0 3 ......
device/
1 ...... 2 type circchar_device, dev_type; 3 ......
device/
1 ...... 2 /dev/circchar u:object_r:circchar_device:s0 3 ......
device/
1 ...... 2 allow system_server circchar_device:chr_file { open read write ioctl }; 3 ......
system/core/rootdir/ueventd.rc
1 ...... 2 /dev/circchar 0660 system system 3 ......
(K) 测试服务
完成以上步骤后,全编译一次,烧录后,可先通过命令测试一下服务是否正常启动:
# service check opersys service check opersys Service opersys:found # service list service list ...... 24 opersys: [android.opersys.IOpersysService] ...... # service call opersys 2 s16 "Hello, World!" service call opersys 2 s16 "Hello, World!" Result: Parcel(00000000 0000000d '........') # service call opersys 1 i32 20 service call opersys 1 i32 20 Result: Parcel( 0x00000000: 00000000 0000000d 00650048 006c006c '........H.e.l.l.' 0x00000010: 002c006f 00570020 0072006f 0064006c 'o.,. .W.o.r.l.d.' 0x00000020: 00000021 '!... ')
执行以上命令得到对应的结果后,说明服务启动正常。接下来就可以在APP上使用了。
(L) 添加测试APP
这个APP的功能是,当用户打开APP时,APP会获得OpersysService服务,然后向这个服务发送
“Hello Opersys”,然后从这个服务读回。
packages/apps/HelloOpersysInternal/src/com/opersys/hellointernal/HelloOpersysInternalActivity.java
1 package com.opersys.hellointernal; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.util.Log; 6 import android.os.ServiceManager; // Will only work in AOSP 7 //import android.opersys.IOpersysService; // Interface "hidden" in SDK 8 import android.opersys.OpersysManager; 9 import android.content.Context; 10 11 public class HelloOpersysInternalActivity extends Activity { 12 private static final String DTAG = "HelloOpersysInternal"; 13 14 /** Called when the activity is first created. */ 15 @Override 16 public void onCreate(Bundle savedInstanceState) { 17 super.onCreate(savedInstanceState); 18 setContentView(R.layout.main); 19 20 //IOpersysService om = IOpersysService.Stub.asInterface(ServiceManager.getService("opersys")); 21 22 OpersysManager om = (OpersysManager)getSystemService(Context.OPERSYS_SERVICE); 23 try { 24 Log.d(DTAG, "Going to write to the \"opersys\" service"); 25 om.write("Hello Opersys"); 26 Log.d(DTAG, "Service returned: " + om.read(20)); 27 } 28 catch (Exception e) { 29 Log.d(DTAG, "FAILED to call service"); 30 e.printStackTrace(); 31 } 32 } 33 }
packages/apps/HelloOpersysInternal/res/layout/main.xml
1 "1.0" encoding="utf-8"?> 2"http://schemas.android.com/apk/res/android" 3 android:layout_width="fill_parent" 4 android:layout_height="fill_parent" 5 android:orientation="vertical" > 6 7 <TextView 8 android:layout_width="fill_parent" 9 android:layout_height="wrap_content" 10 android:text="@string/hello" /> 11 12
packages/apps/HelloOpersysInternal/res/values/strings.xml
1 "1.0" encoding="utf-8"?> 23 4 <string name="hello">Hello World, HelloOpersysInternalActivity!string> 5 <string name="app_name">HelloOpersysInternalstring> 6 7
packages/apps/HelloOpersysInternal/AndroidManifest.xml
1 "1.0" encoding="utf-8"?> 2"http://schemas.android.com/apk/res/android" 3 package="com.opersys.hellointernal" 4 android:versionCode="1" 5 android:versionName="1.0" > 6 7 "10" /> 8 9 <application 10 android:icon="@drawable/ic_launcher" 11 android:label="@string/app_name" > 12 <activity 13 android:label="@string/app_name" 14 android:name=".HelloOpersysInternalActivity" > 15 16 20 21 22 23"android.intent.action.MAIN" /> 17 18 "android.intent.category.LAUNCHER" /> 19
packages/apps/HelloOpersysInternal/Android.mk
1 LOCAL_PATH:= $(call my-dir) 2 include $(CLEAR_VARS) 3 4 LOCAL_MODULE_TAGS := optional 5 6 LOCAL_SRC_FILES := $(call all-java-files-under, src) 7 8 LOCAL_PACKAGE_NAME := HelloOpersysInternal 9 10 include $(BUILD_PACKAGE)
vendor/sprd/open-source/common_packages.mk
1 PRODUCT_PACKAGES += \ 2 ...... 3 HelloOpersysInternal 4 ......
到这里,已经完成了所有步骤,全编译烧录之后,打开HelloOpersysInternalAPP,再打开
logcat,可以看到以下信息:
01-01 08:07:56.137 3729 3729 D HelloOpersysInternal: Going to write to the "opersys" service 01-01 08:07:56.140 1272 3082 D opersyshw_qemu: OPERSYS HW - write()for 13 bytes called 01-01 08:07:56.140 1272 3082 D opersyshw_qemu: write data to driver: Hello Opersys 01-01 08:07:56.140 1272 3082 I OpersysServiceJNI: write data to hal: Hello Opersys 01-01 08:07:56.142 1272 3032 D opersyshw_qemu: OPERSYS HW - read()for 20 bytes called 01-01 08:07:56.142 1272 3032 D opersyshw_qemu: read data from driver: Hello Opersys 01-01 08:07:56.142 1272 3032 I OpersysServiceJNI: read data from hal: Hello Opersys 01-01 08:07:56.143 3729 3729 D HelloOpersysInternal: Service returned: Hello Opersys