第十期 基于模拟器的Helloworld Framework接口 《手机就是开发板》

来源:互联网 发布:c语言教学视频下载 编辑:程序博客网 时间:2024/05/05 04:35
        这一期我们在Android系统的Application Frameworks层提供Java接口的硬件服务,结合上一期添加的JNI方法来调用底层硬件。
        下面提到的代码保存在https://github.com/aggresss/PHDemo.git 的Code目录的hello_Framework文件中,也可以直接访问:
https://github.com/aggresss/PHDemo/tree/master/Code/hello_Framework
        在Android系统中,硬件服务一般是运行在一个独立的进程中为各种应用程序提供服务。因此,调用这些硬件服务的应用程序与这些硬件服务之间的通信需要通过代理来进行。为此,我们要先定义好通信接口。进入到frameworks/base/core/java/android/os目录,新增IHelloService.aidl接口定义文件:
package android.os;     interface IHelloService {      void setVal(int val);      int getVal();  }  
然后进入 frameworks/base目录,打开Android.mk文件,修改LOCAL_SRC_FILES变量的值,增加IHelloService.aidl源文件:
LOCAL_SRC_FILES += /
....................................................................
core/java/android/os/IVibratorService.aidl /
core/java/android/os/IHelloService.aidl /
core/java/android/service/urlrenderer/IUrlRendererService.aidl /
.....................................................................
执行 mmm frameworks/base 这样,就会根据IHelloService.aidl生成相应的IHelloService.Stub接口。
进入到frameworks/base/services/java/com/android/server目录,新增HelloService.java文件:
package com.android.server;  import android.content.Context;  import android.os.IHelloService;  import android.util.Slog;  public class HelloService extends IHelloService.Stub {      private static final String TAG = "HelloService";      HelloService() {          init_native();      }      public void setVal(int val) {          setVal_native(val);      }         public int getVal() {          return getVal_native();      }            private static native boolean init_native();          private static native void setVal_native(int val);      private static native int getVal_native();  };  
修改同目录的SystemServer.java文件,在ServerThread::run函数中增加加载HelloService的代码:

@Override
public void run() {
....................................................................................
try {
Slog.i(TAG, "DiskStats Service");
ServiceManager.addService("diskstats", new DiskStatsService(context));
} catch (Throwable e) {
Slog.e(TAG, "Failure starting DiskStats Service", e);
}
try {
Slog.i(TAG, "Hello Service");
ServiceManager.addService("hello", new HelloService());
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Hello Service", e);
}
......................................................................................
}

执行 mmm frameworks/base/services/java
然后 make snod 重新生成system.img
重新打包后的system.img系统镜像文件就在Application Frameworks层中包含了我们自定义的硬件服务HelloService了,并且会在系统启动的时候,自动加载HelloService。这时,应用程序就可以通过Java接口来访问Hello硬件服务了。
=======================分割线==========================
        但是从android5.0以后的系统引入了SELinux,SELinux定义了系统中每个用户,进程,应用和文件的访问和转变的权限,然后它使用一个安全策略来控制这些实体(用户、进程、应用和文件)之间的交互,安全策略指定如何严格或宽松地进行检查。
Android 5.0以后,因为采取了SEAndroid/SElinux的安全机制,即使拥有root权限,或者对某内核节点设置为777的权限,仍然无法在JNI层访问。要想让JNI可以成功的访问/dev/hello硬件就必须修改SELinux的策略,否则Android系统再启动是就是出现 
add_service ('hello',4e) uid=1000 - PERMISSION DENIED 的错误信息。
这里有两篇参考的博客,里面详细的讲解了怎么修改SELinux策略:
http://blog.csdn.net/eliot_shao/article/details/51770558
http://blog.csdn.net/wh_19910525/article/details/45170755
AOSP中,SELinux相关的策略配置文件保存在 /external/sepolicy/中,为了完成我们这次实验,需要修改5个 .te 文件,可通过访问https://github.com/aggresss/PHDemo/tree/master/Code/hello_Framework/sepolicy 获得

具体的文件改动,都在 #add by aggresss 标签下。
为了确保修改后的 .te 文件被成功的编译进system.img 建议执行一次 make update-api ,然后重新执行 make 进行编译。
=======================分割线==========================
        上面的步骤完成后,我们来验证一下 HelloService 是否启动成功,因为只有验证成功后我们才可以进行下一步,如果每完成一步都不验证,到最后实验出现问题时,一层一层的分析起来太复杂了。
我们验证的方式是查看android启动时的打印信息,因为我们的HelloService的源码内都设置有调试信息,启动成功和失败都会有信息输出,在Android中,当系统内核启动后,所有的打印信息都通过logcat机制进行输出,所以只要获取到logcat信息就可以对已启动的Android系统进行调试分析,这里我们使用android studio 内集成的功能,在开启模拟器前先启动android studio ,选择 Android Monitor 。然后启动模拟器,当内核加载成功进入到Andorid系统后,logcat便会收到系统信息。因为信息非常多,所以我们进行一下筛选,在filter内输出 "hello" ,当andorid系统完全启动后,就能搜索到和hello 相关的信息,如下图所示:

        当出现 Hello JNI: hello device is open. 的信息后就说明我们添加的HelloService服务已经成功运行。
0 0