## 简述
前面的博客中,我们直接添加C lib到APK中,然后使用LoadLibrary加载这个库,同时添加一个class来作为中间层,直接使用这个C库中的native函数来控制硬件,这种做法将硬件与APK牢牢绑定,如果有多个APP来访问同一个硬件就会出现问题,代码也会有很多的重复,在Android中,我们使用Android的SystemServer向ServiceManager来将硬件的功能添加为一个服务,这样当一个APP需要使用硬件的时候就向SystemServer发出请求service服务,然后由ServiceMnager统一提供服务,提供统一的接口与硬件控制,即相当于多添加了一层,从而实现解耦。
## 详细原理
先看下图(图片来源于韦东山的Android视频资料)中的③②①,按照顺序:
1. SystemServer会加载Cpp lib
1. 在JNI_OnLoad中注册各个Service,SystemServer向ServiceManager添加服务
1. 这些service就包括像串口/LED等硬件相关的服务
![](https://box.kancloud.cn/2016-05-05_572afc9e48dc5.jpg)
而使用的时候,就是7~5步骤:
1. AddService:SystemServer向ServiceManager添加服务addServeice
1. getservice:通过getservice来从SystemServer注册了的service中获取服务所具有的功能,例如ledctrl
1. 使用Service的方法:APP使用一个Interface(即以i开头的对象)来使用service提供的功能,将服务请求到SystemServer去
APP/SystemServer/ServiceManager三者都是通过Bindler来通讯。
## 添加Service与使用Service的步骤
### 添加serviceAIDL文件,生成Interface java文件
因为系统中其他都aidl文件都放在frameworks/base/core/java/android/os下,所有我们也参考其他的文件添加一个ILedService.aidl:
~~~
package android.os;
/** {@hide} */
interface ILedService
{
int ledCtrl(int which, int status);
}
~~~
可以看到这个interface前面有个@hide的修饰,表明是个hide class。
同时还需要将此aidl文件添加到Android.mk(Makefile)中:
~~~
$ git diff Android.mk
diff --git a/Android.mk b/Android.mk
index 151621c..7bde511 100644
--- a/Android.mk
+++ b/Android.mk
@@ -150,6 +150,7 @@ LOCAL_SRC_FILES += \
core/java/android/os/IUpdateLock.aidl \
core/java/android/os/IUserManager.aidl \
core/java/android/os/IVibratorService.aidl \
+ core/java/android/os/ILedService.aidl \
core/java/android/service/notification/INotificationListener.aidl \
core/java/android/service/dreams/IDreamManager.aidl \
core/java/android/service/dreams/IDreamService.aidl \
~~~
这个这个Android.mk位于frameworks/base/,编译后就会生成一个ILedService.java
### 添加service的实现cpp
~~~
#define LOG_TAG "LedService"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
//#include <hardware_legacy/led.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
namespace android
{
#define ALOGI printf
#define LED_NUM 3
int leds_fd[LED_NUM];
char path_buff[255];
static jint ledCtrl(JNIEnv *env, jobject clazz, jint which, jint status)
{
int ret = -1;
if(status == 1) {
ret = write(leds_fd[which], "255", 3);
} else {
ret = write(leds_fd[which], "0", 1);
}
if(ret < 0){
return -1;
}
ALOGI("Native ctrl fd = [%d]\n", which);
return 0;
}
static jint ledOpen(JNIEnv *env, jobject clazz)
{
int i = 0;
for(i=0; i<LED_NUM; i++){
sprintf(path_buff, "/sys/class/leds/led%d/brightness", i);
printf("path:%s\n",path_buff);
leds_fd[i] = open(path_buff, O_RDWR);
if(leds_fd[i] < 0){
ALOGI("led%d: %s, open failed\n", i, path_buff);
return -1;
} else {
ALOGI("led%d: %s, open success\n", i, path_buff);
}
}
return 0;
}
static void ledClose(JNIEnv *env, jobject clazz)
{
int i = 0;
for(i=0; i< LED_NUM; i++){
close(leds_fd[i]);
}
}
static JNINativeMethod method_table[] = {
{ "native_ledCtrl", "(II)I", (void*)ledCtrl },
{ "native_ledClose", "()V", (void*)ledClose },
{ "native_ledOpen", "()I", (void*)ledOpen }
};
int register_android_server_LedService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/LedService",
method_table, NELEM(method_table));
}
};
~~~
里面定义好了来调用这个native函数的java class名字为com_android_server_LedService:
~~~
jniRegisterNativeMethods(env, "com/android/server/LedService",
method_table, NELEM(method_table));
~~~
还需要添加到编译中:
~~~
$ git diff services/jni/Android.mk
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index b313d48..fb359cb 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -14,6 +14,7 @@ LOCAL_SRC_FILES:= \
com_android_server_UsbDeviceManager.cpp \
com_android_server_UsbHostManager.cpp \
com_android_server_VibratorService.cpp \
+ com_android_server_LedService.cpp \
com_android_server_location_GpsLocationProvider.cpp \
com_android_server_connectivity_Vpn.cpp \
onload.cpp
~~~
### 添加LedService.java文件
前面有了native c/cpp的实现,接下来就需要用JNI来调用native方法了,因此需要添加LedService.java(frameworks/base/services/java/com/android/server/LedService.java)文件:
~~~
package com.android.server;
import android.os.ILedService;
/**
* Created by hexiongjun on 12/9/15.
* Function:
* Call Native C function to control hardware
*/
public class LedService extends ILedService.Stub{
private static final String TAG = "LedService";
public int ledCtrl(int which, int status) throws android.os.RemoteException {
return native_ledCtrl(which, status);
}
public void LedService(){
native_ledOpen();
}
// Declare the function
public native static int native_ledCtrl(int which, int status);
public native static int native_ledOpen();
public native static void native_ledClose();
}
~~~
内容很简单:
- 声明了native函数
- 在构造函数中调用open打开设备
上层的Android.mk会自动将java文件添加到Android编译中,不需要自己添加。
### 让SystemServer启动的时候加载service
~~~
$ git diff services/jni/onload.cpp
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index 423ebd1..83721fe 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -31,6 +31,8 @@ int register_android_server_SerialService(JNIEnv* env);
int register_android_server_UsbDeviceManager(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
+// From com_android_server_LedService.cpp
+int register_android_server_LedService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_location_GpsLocationProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
@@ -60,6 +62,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
register_android_server_UsbDeviceManager(env);
register_android_server_UsbHostManager(env);
register_android_server_VibratorService(env);
+ register_android_server_LedService(env);
register_android_server_SystemServer(env);
register_android_server_location_GpsLocationProvider(env);
register_android_server_connectivity_Vpn(env);
~~~
这个是因为SystemServer进程启动的时候会去调用LoadLibrary去加载各个库,这个加载的过程就在OnLoad.cpp中。
### 添加Service到ServiceManager中
这个是在SystemServer中完成的:
~~~
$ git diff services/java/com/android/server/SystemServer.java
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9455017..1ccf63a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -129,6 +129,7 @@ class ServerThread extends Thread {
PowerManagerService power = null;
DisplayManagerService display = null;
BatteryService battery = null;
+ LedService led = null;
VibratorService vibrator = null;
AlarmManagerService alarm = null;
MountService mountService = null;
@@ -288,6 +289,11 @@ class ServerThread extends Thread {
battery = new BatteryService(context, lights);
ServiceManager.addService("battery", battery);
+ //Add the led service to SystemServer, so others can use
+ Slog.i(TAG, "Led Service");
+ led = new LedService();
+ ServiceManager.addService("led", led);
+
Slog.i(TAG, "Vibrator Service");
vibrator = new VibratorService(context);
ServiceManager.addService("vibrator", vibrator);
~~~
### LED App中使用添加的Service
到了最后就可以使用这些服务了,但是要使用之前还需要添加包含了LedService的模块,这个模块其实是framework,但是因为我们是java,而framework属于dex格式,因此我们需要添加jar格式的包,这个包编译完成后,位于:
> out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar
因此在APP中添加此模块:
![](https://box.kancloud.cn/2016-05-05_572afc9e69af0.jpg)
并将此模块添加到app的依赖中:
![](https://box.kancloud.cn/2016-05-05_572afc9e88e99.jpg)
然后在代码中导入Interface与ServiceManager,并使用Service:
![](https://box.kancloud.cn/2016-05-05_572afc9ea1aa4.jpg)
## 这种添加Service到ServiceManager方法的问题
现在我们依然将硬件相关的操作放到了一个cpp中,而这个cpp会编译到系统中,因此如果对硬件的操作有变更,我们就需要修改这个文件,修改了这个文件,那么就需要将整个Android系统重新编译,因此图片中还有一个步骤④,这个就是将硬件相关的东西放在一个HAL层,这样子就避免了修改一个文件就需要编译整个系统,同时也可以不放出与硬件相关的源码而仅仅给出一个HAL相关的库(保密)。
## 遇到的问题
### multidex问题
因为包含了framework的classes.jar,而这个jar中有超过65K个的方法,因此就需要开启multidex。
~~~
$ git diff app/build.gradle
diff --git a/app/build.gradle b/app/build.gradle
index 131397f..82a2a62 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -10,6 +10,12 @@ android {
targetSdkVersion 22
versionCode 1
versionName "1.0"
+
+ //Enable multidex
+ multiDexEnabled true
+ }
+ dexOptions {
+ javaMaxHeapSize "4g"
}
sourceSets{
main {
@@ -29,4 +35,6 @@ dependencies {
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:22.2.1'
compile 'com.android.support:design:22.2.1'
+ compile project(':classes')
+ compile 'com.android.support:multidex:1.0.0'
}
~~~
同时还需要更改xml文件:
~~~
$ git diff app/src/main/AndroidManifest.xml
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a77e7a1..1504514 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,6 +3,7 @@
package="com.hexiongjun.led">
<application
+ android:name="android.support.multidex.MultiDexApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
~~~
并对gradle resync。
### jar不匹配的问题
如果重新编译了Android classes.jar但是在APP中依旧使用的是老的,那么会出现一些奇怪的问题:
![](https://box.kancloud.cn/2016-05-05_572afc9ec5da9.jpg)
此时需要先将老的移除掉然后重新添加,或者直接在app的workspace中替换新的。
### javaHeap size的配置
![](https://box.kancloud.cn/2016-05-05_572afc9eebcb2.jpg)
- 前言
- Freescale IMX6 Android (1): 使用HDMI作为Android显示输出的配置
- Freescale IMX6 Android (2): Android NFS启动问题汇总
- Freescale IMX6 Android (3): 手动制作Android启动用SD卡 省去MFGTOOLS烧写
- Freescale IMX6 Android (5): APP通过JNI控制LED
- Freescale IMX6 Android (4): 基于TQIMX6 给Toolbox添加LED控制程序
- Freescale IMX6 Android (6): 向ServerManager中添加Service
- Freescale IMX6 Android (7): Android启动动画死循环 Home界面不出来与pid XXX exit 可能的原因汇总