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等等。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章