Android系统服务分析与Native Service实例_android native service-程序员宅基地

技术标签: 系统服务  终端安全  Native Service  

前言

在前面的文章中:基于AIDL编程实现Android远程Service服务 介绍了如何在 Android 中通过 AIDL 实现远程服务,然而并未将自定义的远程服务注册为系统服务(System Service)。Android 为用户提供的很多功能是通过系统服务实现的,本文将学习记录下 Android 系统服务的相关知识,同时学习下远程服务除了使用 ALDL 编写的 Java 层服务外,还存在的另一种形式——Native Service。

System Service

使用命令 adb shell service list 即可列出当前 Android 系统的系统服务列表信息:
在这里插入图片描述
下面先来了解下 System Java Service 是如何定义和生成的,参考文章:Android系统服务(SystemService)简介

Vibrator 服务分析

我们从一个简单的系统服务 Vibrator 服务(vibrator: [android.os.IVibratorService])来看一下一个系统服务是怎样建立的。Vibrator 服务提供的控制手机振动的接口,应用可以调用 Vibrator 的接口来让手机产生振动,达到提醒用户的目的。

从 Android 的官方文档中可以看到 Vibrator 只是一个抽象类,只有4个抽象接口:

abstract void cancel() 取消振动
abstract boolean hasVibrator() 是否有振动功能
abstract void vibrate(long[] pattern, int repeat) 按节奏重复振动
abstract void vibrate(long milliseconds) 持续振动

1、应用中使用振动服务的方法也很简单,如让手机持续振动500毫秒:

Vibrator mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
mVibrator.vibrate(500);

2、从文档中可以看到 Vibrator 只是定义在 android.os 包里的一个抽象类,在源码里的位置即frameworks/base/core/java/android/os/Vibrator.java,那么应用中实际使用的是哪个实例呢?应用中使用的 Vibrator 实例是通过 Context 的一个方法getSystemService(Context.VIBRATOR_SERVICE)获得的,而 Context 的实现一般都在 ContextImpl 中,那我们就看一下 ContextImpl 是怎么实现 getSystemService 的:

// frameworks/base/core/java/android/app/ContextImpl.java
@Override
public Object getSystemService(String name) {
    
    return SystemServiceRegistry.getSystemService(this, name);
}

3、SystemServiceRegistry 是 Android 6.0 之后才有的,Android 6.0 之前的代码没有该类,下面的代码是直接写在 ContextImpl 里的:

//frameworks/base/core/java/android/app/SystemServiceRegistry.java
 public static Object getSystemService(ContextImpl ctx, String name) {
    
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}

4、SYSTEM_SERVICE_MAP 是一个 HashMap,通过我们服务的名字 name 字符串,从这个 HashMap 里取出一个 ServiceFetcher,再 return 这个 ServiceFetcher 的 getService()。ServiceFetcher 是什么?它的 getService()又是什么?既然他是从 SYSTEM_SERVICE_MAP 这个 HashMap 里 get 出来的,那就找一找这个 HashMap 都 put 了什么。通过搜索 SystemServiceRegistry 可以找到如下代码:

private static <T> void registerService(String serviceName, Class<T> serviceClass,
        ServiceFetcher<T> serviceFetcher) {
    
    SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}

5、这里往 SYSTEM_SERVICE_MAP 里 put 了一对 String 与 ServiceFetcher 组成的 key/value 对,registerService() 又是从哪里调用的?继续搜索可以发现很多类似下面的代码:

static {
    
    registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
            new CachedServiceFetcher<AccessibilityManager>() {
    
        @Override
        public AccessibilityManager createService(ContextImpl ctx) {
    
            return AccessibilityManager.getInstance(ctx);
        }});

    ...
    registerService(Context.VIBRATOR_SERVICE, Vibrator.class,
            new CachedServiceFetcher<Vibrator>() {
    
        @Override
        public Vibrator createService(ContextImpl ctx) {
    
            return new SystemVibrator(ctx);
        }});
    ...
}

6、SystemServiceRegistry 的 static 代码块里通过 registerService 注册了很多的系统服务,其中就包括我们正在调查的VIBRATOR_SERVICE,通过结合上面的分析代码可以可以知道getSystemService(Context.VIBRATOR_SERVICE)得到的是一个 SystemVibrator 的实例,通过查看 SystemVibrator 的代码也可以发现 SystemVibrator 确实是继承自 Vibrator:

public class SystemVibrator extends Vibrator {
    
    ...
}

7、我们再从 SystemVibrator 看一下系统的振动控制是怎么实现的。以 hasVibrator() 为例,这个是查询当前系统是否能够振动,在 SystemVibrator 中它的实现如下:

public boolean hasVibrator() {
    
    ...
    try {
    
        return mService.hasVibrator();
    } catch (RemoteException e) {
    
    }
    ...
}

8、这里直接调用了一个 mService.hasVibrator(),那 mService 是什么?哪来的?搜索一下可以发现:

private final IVibratorService mService;
public SystemVibrator() {
    
    ...
    mService = IVibratorService.Stub.asInterface(
            ServiceManager.getService("vibrator"));
}

9、mService 是一个 IVibratorService,我们先不去管IVibratorService.Stub.asInterface是怎么回事,先看一下 IVibratorService 是什么。搜索一下代码发现这并不是一个 java 文件,而是一个 aidl 文件:

frameworks/base/core/java/android/os/IVibratorService.aidl

AIDL (Android Interface Definition Language) 是Android中的接口定义文件,为系统提供了一种简单跨进程通信方法。IVibratorService 中定义了几个接口,SystemVibrator中使用的也是这几个接口,包括我们刚才使用的hasVibrator():

interface IVibratorService
{
    
    boolean hasVibrator();
    void vibrate(...);
    void vibratePattern(...);
    void cancelVibrate(IBinder token);
}

10、这里又只是接口定义,接口实现在哪呢?通过在 frameworks/base 目录下进行 grep 搜索,或者在 AndroidXRef 搜索,可以发现 IVibratorService 接口的实现在frameworks/base/services/java/com/android/server/VibratorService.java

public class VibratorService extends IVibratorService.Stub

11、可以看到 VibratorService 实现了 IVibratorService 定义的所有接口,并通过 JNI 调用到 native 层,进行更底层的实现。更底层的实现不是这篇文档讨论的内容,我们需要分析的是 VibratorService 怎么成为系统服务的。那么 VibratorService 是怎么注册为系统服务的呢?在 SystemServer 里面:

VibratorService vibrator = null;
...
//实例化VibratorService并添加到ServiceManager
traceBeginAndSlog("StartVibratorService");
vibrator = new VibratorService(context);
ServiceManager.addService("vibrator", vibrator);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
...
//通知服务系统启动完成
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeVibratorServiceReady");
try {
    
    vibrator.systemReady();
} catch (Throwable e) {
    
    reportWtf("making Vibrator Service ready", e);
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);

这样在 SystemVibrator 里就可以通过下面的代码连接到 VibratorService,与底层的系统服务进行通信了:

IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator"));

mService 相当于 IVibratorService 在应用层的一个代理,所有的实现还是在 SystemServer 的 VibratorService 里。看代码时可以发现 registerService 是在 static 代码块里静态调用的,所以 getSystemServcr 获得的各个 Manager 也都是单例的。

系统服务实现流程

画个图归纳如下:
在这里插入图片描述
从上面的分析,我们可以总结出 Vibrator 服务的整个实现流程:

1、定义一个抽象类 Vibrator,定义了应用中可以访问的一些抽象方法:
frameworks/base/core/java/android/os/Vibrator.java;

2、定义具体的类 SystemVibrator 继承 Vibrator,实现抽象方法:
frameworks/base/core/java/android/os/SystemVibrator.java;

3、定义一个 AIDL 接口文件 IVibratorService,定义系统服务接口:
frameworks/base/core/java/android/os/IVibratorService.aidl

4、定义服务 VibratorService,实现 IVibratorService 定义的接口:
frameworks/base/services/java/com/android/server/VibratorService.java

public class VibratorService extends IVibratorService.Stub

5、将VibratorService添加到系统服务:
frameworks/base/services/java/com/android/server/SystemServer.java

VibratorService vibrator = null;
...
//实例化VibratorService并添加到ServiceManager
Slog.i(TAG, "Vibrator Service");
vibrator = new VibratorService(context);
ServiceManager.addService("vibrator", vibrator);
...
//通知服务系统启动完成
try {
    
    vibrator.systemReady();
} catch (Throwable e) {
    
    reportWtf("making Vibrator Service ready", e);
}

