Jimmy Chen

A Programmer

Java跨层访问native(C/C++)注册的service

Advertisements
Advertisements
Advertisements

  进行Binder开发时,比较多是在同语言下进行的,比如在JAVA下可以手动实现或者通过AIDL实现,在C++下手动实现。但是Binder也是可以夸语言通信的,这里打算分两篇来进行说明,其中一篇是在通过native层实现service的代码,并注册到system_server中,在上层通过Java实现访问;另一篇则相反,在Java层中实现service的代码,在native通过程序访问;那下面开始看代码实现,不过需要注意的是这里面的代码添加相应的selinux权限,所以一切实验都是在开机后通过adb shell setenforce 0来关闭selinux后进行的。

native层实现service

  native层实现service也不难,博主在之前的博客也有写过,不过之前的service启动代码是集成到system_server中的,这次我们就不使用这种方式了,我们自己编写可执行程序来启动service,并且编写一个简单的测试程序。

IArithmeticService.h

#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

这个是service和client都需要实现的基类,继承IInterface,其中需要特别注意的就是DECLARE_META_INTERFACE这个定义宏了,这个比较简单,没啥好说的。

IArithmeticService.cpp

这个类主要是用来实现上面.h中定义的代码

#define LOG_TAG "ArithmeticService"
#include <stdint.h>
#include <sys/types.h>

#include <binder/Parcel.h>
#include <binder/IMemory.h>
#include "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)
        {
            ALOGD("getInterfaceDescriptor = %s", String8(IArithmeticService::getInterfaceDescriptor()).string());
            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, "com.jimmy.service.ArithmeticService");

    // BnArithmeticService::onTransact方法的定义,如前所述根据具体的code值
    // 调用实际的方法进行数据处理,并将结果写入reply中返回
    status_t BnArithmeticService::onTransact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
    {
        switch(code) {
            case ADD: {
                ALOGD("Binder call 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);

        }
    }

}

  上面主要体现的是binder同学的client端的实现,流程很简单,就是将调用端的数据打包成Parcel,然后通过transact发送到service端即可。因为在.h文件中我们使用了DECLARE_META_INTERFACE这个定义宏,所以在实现的时候使用的是IMPLEMENT_META_INTERFACE,注意传递给该宏的第二个参数”com.jimmy.service.ArithmeticService”,这个参数表示的是后面进行binder通信,客户端和服务端需要进行校验的Token。同时需要注意看,如果我们在client会通过writeInterfaceToken方法,将Token写入到Parcel中,那么在服务端就必须通过CHECK_INTERFACE来对Parcel中的Token进行校验,否则会导致Parcel中的数据读写顺序不一致,而导致出错的。最后还实现了Service端的onTransaction方法,该方法作用就是读取客户端传递过来的数据,然后调用实际工作的方法,然后见执行的结果再通过Parcel传递会给客户端。

  稍微总结一下就是,到目前位置,我们已经完成了客户端代理的代码和服务端onTransact的代码,剩下的就是具体服务端的代码实现。这里博主写的代码主要实现演示怎么实现binder客户端和服务端的,所以服务端的具体实现就很简单了,拿个四则运算作为服务端的具体实现了。

ArithmeticService.h

#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);
    };

}

  头文件继承BnArithmeticService即可。

ArithmeticService.cpp

#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 "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;
}
}

具体实现也很简单,没什么特殊的。另外博主讲注册service需要使用的代码也整合到这里了,主要就是调用defaultServiceManager的addservcie方法,这个方法就是给native调用的。

TestService.cpp

#define LOG_TAG "ArithmeticService"

#include <fcntl.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>

#include "ArithmeticService.h"

int main(void)
{
    /* addService */

    /* 打开驱动, mmap */
    android::sp<android::ProcessState> proc(android::ProcessState::self());

    /* add service */
    android::ArithmeticService::instantiate();

    /* 消息循环体 */
    android::ProcessState::self()->startThreadPool();
    android::IPCThreadState::self()->joinThreadPool();

    return 0;
}

因为之前我们已经将添加服务的代码整合到service端的代码了,所以这里只需要调用就行,很简单,最后注意执行消息循环,不要让service进行退出就行。

TestClient.cpp

#define LOG_TAG "ArithmeticService"

#include <fcntl.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <utils/String16.h>

#include "ArithmeticService.h"

int main(void)
{
    /* get service */
    android::sp<android::IServiceManager> sm = android::defaultServiceManager();
    android::sp<android::IBinder> binder = sm->getService(android::String16("arithmetic"));
    if (binder == NULL) {
        ALOGD("client get service return NULL");
        return -1;
    }

    android::sp<android::IArithmeticService> service = android::interface_cast<android::IArithmeticService>(binder);
    service->add(1.0, 2.0);

    return 0;
}

上面是native测试的代码,经典套路,getServcie然后转换一下调用即可。

Android.mk

这里给出编译用的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

include $(BUILD_SHARED_LIBRARY)



include $(CLEAR_VARS)

LOCAL_SRC_FILES := \
        TestClient.cpp


LOCAL_SHARED_LIBRARIES := \
        libbinder           \
        libcutils           \
        liblog          \
        libutils            \
        libarithmeticservice \

LOCAL_C_INCLUDES :=             \
        $(TOP)/frameworks/arithmetic/include    \
        $(TOP)/frameworks/native/include

LOCAL_CLANG := true

LOCAL_MODULE := test_arithmetic_client

include $(BUILD_EXECUTABLE)


include $(CLEAR_VARS)

LOCAL_SRC_FILES := \
        TestService.cpp


LOCAL_SHARED_LIBRARIES := \
        libbinder           \
        libcutils           \
        liblog          \
        libutils            \
        libarithmeticservice \

LOCAL_C_INCLUDES :=             \
        $(TOP)/frameworks/arithmetic/include    \
        $(TOP)/frameworks/native/include

LOCAL_CLANG := true

LOCAL_MODULE := test_arithmetic_service

include $(BUILD_EXECUTABLE)


include $(call all-makefiles-under,$(LOCAL_PATH))

Java层访问native service

  上面我们已经实现了native的服务了,那么我们应该如何在Java层来访问这个native层实现的服务呢?其实很简单,我们只需要查看native层方法实现的顺序,然后在Java层够着一个AIDL文件即可。下面是博主构造出的AIDL文件。需要特别说明的是:

  • AIDL文件里面的方法顺序一定要和native层中enum中定义的方法顺序一致才行,不然会出现方法调用错乱的问题,例如明明我调用的是add方法,可是结果确实mul的结果
  • AIDL文件的包名需要和native中IMPLEMENT_META_INTERFACE宏定义传入的Token一致,否则会出现因Token不匹配导致调用失败的问题
// ArithmeticService.aidl
package com.jimmy.service;

// Declare any non-default types here with import statements

interface ArithmeticService {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    double add(double a, double b);
    double div(double a, double b);
    double mul(double a, double b);
    double sub(double a, double b);
}

  AIDL构造出来后,一切就简单了,我们就可以直接在Java代码中调用AIDL的方法进行传输了

package com.jimmy.arithmeticaidlbindertest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import com.jimmy.service.ArithmeticService;

import java.lang.reflect.Method;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "AIDLBinderTest";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        IBinder binder = null;

        try {
            // ServiceManager.getService是一个灰名单接口,可以反射调用
            Class<?> serviceManager = Class.forName("android.os.ServiceManager");
            Method getServiceMethod = serviceManager.getDeclaredMethod("getService", String.class);
            binder = (IBinder)getServiceMethod.invoke(null, "arithmetic");
            if (binder == null) {
                Log.d(TAG, "getService failed!");
                return;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        ArithmeticService service = ArithmeticService.Stub.asInterface(binder);
        try {
            service.add(1.0, 2.0);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

这样就可以了,相当的简单的。

Advertisements
Advertisements
Advertisements

发表评论

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

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

%d 博主赞过: