您的位置:首页 > 其它

Native Service的实现

2016-01-12 21:28 323 查看
步骤1 创建一个继承于 IInterface的基类

参考代码如下

在头文件中:

class IMyTestBase: public IInterface

{

public:

DECLARE_META_INTERFACE(MyTestBase);

virtual int Test( unsigned int input,unsigned int * output ) = 0;

};

说明:

DECLARE_META_INTERFACE 定义在frameworks\native\include\binder\IInterface.h中,

#define DECLARE_META_INTERFACE(INTERFACE) \

static const android::String16 descriptor; \

static android::sp<I##INTERFACE> asInterface( \

const android::sp<android::IBinder>& obj); \

virtual const android::String16& getInterfaceDescriptor() const; \

I##INTERFACE(); \

virtual ~I##INTERFACE();

这个宏实际上就是定义了这个类的构造函数和析构函数。定义了一个descriptor的字符串和getInterfaceDescriptor和asInterface的方法,descriptor是service的标示,用于BP和BN通讯的唯一标识。

DECLARE_META_INTERFACE的参数一定要和类名I后面的名称是对应的。

IMyTestBase类中的Test就是我们要提供的接口,也就是在其他的应用中通过Remote端要调用的接口。

在C文件中:

IMPLEMENT_META_INTERFACE(MyTestBase, "MyTestBase.name");

说明:

IMPLEMENT_META_INTERFACE也是定义在frameworks\native\include\binder\IInterface.h中

#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \

const android::String16 I##INTERFACE::descriptor(NAME); \

const android::String16& \

I##INTERFACE::getInterfaceDescriptor() const { \

return I##INTERFACE::descriptor; \

} \

android::sp<I##INTERFACE> I##INTERFACE::asInterface( \

const android::sp<android::IBinder>& obj) \

{ \

android::sp<I##INTERFACE> intr; \

if (obj != NULL) { \

intr = static_cast<I##INTERFACE*>( \

obj->queryLocalInterface( \

I##INTERFACE::descriptor).get()); \

if (intr == NULL) { \

intr = new Bp##INTERFACE(obj); \

} \

} \

return intr; \

} \

I##INTERFACE::I##INTERFACE() { } \

I##INTERFACE::~I##INTERFACE() { }

通过上面的定义可以看出,这个宏实际上就是实现了IMyTestBase类。

步骤2 定义一个本地的服务BnXXX,继承于BnInterface<IXXXService>,并实现OnTransact()函数。然后实现一个service,继承于BnXXX,在该类实现本服务要提供的接口。另外实现一个静态函数static int instantiate()在这个函数里调用addService添加本服务。

下面是示例代码:

1 实现BnMyTestBase

在头文件中定义一个BnMyTestBase

class BnMyTestBase: public BnInterface<IMyTestBase>

{

public:

virtual status_t onTransact( uint32_t code,

const Parcel& data,

Parcel* reply,

uint32_t flags = 0);

};

在C文件中实现该类

status_t BnMyTestBase::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

switch(code)

{

case TEST_CODE:

{

CHECK_INTERFACE(IMyTestBase, data, reply);

unsigned int input = (unsigned int )data.readInt32();

unsigned int output ;

int ret = Test(input, &output );

reply->writeInt32(output);

reply->writeInt32(ret);

return NO_ERROR;

} break;

default:

return BBinder::onTransact(code, data, reply, flags);

}

}

2 实现 MyTestService

在头文件中:

class MyTestService: public BnMyTestBase

{

public:

static int instantiate();

virtual int Test( unsigned int input,unsigned int * output );

MyTestService( void );

~MyTestService( void );

};

在C文件中

int MyTestService::instantiate()

{

status_t ret = defaultServiceManager()->addService( String16("MYTEST"), new MyTestService());

if(ret == NO_ERROR)

{

ALOGD("addService MyTestService sucess" );

return 0;

}

else

{

ALOGD("addService MyTestService failed ret=0x%x" ,ret );

return -1;

}

}

MyTestService::MyTestService( )

{

ALOGD("MyTestService init" );

}

int MyTestService::Test( unsigned int input,unsigned int * output )

{

ALOGD("test input=%d" ,input);

*output=0xaa;

return 0;

}

MyTestService::~MyTestService( void )

{

ALOGD("MyTestService deinit" );

}

步骤3 定义一个remote sevice BpXXX,继承BpInterface<IXXXService>

示例代码如下:

在头文件中

class BpMyTestBase: public BpInterface<IMyTestBase>

{

public:

BpMyTestBase(const sp<IBinder>& impl)

: BpInterface<IMyTestBase>(impl)

{

}

virtual int Test( unsigned int input,unsigned int * output )

{

Parcel data, reply;

data.writeInterfaceToken(IMyTestBase::getInterfaceDescriptor());

data.writeInt32(input);

remote()->transact(TEST_CODE, data, &reply);

*output = reply.readInt32();

return (reply.readInt32());

}

};

完成这3步,我们的服务就定义好了。

说明:

1 我们定义的服务一定要用namespace android 括起来,否则编译时会报错。

2 在BN和BP之间通过Parcel 传递参数的时候,write和read的顺序是一样的。这里不是堆栈,不存在后进先出。

3 在MyTestService::instantiate函数中addService的第一个参数是添加的服务的名字,这个名字是唯一的,我们在应用中要使用这个服务,就是通过这个名字来寻找的。

步骤4 :启动这个服务

我们定义一个可执行程序,来调用instantiate,添加服务。然后再Init.rc文件中启动这个可执行程序。

参考代码如下:

using namespace android;

int main(int argc, char** argv)

{

sp<ProcessState> proc(ProcessState::self());

sp<IServiceManager> sm = defaultServiceManager();

if(MyTestService::instantiate()<0)

{

exit(0);

}

ProcessState::self()->startThreadPool();

IPCThreadState::self()->joinThreadPool();

return 0;

}

编译下载好后重新启动机器,在adb shell界面中输入service list命令,就可以看到我们定义的服务了。

如果服务没有启动,可能是权限问题,可以参考《android init.rc文件语法详解》添加selinux的权限。

步骤5:在应用中调用这个服务

参考代码如下

sp<IMyTestBase> sService ;

const sp<IMyTestBase>& getService( )

{

int icount =0;

if (sService.get() == 0)

{

sp<IServiceManager> sm = defaultServiceManager();

sp<IBinder> binder;

binder = sm->getService(String16("MYTEST"));

if (binder != 0)

{

sService = interface_cast<IMyTestBase>(binder);

}

}

return sService;

}

调用服务的接口

const sp<IMyTestBase>& service(getService());

unsigned int input=0,output;

service->Test(input,&output);

OK !大功告成!

不过如果在android5.0之后,可能调用会失败,因为我们还没有定义selinux的权限。

下面介绍下如何添加权限。

假设我们启动这个服务的应用是/system/bin/Start__server这个程序。

首先我们要在创建一个Start__server.te。在里面定义

type Start__server, domain;

type Start__server_exec, exec_type, file_type;

init_daemon_domain(Start__server)

在file_contexts文件里面添加

/system/bin/Start__server u:object_r:Start__server_exec:s0

在service_contexts文件中添加

MYTEST u:object_r:system_server_service:s0

注意这里的MYTEST就是我们调用addService的第一个参数,这里要完全对应。

然后重新编译运行,我们会看到logcat的调试信息由很多下面的错误

avc: denied { add } for service=MYTEST scontext=u:r:Start__server:s0 tcontext=u:object_r:system_server_service:s0 tclass=service_manager

avc: denied { call } for scontext=u:r:untrusted_app:s0 tcontext=u:r:Start__server:s0 tclass=binder permissive=0avc: denied { search } for name="150" dev="proc" ino=4877 scontext=u:r:servicemanager:s0 tcontext=u:r:Start__server:s0 tclass=dir permissive=0

等等很多错误信息。

我们可以参考《android init.rc文件语法详解》介绍的方法在Start__server.te中一条条添加相应的权限。

另外还有更简洁的办法,google已经定义好了宏,我们直接拿来用就好了。

我们只要简单的在Start__server.te添加下面的语句

allow Start__server system_server_service:service_manager add;

binder_use(Start__server)

binder_call(appdomain,Start__server)

说明binder_call第一个参数是允许使用这个服务的应用的类型。

appdomain则说明说有的应用都可以使用。

我们可以根据需要制定其他的类型例如platform_app、untrusted_app

也可以指定某一个应用程序例如shell、adbd、mediaserver等等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: