binder 一個簡單的c++服務的實現,與callback實現

飛巖走壁發表於2012-08-17

 

 研究了android binder 有兩天了,感覺這是一個簡單易用的東西,也許以後在非android的應用環境中也可以使用這種IPC方案,所以把一些學習記錄下來,以將來備用.

今天主要是實現了一個簡單的c++ service服務程式,客戶端通過binder呼叫服務上的,已經註冊的介面函式。並在客戶端實現了一個callback介面。

關於callback的介面這個例子只是一個簡單的實現。估計以後實際使用中還有變數。

關於callback介面的一些想法

a. 現有測試程式中的client端並沒有啟動 IPCThreadState的joinThreadPool 函式

b. 現有測試程式中的client端並沒有啟動 ProcessState 的startThreadPool 函式

c. 現有測試程式中的client端的callback觸發,是通過client主動呼叫service端的一個函式.在這個函式內回撥,之前註冊的callback介面中的函式,實現的。

d. 現在這種模式的測試通過,說明了service和client是可以雙向溝通的

e. 將來的實現模式中,如果service端在需要時主動callback回來,估計client 端就需要啟動

startThreadPool 函式了,至於 joinThreadPool 我目前的看法是沒必要啟動的。因為只要有了threadpool 客戶端就可以實現服務了,當然,client端可不能隨便退出來,至少退出來前要通知service端,自己已經退出,要不service端回撥回來可就找不到北了.

以下貼出程式碼,以備後用:

一. 編寫一個公用的動態庫 libITestBinderInterface01

這個動態庫為service端和client端共用。

 

Android.mk

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
TestBinderInterface01.cpp \
ITestBinderInterface01.cpp 

base := /mnt/usbdisk/infoDroid2.2-1.8/infodroid-2.2/infodroid/frameworks/base/include

LOCAL_C_INCLUDES := \
$(JNI_H_INCLUDE) \
$(base)
LOCAL_SHARED_LIBRARIES := \
libutils \
liblog \
libbinder

LOCAL_PRELINK_MODULE := false
LOCAL_MODULE := libITestBinderInterface01
include $(BUILD_SHARED_LIBRARY) 

//Android.mk end


二.動態庫的實現


//ITestBinderInterface01.h

#ifndef ITESTBINDERINTERFACE01_H
#define ITESTBINDERINTERFACE01_H
#include <utils/RefBase.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/IMemory.h>
namespace android {

//call back interface ,call by service
class ITestBinderInterface01_CB : public IInterface
{
public:
enum {
CALLBACK01 = IBinder::FIRST_CALL_TRANSACTION,
CALLBACK02
};
public:
DECLARE_META_INTERFACE(TestBinderInterface01_CB);
virtual void notifyCallback01(int32_t msgType, int32_t ext1, int32_t ext2) = 0;//call from service 
virtual void notifyCallback02(int32_t msgType, const sp<IMemory>& pmem) = 0;//test IMemory interface todo
};

class ITestBinderInterface01 : public IInterface
{
public:
enum {
SETCALLBACK = IBinder::FIRST_CALL_TRANSACTION,
ADD,
CALLBACKTEST
};
public:
DECLARE_META_INTERFACE(TestBinderInterface01);

virtual int InitParam(const sp<ITestBinderInterface01_CB>& callback) = 0;//init parameter

virtual int Add(int a,int b) = 0; //return a+b
virtual int CallbackTest() = 0; //force callback
};

class BnTestBinderInterface01: public BnInterface<ITestBinderInterface01>
{
private:
protected:
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};

class BnTestBinderInterface01_CB: public BnInterface<ITestBinderInterface01_CB>
{
private:
protected:
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};

}; //namespace

#endif


//---------------------------------------------------------------------------------------

ITestBinderInterface01.cpp

#include <stdint.h>
#include <sys/types.h>

#include <binder/Parcel.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>

#include "ITestBinderInterface01.h"

namespace android {

class BpTestBinderInterface01: public BpInterface<ITestBinderInterface01>
{

public:
BpTestBinderInterface01(const sp<IBinder>& impl)
: BpInterface<ITestBinderInterface01>(impl)
{

}

virtual int InitParam(const sp<ITestBinderInterface01_CB>& callback)//init parameter
{
Parcel data, reply;
// data.writeInterfaceToken(ITestBinderInterface01::getInterfaceDescriptor());//why do that?? todo
data.writeStrongBinder(callback->asBinder());
remote()->transact(ITestBinderInterface01::SETCALLBACK, data, &reply);
return (reply.readInt32());
}
virtual int Add(int a,int b)  //return a+b
{
Parcel data, reply;
// data.writeInterfaceToken(ITestBinderInterface01::getInterfaceDescriptor());//why do that?? todo
data.writeInt32(a); 
data.writeInt32(b);
remote()->transact(ITestBinderInterface01::ADD, data, &reply);
return (reply.readInt32());
}
virtual int CallbackTest()  //force callback
{
Parcel data, reply;
// data.writeInterfaceToken(ITestBinderInterface01::getInterfaceDescriptor());//why do that?? todo
remote()->transact(ITestBinderInterface01::CALLBACKTEST, data, &reply);
return (reply.readInt32());
}

};

IMPLEMENT_META_INTERFACE(TestBinderInterface01, "android.hardware.ITestBinderInterface01");


class BpTestBinderInterface01_CB: public BpInterface<ITestBinderInterface01_CB>
{

public:
BpTestBinderInterface01_CB(const sp<IBinder>& impl)
: BpInterface<ITestBinderInterface01_CB>(impl)
{

}

virtual void notifyCallback01(int32_t msgType, int32_t ext1, int32_t ext2)
{
Parcel data, reply;
// data.writeInterfaceToken(ITestBinderInterface01_CB::getInterfaceDescriptor());//why do that?? todo
data.writeInt32(msgType);
data.writeInt32(ext1);
data.writeInt32(ext2);
remote()->transact(ITestBinderInterface01_CB::CALLBACK01, data, &reply);
}
virtual void notifyCallback02(int32_t msgType, const sp<IMemory>& pmem) //return a+b
{
Parcel data, reply;
// data.writeInterfaceToken(ITestBinderInterface01_CB::getInterfaceDescriptor());//why do that?? todo
data.writeInt32(msgType); 
data.writeStrongBinder(pmem->asBinder());
remote()->transact(ITestBinderInterface01_CB::CALLBACK02, data, &reply);
}
};

IMPLEMENT_META_INTERFACE(TestBinderInterface01_CB, "android.hardware.TestBinderInterface01_CB");



// ----------------------------------------------------------------------

status_t BnTestBinderInterface01::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case SETCALLBACK: {
sp<ITestBinderInterface01_CB> callback = interface_cast<ITestBinderInterface01_CB>(data.readStrongBinder());
reply->writeInt32(InitParam(callback));
return NO_ERROR;
} break;
case ADD: {
int a = data.readInt32();
int b = data.readInt32();
reply->writeInt32(Add(a,b));
return NO_ERROR;
} break;
case CALLBACKTEST: {
reply->writeInt32(CallbackTest());
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}


status_t BnTestBinderInterface01_CB::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case CALLBACK01: {
int32_t msgType = data.readInt32();
int32_t ext1 = data.readInt32();
int32_t ext2 = data.readInt32();
notifyCallback01(msgType, ext1, ext2);
return NO_ERROR;

} break;
case CALLBACK02: {
int32_t msgType = data.readInt32();
sp<IMemory> pmem = interface_cast<IMemory>(data.readStrongBinder());
notifyCallback02(msgType, pmem);
return NO_ERROR;
} break;

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

// ----------------------------------------------------------------------------

}; // namespace android


 

//至此 介面部分實現完了,接下來做一個簡單的實現


 

TestBinderInterface01.h

#ifndef TESTBINDERINTERFACE01_H
#define TESTBINDERINTERFACE01_H
#include "ITestBinderInterface01.h"
namespace android {
class TestBinderInterface01 : public BnTestBinderInterface01
{
public:
static void instantiate();
virtual int InitParam(const sp<ITestBinderInterface01_CB>& callback) ;//init parameter
virtual int Add(int a,int b) ; //return a+b
virtual int CallbackTest() ; //force callback

private:
TestBinderInterface01();
sp<ITestBinderInterface01_CB> _callback;
public:
virtual ~TestBinderInterface01();
};

class TestBinderInterface01_CB : public BnTestBinderInterface01_CB
{
public:
virtual void notifyCallback01(int32_t msgType, int32_t ext1, int32_t ext2);//call from service 
virtual void notifyCallback02(int32_t msgType, const sp<IMemory>& pmem) ;//test IMemory interface todo
public:
TestBinderInterface01_CB();
virtual ~TestBinderInterface01_CB();
};
}; // namespace android
#endif


//---------------------------------------------------------------------

TestBinderInterface01.cpp

#define LOG_TAG "TestBinderInterface01"

#include <utils/Log.h>

#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <utils/String16.h>
#include <utils/Errors.h>
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
#include <ui/Overlay.h>

#include <hardware/hardware.h>
#include "TestBinderInterface01.h"

#include <cutils/atomic.h>

namespace android {

extern "C" {
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <sys/ioctl.h>
}


void TestBinderInterface01::instantiate() {
defaultServiceManager()->addService(
String16("kali.testbinder01"), new TestBinderInterface01);
}
TestBinderInterface01::TestBinderInterface01() :
BnTestBinderInterface01()
{
LOGI("TestBinderInterface01 started: pid=%d", getpid());
}

TestBinderInterface01::~TestBinderInterface01()
{

}
int TestBinderInterface01::InitParam(const sp<ITestBinderInterface01_CB>& callback)
{
_callback = callback;
printf("[service] InitParam pid=%d,tid=%d\n",getpid(),gettid());
return 0;
}

int TestBinderInterface01::Add(int a,int b)
{
printf("[service] Add a=%d,b=%d pid=%d,tid=%d\n",a,b,getpid(),gettid());
return (a+b);
}

int TestBinderInterface01::CallbackTest()
{
printf("[service] CallbackTest pid=%d,tid=%d\n",getpid(),gettid());
if( _callback.get() )
{
printf("[service] call notifyCallback01 \n");
_callback->notifyCallback01(0, 1, 2);
}
return 0;
}

TestBinderInterface01_CB::TestBinderInterface01_CB()
{

}
TestBinderInterface01_CB::~TestBinderInterface01_CB()
{

}
void TestBinderInterface01_CB::notifyCallback01(int32_t msgType, int32_t ext1, int32_t ext2)
{
printf("is call back01: msgType=%d,ext1=%d,ext2=%d pid=%d,tid=%d\n",msgType,ext1,ext2,getpid(),gettid());
}
void TestBinderInterface01_CB::notifyCallback02(int32_t msgType, const sp<IMemory>& pmem)
{
printf("is call back02 pid=%d,tid=%d\n",getpid(),gettid());
}

};//namespace android
//---------------------------------------------------------------------




好了。動態庫libITestBinderInterface01 實現好了,mm一下就行了

接下來實現一個測試用的service和client。Android.mk如下。把兩個程式的編譯寫在一起方便些

testApp/Android.mk

# Copyright (C) 2008 The Android Open Source Project
# #
# # Licensed under the Apache License, Version 2.0 (the "License");
# # you may not use this file except in compliance with the License.
# # You may obtain a copy of the License at
# #
# # http://www.apache.org/licenses/LICENSE-2.0
# #
# # Unless required by applicable law or agreed to in writing, software
# # distributed under the License is distributed on an "AS IS" BASIS,
# # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# # See the License for the specific language governing permissions and
# # limitations under the License.


LOCAL_PATH := $(call my-dir)


# HAL module implemenation, not prelinked and stored in
# hw/<HWCURSOR_HARDWARE_MODULE_ID>.<ro.product.board>.so
include $(CLEAR_VARS)
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
LOCAL_SHARED_LIBRARIES := liblog libcutils liblx_ipc libITestBinderInterface01
#LOCAL_SHARED_LIBRARIES := lpthread libcutils libc

LOCAL_SRC_FILES := main_BinderTestService.cpp

LOCAL_MODULE := BinderTestService
#LOCAL_CFLAGS:= -DLOG_TAG="hwcursor"
include $(BUILD_EXECUTABLE)

#client

include $(CLEAR_VARS)
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
LOCAL_SHARED_LIBRARIES := liblog libcutils liblx_ipc libITestBinderInterface01
#LOCAL_SHARED_LIBRARIES := lpthread libcutils libc

LOCAL_SRC_FILES := main_BinderTestClient.cpp

LOCAL_MODULE := BinderTestClient
#LOCAL_CFLAGS:= -DLOG_TAG="hwcursor"
include $(BUILD_EXECUTABLE)

//Android.mk end ---------------------------------------

簡單的service端:

testApp/main_BinderTestService.cpp

#include <sys/types.h>
#include <unistd.h>
#include <grp.h>

#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <utils/Log.h>

#include "../TestBinderInterface01.h"

using namespace android;

int main(int argc, char** argv)
{

sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
printf("TestBinderInterface01 pid=%d,tid=%d, is running...\n",getpid(),gettid());
LOGI("ServiceManager: %p", sm.get());
TestBinderInterface01::instantiate();
ProcessState::self()->startThreadPool(); //啟動執行緒池.這樣binder驅動在需要時可以隨時從驅動裡

返回 BR_SPAWN_LOOPER 讓IPCThreadState類的

executeCommand函式響應,並可以執行

 case BR_SPAWN_LOOPER:
 mProcess->spawnPooledThread(false);
break;

看了半天才知道這個執行緒池是這麼用的。不容易啊

IPCThreadState::self()->joinThreadPool(); //我試過,換成while(1) usleep(100); 一樣可行,因為

上面的threadpool已經啟動了,驅動可以主要要求啟線

程了
}


 

//------------------------------------------------------------

testApp/main_BinderTestClient.cpp 一個簡單的客戶端

#include <sys/types.h>
#include <unistd.h>
#include <grp.h>

#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <utils/Log.h>

#include "../TestBinderInterface01.h"

using namespace android;
Mutex mLock;
sp<ITestBinderInterface01> getTestService() {

Mutex::Autolock _l(mLock);
sp<ITestBinderInterface01> pTestService;
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
binder = sm->getService(String16("kali.testbinder01"));
if (binder != 0)
break;
LOGW("kali.testbinder01 not published, waiting...");
usleep(500000); // 0.5 s
} while(true);

pTestService = interface_cast<ITestBinderInterface01>(binder);
LOGE_IF(pTestService==0, "no FingerScanService!?");
return pTestService;
}

int main(int argc, char** argv)
{
// sp<ProcessState> proc(ProcessState::self());
sp<ITestBinderInterface01> pTestService = getTestService();

printf("[client] running pid=%d,tid=%d\n",getpid(),gettid());
printf("[client] call InitParam\n");
pTestService->InitParam(new TestBinderInterface01_CB());
printf("[client] call Add result =%d \n",pTestService->Add(10,20));

printf("[client] call CallbackTest\n");
pTestService->CallbackTest();

printf("[client] end\n");
// TestBinderInterface01::instantiate();
// ProcessState::self()->startThreadPool();
// IPCThreadState::self()->joinThreadPool();
}

//------------------------------程式碼結束


 

mm 一下,生成

BinderTestService

BinderTestClient

libITestBinderInterface01.so

執行

BinderTestService &

BinderTestClient &

輸出結果如下:

# ./BinderTestService &
# TestBinderInterface01 pid=2503,tid=2503, is running...

#
# ./BinderTestClient &
# [client] running pid=2506,tid=2506
[client] call InitParam
[service] InitParam pid=2503,tid=2504
[service] Add a=10,b=20 pid=2503,tid=2505
[client] call Add result =30
[client] call CallbackTest
[service] CallbackTest pid=2503,tid=2504
[service] call notifyCallback01
is call back01: msgType=0,ext1=1,ext2=2 pid=2506,tid=2506
[client] end

不錯,結果是想像中的那樣。我好像對IMemory 這個介面還不太會用,這個介面從名字上來看,應該很有用,可以高效傳輸大記憶體塊的.過兩天有空了再學學

 

相關文章