要编写Native层的binder服务,其实大家可以参考Android系统中带有的服务进行编写,这里主要讲流程,原理性的东西大家可以参考博主之前的文章。另外我们这里编写的服务程序只是简单的提供加减乘除运算的,不做特别难的事情。
流程
在着手编写native层binder服务前,我们先整理一下Binder调用的整个流程
Client调用Service
client要调用Service的功能,首先需要获取到一个BpXXXXXService对象,然后就可以直接调用BpXXXXXService类中定义的方法,在这些方法中,并不会执行真正的操作,而是包装好数据后再调用remote()->transact方法将这些数据发送出去。这里的remote()方法是在BpRefBase中实现的,remote()方法返回的就是一个BpBinder对象,所以其实这里调用的就是BpBinder的transact方法。
service处理请求
client的数据发送出去之后,service通过IPCThreadState接收到client的请求后,会调用BBinder的transact方法,BBInder的transact方法再调用其子类BnXXXXXService中实现的onTransact方法。onTransact方法会根据传进来的code调用到实际XXXXXService中的方法,进行实际的操作,然后再将返回值返回给onTransact,onTransact将结果写入到reply里面返回给BpXXXXXService,这样就完成了一轮操作。
IArithmeticService.h
我的目录结构如下,创建一个名为arithmetic的文件夹,文件夹内再创建一个include文件夹,include文件夹内创建一个IArithmeticService.h文件,然后再arithmetic文件夹中创建Android.mk、ArithmeticService.cpp、ArithmeticService.h和IArithmeticService.cpp这四个文件。下面开始编写代码。
首先创建一个IArithmeticService类,这个类作为BpArithmeticService和BnArithmeticService的父类存在,我们在这里定义实际需要完成的Binder工作函数,同时定义出BnArithmeticService类,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#ifndef ANDROID_IARITHMETIC_H #define ANDROID_IARITHMETIC_H #include <utils/Errors.h> // for status_t #include <utils/RefBase.h> #include <utils/String8.h> #include <binder/IInterface.h> #include <binder/Parcel.h> namespace android { class IArithmeticService : public IInterface { public: // 重要的宏定义,提供Service的asInterface方法和descriptor成员 DECLARE_META_INTERFACE(ArithmeticService); // 实际工作的成员函数 virtual double add(double a, double b) = 0; virtual double div(double a, double b) = 0; virtual double mul(double a, double b) = 0; virtual double sub(double a, double b) = 0; }; class BnArithmeticService : public BnInterface<IArithmeticService> { public: virtual status_t onTransact( uint32_t code, const Parcel & data, Parcel * reply, uint32_t flags = 0); }; } #endif |
IArithmeticService.cpp
在这个文件中,需要完成BpArithmeticService和BnArithmeticService类的实际编写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
#include <stdint.h> #include <sys/types.h> #include <binder/Parcel.h> #include <binder/IMemory.h> #include <IArithmeticService.h> #include <utils/Errors.h> #include <utils/String8.h> namespace android { // 定义Binder传输的code值 // 注意第一个值都必须是IBinder::FIRST_CALL_TRANSACTION enum { ADD = IBinder::FIRST_CALL_TRANSACTION, SUB, MUL, DIV }; // BpArithmeticService从BpInterface模板类继承而来 class BpArithmeticService : public BpInterface<IArithmeticService> { public: BpArithmeticService(const sp<IBinder>& impl) : BpInterface<IArithmeticService>(impl) { } // 如前所述,没有做什么特别工作,只是打包数据并发送 virtual double add(double a, double b) { Parcel data, reply; double result; data.writeInterfaceToken(IArithmeticService::getInterfaceDescriptor()); data.writeDouble(a); data.writeDouble(b); remote()->transact(ADD, data, &reply); reply.readDouble(&result); return result; } virtual double sub(double a, double b) { Parcel data, reply; double result; data.writeInterfaceToken(IArithmeticService::getInterfaceDescriptor()); data.writeDouble(a); data.writeDouble(b); remote()->transact(SUB, data, &reply); reply.readDouble(&result); return result; } virtual double mul(double a, double b) { Parcel data, reply; double result; data.writeInterfaceToken(IArithmeticService::getInterfaceDescriptor()); data.writeDouble(a); data.writeDouble(b); remote()->transact(MUL, data, &reply); reply.readDouble(&result); return result; } virtual double div(double a, double b) { Parcel data, reply; double result; data.writeInterfaceToken(IArithmeticService::getInterfaceDescriptor()); data.writeDouble(a); data.writeDouble(b); remote()->transact(DIV, data, &reply); reply.readDouble(&result); return result; } }; // 关键的宏,完成DECLARE_META_INTERFACE宏中定义的方法 IMPLEMENT_META_INTERFACE(ArithmeticService, "ArithmeticService"); // BnArithmeticService::onTransact方法的定义,如前所述根据具体的code值 // 调用实际的方法进行数据处理,并将结果写入reply中返回 status_t BnArithmeticService::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case ADD: { CHECK_INTERFACE(IArithmeticService, data, reply); const double a = data.readDouble(); const double b = data.readDouble(); double result = add(a, b); reply->writeDouble(result); return NO_ERROR; } break; case SUB: { CHECK_INTERFACE(IArithmeticService, data, reply); const double a = data.readDouble(); const double b = data.readDouble(); double result = sub(a, b); reply->writeDouble(result); return NO_ERROR; } break; case MUL: { CHECK_INTERFACE(IArithmeticService, data, reply); const double a = data.readDouble(); const double b = data.readDouble(); double result = mul(a, b); reply->writeDouble(result); return NO_ERROR; } break; case DIV: { CHECK_INTERFACE(IArithmeticService, data, reply); const double a = data.readDouble(); const double b = data.readDouble(); double result = div(a, b); reply->writeDouble(result); return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } } |
ArithmeticService.h
这里就是实际的Service类,从BnXXXXXService类中继承而来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <utils/Errors.h> #include "include/IArithmeticService.h" namespace android { class ArithmeticService : public BnArithmeticService { public: ArithmeticService(); // 注册service时调用 static void instantiate(); virtual double add(double a, double b); virtual double sub(double a, double b); virtual double mul(double a, double b); virtual double div(double a, double b); }; } |
ArithmeticService.cpp
最终的Service文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
#define LOG_TAG "ArithmeticService" #include <utils/Log.h> #include <cutils/log.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <utils/Errors.h> #include <utils/String8.h> #include <utils/String16.h> #include <IArithmeticService.h> #include "ArithmeticService.h" namespace android{ // 注册service用 void ArithmeticService::instantiate() { ALOGD("%s start", __FUNCTION__); defaultServiceManager()->addService(String16("arithmetic"), new ArithmeticService()); } ArithmeticService::ArithmeticService() { ALOGD("ArithmeticService constructor."); } double ArithmeticService::add(double a, double b) { double result = a + b; ALOGD("a = %lf, b = %lf, result = %lf", a ,b, result); return result; } double ArithmeticService::sub(double a, double b) { double result = a - b; ALOGD("a = %lf, b = %lf, result = %lf", a ,b, result); return result; } double ArithmeticService::mul(double a, double b) { double result = a * b; ALOGD("a = %lf, b = %lf, result = %lf", a ,b, result); return result; } double ArithmeticService::div(double a, double b) { double result = a / b; ALOGD("a = %lf, b = %lf, result = %lf", a ,b, result); return result; } } |
添加启动代码
首先在framework/av/media/mediaserver/Android.mk中的LOCAL_C_INCLUDES中添加如下一行: framework/arithmetic/ \ 记得最后还要加上‘\’反斜杠;在LOCAL_SHARED_LIBYARIES中添加如下一行:libarithmeticservice \ 当然也要记得最后的‘\’反斜杠。
然后我们模仿MediaPlayerService那样,在main_mediaserver.cpp文件中添加ArithmeticService的启动代码,当然你也可以自己编写一个c程序来启动这个service
1 2 3 4 5 6 |
…………… #include "ArithmeticService.h" ……………… MediaPlayerService::instantiate(); ArithmeticService::instantiate(); ResourceManagerService::instantiate(); |
Selinux权限
Selinux权限的设置分为三步
(1)首先需要服务起来的时候,服务需要有一个定义的type,所以我们在service.te文件中为我们的service定一个type
1 |
type arithmetic_service, service_manager_type; |
(2)为service定义了一个type后,那么就需要将这个type赋予我们的service了,我们在service_contexts中添加如下代码,这样service起来后,它的type就是arithmetic_service了
1 |
arithmetic u:object_r:arithmetic_service:s0 |
(3)最后就是添加allow规则,因为闲杂我们的service是在MediaServer中加载起来的,而所以我们在mediaserver.te文件中添加如下allow规则
1 |
allow mediaserver arithmetic_service:service_manager {add find}; |
这样,Selinux权限方面的内容就OK了
编译运行
然后就是编写相应的Android.mk文件了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ ArithmeticService.cpp \ IArithmeticService.cpp LOCAL_SHARED_LIBRARIES := \ libbinder \ libcutils \ liblog \ libutils \ LOCAL_C_INCLUDES := \ $(TOP)/frameworks/arithmetic/include \ $(TOP)/frameworks/native/include LOCAL_CLANG := true LOCAL_MODULE := libarithmeticservice LOCAL_32_BIT_ONLY := true include $(BUILD_SHARED_LIBRARY) include $(call all-makefiles-under,$(LOCAL_PATH)) |
编写完Android.mk文件后,直接将整个arithmetic文件夹放到android源码根目录下的framework文件夹下面编译即可。编译完成后,可以在out/target/product/{Project}/system/lib目录下找到一个名为libarithmeticservice.so的文件。
然后就是刷机启动了,简单的判断service有没有起来的方法就是手机开机以后使用adb连接手机,然后通过service list指令就可以列出手机当前运行的service,一切都没有问题的话,我们添加的arithmetic服务就运行起来了。
编写client程序
直接在arithmetic目录下面添加一个ari_client目录,在目录内添加Android.mk和main_client.cpp文件。main_client.cpp文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#define LOG_TAG "ArithmeticClient" #include <utils/Log.h> #include <cutils/log.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <utils/Errors.h> #include <utils/String8.h> #include <utils/String16.h> #include <utils/RefBase.h> #include <IArithmeticService.h> #include "ArithmeticService.h" using namespace android; // 死亡通知函数 class ServiceDeathRecipient : public IBinder::DeathRecipient { virtual void binderDied(const wp<IBinder>& who __unused) { ALOGD("The ArithmeticService is dead!\n"); } }; int main(int artc __unused, char ** argv __unused) { // 之前的博客有说到,获取servicemanager sp<IServiceManager> sm = defaultServiceManager(); sp<IBinder> binder = sm->getService(String16("arithmetic")); sp<IArithmeticService> ArithmeticService; if(binder == 0) { ALOGE("get arithmetic srevice error!"); exit(1); } // 注册死亡通知函数 sp<ServiceDeathRecipient> dr = new ServiceDeathRecipient(); binder->linkToDeath(dr); ArithmeticService = interface_cast<IArithmeticService>(binder); double result_add = ArithmeticService->add(1.0, 2.0); ALOGD("Call Add method: 1.0 + 2.0 = %lf", result_add); double result_sub = ArithmeticService->sub(1201.2, 32.10); ALOGD("Call Sub method: 1201.2 + 32.10 = %lf", result_sub); double result_mul = ArithmeticService->mul(32.5, 40.2); ALOGD("Call Mul method: 32.5 + 40.2 = %lf", result_mul); double result_div = ArithmeticService->div(1000.0, 4); ALOGD("Call Div method: 1000.0 + 4 = %lf", result_div); } |
Android.mk文件的内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ main_client.cpp LOCAL_SHARED_LIBRARIES := \ libbinder \ libcutils \ liblog \ libutils \ libarithmeticservice \ LOCAL_C_INCLUDES := \ $(TOP)/frameworks/arithmetic \ $(TOP)/frameworks/arithmetic/include \ $(TOP)/frameworks/native/include LOCAL_CLANG := true LOCAL_MODULE := arithmeticclient LOCAL_32_BIT_ONLY := true include $(BUILD_EXECUTABLE) include $(call all-makefiles-under,$(LOCAL_PATH)) |
按照原本的目录层次,将更新后的代码放到framework文件夹下面,用mmm编译,就可以在out/target/product/{Project}/system/bin路劲下看到一个arithmeticclient的可执行程序。如果之前编译的是user版本,那么只能执行make刷机了,如果是eng版本的软件可以通过usb插上手机,执行adb remount,然后将这个文件push到手机/system/bin路劲下,然后直接执行就可以了。因为使用的是Android的Log输出,所以要用adb shell logcat ArithmeticClient:D ArithmeticService:D *:s -v threadtime才能看到最后的输出的内容