Android 瞎jer笔记:如何使用aidl调用系统服务(native framework)

没有彻底白盒化,所以简单当个工程笔记看看就是,在此就不解释binder通信原理或者aidl生成代码之类的了

而且本笔记由于其记录的工程过程本身,故着重于“客户端怎么调用服务”,关于“服务端怎么准备”则是着重于“修改”而不是“创建”,故关于服务端的记录恐有谬误

另外,虽然谷狗官方aidl文档里给出了一种c++实现:

实现服务

...

在C++后端中:

    #include <my/package/BnFoo.h>
    class MyFoo : public my::package::BnFoo { ... }

...

 

注册和获取服务

...

在 CPP 后端中:

    #include <binder/IServiceManager.h>
    // registering
    defaultServiceManager()->addService(String16("service-name"), myService);
    // getting
    status_t err = getService<IFoo>(String16("service-name"), &myService);
    // waiting until service comes up (new in Android 11)
    myService = waitForService<IFoo>(String16("service-name"));
    // waiting for declared (VINTF) service to come up (new in Android 11)
    myService = waitForDeclaredService<IFoo>(String16("service-name"));

 

但我就是无法成功用这个方法取得对应的服务(Android R),编译时抛出的找不到xxx的错误根本无法解决,只能借猫画虎地使用NDK的实现来获取服务。

 

========正片开始=========

服务端的准备:

    1. 修改.aidl文件,新增功能接口。(此文仅修改.aidl文件以新增一个接口函数供客户端调用,故默认.aidl文件已经存在,也已经有一个服务去使用这个接口。关于如何创建一个新的.aidl接口请移步他处)

    此文的修改以IResourceManagerService.aidl(frameworks/av/media/libmedia/aidl/android/media/IResourceManagerService.aidl)为例:

package android.media;
//原有代码...
interface IResourceManagerService {

    //原有声明...
    
    int mWTFunc(int input); //此处我们新增的接口方法/函数
}

 

    2. 前往实现该接口.h头文件处再次声明该函数(使用了IResourceManagerService.aidl接口的头文件为frameworks/av/services/mediaresourcemanager/ResourceManagerService.h)

//原有代码...
class ResourceManagerService : public BnResourceManagerService {
public:
    //原有代码...

    //_aidl_return便是在aidl中定义的返回值,input则是aidl中定义的输入参数
    //而C++的return值固定是Status,这个设计应当是方便调用方判断函数执行的结果和错误与否
//关键字override必需!
Status mWTFunc(int32_t* _aidl_return, int32_t input) override; }

    aidl定义的int,在c++则应当用int32_t来对待,相应的数据类型表可以参照谷狗的AIDL后端文档

 

    3. 前往头文件对应的.cpp文件里实现该函数(frameworks/av/services/mediaresourcemanager/ResourceManagerService.cpp)

//原有代码...

namespace android {
//原有代码...

Status ResourceManagerService::mWTFFunc(int32_t* _aidl_return, int32_t input){
    //各种谜之功能
    
    //总之,_aidl_return肯定需要赋值,作为函数(功能性上)的返回值
    *_aidl_return = 0;

    //最后,c++ return,此值作为函数执行状态,用于给其它逻辑(如果有)进行运行结果判定
    return Status::ok();
}
//其它原有代码...

 

