Android硬件访问服务由HAL层到APP以及添加自定义权限限制访问
来源:互联网 发布:minix源码解读 编辑:程序博客网 时间:2024/06/11 21:02
本文主要内容,在硬件访问的基础上添加了权限控制以及app调用新增API的方法。
- 1编写HAL库控制硬件
- 2添加jni访问HAL库
- 3java调用jni的实现以及AIDL
- 4实现管理类来对java的访问服务进行操作以及APK调用方法
- 5对硬件访问服务进行访问硬件的权限检查在framework添加新的权限
1编写HAL库控制硬件
以Android5.0为例在如下目录创建一个目录添加一个C文件和一个Android.mk文件hardware/libhardware/modules/leds/Android.mk //编译HAL库的规则hardware/libhardware/modules/leds/leds.c //生成HAL库的逻辑代码hardware/libhardware/modules/include/hardware/leds.h如何实现对应的代码这里首先需要分析一下HAL的架构。首先是三个结构体。对应目录hardware/libhardware/include/hardware/hardware.h未说明的结构体成员在这个hardware.h文件下有很详细的说明了。
/*这个结构体主要是为了通过id成员找到methods成员,methods成员通过调用该methods->open函数打开对硬件操作的一个类似句柄一样的东西*/struct hw_module_t{ uint32_t tag; uint16_t version_major; uint16_t version_minor; const char *id; const char *name; const char * author; struct hw_module_methods_t* methods; //指向封装有open函数指针的结构体这个重点说明一下看下一段代码片。 void* dso; uint32_t reserved[32-7]; };
/* 硬件对象的open方法描述结构体,通过函数参数将hw_device_t的指针分配的空间返回回来。这个又是何方神圣?接下来再看。*/ struct hw_module_methods_t{ // 只封装了open函数指针 int (*open)(const struct hw_module_t* module, const char * id, struct hw_device_t** device); };
/* 这个hw_device_t内容是不是让你看的一头雾水?没什么主要的功能呀,除了一个close方法可能联想到硬件操作。那其他方法呢?这里实际上是用C语言来实现一“个面向对象”“继承”这么一个概念。对于具体的硬件可以继承这个结构体。接下来看下一个代码片如何继承。 */ struct hw_device_t{ uint32_t tag; uint32_t version; struct hw_module_t* module; // 这里头可是封装了methods与id号的。 uint32_t reserved[12]; int (*close)(struct hw_device_t* device); // 该设备的关闭函数指针,可以看做硬件的close方法 };
typedef struct leds_device { struct hw_device_t common; //这里所谓继承,必须写在第一个成员里。因为内存对齐,不被编译器优化的情况下,leds_device的第一个成员的地址就是leds_device的地址。看不懂的请回去恶补C语言结构体内存分布。methods下的open函数hw_device_t**这个参数传入的时候是需要用这个派生类进行强制转化成基类(hw_device_t的)。 int (*leds_control)( int status, int which);//这个是派生类扩充的功能。也就是我们具体硬件led的操作函数了。} leds_device_t;
以上是HAL三个结构体的内容。HAL库的调用端利用
int hw_get_module(const char *id, const struct hw_module_t **module); 获取module,这个获取过程不再详述。再利用这个module的methods方法从传入的参数中获取到leds_device_t结构体。这样就能获取到leds_device_t结构体里的leds_control方法了。关于以上三个结构体填充与具体实现过程如下。
leds.c文件
#include <hardware/leds.h>#include <hardware/hardware.h>#include <cutils/log.h>#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/ioctl.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#define LED_DEVICE_PATH "/dev/leds"static int leds_exists(void) { int fd; ALOGE("Leds leds_exists"); fd = open(LED_DEVICE_PATH, O_RDWR); if ( fd < 0 ) return 0; close(fd); return 1;}/*led具体硬件操作实现*/static int leds_ctl (int status, int whitch) { int ret,fd; ALOGE("Leds leds_ctl"); fd = open(LED_DEVICE_PATH, O_RDWR); ret = ioctl(fd,status,whitch); close(fd); return ret;}/*释放硬件句柄分配的空间*/static int leds_close (hw_device_t *dev) { if(dev != NULL) free(dev); return 0;}/*该函数实现调用获取硬件句柄方法。*/static int leds_open_exists(const hw_module_t* module, const char* id __unused, hw_device_t** device __unused) { if (!leds_exists()) { ALOGE("Leds device does not exist. Cannot start leds"); return -ENODEV; } //对具体实现的派生类分配空间。 leds_device_t *ledsdev = calloc(1, sizeof(leds_device_t)); if (!ledsdev) { ALOGE("Can not allocate memory for the leds device"); return -ENOMEM; } memset(ledsdev, 0, sizeof(leds_device_t)); ledsdev->common.tag = HARDWARE_DEVICE_TAG; ledsdev->common.module = (hw_module_t *) module; ledsdev->common.version = HARDWARE_DEVICE_API_VERSION(1,0); ledsdev->common.close = leds_close; ledsdev->leds_control = leds_ctl;//led操作函数的实现 //传回的是以基类返回。印证上面的知识点结构体内存分布的内容。 *device = (hw_device_t *) ledsdev; return 0;}/*===========================================================================*//* Default leds HW module interface definition *//*===========================================================================*/static struct hw_module_methods_t leds_module_methods = { .open = leds_open_exists,//打开获取硬件操作句柄的具体实现。};struct hw_module_t HAL_MODULE_INFO_SYM = {//必须这个值看hardware.h解说 .tag = HARDWARE_MODULE_TAG,//必须这个值 .module_api_version = LEDS_API_VERSION, .hal_api_version = HARDWARE_HAL_API_VERSION, .id = LEDS_HARDWARE_MODULE_ID,//led 设备的id。 .name = "Default leds HAL", .author = "JerryRowe", .methods = &leds_module_methods,//methods获取句柄的方法};
对应的leds.h文件
#ifndef _HARDWARE_LEDS_H#define _HARDWARE_LEDS_H#include <hardware/hardware.h>__BEGIN_DECLS#define LEDS_API_VERSION HARDWARE_MODULE_API_VERSION(1,0)/** * The id of this module */#define LEDS_HARDWARE_MODULE_ID "leds"/** * The id of the main vibrator device */#define LEDS_DEVICE_ID_MAIN "main_leds"typedef struct leds_device { /** * Common methods of the leds device. This *must* be the first member of * leds_device as users of this structure will cast a hw_device_t to * leds_device pointer in contexts where it's known the hw_device_t references a leds_device. */ struct hw_device_t common; /** Turn on/off leds * * @param status on/off 0-1 * * @param which which led to on/off 0-3 * * @return 0 in case of success, negative errno code else */ int (*leds_control)( int status, int which);} leds_device_t;/*这里简单对methods的open进行了封装,这样在jni层要调用该方法时候只要include该头文件即可*/static inline int leds_open( const struct hw_module_t* module, leds_device_t** device)//注意这里,传入的依旧是派生类{ return module->methods->open(module, LEDS_DEVICE_ID_MAIN, (struct hw_device_t**)device);//而调用open时,向父类转化了。}__END_DECLS#endif // _HARDWARE_LEDS_H
对应Android.mk文件
#本地当前LOCAL_PATH := $(call my-dir)#清除变量include $(CLEAR_VARS)LOCAL_MODULE := leds.default# HAL module implementation stored in# hw/<LEDS_HARDWARE_MODULE_ID>.default.soLOCAL_MODULE_RELATIVE_PATH := hw#C语言头文件路径LOCAL_C_INCLUDES := hardware/libhardware#C源文件LOCAL_SRC_FILES := leds.c#由于用了log打印,链接的动态库名LOCAL_SHARED_LIBRARIES := liblog#这个不用多说了吧。LOCAL_MODULE_TAGS := optional#调用动态库编译规则include $(BUILD_SHARED_LIBRARY)
查看编译log可以知道生成的so路径在哪里。
总结:框架中主要难以理解可能是利用C语言实现继承的过程。
2、添加jni访问HAL库
这里主要是实现对应java层的native方法的功能,并且加载调用HAL层leds库的功能。对应实现如下。
frameworks/base/services/core/jni/com_android_server_LedService.cpp
frameworks/base/services/core/jni/onload.cpp
frameworks/base/services/core/jni/Android.mk
这里没什么框架难度。主要还是业务逻辑的代码,还有就是找到对应位置添加注册,以及添加业务逻辑的代码编译选项。
com_android_server_LedService.cpp
#define LOG_TAG "native_LedService"#include "jni.h"#include "JNIHelp.h"#include "android_runtime/AndroidRuntime.h"#include <utils/misc.h>#include <utils/Log.h>/*注意这里添加leds头文件*/#include <hardware/leds.h> #include <hardware/hardware.h>#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>namespace android{ /*声明需要获取的硬件操作句柄类型*/ leds_device_t *leds_device = NULL;static jint ledControl(JNIEnv *env, jobject clazz,jint which, jint status){ //通过硬件句柄控制led。 return leds_device->leds_control(status,which);}static jint ledOpen(JNIEnv *env, jobject clazz){ hw_module_t* leds_module = NULL; ALOGI("ledOpen "); /*利用LEDS的id获取leds_module,这样就能获取methods->open方法,从而获取leds_device的硬件句柄*/ if ( hw_get_module(LEDS_HARDWARE_MODULE_ID, (const hw_module_t **)&leds_module) == 0 ) { ALOGI("HAL leds.default.so find!!"); leds_open(leds_module, &leds_device);//这个是leds头文件里inline封装的函数第一节已经提到过。 return 0; } else { ALOGI("HAL leds.default.so no exists!!"); return -1; }}//jni函数映射表。static JNINativeMethod method_table[] = { { "native_ledControl", "(II)I", (void*)ledControl }, { "native_ledOpen", "()I", (void*)ledOpen },};//注册jni到对应的java文件。int register_android_server_LedService(JNIEnv *env){ return jniRegisterNativeMethods(env, "com/android/server/LedService", method_table, NELEM(method_table));}};
onload.cpp添加jni注册位置。
namespace android { ……………… //注册jni服务函数声明 int register_android_server_LedService(JNIEnv* env); ………………};extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved){ ……………… //注册jni服务函数调用 register_android_server_LedService(env); ………………}
Android.mk添加com_android_server_LedService.cpp的编译
LOCAL_SRC_FILES += \ ………… $(LOCAL_REL_DIR)/com_android_server_LedService.cpp \ …………
3、java调用jni的实现以及AIDL
LED服务涉及到android进程间通信aidl编程。不熟悉的朋友请自行恶补一下。涉及一下文件frameworks/base/core/java/android/os/ILedService.aidlframeworks/base/Android.mkframeworks/base/services/core/java/com/android/server/LedService.javaframeworks/base/services/java/com/android/server/SystemServer.java
首先来看aidl,对于应用层操作硬件来说。越简单越好。因此对应用层只管亮灭。因此api也要设计的简洁。
package android.os;/** {@hide} */interface ILedService{ int ledControl(int which, int status);}
在frameworks/base/Android.mk添加编译
LOCAL_SRC_FILES += \ ………… core/java/android/os/ILedService.aidl \ …………
此处编译完后会自动生成对应的ILedService.java自动实现进程间通信,out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/os/ILedService.java
再看看LedService.java会继承该Stub
package com.android.server;import android.os.ILedService;public class LedService extends ILedService.Stub { //app访问的api。aidl定义的api需要实现 public int ledControl(int which, int status) { int ret; ret = native_ledControl(which,status); return ret; } //打开设备实际上这里最终会调用初始化和加载HAL的led库。 public LedService() { native_ledOpen(); } // 这里两个native方法。在上一节jni实现了。 native private int native_ledControl(int which, int status); native private int native_ledOpen();}
最后要在SystemServer.java添加我们自己建立的led服务。
public final class SystemServer { ………… Slog.i(TAG, "Led Service"); ServiceManager.addService("led", new LedService()); …………
4、实现管理类来对java的访问服务进行操作以及APK调用方法
实际上第三节过后,app已经具备可以调用操作ILedService的能力了。只是为了符合标准,我们应当像
context.getSystemService(Context.XXX_SERVICE);
这样获取服务进行操才符合应用人员开发调用服务的思路。本节就是扩充一个LedManager来实现管理类。
涉及到的文件
frameworks/base/core/java/android/app/ContextImpl.java
frameworks/base/core/java/android/content/Context.java
frameworks/base/core/java/android/service/led/LedManager.java
先来看看管理类。LedManager.java
package android.service.led;import android.content.Context;import android.os.ILedService;import android.os.RemoteException;import android.content.pm.PackageManager;import android.util.Log;public class LedManager { private Context mContext;//保存调用的进程上下文 private ILedService mService;//访问ILedService服务 public LedManager(Context context, ILedService service) {//这两个参数是在getSystemService里传入的。 mContext = context;//保存进程上下文 mService = service;//保存获取到的服务 } public int ledControl(int which, int status) { try{ //调用控制led return mService.ledControl(which, status); } catch (RemoteException e) { e.printStackTrace(); return -1; } }}
getSystemService是一个Context的抽象方法,具体实现的地方
来看看ContextImpl.java
…………import android.service.led.LedManager;import android.os.ILedService;………… @Override //抽象方法实现处,从ServiceFetcher下获取对应的方法。并且返回fetcher由获取这个服务。由于篇幅较多不继续深入。 public Object getSystemService(String name) { ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name); return fetcher == null ? null : fetcher.getService(this); } /*最终这里我们需要注册一个LED的服务让etcher拿到这个服务。*/ registerService(LED_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(LED_SERVICE);//此处LED_SERVICE是在Context添加的。 ILedService service = ILedService.Stub.asInterface(b); return new LedManager(ctx, service);//这里调用LedManager构造函数 }});
最后在Context.java添加LED_SERVICE的字符串
………… public static final String LED_SERVICE = "led";//添加led的字符串 ………… @StringDef({ ………… LED_SERVICE,//添加led资源描述,不了解请参看这里:http://www.linuxidc.com/Linux/2015-08/121993.htm ………… }) ………… //这里是文档描述依赖应该不用加也可,并且make update-api了 * @see #LED_SERVICE * @see android.service.LedManager ………… public abstract Object getSystemService(@ServiceName @NonNull String name);
编译后生成的jar包路径在out/target/common/obj/JAVA_LIBRARIES,Android studio导入这个包依赖后,可以使用apk调用LedManager。但是还是找不到LED_SERVICE这个字符串符号。这里提供两种解决办法。1、在android源码树里make sdk出来的android.jar替换Android studio sdk路径下的android.jar。如何make sdk出来请移步http://www.360doc.com/content/14/0214/23/9075092_352577757.shtml2、利用反射。由于本次反射过程不复杂,因此本例采用反射。app代码如下。
package com.example.jerryrowe.led;import android.content.Context;import android.service.led.LedManager;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.widget.CheckBox;import android.widget.CompoundButton;import android.widget.Toast;import java.lang.reflect.Field;public class LedMainActivity extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener { private CheckBox[] checkBoxes = new CheckBox[5]; private int[] checkBoxId = {R.id.led0, R.id.led1, R.id.led2, R.id.led3, R.id.ledall}; LedManager ledManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_led_main); for(int i = 0; i < checkBoxes.length; i++) { checkBoxes[i] = (CheckBox) findViewById(checkBoxId[i]); checkBoxes[i].setOnCheckedChangeListener(LedMainActivity.this); } ledManager = getLedManager();//该函数利用反射获取server } private LedManager getLedManager() { Class mContext = Context.class;//获取class Field fields[] = (mContext.getDeclaredFields());//class拥有的所有属性 for(int i = 0; i < fields.length; i++) { //fields[i].setAccessible(true);对于private成员使用。 if("LED_SERVICE".equals(fields[i].getName())){//利用成员变量名反射出自己需要的属性。 try { String led_service = (String) fields[i].get(mContext); Toast.makeText(getApplicationContext(), fields[i].getName()+ ":" + led_service, Toast.LENGTH_SHORT).show();//打印出属性值是否为leds return (LedManager) getApplicationContext().getSystemService(led_service); } catch (IllegalAccessException e) { e.printStackTrace(); return null; } } } return null; } @Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) { if (compoundButton.getId() == R.id.led0) { ledManager.ledControl(0,b ? 1:0); } if (compoundButton.getId() == R.id.led1) { ledManager.ledControl(1,b ? 1:0); } if (compoundButton.getId() == R.id.led2) { ledManager.ledControl(2,b ? 1:0); } if (compoundButton.getId() == R.id.led3) { ledManager.ledControl(3,b ? 1:0); } if (compoundButton.getId() == R.id.ledall) { ledManager.ledControl(0,b ? 1:0); ledManager.ledControl(1,b ? 1:0); ledManager.ledControl(2,b ? 1:0); ledManager.ledControl(3,b ? 1:0); } }}
至此,从HAL到app调用的整个过程已经打通。已经能正常访问硬件服务了。
5、对硬件访问服务进行访问硬件的权限检查,在framework添加新的权限
仔细思考apk开发流程中,是不是涉及到对每个资源的访问都要在AndroidManifest.xml 中进行权限声明呢?比如<uses-permission android:name="android.permission.XXX"/>这样本节内容就是对自己添加的硬件访问服务进行权限的限制。没有声明权限的则同样无法访问led涉及到的文件frameworks/base/core/res/AndroidManifest.xmlframeworks/base/core/res/res/values/strings.xmlframeworks/base/core/res/res/values-xx-xx/strings.xmlframeworks/base/core/java/android/service/led/LedManager.java
首先添加对应的权限描述在AndroidManifest.xml添加一下permission
<!-- Allows access to the led --> <permission android:name="android.permission.LED" android:permissionGroup="android.permission-group.AFFECTS_BATTERY" android:protectionLevel="normal" android:label="@string/permlab_led" android:description="@string/permdesc_led" />
然后在strings.xml下添加对应引用的@string/xxx以及各国语言下的values-xx-xx/strings.xml各国语言的我就省略了。
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_led">control led</string> <!-- Description of an application permission, listed so the user can c hoose whether they want to allow the application to do this. --> <string name="permdesc_led">Allows the app to control the led.</string>
最后在LedManager.java下添加控制权校验的方法。不贴源码了只贴出增加的部分。
package android.service.led;import android.content.Context;import android.os.ILedService;import android.os.RemoteException;import android.content.pm.PackageManager;import android.util.Log;public class LedManager { private Context mContext; private ILedService mService; public LedManager(Context context, ILedService service) { mContext = context; mService = service; } public int ledControl(int which, int status) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.LED)!= PackageManager.PERMISSION_GRANTED) {//这句if就是判断是否声明了该权限。如果错误打印错误信息。 Log.e("LedManager","Permission Dneial: can't control led from" + mContext.getApplicationInfo()); return -1; } try{ return mService.ledControl(which, status); } catch (RemoteException e) { e.printStackTrace(); return -1; } }}
编译LedManager.java可能出现找不到android.Manifest.permission.LED,这个错误在于我们更改了framework的res,却还没编译生成新的res因此要先编译res。mmm frameworks/base/core/res/ 这样再编译LedManager.java就不会报错了。
最后应用要访问ledControl的函数需要在应用的AndroidManifest.xml文件下增加一行,
apk的java访问代码在第4节就已经贴出来了。
- Android硬件访问服务由HAL层到APP以及添加自定义权限限制访问
- tiny210 hal 6 Android系统中编写APP通过应用程序框架层访问硬件服务。
- Android硬件访问服务-HAL
- Android硬件访问服务学习之(三)Android加入HAL层访问硬件
- Android添加硬件访问服务
- 在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
- 为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
- 在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
- 在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
- 在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
- 在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
- 在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
- 在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
- 在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
- 在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
- (四)在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
- 在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
- 为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
- Android2.2以上使用HorizontalScrollView取代Gallery
- 创建window服务
- HDOJ 1757 A Simple Math Problem
- oj-14-A-数组逆序
- 每天一个linux命令(9):touch 命令
- Android硬件访问服务由HAL层到APP以及添加自定义权限限制访问
- 进制转换(十进制转二进制)
- 对于大流量的网站,您采用什么样的方法来解决访问量问题?
- sqlsever自动收缩日志文件
- 第十四周:C语言:数字逆行
- Android paint 效果研究
- 第十四周-C语言 oj上机题目(插入数到有序数组中)
- 矩阵对角线元素之和
- 求3*3矩阵对角线元素之和