Native Service的實現
步驟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等等。
參考代碼如下
在頭文件中:
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等等。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.