6、在 SystemVibrator 中通过 IVibratorService 的代理连接到 VibratorService,这样 SystemVibrator 的接口实现里就可以调用 IVibratorService 的接口:
frameworks/base/core/java/android/os/SystemVibrator.java

private final IVibratorService mService;
...
public SystemVibrator() {
    
    ...
    mService = IVibratorService.Stub.asInterface(
            ServiceManager.getService("vibrator"));
    ...
    public boolean hasVibrator() {
    
        ...
        try {
    
            return mService.hasVibrator();
        } catch (RemoteException e) {
    
        }
        ...
    }
}

7、在 Context 里定义一个代表 Vibrator 服务的字符串:
frameworks/base/core/java/android/content/Context.java

public static final String VIBRATOR_SERVICE = "vibrator";

8、在 ContextImpl 里添加 SystemVibrator 的实例化过程:
frameworks/base/core/java/android/app/ContextImpl.java

registerService(VIBRATOR_SERVICE, new ServiceFetcher() {
    
public Object createService(ContextImpl ctx) {
    
    return new SystemVibrator(ctx);
}});  

9、在应用中使用 Vibrator 的接口:

Vibrator mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
mVibrator.vibrate(500);

10、为保证编译正常,还需要将 AIDL 文件添加到编译配置里frameworks/base/Android.mk

LOCAL_SRC_FILES += \
...
core/java/android/os/IVibratorService.aidl \

Native Service

Android 系统服务大致分为三大类:本地守护进程、Native 系统服务和 Java 系统服务,如下图所示:
在这里插入图片描述
本地守护进程

init 进程根据 init.rc 文件中的定义,启动本地守护进程。这些进程会常驻在系统中,有的只会启动一次,有的如果退出了,还会被 init 启动。具体的启动方式就在 init.rc 中定义。下面大体列举几个守护进程及其功能。
在这里插入图片描述
Native系统服务

Native Service,这是 Android 系统里的一种特色,就是通过 C++ 或是 C 代码写出来的、供 Java 进行远程调用的 Remote Service,因为C/C++代码生成的是Native代码(机器代码),于是叫 Native Service。Java语言不能直接进行系统调用,必须透过 JNI 来调用 C 代码来访问系统功能,而 Native Service 则完全不同,C++具备直接进行系统调用的能力,于是在访问操作系统或是硬件功能时,不再需要 JNI,可以直接进行调用,代码实现上会更加统一。显然 Native 代码比 Java 这种解释型语言高得多的执行效率,随着 Android 系统的性能需求越来越高,Native Service 需求将越来越高。

Native Service 运行在本地守护进程中,比如 mediaserver 守护进程中就包含 AudioFlinger、MediaPlayerService、CameraService、AudioPolicyService和SoundTriggerHwService 等服务。在 mediaserver 进程的 main 函数中,初始化这些服务的实例,代码如下:

int main(int argc __unused, char** argv)
{
    
    ...
    sp<ProcessState> proc(ProcessState::self());
    sp<IServiceManager> sm = defaultServiceManager();
    ALOGI("ServiceManager: %p", sm.get());
    AudioFlinger::instantiate();
    MediaPlayerService::instantiate();
    CameraService::instantiate();
    AudioPolicyService::instantiate();
    SoundTriggerHwService::instantiate();
    ...
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
}

在所属进程初始化的时候会将 Native 系统服务注册到 ServiceManager 中。这样,其他的应用或服务就可以通过 binder 机制调用 Native 系统服务了。当然,我们也可以自己开发一个 Native 系统服务,实现其 Binder 接口,这样 Native 层的其他应用或服务就可以调用该服务了。如果我们开发的 Native 系统服务想提供给 Java 层应用使用,就需要实现一个 Java 接口,然后通过 JNI 调用 Native 系统服务

在这里插入图片描述
与 Java 类型的 System Service 中的 Stub.Proxy 对象的实现相对应,Native Service 也会定义 BpXXX 对象,其中B代表Binder,p代码Proxy,所需要的接口名为XXX。因为所需要发送的Binder通信都是经由 BpBinder::transact() 方法发送,于是Java环境与Native环境的Proxy在本质上是一回事,只是提供不同编程语言环境里的不同实现而已。同时,在接收与处理端, IPCThreadState 对象回调到 BBinder 引用的 onTransact() 时,此时的 BBinder 引用的并不再是一个 JavaBBinder 对象,而是拓展出来的 BnXXX 对象,n代表Native。于是 BBinder::transact() 就会直接调用 BnXXX 里实现的 onTransact(),在这一 onTransact() 方法里就可以处理 Binder 消息,并将结果返回。

蓝牙设置实例

Demo 项目结构如下:
在这里插入图片描述

IDeviceMac.h接口文件

通过一个读取和设置蓝牙地址的例子为例,接口名为 IDeviceMac, 代码如下:

#ifndef XTC_IDEVICEMAC_H
#define XTC_IDEVICEMAC_H

#include <utils/RefBase.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <utils/String8.h>
#include <android/log.h>

#ifdef TAG
#undef TAG
#endif
#define TAG "DeviceMac"

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__)

namespace android {
    

class IDeviceMac : public IInterface {
    
public:
    enum {
    
        SET_BT_MAC = IBinder::FIRST_CALL_TRANSACTION,
        GET_BT_MAC,
    };

    virtual int setBTMac(String8 bt) = 0;

    virtual String8 getBTMac() = 0;

    DECLARE_META_INTERFACE(DeviceMac);
};

class BnDeviceMac : public BnInterface<IDeviceMac> {
    
public:
    virtual status_t onTransact( uint32_t code,
                                const Parcel& data,
                                Parcel* reply,
                                uint32_t flags);
};
   
} // end namespace android
#endif

以上代码很简单:

  1. 定义一个 IDeviceMac 类继承自接口类IInterface( IInterface 跟Java环境一下,可用于提供 asBinder() 方法,返回一个IBinder引用), IDeviceMac 类里定义了对外提供的服务接口(如setBTMac()、getBTMac());
  2. 同时注意到DECLARE_META_INTERFACE(DeviceMac);是一个宏定义,用来定义继承IInterface必须实现的两个方法,asInterface()函数和 getInterfaceDescriptor() 函数;
  3. Java 环境里的 IBinder 会有 asInterface() 接口方法,在 libbinder 里通过 C++ 实现的 IBinder 则不能提供这一接口,于是需要通过一个全局有效的 interface_cast() 宏来完成这一功能,interface_cast() 是调用一个尚未定义的 INTERFACE::asInterface() 宏,于是只会在有明确定义 asInterface() 的地方,interface_cast() 才会有效;
  4. 可以看到我们定义IDeviceMac后,还定义了一个类BnDeviceMac,这个是Binder调用的一个规范,即定义Ixxx接口后,Bpxxx表示Client端接口,Bnxxx表示Service端接口, BpxxxBnxxx都需要我们去实现具体内容,并且BnxxxBpxxx中的方法和Ixxx中的方法是一一对应的。

IDeviceMac.cpp接口实现文件

接下来继续编写 IDeviceMac.cpp 文件:

#include "IDeviceMac.h"

namespace android {
    

class BpDeviceMac : public BpInterface<IDeviceMac> {
    

public:
    BpDeviceMac(const sp<IBinder>& impl) : BpInterface<IDeviceMac>(impl)
    {
    
    }

    int setBTMac(String8 bt) {
    
        LOGI("Bp setBT");
        Parcel data, reply;
        data.writeInterfaceToken(IDeviceMac::getInterfaceDescriptor());
        data.writeString8(bt);
        remote()->transact(SET_BT_MAC, data, &reply);
        return reply.readInt32();
    }

    String8 getBTMac() {
    
        LOGI("Bp getBT");
        Parcel data, reply;
        data.writeInterfaceToken(IDeviceMac::getInterfaceDescriptor());
        remote()->transact(GET_BT_MAC, data, &reply);
        return reply.readString8();
    }
};

IMPLEMENT_META_INTERFACE(DeviceMac, "DeviceMac");
/* Macro above expands to code below.
const android::String16 IDeviceMac::descriptor("DeviceMac");
const android::String16& IDeviceMac::getInterfaceDescriptor() const {
    return IDeviceMac::descriptor;
}
android::sp<IDeviceMac> IDeviceMac::asInterface(const android::sp<android::IBinder>& obj) {
    android::sp<IDeviceMac> intr;
    if (obj != NULL) {
        intr = static_cast<IDeviceMac*>(obj->queryLocalInterface(IDeviceMac::descriptor).get());
        if (intr == NULL) {
            intr = new BpDeviceMac(obj);
        }
    }
    return intr;
}
*/

status_t BnDeviceMac::onTransact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
    
    CHECK_INTERFACE(IDeviceMac, data, reply);
    LOGI("Bn onTransact code:%d", code);
    switch(code) {
    
        case SET_BT_MAC:
            reply->writeInt32(setBTMac(data.readString8()));
            return NO_ERROR;
        case GET_BT_MAC:
            reply->writeString8(getBTMac());
            return NO_ERROR;
        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
}

} // end namespace android

代码简单解读:

  1. 上面代码中IMPLEMENT_META_INTERFACE(DeviceMac, "DeviceMac");下面注释掉的内容就是这个宏定义代表的实际代码,也是就是说IDeviceMac.h中的那个宏定义其实就是定义这两个方法;
  2. BpDeviceMac 里面的内容就是把相关参数写到Parcel中,这是一个用来读写跨进程参数的类, 然后调用remote()->transact(), 就调用到BnDeviceMac::onTransact()中,BnDeviceMac::onTransact()函数中已经跨过进程了,具体怎么做到的就涉及到 IPC 原理了,这里不做讨论;
  3. 接下来定义的 BnDeviceMac::onTransact 做的事情也很简单,就是从Parcel中将Client传过来的数据读出来,然后调用BnDeviceMac中对应的实现方法,这里需要注意,由于BnDeviceMac::onTransact()代码和BpDeviceMac写在了同一个文件中,看起来有点像BnDeviceMac调用BpDeviceMac的 一样,其实是BnDeviceMac::onTranscat()中调用的setBTMac() getBTMac()是在调用BnDeviceMac中实现的方法, 接下来就讲BnDeviceMac的实现。

DeviceMacService服务实现文件

1、DeviceMacService.h 如下:

#ifndef XTC_DEVICEMACSERVICE_H
#define XTC_DEVICEMACSERVICE_H

#include "IDeviceMac.h"

#define SERVER_NAME "DeviceMacService"

namespace android {
    

class DeviceMacService : public BnDeviceMac {
    
public:
    DeviceMacService();
    virtual ~DeviceMacService();
    //IDeviceMac
    virtual int setBTMac(String8 bt);
    virtual String8 getBTMac();
};

} // end namespace android
#endif

2、DeviceMacService.cpp 如下:

#include "DeviceMacService.h"

namespace android {
    

DeviceMacService::DeviceMacService() {
    

}

DeviceMacService::~DeviceMacService() {
    

}

int DeviceMacService::setBTMac(String8 bt) {
    
    LOGI("Bn setBT, bt:%s", bt.string());
    return NO_ERROR;
}

String8 DeviceMacService::getBTMac() {
    
    LOGI("Bn getBT");
    return String8("4a:4b:4c:3a:3b:3c");
}

} // end namespace android

DeviceMacService这个类继承了BnDeviceMac, 实现了其中的方法,所以BnDeviceMac::onTransact()方法中相关调用就会调到DeviceMacService,在DeviceMacService中,我们就能做我们实际想做的事情了。

注册Service和Client端调用

main_server.cpp 文件如下:

#include "DeviceMacService.h"
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>

using namespace android;

sp<IDeviceMac> getService() {
    
    sp<IServiceManager> sm = defaultServiceManager();
    sp<IBinder> binder = sm->getService(String16(SERVER_NAME));
    //interfa_cast()的函数
    sp<IDeviceMac> service = interface_cast<IDeviceMac>(binder);
    return service;
}

int main(int argc, char** argv) {
    
    if (argc == 1) {
    
        LOGI("start DeviceMacService");
        //addService()函数用于注册服务
        defaultServiceManager()->addService(String16(SERVER_NAME), new DeviceMacService());
        android::ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
    } else if (argc == 2) {
    
        sp<IDeviceMac> devMacServer = getService();
        devMacServer->setBTMac(String8("1a:1b:1c:1a:1b:1c"));
        String8 bt = devMacServer->getBTMac();
        LOGI("get bt mac:%s", bt.string());
    }
    return 0;
}

以上代码注意几个点:

  1. 添加服务的代码很简单, 三行代码,固定的操作;
  2. 获取服务过程中,有个interfa_cast的函数,会将IBinder作为参数 new 一个BpDeviceMac对象,我们通过这个对象进行相关接口调用,最终调用到DeviceMacService
  3. 注: 为了测试方便,此处将添加 Service 和调用 Service 写在了同一个可执行文件中,实际项目都是分开的。

代码的编译与运行调用

现在万事具备,只等编译运行了, Android.mk代码如下:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := macserver
LOCAL_MODULE_TAGS := optional

LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \
        frameworks/native/include \
        system/core/include

LOCAL_SRC_FILES := IDeviceMac.cpp DeviceMacService.cpp main_server.cpp
LOCAL_SHARED_LIBRARIES := libutils libcutils libbinder libhardware

include $(BUILD_EXECUTABLE)

编译与运行方法:

  1. 确保当前Android源码全部编译通过(有些依赖需先编译好);
  2. 将service目录放到Android源码目录中(比如vendor/qcom/service);
  3. 在Android源码根目录执行 mmm vendor/qcom/service
  4. 执行完后编译的可执行文件在out/target/product/xxx/system/bin/下面(xxx为lunch的product);
  5. 将编译好的可执行文件macserver通过adb push 到手机system/bin/下面(adb需要root, 即执行 adb root , adb remount);
  6. 执行adb shell chmod 777 /system/bin/macserver加上可执行权限, 然后启动服务, 执行adb shell /system/bin/macserver(会阻塞当前窗口);
  7. 重新开一个窗口执行adb命令adb shell /system/bin/macserver 1即可调用Service,可以通过logcat过滤 DeviceMac 来查看log。

如果你想在开机后就自动启动服务,并且指定Service所属的用户组,可在 init.rc 中加入如下代码:

service macserver /system/bin/macserver 
     class main
     user root
     group root

拓展——编写AIDL

如果你既想少写点代码,又想调用起来比较方便,这个也有实现方法,就是编写AIDL文件,和 Java 里面的 AIDL 类似,只不过你要放在Android源码里面进行编译,系统会自动根据Ixxx.aidl在编译过程中生成Ixxx.cpp,这个cpp文件中就和上面我们写的IDeviceMac.cpp内容基本一致,也就是说这部分代码可以自动生成了,然后你只需要在Service端写一个类继承Bnxxx然后实现AIDL文件中定义的方法即可,使用非常方便,Android 7.1 上面的ICameraService.aidl就是以这种方式实现的,部分代码如下,可以参考一下:
frameworks/av/camera/aidl/android/hardware/ICameraService.aidl

  /**
     * Types for getNumberOfCameras
     */
    const int CAMERA_TYPE_BACKWARD_COMPATIBLE = 0;
    const int CAMERA_TYPE_ALL = 1;

    /**
     * Return the number of camera devices available in the system
     */
    int getNumberOfCameras(int type);

    /**
     * Fetch basic camera information for a camera device
     */
    CameraInfo getCameraInfo(int cameraId);

    /**
     * Default UID/PID values for non-privileged callers of
     * connect(), connectDevice(), and connectLegacy()
     */
    const int USE_CALLING_UID = -1;
    const int USE_CALLING_PID = -1;

    /**
     * Open a camera device through the old camera API
     */
    ICamera connect(ICameraClient client,
            int cameraId,
            String opPackageName,
            int clientUid, int clientPid);

如果以这种方式实现的话,编译的 Android.mk 中需要加入如下代码:

LOCAL_AIDL_INCLUDES := \
    frameworks/av/camera/aidl \

LOCAL_SRC_FILES := \
    aidl/android/hardware/ICameraService.aidl \

即要引入头文件路径和aidl源文件。

如果你想要看下自动生成的Ixxx.cpp的代码,其路径为:out/target/product/xxx1/obj/xxx2/xxx3_intermediates/aidl-generated/
xxx1表示你 lunch 时选的 product, xxx2表示你编译的模块类型,通常是 SHARED_LIBRARIES 或者
EXECUTABLES,xxx3表示你编译的模块中 LOCAL_MODULE 定义的名字。例如: out/target/product/msm8953/obj/SHARED_LIBRARIES/libcamera_client_intermediates/aidl-generated/src/aidl/android/hardware/ICameraService.cpp

加减乘除实例

目录结构如下:

  1. 创建一个名为 arithmetic 的文件夹,创建 Android.mk、ArithmeticService.cpp、ArithmeticService.h 和 IArithmeticService.cpp 这四个文件;
  2. 在 arithmetic 文件夹内再创建一个 include 子文件夹并创建一个 IArithmeticService.h 文件;
  3. 在 arithmetic 文件夹内再添加一个 ari_client 目录,在目录内添加 Android.mk 和 main_client.cpp 文件。

下面开始编写代码。

IArithmeticService.h

首先创建一个 IArithmeticService.h 类,这个类作为 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<IArithmeticService>
    {
    
    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<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 类中继承而来。

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

启动设置与编译运行

1、添加启动代码

首先在 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();

2、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};

3、编译运行

然后就是编写相应的 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 服务就运行起来了。

main_client.cpp

编写 client 程序:

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

int main(int artc __unused, char ** argv __unused) 
{
    
    sp<IServiceManager> sm = defaultServiceManager();
    sp<IBinder> binder = sm->getService(String16("arithmetic"));
    sp<IArithmeticService> ArithmeticService;
    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 文件的内容:

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

总结

参考文章:

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_39190897/article/details/122204264

智能推荐

从零开始搭建Hadoop_创建一个hadoop项目-程序员宅基地

文章浏览阅读331次。第一部分:准备工作1 安装虚拟机2 安装centos73 安装JDK以上三步是准备工作,至此已经完成一台已安装JDK的主机第二部分:准备3台虚拟机以下所有工作最好都在root权限下操作1 克隆上面已经有一台虚拟机了,现在对master进行克隆,克隆出另外2台子机;1.1 进行克隆21.2 下一步1.3 下一步1.4 下一步1.5 根据子机需要,命名和安装路径1.6 ..._创建一个hadoop项目

心脏滴血漏洞HeartBleed CVE-2014-0160深入代码层面的分析_heartbleed代码分析-程序员宅基地

文章浏览阅读1.7k次。心脏滴血漏洞HeartBleed CVE-2014-0160 是由heartbeat功能引入的,本文从深入码层面的分析该漏洞产生的原因_heartbleed代码分析

java读取ofd文档内容_ofd电子文档内容分析工具(分析文档、签章和证书)-程序员宅基地

文章浏览阅读1.4k次。前言ofd是国家文档标准,其对标的文档格式是pdf。ofd文档是容器格式文件,ofd其实就是压缩包。将ofd文件后缀改为.zip,解压后可看到文件包含的内容。ofd文件分析工具下载:点我下载。ofd文件解压后,可以看到如下内容: 对于xml文件,可以用文本工具查看。但是对于印章文件(Seal.esl)、签名文件(SignedValue.dat)就无法查看其内容了。本人开发一款ofd内容查看器,..._signedvalue.dat

基于FPGA的数据采集系统(一)_基于fpga的信息采集-程序员宅基地

文章浏览阅读1.8w次,点赞29次,收藏313次。整体系统设计本设计主要是对ADC和DAC的使用,主要实现功能流程为:首先通过串口向FPGA发送控制信号,控制DAC芯片tlv5618进行DA装换,转换的数据存在ROM中,转换开始时读取ROM中数据进行读取转换。其次用按键控制adc128s052进行模数转换100次,模数转换数据存储到FIFO中,再从FIFO中读取数据通过串口输出显示在pc上。其整体系统框图如下:图1:FPGA数据采集系统框图从图中可以看出,该系统主要包括9个模块:串口接收模块、按键消抖模块、按键控制模块、ROM模块、D.._基于fpga的信息采集

微服务 spring cloud zuul com.netflix.zuul.exception.ZuulException GENERAL-程序员宅基地

文章浏览阅读2.5w次。1.背景错误信息:-- [http-nio-9904-exec-5] o.s.c.n.z.filters.post.SendErrorFilter : Error during filteringcom.netflix.zuul.exception.ZuulException: Forwarding error at org.springframework.cloud..._com.netflix.zuul.exception.zuulexception

邻接矩阵-建立图-程序员宅基地

文章浏览阅读358次。1.介绍图的相关概念  图是由顶点的有穷非空集和一个描述顶点之间关系-边(或者弧)的集合组成。通常,图中的数据元素被称为顶点,顶点间的关系用边表示,图通常用字母G表示,图的顶点通常用字母V表示,所以图可以定义为:  G=(V,E)其中,V(G)是图中顶点的有穷非空集合,E(G)是V(G)中顶点的边的有穷集合1.1 无向图:图中任意两个顶点构成的边是没有方向的1.2 有向图:图中..._给定一个邻接矩阵未必能够造出一个图

随便推点

MDT2012部署系列之11 WDS安装与配置-程序员宅基地

文章浏览阅读321次。(十二)、WDS服务器安装通过前面的测试我们会发现,每次安装的时候需要加域光盘映像,这是一个比较麻烦的事情,试想一个上万个的公司,你天天带着一个光盘与光驱去给别人装系统,这将是一个多么痛苦的事情啊,有什么方法可以解决这个问题了?答案是肯定的,下面我们就来简单说一下。WDS服务器,它是Windows自带的一个免费的基于系统本身角色的一个功能,它主要提供一种简单、安全的通过网络快速、远程将Window..._doc server2012上通过wds+mdt无人值守部署win11系统.doc

python--xlrd/xlwt/xlutils_xlutils模块可以读xlsx吗-程序员宅基地

文章浏览阅读219次。python–xlrd/xlwt/xlutilsxlrd只能读取,不能改,支持 xlsx和xls 格式xlwt只能改,不能读xlwt只能保存为.xls格式xlutils能将xlrd.Book转为xlwt.Workbook,从而得以在现有xls的基础上修改数据,并创建一个新的xls,实现修改xlrd打开文件import xlrdexcel=xlrd.open_workbook('E:/test.xlsx') 返回值为xlrd.book.Book对象,不能修改获取sheett_xlutils模块可以读xlsx吗

关于新版本selenium定位元素报错:‘WebDriver‘ object has no attribute ‘find_element_by_id‘等问题_unresolved attribute reference 'find_element_by_id-程序员宅基地

文章浏览阅读8.2w次,点赞267次,收藏656次。运行Selenium出现'WebDriver' object has no attribute 'find_element_by_id'或AttributeError: 'WebDriver' object has no attribute 'find_element_by_xpath'等定位元素代码错误,是因为selenium更新到了新的版本,以前的一些语法经过改动。..............._unresolved attribute reference 'find_element_by_id' for class 'webdriver

DOM对象转换成jQuery对象转换与子页面获取父页面DOM对象-程序员宅基地

文章浏览阅读198次。一:模态窗口//父页面JSwindow.showModalDialog(ifrmehref, window, 'dialogWidth:550px;dialogHeight:150px;help:no;resizable:no;status:no');//子页面获取父页面DOM对象//window.showModalDialog的DOM对象var v=parentWin..._jquery获取父window下的dom对象

什么是算法?-程序员宅基地

文章浏览阅读1.7w次,点赞15次,收藏129次。算法(algorithm)是解决一系列问题的清晰指令,也就是,能对一定规范的输入,在有限的时间内获得所要求的输出。 简单来说,算法就是解决一个问题的具体方法和步骤。算法是程序的灵 魂。二、算法的特征1.可行性 算法中执行的任何计算步骤都可以分解为基本可执行的操作步,即每个计算步都可以在有限时间里完成(也称之为有效性) 算法的每一步都要有确切的意义,不能有二义性。例如“增加x的值”,并没有说增加多少,计算机就无法执行明确的运算。 _算法

【网络安全】网络安全的标准和规范_网络安全标准规范-程序员宅基地

文章浏览阅读1.5k次,点赞18次,收藏26次。网络安全的标准和规范是网络安全领域的重要组成部分。它们为网络安全提供了技术依据,规定了网络安全的技术要求和操作方式,帮助我们构建安全的网络环境。下面,我们将详细介绍一些主要的网络安全标准和规范,以及它们在实际操作中的应用。_网络安全标准规范