    如此一来,服务端的代码便准备就绪了。

 

 

客户端的操作(framework/native/.../*.cpp 或 *.h):  (此处解析已有的原生代码:MediaCodec.cpp)

一、编程方向的工作:

    1. 根据服务器接口(`.aidl`)所在的位置/包,首先需要`#include`此aidl对应的头文件(MediaCodec使用了`IResourceManagerService.aidl`所定义的接口,故需要添加`#include <aidl/android/media/IResourceManagerService.h>`来引入接口定义)

    2. 另外,若没有`#include`binder相关的头文件,那么也需要一并添加

 

    1与2相关代码如下:

//...之前的代码

//1需要的头文件,对应的.aidl为 frameworks/av/media/libmedia/aidl/android/media/IResourceManagerService.aidl
//很容易就能发现aidl文件路径和头文件路径的关系
#include <aidl/android/media/IResourceManagerService.h>
#include <android/binder_ibinder.h> // 2需要的头文件
#include <android/binder_manager.h>

//之后的代码...
View Code

 

    3. 实际调用

     首先,需要在代码开头声明服务端代码命名空间(不然找不到字段)

//aidl是固定前缀,然后就是.aidl文件中的"package android.media"一行所指定的包,最后则是接口名
using aidl::android::media::IResourceManagerService;

     参照示例总是能最快地理解该怎么做。比较懒的话可以一行解决:

std::shared_ptr<IResourceManagerService> mService = IResourceManagerService::fromBinder(ndk::SpAIBinder(AServiceManager_getService("media.resource_manager")));

    按部就班的话可以像MediaCodec.cpp原始的代码一样(也就两行而已):

    ::ndk::SpAIBinder binder(AServiceManager_getService("media.resource_manager"));  //这里的"media.resource_manager"是向ServiceManager注册服务时使用的服务名字符串
    mService = IResourceManagerService::fromBinder(binder);

    // 获取只需要两行/一行,但之后还是要验证一下有没有成功获取到
    if (mService == nullptr) {
        ALOGE("Failed to get ResourceManagerService");
        return;
    }

    然后就可以使用这个指针里的接口函数来调用服务端的相关函数功能了:

    //这里的mWTFunc是aidl文件中定义的接口函数,在调用之前也需要先在服务端的cpp代码中实现。没实现的话clang会提醒你的(指报错

    int32_t inPara = 0;  //传入参数的例子
    int32_t returnValue; //需要定义一个变量用以接收其(接口定义上的)返回值
    ndk::ScopedAStatus status = mService->mWTFunc(&returnValue, inPara);  //这句话执行以后,returnValue就会变为目标值, 需要对status进行判断的话也可以定义一个status变量来储存之

 

 

二、配置方向的工作

    由于加入了NDK相关代码,故需要在代码相应的`Android.bp`文件中添加ndk及服务提供库的相关依赖以及才能正确链接,否则编译时会报链接错误

    此处只对相关的`Android.bp`做出最低限度的解释

    视乎你加入前述ndk代码的文件及其位置,打开对应的Android.bp文件(以原生的frameworks/av/media/libstagefright/MediaCodec.cpp为例,其相应的bp文件为frameworks/av/media/libstagefright/Android.bp)(此处是解释原生已有的实现,也就是“如何借猫”,并不实际演示“画虎”的过程)

    .bp文件使用json来描述编译代码的各种参数信息,故首先找到含有MediaCodec.cpp文件的模块:

cc_library {
    name: "libstagefright",

    defaults: [
        ...
    ],

    srcs: [
        ...
        "MediaCodec.cpp",
        ...
...

    由此可知,使用了MediaCodec.cpp的模块为libstagefright, 接下来向下翻,找到模块的shared_libs列表:

    shared_libs: [
        ...
        "libbinder",
        "libbinder_ndk",
        ...
        "libdl",
        ...
        "libmedia",
        "libmedia_codeclist",
        "libmedia_omx",
        "libmedia_omx_client",
        ...
        

其中,libbinder、libbinder_ndk和libdl是ndk相关的依赖库,是使用上述的ndk代码都需要链接的

而下方的libmedia等是由于此前使用的ResourceManagerService是属于libmedia库中的代码,故调用其服务的代码也依赖于libmedia库

画虎的话,将这些依赖库添加进.bp文件中代码对应模块的库配置中就行了。

如果有时候报诡异的找不到头文件的错误,如果你引用服务的代码不是位于vendor路径下(vendor下的代码和framework里的代码在编译时有某种程度上的隔离),那可能还需要看看对应模块的include_dirs配置。此处不做赘述。

 

这么一来,至少我可以编译代码并调用aidl接口提供的服务了。

posted @ 2020-12-02 11:22  神必人  阅读(727)  评论(0)    收藏  举报