Jimmy Chen

A Programmer

(原创)在Native层使用Binder创建服务

  要编写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类,代码如下:

#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
    {
    public:
        virtual status_t onTransact( uint32_t code, const Parcel & data, Parcel * reply, uint32_t flags = 0);       
    };
    
}

#endif

IArithmeticService.cpp

在这个文件中,需要完成BpArithmeticService和BnArithmeticService类的实际编写。

#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
    {
    public:
        BpArithmeticService(const sp& impl)
                : BpInterface(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类中继承而来。

#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文件

#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

……………
#include "ArithmeticService.h"
………………
    MediaPlayerService::instantiate();
    ArithmeticService::instantiate();
    ResourceManagerService::instantiate();

Selinux权限

Selinux权限的设置分为三步

(1)首先需要服务起来的时候,服务需要有一个定义的type,所以我们在service.te文件中为我们的service定一个type

type arithmetic_service,            service_manager_type;

(2)为service定义了一个type后,那么就需要将这个type赋予我们的service了,我们在service_contexts中添加如下代码,这样service起来后,它的type就是arithmetic_service了

arithmetic      u:object_r:arithmetic_service:s0

(3)最后就是添加allow规则,因为闲杂我们的service是在MediaServer中加载起来的,而所以我们在mediaserver.te文件中添加如下allow规则

allow mediaserver arithmetic_service:service_manager {add find};

这样,Selinux权限方面的内容就OK了

编译运行

  然后就是编写相应的Android.mk文件了

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文件内容如下:

#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 

#include "ArithmeticService.h"

using namespace android;

// 死亡通知函数
class ServiceDeathRecipient : public IBinder::DeathRecipient {
    virtual void binderDied(const wp& who __unused) {
        ALOGD("The ArithmeticService is dead!\n");
    }
};

int main(int artc __unused, char ** argv __unused) 
{
    // 之前的博客有说到,获取servicemanager
    sp sm = defaultServiceManager();
    sp binder = sm->getService(String16("arithmetic"));
    sp ArithmeticService;
    
    if(binder == 0) {
        ALOGE("get arithmetic srevice error!");
        exit(1);
    }
    
    // 注册死亡通知函数
    sp dr = new ServiceDeathRecipient();
    binder->linkToDeath(dr);
    ArithmeticService = interface_cast(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文件的内容:

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才能看到最后的输出的内容

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d 博主赞过: