編輯:關於Android編程
一句話概括進程通信:進程間的數據傳遞。
Binder是Anroid系統裡最重要的進程通信方式,很多文章會直接用代碼、原理類的文字進行描述,對於接觸Android與Linux不是特別深的人來說,特別晦澀難懂,經常是看了這忘了那裡,其實探索Binder通信的一條核心就是:Client如何找Server,將請求發送給Server,Server再將結果返回給Client。
Binder基於OpenBinder,被引入後添加了很多Android特性,比如,在驅動層添加了ServiceManager邏輯,搭建起ServiceManger-Clien-Server框架模型。Android基於Linux內核,其進程管理模型完全沿用了Linux的進程/線程模型,進程劃分為用戶空間與內核空間,在用戶空間,進程間是無法通信的,只有通過內核空間才能傳遞數據。 Binder自身的意義傾向於通信,只是進程間通信的一種方式,但是在Android系統中,Binder被提到了核心高度,Android基本可以看做基於Binder模型實現的是一種Android RPC模型(遠程過程調用協議 Remote Procedure Call Protocal ),即:C/S架構。
Binder只是定義了Android通信模型,至於內部的業務實現,還是要有Server自身來實現,不要把數據傳輸跟業務處理弄混淆,Android只是基於Binder,搭建了一個C/S通信框架、或者說通信協議。Android基於Linux內核,在Linux中,Binder被看做一個字符設備,Binder驅動會為每個打開Binder的進程在內核裡分配一塊地址空間,Client向Server傳遞數據,其實就是將數據寫道內核空間中Server的地址裡面,然後通知Server去取數據。原理其實很簡單,但是Google為了更加合理的使用Binder,自己進行了很多層次的封裝與優化,導致代碼看的昏頭轉向, 比較難的就是進程或者線程的掛起與喚醒以及Android CS框架。
ServiceManager如何管理Servers
每個Server進程在注冊的時候,首先往本地進程的內核空間的Binders紅黑樹種插入Binder實體服務的bind_node節點,然後會在ServiceManager的進程的內核空間中為其添加引用ref結構體,ref,會保存相應的信息、名字、ptr地址等。
Client如何找到Server,並且向其發送請求
Client在getService的時候,ServiceManager會找到Server的node節點,並在在Client中創建Server的bind_ref引用,Client可以在自己進程的內核空間中找到該引用,最終獲取Server的bind_node節點,直接訪問Server,傳輸數據並喚醒。
Client端,服務實體的引用bind_ref存在哪裡了,與Handler的關系式怎麼樣的
Binder驅動會在內核空間為打開Binder設備的進程(包括Client及Server端)創建bind_proc結構體,bind_proc包含4棵紅黑樹:threads、bind_refs、bind_nodes、bind_desc這四棵樹分別記錄該進程的線程樹、Binder引用樹、本地Binder實體,等信息,方便本地查找。Handler其是ServiceManager為了方便客戶端查找bind_ref做的一套處理,只是為了標定目標。
如何喚醒目標進程或者線程:
每個Binder進程或者線程在內核中都設置了自己的等待隊列,Client將目標進程或者線程告訴Binder驅動,驅動負責喚醒掛起在等待隊列上的線程或者進程。
Server如何找到返回目標進程或者線程,Client在請求的時候,會在bind_trasaction的from中添加請求端信息
如何Binder節點與ref節點的添加時機
驅動中存在一個TYPE_BINDER與TYPR_HANDLE的轉換,Binder節點是Binder Server進程(一般是Native進程)在向Servicemanager注冊時候添加的,而ref是Client在getService的時候添加的,並且是由ServiceManager添加的。
Binder如何實現只拷貝一次
數據從用戶空間拷貝到內核中的時候,是直接拷貝到目標進程的內核空間,這個過程是在請求端線程中處理的,只不過操作對象是目標進城的內核空間。其實,內核中的bind_trasaction_data是直接在目標進程匯總分配的,由於Binder進程的Binder內存部分在內核空間跟用戶空間只存在一個偏差值,用戶空間不需要再次拷貝數據就可
以完成訪問。
Binder接收線程管理:請求發送時沒有特別標記,驅動怎麼判斷哪些數據包該送入全局to-do隊列,哪些數據包該送入特定線程的to-do隊列呢?這裡有兩條規則:【1】
規則1:Client發給Server的請求數據包都提交到Server進程的全局to-do隊列。不過有個特例,當進程P1的中的線程T1向進程P2發送請求時,驅動會先查看一下線程T1是否也正在處理來自P2某個線程請求,(尚在處理,未完成,沒有發送回復),這種情況通常發生在兩個進程都有Binder實體並互相對發時請求的時候。如果在進程P2中發現了這樣的線程,比如說T2,就會要求T2來處理T1的這次請求。因為T2既然向T1發送了請求尚未得到返回包,說明T2肯定(或將會)阻塞在讀取返回包的狀態。這時候可以讓T2順便做點事情,總比等在那裡閒著好。而且如果T2不是線程池中的線程還可以為線程池分擔部分工。經過優化,來自T1的請求不是提交給P2的全局to-do隊列,而是送入了T2的私有to-do隊列。
規則2:對同步請求的返回數據包(由BC_REPLY發送的包)都發送到發起請求的線程的私有to-do隊列中。如上面的例子,如果進程P1的線程T1發給進程P2的線程T2的是同步請求,那麼T2返回的數據包將送進T1的私有to-do隊列而不會提交到P1的全局to-do隊列。
Binder Server都會在ServiceManager中注冊嗎?
Java層的Binder實體就不會去ServiceManager,尤其是bindService這樣一種,其實是ActivityManagerService充當了ServiceManager的角色。
IPCThreadState::joinThreadPool的真正意義是什麼?
可以理解加入該進程內核的線程池,進行循環,多個線程開啟,其實一個就可以,怕處理不過來,可以開啟多個線程處理起來,其實跟線程池類似。
為何ServiceManager啟動的時候沒有采用joinThreadPool,而是自己通過for循環來實現自己Loop
因為Binder環境還沒准備好啊,所以,自己控制,所以也咩有talkWithDriver那套邏輯,不用onTransact實現。因為前文也說過,Binder為Android做了深層的改變,其實在驅動裡面ServiceManager也是特殊對待的,在binder_transaction中,會對目標是ServiceManager的請求進行特殊處理。
在應用層ServiceManager的使用一般如下:(基於源碼4.3)
public abstract Object getSystemService(@ServiceName @NonNull String name);
那麼ServiceManager是什麼時候啟動的呢?ServiceManager代碼位於/frameworks/native/cmds/servicemanager/中,在init.rc中可以看到
service servicemanager /system/bin/servicemanager
class core
user system
group system
critical
onrestart restart zygote
onrestart restart media
onrestart restart surfaceflinger
onrestart restart drm
所以,ServiceManager是有init進程啟動的,在Linux系統中init是一切用戶空間進程的父進程,ServiceManager因此不依賴與任何Android服務進程。完全由系統的init進程加載進來。
init進程啟動的servicemanager的入口是service_manager.c的main函數:
int main(int argc, char **argv)
{
struct binder_state *bs;
void *svcmgr = BINDER_SERVICE_MANAGER;
bs = binder_open(128*1024);
if (binder_become_context_manager(bs)) {
LOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
svcmgr_handle = svcmgr;
binder_loop(bs, svcmgr_handler);
return 0;
}
主要做了以下幾件事情
第一步:調用函數binder_open打開設備文件/dev/binder 第二步:調用binder_become_context_manager將自己注冊為Binder進程間通信機制的上下文管理者;第三步:調用函數binder_loop開啟循環,監聽Client進程的通信要求。
下面詳細的分析一下核心函數代碼:
int binder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
進入binder驅動
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
. . . . . .
. . . . . .
case BINDER_SET_CONTEXT_MGR:
. . . . . .
. . . . . .
binder_context_mgr_uid = current->cred->euid;
binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
if (binder_context_mgr_node == NULL)
{
ret = -ENOMEM;
goto err;
}
binder_context_mgr_node->local_weak_refs++;
binder_context_mgr_node->local_strong_refs++;
binder_context_mgr_node->has_strong_ref = 1;
binder_context_mgr_node->has_weak_ref = 1;
break;
. . . . . .
. . . . . .
}
Binder驅動為ServiceManager生成一個binder_node節點,並記入靜態變量binder_context_mgr_node。一般情況下,應用層的每個binder實體都會在binder驅動層對應一個binder_node節點,然而binder_context_mgr_node比較特殊,它沒有對應的應用層binder實體。系統規定:任何應用都必須使用句柄0來跨進程地訪問它,因為ServiceManager獨一無二,並且只有一個服務實體,所以並不需要區分Binder實體。Android規定幾乎任何用戶進程都可以通過0號句柄訪問ServiceManager,其余Server的遠程接口句柄之都是一個Binder驅動分配的大於0的值,可以在Binder驅動中看到對於請求是ServiceManager的特殊處理
關於binder_loop開啟循環
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr;
unsigned readbuf[32];
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER;
binder_write(bs, readbuf, sizeof(unsigned));
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (unsigned) readbuf;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
....
}
}
可以看出ServiceManager直接通過for的ioctl進行binder字符設備的讀取,如果沒請求到來,就將自己的進程掛起,等待請求喚醒。一般Client的請求目標如果是ServiceManager會進行如下區分處理:
if (tr->target.handle) {//如果不是ServiceManager,也就是tr->target.handle!=0
struct binder_ref *ref;
ref = binder_get_ref(proc, tr->target.handle);
if (ref == NULL) {
binder_user_error("binder: %d:%d got "
"transaction to invalid handle\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_invalid_target_handle;
}
target_node = ref->node;
} else {//如果不是ServiceManager,也就是tr->target.handle==0
target_node = binder_context_mgr_node;
if (target_node == NULL) {
return_error = BR_DEAD_REPLY;
goto err_no_context_mgr_node;
}
到這裡我們就知道,ServiceManager怎麼啟動、究竟做什麼,以及客戶端如何找到ServiceManager的邏輯。下面就來看一下Server如何將自己注冊到ServiceManager中,以及Client如何通過,。ServiceManager找到目標Server。
系統啟動的時候,init進程會先啟動ServicManager進程,之後,init會啟動mediaserver進程,注意mediaserver不是SysytemServer啟動的,而是init啟動的。MediaPlayerServiceService的,SystemServer.java通過init1調用Jni函數,mediaserver配置在init.rc配置文件中,init.rc中的Service服務進程是順序啟動的。
service media /system/bin/mediaserver
class main
user media
group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
ioprio rt 4
mediaserver的入口是/frameworks/av/media/mediaserver/main_mediaserver.cpp的main函數:
int main(int argc, char** argv){
sp proc(ProcessState::self());
sp sm = defaultServiceManager();
AudioFlinger::instantiate(); //內含注冊到ServiceManager
MediaPlayerService::instantiate();//內含注冊到ServiceManager
CameraService::instantiate();//內含注冊到ServiceManager
AudioPolicyService::instantiate();//內含注冊到ServiceManager
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
通過以上代碼,可以看出一個進程可以同時注冊了多個服務,那麼到底是怎麼注冊到ServiceManager呢。主觀上想就是通過defaultServiceManager()獲取ServiceManager的遠程代理,然後通過代理發送請求,將服務Server信息注冊到ServiceManager中。
defaultServiceManager函數定義在IServiceManager.h中,但是不屬於某個類,類似全局方法,其實現也透漏著單利模式的影子:
sp defaultServiceManager()
{
if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
{
AutoMutex _l(gDefaultServiceManagerLock);
if (gDefaultServiceManager == NULL) {
gDefaultServiceManager = interface_cast(
ProcessState::self()->getContextObject(NULL));
}
}
return gDefaultServiceManager;
}
gDefaultServiceManager定義在namespace android中,是個全局變量,如果非要找源碼,可以再static.h中找到它
namespace android {
// For ProcessState.cpp
extern Mutex gProcessMutex;
extern sp gProcess;
// For ServiceManager.cpp
extern Mutex gDefaultServiceManagerLock;
extern sp gDefaultServiceManager;
extern sp gPermissionController;
}// namespace android
在IServiceManager /ProcessState.cpp中發現#include
template
class BpInterface : public INTERFACE, public BpRefBase
{
public: BpInterface(const sp& remote);
protected: virtual IBinder* onAsBinder();
};
template
inline BpInterface::BpInterface(const sp& remote) : BpRefBase(remote){}
template
inline IBinder* BpInterface::onAsBinder()
{
return remote();
}
BpRefBase::BpRefBase(const sp& o)
: mRemote(o.get()), mRefs(NULL), mState(0)
{
extendObjectLifetime(OBJECT_LIFETIME_WEAK);
if (mRemote) {
mRemote->incStrong(this); // Removed on first IncStrong().
mRefs = mRemote->createWeak(this); // Held for our entire lifetime.
}
}
用於通信的BpBinder最終被賦值給mRemote,這牽扯到Android智能指針的問題,有時間大家自己當做一個專題去學習一下, 到此,就真正完成了BpServiceManager的創建於分析。既然創建好了,那麼用一下,去ServiceManager登記當前系統Server
defaultServiceManager()->addService(String16("media.player"), new MediaPlayerService());
這裡其實就是調用BpServiceManager的addService,看一下源碼:
virtual status_t addService(const String16& name, const sp& service)
{
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
data.writeStrongBinder(service);
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
關鍵是remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply),而remote()最終返回的其實就是mRemote,也就是BpBinder(0),
inline IBinder* remote() { return mRemote; }
再來看一下BpBinder的transact函數,
status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){
if (mAlive) {
status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status; }
return DEAD_OBJECT;
}
可以看出最終調用:
status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags);
我們看一下關鍵代碼:
status_t IPCThreadState::transact(int32_t handle, uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
status_t err = data.errorCheck();
flags |= TF_ACCEPT_FDS;
if (err == NO_ERROR) {
// 寫緩存
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); //寫緩存 mout
}
if ((flags & TF_ONE_WAY) == 0) {
if (reply) {
err = waitForResponse(reply); // 訪問Binder驅動,交互
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
} else {
err = waitForResponse(NULL, NULL);
}
return err;
}
waitForResponse向ServiceManager發送請求,並阻塞等待回復,如何發送的呢:talkWithDriver,
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult){
int32_t cmd;
int32_t err;
while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break;
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
cmd = mIn.readInt32();
IF_LOG_COMMANDS() {
alog << "Processing waitForResponse Command: "
<< getReturnString(cmd) << endl;
}
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
case BR_DEAD_REPLY:
err = DEAD_OBJECT;
goto finish;
case BR_FAILED_REPLY:
err = FAILED_TRANSACTION;
goto finish;
case BR_ACQUIRE_RESULT: {
LOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");
const int32_t result = mIn.readInt32();
if (!acquireResult) continue;
*acquireResult = result ? NO_ERROR : INVALID_OPERATION;
}
goto finish;
case BR_REPLY:
{
binder_transaction_data tr;
err = mIn.read(&tr, sizeof(tr));
if (reply) {
if ((tr.flags & TF_STATUS_CODE) == 0) {
reply->ipcSetDataReference(
reinterpret_cast(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t),
freeBuffer, this);
} else {
err = *static_cast(tr.data.ptr.buffer);
freeBuffer(NULL, reinterpret_cast(tr.data.ptr.buffer),tr.data_size,
reinterpret_cast(tr.data.ptr.offsets),tr.offsets_size/sizeof(size_t), this);
}} else {
freeBuffer(NULL,
reinterpret_cast(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t), this);
continue; }}
goto finish;
default:
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
break; }}
finish:
if (err != NO_ERROR) {
if (acquireResult) *acquireResult = err;
if (reply) reply->setError(err);
mLastError = err;
}
return err;
}
talkWithDriver()的涉及到ioctrl,去訪問Binder驅動,這裡牽扯的驅動的問題。
status_t IPCThreadState::talkWithDriver(bool doReceive) {
binder_write_read bwr;
// Is the read buffer empty?
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
// We don't want to write anything if we are still reading
// from data left in the input buffer and the caller
// has requested to read the next data.
//正在讀取的時候不寫
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail;
bwr.write_buffer = (long unsigned int)mOut.data();
// This is what we'll read.
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (long unsigned int)mIn.data();
} else {
bwr.read_size = 0;
}
// Return immediately if there is nothing to do.
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
IF_LOG_COMMANDS() {
alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
}//這個log說明,talkWithDriver是讀寫一體的,並且,請求段是采用阻塞的方式來等待請求返回的
} while (err == -EINTR);
if (err >= NO_ERROR) {
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < (ssize_t)mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
else
mOut.setDataSize(0);
}
if (bwr.read_consumed > 0) {//通知去讀
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
return NO_ERROR;
}
return err;
}
看ioctrl,系統調用函數,其對應的bind_ioctrl位於Binder驅動中,首先會寫數據,如果是異步傳輸,不需要等待回復數據,如果是同步請求,需要阻塞等在數據返回。在binder_transcation_data中的flags域中可以體現出來,也就是flags的TF_ONE_WAY位為1,就表示需要異步傳輸。
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
// 當前進程對應的binder_proc
struct binder_proc *proc = filp->private_data;
// 進程的每個線程在binder驅動中的表示
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
binder_lock(__func__);
thread = binder_get_thread(proc);
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}
switch (cmd) {
case BINDER_WRITE_READ: {
struct binder_write_read bwr;
if (size != sizeof(struct binder_write_read)) {
ret = -EINVAL;
goto err;
}
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto err;
}
// > 0, 表示本次ioctl有待發送的數據
if (bwr.write_size > 0) {
// 寫數據
ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
trace_binder_write_done(ret);
// 成功返回0,出錯小於0
if (ret < 0) {
bwr.read_consumed = 0;
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto err;
}
}
//> 0表示本次ioctl想接收數據
if (bwr.read_size > 0) {
// binder驅動接收讀數據函數,這裡會阻塞,然後被喚醒
ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
trace_binder_read_done(ret);
/* 讀返回的時候如果發現todo任務隊列中有待處理的任務,那麼將會喚醒binder_proc.wait中下一個等待著的空閒線程。*/
if (!list_empty(&proc->todo))
wake_up_interruptible(&proc->wait);
if (ret < 0) {
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto err;
}
}
在寫請求與數據的過程中,會調用binder_transaction函數,這個函數是比較關鍵的函數,裡面涉及到Binder驅動管理的核心部分,包括:
找到目標進程或者線程 Binder數據一次拷貝原理 目標線程或者進程的喚醒
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply)
{
// // 發送請求時使用的內核傳輸數據結構
struct binder_transaction *t;
struct binder_work *tcomplete;
size_t *offp, *off_end;
// 目標進程對應的binder_proc
struct binder_proc *target_proc;
// 目標線程對應的binder_thread
struct binder_thread *target_thread = NULL;
//目標binder實體在內核中的節點結構體
struct binder_node *target_node = NULL;
//
struct list_head *target_list;
wait_queue_head_t *target_wait;
struct binder_transaction *in_reply_to = NULL;
struct binder_transaction_log_entry *e;
uint32_t return_error;
e = binder_transaction_log_add(&binder_transaction_log);
e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY);
e->from_proc = proc->pid;
e->from_thread = thread->pid;
e->target_handle = tr->target.handle;
e->data_size = tr->data_size;
e->offsets_size = tr->offsets_size;
// 如過是返回
if (reply) {
// 這裡的in_reply_to其實是在Server進程或者線程
in_reply_to = thread->transaction_stack;
// ¥
binder_set_nice(in_reply_to->saved_priority);
// 這裡是判斷是否是正確的返回線程
if (in_reply_to->to_thread != thread) {
// ¥
}
thread->transaction_stack = in_reply_to->to_parent;
// 獲取目標線程,這裡是在請求發送是寫入的
target_thread = in_reply_to->from;
if (target_thread->transaction_stack != in_reply_to) {
//$
}
// 根據目標線程獲取目標進程
target_proc = target_thread->proc;
}
// 如過是請求
else {
// 如果這裡目標服務是普通服務 不是SMgr管理類進程
if (tr->target.handle) {
struct binder_ref *ref;
// 從proc進程中,這裡其實是自己的進程,查詢出bind_node的引用bind_ref,
// 對於一般的進程,會在getService的時候有Servicemanager為自己添加
// 注意handle 是記錄在本地的,用來本地索引ref用的
ref = binder_get_ref(proc, tr->target.handle);
//¥
target_node = ref->node;
} else {
// 如果這裡目標服務是Servicemanager服務
// 如果ioctl想和SMgr的binder實體建立聯系,需要使tr->target.handle = 0
target_node = binder_context_mgr_node;
}
e->to_node = target_node->debug_id;
// 獲取目標進程
target_proc = target_node->proc;
可以看到,如果是請求,會根據target.handle在本進程中找到目標進程的proc節點,當然,第一次一般是找到binder_context_mgr_node,然後binder_context_mgr_node會在Client進程中設置目標Service節點的ref,這樣Client就可以找到target_proc了。
//*
在通過進行binder事物的傳遞時,如果一個binder事物(用struct binder_transaction結構體表示)需要使用到內存,
就會調用binder_alloc_buf函數分配此次binder事物需要的內存空間。
需要注意的是:從目標進程所在binder內存空間分配所需的內存
//
//從target進程的binder內存空間分配所需的內存大小,這也是一次拷貝,完成通信的關鍵,直接拷貝到目標進程的內核空間
//由於用戶空間跟內核空間僅僅存在一個偏移地址,所以也算拷貝到用戶空間
t->buffer = binder_alloc_buf(target_proc, tr->data_size,
tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
//¥
t->buffer->allow_user_free = 0;
t->buffer->debug_id = t->debug_id;
//該binder_buffer對應的事務
t->buffer->transaction = t;
//該事物對應的目標binder實體 ,因為目標進程中可能不僅僅有一個Binder實體
t->buffer->target_node = target_node;
trace_binder_transaction_alloc_buf(t->buffer);
//
if (target_node)
binder_inc_node(target_node, 1, 0, NULL);
// 計算出存放flat_binder_object結構體偏移數組的起始地址,4字節對齊。
offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
// struct flat_binder_object是binder在進程之間傳輸的表示方式 //
// 這裡就是完成binder通訊單邊時候在用戶進程同內核buffer之間的一次拷貝動作 //
// 這裡的數據拷貝,其實是拷貝到目標進程中去,因為t本身就是在目標進程的內核空間中分配的,
if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) {
binder_user_error("binder: %d:%d got transaction with invalid "
"data ptr\n", proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_copy_data_failed;
}
// 拷貝內嵌在傳輸數據中的flat_binder_object結構體偏移數組
if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {
//¥
}
if (!IS_ALIGNED(tr->offsets_size, sizeof(size_t))) {
// ¥
}
//這裡不一定一定執行,比如 MediaPlayer getMediaplayerservie的時候,傳遞數據中不包含對象, off_end為null
// 這裡就不會執行,一定要攜帶傳輸的數據才會走到這裡
//off_end = (void *)offp + tr->offsets_size;
//flat_binder_object結構體偏移數組的結束地址
off_end = (void *)offp + tr->offsets_size;
for (; offp < off_end; offp++) {
struct flat_binder_object *fp;
// *offp是t->buffer中第一個flat_binder_object結構體的位置偏移,相
當於t->buffer->data的偏移,這裡的if語句用來判斷binder偏移數組的第一個
元素所指向的flat_binder_object結構體地址是否是在t->buffer->data的有效范
圍內,或者數據的總大小就小於一個flat_binder_object的大小,或者說這個數組的元
素根本就沒有4字節對齊(一個指針在32位平台上用4個字節表示)。//
if (*offp > t->buffer->data_size - sizeof(*fp) ||
t->buffer->data_size < sizeof(*fp) ||
!IS_ALIGNED(*offp, sizeof(void *))) {
// $
}
// 取得第一個flat_binder_object結構體指針
fp = (struct flat_binder_object *)(t->buffer->data + *offp);
switch (fp->type) {
// 只有具有binder實體的進程才有權利發送這類binder。這裡其實是在binder實體中
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
struct binder_ref *ref;
// 根據flat_binder_object.binder這個binder實體在進程間的地
// 址搜索當前進程的binder_proc->nodes紅黑樹,看看是否已經創建了binder_node內核節點
// 如果沒創建,在自己的進程中為自己創建node節點
struct binder_node *node = binder_get_node(proc, fp->binder);
if (node == NULL) {
// 如果為空,創建
node = binder_new_node(proc, fp->binder, fp->cookie);
if (node == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_new_node_failed;
}
node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
}
// 校驗
if (fp->cookie != node->cookie) {
// #
}
if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_for_node_failed;
}
// 在目標進程中為它創建引用,其實類似(在ServiceManager中創建bind_ref其實可以說,Servicemanager擁有全部Service的引用)
ref = binder_get_ref_for_node(target_proc, node);
// ¥
// 修改flat_binder_object數據結構的type和handle域,接下來要傳給接收方
if (fp->type == BINDER_TYPE_BINDER)
fp->type = BINDER_TYPE_HANDLE;
else
fp->type = BINDER_TYPE_WEAK_HANDLE;
fp->handle = ref->desc;
binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
&thread->todo);
trace_binder_transaction_node_to_ref(t, node, ref);
binder_debug(BINDER_DEBUG_TRANSACTION,
" node %d u%p -> ref %d desc %d\n",
node->debug_id, node->ptr, ref->debug_id,
ref->desc);
} break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
// 通過引用號取得當前進程中對應的binder_ref結構體
struct binder_ref *ref = binder_get_ref(proc, fp->handle);
// ¥
if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_failed;
}
// 如果目標進程正好是提供該引用號對應的binder實體的進程,那麼按照下面的方式
修改flat_binder_object的相應域: type 和 binder,cookie。//
// 不太理解???
if (ref->node->proc == target_proc) {
if (fp->type == BINDER_TYPE_HANDLE)
fp->type = BINDER_TYPE_BINDER;
else
fp->type = BINDER_TYPE_WEAK_BINDER;
fp->binder = ref->node->ptr;
fp->cookie = ref->node->cookie;
binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);
trace_binder_transaction_ref_to_node(t, ref);
// $
} else {
// 否則會在目標進程的refs_by_node紅黑樹中先搜索看是否之前有創建過對應的b
inder_ref,如果沒有找到,那麼就需要為ref->node節點在目標進程中新建一個目標進程的bi
nder_ref掛入紅黑樹refs_by_node中。//
struct binder_ref *new_ref;
new_ref = binder_get_ref_for_node(target_proc, ref->node);
if (new_ref == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_for_node_failed;
}
//此時只需要將此域修改為新建binder_ref的引用號
fp->handle = new_ref->desc;
binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);
trace_binder_transaction_ref_to_ref(t, ref,
new_ref);
}
} break;
// 這裡如何獲取 target->thread(如果有的化), 目標進程或者線程,ref 對應node會有記錄
if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
struct binder_transaction *tmp;
// 目前肯定只有自己,因為阻塞,只能有自己
tmp = thread->transaction_stack;
這裡嘗試解釋一下 http://blog.csdn.net/universus/article/details/6211589 不知道是不是正確
關於工作線程的啟動,Binder驅動還做了一點小小的優化。當進程P1的線程T1向進程P2發送請求時,
驅動會先查看一下線程T1是否也正在處理來自P2某個線程請求但尚未完成(沒有發送回復)。這種情況
通常發生在兩個進程都有Binder實體並互相對發時請求時。假如驅動在進程P2中發現了這樣的線程,比如說T2
,就會要求T2來處理T1的這次請求。因為T2既然向T1發送了請求尚未得到返回包,說明T2肯定(或將會)阻塞在讀取返
回包的狀態。這時候可以讓T2順便做點事情,總比等在那裡閒著好。
而且如果T2不是線程池中的線程還可以為線程池分擔部分工作,減少線程池使用率。//
規則1:Client發給Server的請求數據包都提交到Server進程的全局to-do隊列。不過有個特例,就
是上節談到的Binder對工作線程啟動的優化。經過優化,來自T1的請求不是提交給P2的全局to-do隊列,而是送入了T2
的私有to-do隊列。規則2:對同步請求的返回數據包(由BC_REPLY發送的包)都發送到發起請求的線程的私有to-do隊列
中。如上面的例子,如果進程P1的線程T1發給進程P2的線程T2的是同步請求,那麼T2返回的數據包將送進T1的私有to-do隊
列而不會提交到P1的全局to-do隊列。
數據包進入接收隊列的潛規則也就決定了線程進入等待隊列的潛規則,
即一個線程只要不接收返回數據包則應該在全局等待隊列中等待新任務,
否則就應該在其私有等待隊列中等待Server的返回數據。還是上面的例子,
T1在向T2發送同步請求後就必須等待在它私有等待隊列中,
而不是在P1的全局等待隊列中排隊,否則將得不到T2的返回的數據包。
只有相互請求,才會在請求的時候發送到某個特定的線程。
while (tmp) {
if (tmp->from && tmp->from->proc == target_proc)
target_thread = tmp->from;
tmp = tmp->from_parent;
}
}
}
// 這裡如果目標是線程,那麼喚醒的就是線程的等待隊列,如果是進程,就喚醒進程等待隊列
// 其實喚醒的時候,對應是queuen ,
if (target_thread) {
e->to_thread = target_thread->pid;
target_list = &target_thread->todo;
target_wait = &target_thread->wait;
} else {
target_list = &target_proc->todo;
target_wait = &target_proc->wait;
}
e->to_proc = target_proc->pid;
// TODO: reuse incoming transaction for reply 如何復用incoming transaction//
t = kzalloc(sizeof(*t), GFP_KERNEL);
//¥
binder_stats_created(BINDER_STAT_TRANSACTION);
//分配本次單邊傳輸需要使用的binder_work結構體內存
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
//¥
binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);
t->debug_id = ++binder_last_id;
e->debug_id = t->debug_id;
//¥ ;
// reply 是不是回復 如果是同步傳輸的發送邊,這裡將當前的binder_thre
if (!reply && !(tr->flags & TF_ONE_WAY))
// 如果是同步傳輸的發送邊,這裡將當前的binder_thread記錄
// 在binder_transaction.from中,以供在同步傳輸的回復邊時,驅動可以根據這個找到回復的目的task。
t->from = thread;
else
// 如果是BC_REPLY或者是異步傳輸,這裡不需要記錄和返回信息相關的信息。//
t->from = NULL;
t->sender_euid = proc->tsk->cred->euid;
//事務的目標進程
t->to_proc = target_proc;
//事物的目標線程
t->to_thread = target_thread;
// 這個保持不變,驅動不會關心它
t->code = tr->code;
t->flags = tr->flags;
//線程的優先級的遷移
t->priority = task_nice(current);
trace_binder_transaction(reply, t, target_node);
....
if (reply) {
BUG_ON(t->buffer->async_transaction != 0);
// 這裡是出棧操作
binder_pop_transaction(target_thread, in_reply_to);
}
// 如果是同步傳輸,請求方需要獲取返回
else if (!(t->flags & TF_ONE_WAY)) {
BUG_ON(t->buffer->async_transaction != 0);
// 需要帶返回數據的標記
t->need_reply = 1;
// binder_transaction的的上一層任務,由於是堆棧,所以抽象為parent,惹歧義啊!這裡是入棧操作
t->from_parent = thread->transaction_stack;
// 正在處理的事務棧,為何會有事務棧呢?因為在等待返回的過程中,還會有事務插入進去,比如null的reply
thread->transaction_stack = t;
//
如果本次發起傳輸之前,當前task沒有處於通訊過程中的話,這裡必然為
NULL。而且第一次發起傳輸時,這裡也是為NULl。如果之前有異步傳輸沒處理完,沒處理完,就還沒有release,
那麼這裡不為null(為了自己,因為自己之前的任務還在執行,還沒完),如果之前本task正在處理接收請求,這裡也不為NULL(為了其他進程),
這裡將傳輸中間數據結構保存在binder_transaction鏈表頂部。這個transaction_stack實際
上是管理這一個鏈表,只不過這個指針時時指向最新加入該鏈表的成員,最先加入的成員在最底部,有點類似於
stack,所以這裡取名叫transaction_stack。
//
}
// 異步傳輸,不需要返回
else {
// 不需要返回的情況下 ,下面是對異步通訊的分流處理
BUG_ON(target_node == NULL);
BUG_ON(t->buffer->async_transaction != 1);
// 如果目標task仍然還有一個異步需要處理的話,該標志為1。//
if (target_node->has_async_transaction) {
// 將當前的這個的異步傳輸任務轉入目標進程binder_node的異步等待隊列async_todo中。為了加到異步中秋//
target_list = &target_node->async_todo;
// 將後來的異步交互轉入異步等待隊列。就不喚醒了,因為有在執行的
target_wait = NULL;
} else
// 如果沒有,設置目標節點存在異步任務,設置也是在這裡設置的,
target_node->has_async_transaction = 1;
}
//
t->work.type = BINDER_WORK_TRANSACTION;
// 添加到目標的任務列表
list_add_tail(&t->work.entry, target_list);
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
// 添加到請求線程,也就是自己的待處理任務列表
list_add_tail(&tcomplete->entry, &thread->todo);
// 喚醒目標進程,查看是否需要喚醒,如果target_wait!=null
// 當前task等待在task自己的等待隊列中(binder_thread.todo),永遠只有其自己。//
if (target_wait)
wake_up_interruptible(target_wait);
return;
//err: ¥異常處理 。。。
通過以上三步,binder驅動就會喚醒阻塞的ServiceManager,ServiceManager會解析、處理addService請求:在前面分析ServiceManager成為關鍵的時候,ServiceManager阻塞在binder_loop(bs, svcmgr_handler)中,被binder驅動喚醒後繼續運行:
int svcmgr_handler(struct binder_state *bs, struct binder_txn *txn, struct binder_io *msg, struct binder_io *reply) { struct svcinfo *si; uint16_t *s; unsigned len; void *ptr; uint32_t strict_policy;
if (txn->target != svcmgr_handle)
strict_policy = bio_get_uint32(msg);
s = bio_get_string16(msg, &len);
if ((len != (sizeof(svcmgr_id) / 2)) ||
memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
fprintf(stderr,"invalid id %s\n", str8(s));
return -1;
}
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
s = bio_get_string16(msg, &len);
ptr = do_find_service(bs, s, len);
if (!ptr)
break;
bio_put_ref(reply, ptr);
return 0;
case SVC_MGR_ADD_SERVICE:
s = bio_get_string16(msg, &len);
ptr = bio_get_ref(msg);
if (do_add_service(bs, s, len, ptr, txn->sender_euid))
return -1;
break;
case SVC_MGR_LIST_SERVICES: {
unsigned n = bio_get_uint32(msg);
si = svclist;
while ((n-- > 0) && si)
si = si->next;
if (si) {
bio_put_string16(reply, si->name);
return 0;
}
return -1;
}
default:
LOGE("unknown code %d\n", txn->code);
return -1;
}
bio_put_uint32(reply, 0);
return 0; }
可以看到,addService最終會調用do_add_service函數:
int do_add_service(struct binder_state *bs,
uint16_t *s, unsigned len,
void *ptr, unsigned uid, int allow_isolated)
{
struct svcinfo *si;
if (!ptr || (len == 0) || (len > 127))
return -1;
if (!svc_can_register(uid, s)) {
return -1;
}
si = find_svc(s, len);
if (si) {
if (si->ptr) {
svcinfo_death(bs, si);
}
si->ptr = ptr;
} else {
si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
if (!si) {
ALOGE("add_service('%s',%p) uid=%d - OUT OF MEMORY\n",
str8(s), ptr, uid);
return -1;
}
si->ptr = ptr;
si->len = len;
memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
si->name[len] = '\0';
si->death.func = svcinfo_death;
si->death.ptr = si;
si->allow_isolated = allow_isolated;
si->next = svclist;
svclist = si;
}
binder_acquire(bs, ptr);
binder_link_to_death(bs, ptr, &si->death);
return 0;
}
這樣就將Service添加到ServiceManager的svclist列表中去了,只有,Client就可以同getService去ServiceManager查找相應Service了。
Native層,Binder實體一般都要實現BBinder接口,關鍵是實現onTransact函數,這是Android對於Binder通信框架的抽象。剛才在Add的時候,只談到了BpServiceManager,沒說Server的MediaPlayerService實體,其實MediaPlayerService繼承自BnMediaPlayerServiceclass, BnMediaPlayerService又繼承與BnInterface,這裡就是Bn與Bp對於Binder框架最好的描述,兩者利用泛型與繼承的結合,在上層很好的用依賴倒置的方式實現了對框架的概括,需要實現什麼業務邏輯,只需要定義一份接口,然後讓Bn與Bp實現即可。Anroid的Binder的框架如何抽象非常完美,業務邏輯與底層通信實現了完美的分離與統一,既兼顧了通信的統一與考慮了應用的擴展。業務邏輯的實現是對應於BBinder實體,但是循環監聽的開啟對應於進程與線程,一個Native進程,只需要調用以下函數即可讓自己進入Binder監聽Loop ,其核心函數還是talkWithDriver:
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
其實只需要最上面一行就夠了,IPCThreadState::self()->joinThreadPool();linux中如果主線程如果退出,程序就退出,所以主線程必須要進入循環,但是與其讓他退出,不如重復利用,於是兩個Binder線程中,一個是專門開啟的,一個是贈送的,核心的binder驅動交互還是要走到talkWithDriver
void IPCThreadState::joinThreadPool(bool isMain){
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
status_t result;
do {
...
result = talkWithDriver();
if (result >= NO_ERROR) {
size_t IN = mIn.dataAvail();
if (IN < sizeof(int32_t)) continue;
cmd = mIn.readInt32();
result = executeCommand(cmd);
}
} while (result != -ECONNREFUSED && result != -EBADF);
(void*)pthread_self(), getpid(), (void*)result);
mOut.writeInt32(BC_EXIT_LOOPER);
talkWithDriver(false);
};
如果是Java層App,所有的進程都是Zygote的子進程,不用擔心因為ActivityThread會保證UI Thread進入循環。startThreadPool回傳給底層BC_ENTER_LOOPER參數,其目的是什麼,只是向Binder驅動注冊通報一聲,我這個服務要作為Binder監聽主線程服務了,因為有些Client在請求Server的時候,Server可能還沒進入Loop,當前進程中實現了BBinder服務,並且完成了注冊,那麼ProcessState::self()->startThreadPool();開啟的線程就可以訪問改BBinder,這個底層是統一的。只要進入循環,Binder就起來了,起來兩個線程,那麼最終請求端會喚醒哪個線程呢?後面討論,到這裡,其實Service的Loop就算
起來了。
Java層實現跟Native層類似,實現服務,之後將自己注冊到ServiceManager,其實更加類似於Java程序,這裡的Java層系統服務,是不提供界面的,與Application要分開,以ActivityServiceManager為例子:
public static final Context main(int factoryTest) {
AThread thr = new AThread();
thr.start();
synchronized (thr) {
while (thr.mService == null) {
try {
thr.wait();
} catch (InterruptedException e) {
}
}
}
ActivityManagerService m = thr.mService;
mSelf = m;
ActivityThread at = ActivityThread.systemMain();
mSystemThread = at;
Context context = at.getSystemContext();
context.setTheme(android.R.style.Theme_Holo);
m.mContext = context;
m.mFactoryTest = factoryTest;
m.mMainStack = new ActivityStack(m, context, true, thr.mLooper);
m.mIntentFirewall = new IntentFirewall(m.new IntentFirewallInterface());
public static void setSystemProcess() {
try {
ActivityManagerService m = mSelf;
ServiceManager.addService("activity", m, true);
ServiceManager.addService("meminfo", new MemBinder(m));
ServiceManager.addService("gfxinfo", new GraphicsBinder(m));
ServiceManager.addService("dbinfo", new DbBinder(m));
if (MONITOR_CPU_USAGE) {
ServiceManager.addService("cpuinfo", new CpuBinder(m));
}
ServiceManager.addService("permission", new PermissionController(m));
Client請求是一般是在當前線程中,看下MediaPlayService的用法,在java代碼中,MediaPlayService的體現是MediaPlayer這個類,而開發者的用法一般如下
MediaPlayer mMediaPlayer = new MediaPlayer();
mMediaPlayer.setDataSource(path);
mMediaPlayer.prepareAsync();
Java是解釋類語言,其具體實現要去Native代碼中去查找
public MediaPlayer/* {
// Native setup requires a weak reference to our object. * It’s easier to create it here than in C++. */
native_setup(new WeakReference(this));//關鍵是這句,JNI的使用 }
native_setup函數在android_media_MediaPlayer.cpp中實現實現代碼如下:
static void android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) {
sp mp = new MediaPlayer(); //創建於Java層對應的c++層MediaPlayer實例,並設置給Java
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
// create new listener and give it to MediaPlayer
sp listener = new JNIMediaPlayerListener(env, thiz, weak_this);
mp->setListener(listener);
setMediaPlayer(env, thiz, mp);
}
以上代碼的主要功能是,創建Java與C++之間的的相互引用,類似於實現C++的對象封裝,並沒有與Binder的進行交互。繼續看MediaPlayer的使用setDataSource,
status_t MediaPlayer::setDataSource(const sp &source)
{
ALOGV("setDataSource");
status_t err = UNKNOWN_ERROR;
const sp& service(getMediaPlayerService());
if (service != 0) {
sp player(service->create(this, mAudioSessionId));
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
(NO_ERROR != player->setDataSource(source))) {
player.clear();
}
err = attachNewPlayer(player);
}
return err;
}
這部分代碼的主要功能是,創建MediaPlayer代理,其實就是變相的創建BpMediaPlayerService。getMediaPlayerService這裡是獲取的Hander引用, getMediaPlayerService在IMediaDeathNotifier.cpp中。
// establish binder interface to MediaPlayerSer/*ce
//static*/const sp&
IMediaDeathNotifier::getMediaPlayerService()
{
ALOGV("getMediaPlayerService");
Mutex::Autolock _l(sServiceLock);
if (sMediaPlayerService == 0) {
sp sm = defaultServiceManager();
sp binder;
do {
binder = sm->getService(String16("media.player")); //這裡開始獲取MediaPlayer服務
if (binder != 0) {
break;
}
ALOGW("Media player service not published, waiting...");
usleep(500000); // 0.5 s
} while (true);
if (sDeathNotifier == NULL) {
sDeathNotifier = new DeathNotifier();
}
binder->linkToDeath(sDeathNotifier);
sMediaPlayerService = interface_cast(binder);
}
return sMediaPlayerService;
}
這裡終於涉及到Binder部分,比較重要的三句話:
defaultServiceManager()獲取SVM getService查詢並獲取Service interface_cast 創建Service本地代理如此,Client去獲取Service代理的路就通了。首先看第一部分defaultServiceManager(),這部分代碼位於IServiceManager.cpp中,之前addService已做過分析,這裡再細化一下:
sp defaultServiceManager()
{
if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
{
AutoMutex _l(gDefaultServiceManagerLock);
if (gDefaultServiceManager == NULL) {
gDefaultServiceManager = interface_cast(
ProcessState::self()->getContextObject(NULL));
}
}
return gDefaultServiceManager; }
順著這條單路走下去,最後會發現,上面的代碼可以轉化為
gDefaultServiceManager = interface_cast(new BpBinder(0));
android::sp IServiceManager::asInterface(const android::sp& obj)
{
android::sp intr;
if (obj != NULL) {
intr = static_cast(
obj->queryLocalInterface(
IServiceManager::descriptor).get());
if (intr == NULL) {
intr = new BpServiceManager(obj);
}
}
return intr;
}
BpServiceManager是一個BpRef對象,因為內部必有一個對象引用BpBinder,果不其然,mRemote就是這個引用。BpServiceManager其實自己的代碼更加傾向於抽象ServiceManager服務接口,而BpBinder對客戶單而言,是真正用於Binder進程通信的工具,BpBinder同業務邏輯沒有任何關系,比如Mediaplayer的 play,stop與BpBinder完全不關系,BpBinder它只負責將請求傳輸到Binder驅動,這裡就可以看出業務跟通信的分離BpServiceManager(BpBider)這裡第一部分就完結了。
第二部分,目標Service遠程代理的的獲取,代碼如下:
virtual sp getService(const String16& name) const
{
unsigned n;
for (n = 0; n < 5; n++){
sp svc = checkService(name);
if (svc != NULL) return svc;
sleep(1);
}
return NULL;
}
virtual sp checkService( const String16& name) const
{
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
return reply.readStrongBinder();
}
可以看出,最終會調用checkService去向SVM發送請求,最終返回IBinder類,作為Client和目標對象通信的接口。remote()返回的其實是BpBinder的引用,remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply)最終調用的是BpBinder的transact函數:
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// Once a Binder has died, it will never come back to life.
if (mAlive) {
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
IPCThreadState最終調用那個自己的transact函數,將請求傳遞給Binder驅動,transact關鍵代碼如下:
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags){
status_t err = data.errorCheck();
flags |= TF_ACCEPT_FDS;
if (err == NO_ERROR) {
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}
if ((flags & TF_ONE_WAY) == 0) {
。。。
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
IPCThreadState首先調用那個writeTransactionData整合數據,注意,只是整合數據,之後調用waitForResponse向SVM發送數據,
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t BinderFlags,
int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
Binder_transaction_data tr;
tr.target.handle = handle;
tr.code = code;
tr.flags = BinderFlags;
...
tr.data_size = data.ipcDataSize();
tr.data.ptr.buffer = data.ipcData();
tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t);
tr.data.ptr.offsets = data.ipcObjects();
...
mOut.writeInt32(cmd);
mOut.write(&tr, sizeof(tr));
return NO_ERROR;
}
上面的代碼並沒有直接與Binder交互,只是整合數據,waitForResponse才會開始交互
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) { int32_t cmd; int32_t err;
while (1) {
//寫請求 if ((err=talkWithDriver()) < NO_ERROR) break;
//mIn 這是已經寫入了數據,下面就是根據返回去處理
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
cmd = mIn.readInt32();
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
case BR_DEAD_REPLY:
err = DEAD_OBJECT;
goto finish;
...
case BR_REPLY:
{
Binder_transaction_data tr;
err = mIn.read(&tr, sizeof(tr));
LOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
if (err != NO_ERROR) goto finish;
if (reply) {
if ((tr.flags & TF_STATUS_CODE) == 0) {
reply->ipcSetDataReference(
reinterpret_cast(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t),
freeBuffer, this);
} else {
err = *static_cast(tr.data.ptr.buffer);
freeBuffer(NULL,
reinterpret_cast(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t), this);
}
} ...
}
goto finish;
default:
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
...
注意While(1)的次數與作用,在waitForResponse的作用:一直等到有意義的反饋,才會停止等待,這裡其實又多次握手的。
while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break;
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
talkWithDriver死循環保證數據正確的傳遞到內核:
do {
#if defined(HAVE_ANDROID_OS)
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno; #else
err = INVALID_OPERATION; #endif
IF_LOG_COMMANDS() {
alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
}
} while (err == -EINTR);
ioctrl 寫命令,寫返回,讀取返回,為什麼先要寫返回,這裡類似tcp協議,告訴已發送,在讓其進入等待,分階段來處理命令的發送。
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int ret; struct Binder_proc *proc = filp->private_data; struct Binder_thread *thread; unsigned int size = _IOC_SIZE(cmd);
switch (cmd) {
case BINDER_WRITE_READ: {
struct Binder_write_read bwr;
if (size != sizeof(struct Binder_write_read)) {
ret = -EINVAL;
goto err;
}
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto err;
} ….
if (bwr.write_size > 0) {
ret = Binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
trace_Binder_write_done(ret);
if (ret < 0) {
bwr.read_consumed = 0;
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto err;
}
}
…..
if (bwr.read_size > 0) {
ret = Binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
trace_Binder_read_done(ret);
if (!list_empty(&proc->todo))
wake_up_interruptible(&proc->wait);
if (ret < 0) {
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto err;
}
}
…..
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto err;
}
break;
else
Binder_context_mgr_uid = current->cred->euid;
Binder_context_mgr_node = Binder_new_node(proc, NULL, NULL);
if (Binder_context_mgr_node == NULL) {
ret = -ENOMEM;
goto err;
}
goto err;
}
if (thread)
thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
Binder_unlock(__func__);
wait_event_interruptible(Binder_user_error_wait, Binder_stop_on_user_error < 2);
if (ret && ret != -ERESTARTSYS)
printk(KERN_INFO "Binder: %d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret); err_unlocked:
trace_Binder_ioctl_done(ret);
return ret;
}
ioctrl 寫命令,寫返回,讀取返回,為什麼先要寫返回,類似tcp協議,告訴已發送,在讓其進入等待,分階段來處理命令的發送。這裡還要注意,比較重要的是下面的Smgr的寫返回處理,在那裡,要為客戶端添加bind_node的ref,並且在客戶端中創建引用句柄,注意,注意,引用句柄只是對客戶端有效,客戶端用句柄在客戶端查找出Server的bind_proc,然後處理。
void *do_find_service(struct binder_state *bs, uint16_t *s, unsigned len, unsigned uid)
{
struct svcinfo *si;
si = find_svc(s, len);
// ALOGI("check_service('%s') ptr = %p\n", str8(s), si ? si->ptr : 0);
if (si && si->ptr) {
if (!si->allow_isolated) {
// If this service doesn't allow access from isolated processes,
// then check the uid to see if it is isolated.
unsigned appid = uid % AID_USER;
if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) {
return 0;
}
}
return si->ptr;
} else {
return 0;
}
}
do_find_service這裡返回時Binder驅動裡Service的ref指針,回寫的時候,采用的方式是BINDER_TYPE_HANDLE,
void bio_put_ref(struct binder_io *bio, void *ptr)
{
struct binder_object *obj;
if (ptr)
obj = bio_alloc_obj(bio);
else
obj = bio_alloc(bio, sizeof(*obj));
if (!obj)
return;
obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
obj->type = BINDER_TYPE_HANDLE;
obj->pointer = ptr;
obj->cookie = 0;
}
在Binder驅動裡,如果需要會做相應的轉換,比如,如果目標進程恰好是Binder實體的進程,就轉換成BINDER_TYPE_BINDER,否則還是BINDER_TYPE_HANDLE,並且在Client進程的內核空間中創建Binder實體node的ref要引用,這樣就為Client搭建起了通信線路。
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
// 通過引用號取得當前進程中對應的binder_ref結構體
struct binder_ref *ref = binder_get_ref(proc, fp->handle);
// ¥
if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_failed;
}
/* 如果目標進程正好是提供該引用號對應的binder實體的進程,那麼按照下面的方式
修改flat_binder_object的相應域: type 和 binder,cookie。*/
// 不太理解???
if (ref->node->proc == target_proc) {
if (fp->type == BINDER_TYPE_HANDLE)
fp->type = BINDER_TYPE_BINDER;
else
fp->type = BINDER_TYPE_WEAK_BINDER;
fp->binder = ref->node->ptr;
fp->cookie = ref->node->cookie;
binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);
trace_binder_transaction_ref_to_node(t, ref);
// $
} else {
/* 否則會在目標進程的refs_by_node紅黑樹中先搜索看是否之前有創建過對應的b
inder_ref,如果沒有找到,那麼就需要為ref->node節點在目標進程中新建一個目標進程的bi
nder_ref掛入紅黑樹refs_by_node中。*/
struct binder_ref *new_ref;
new_ref = binder_get_ref_for_node(target_proc, ref->node);
if (new_ref == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_for_node_failed;
}
//此時只需要將此域修改為新建binder_ref的引用號
fp->handle = new_ref->desc;
binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);
trace_binder_transaction_ref_to_ref(t, ref,
new_ref);
}
}
之後,數據被寫入到Client進程的內核空間,同樣基於一次拷貝的原理,Client的用戶空間可以直接使用返回數據,Client之後利用readStrongBinder()獲取BpBinder,
status_t unflatten_binder(const sp& proc,
const Parcel& in, sp* out)
{
const flat_binder_object* flat = in.readObject(false);
if (flat) {
switch (flat->type) {
case BINDER_TYPE_BINDER:
*out = static_cast(flat->cookie);
return finish_unflatten_binder(NULL, *flat, in);
case BINDER_TYPE_HANDLE:
*out = proc->getStrongProxyForHandle(flat->handle);
return finish_unflatten_binder(
static_cast(out->get()), *flat, in);
}
}
return BAD_TYPE;
}
再利用Binder框架將業務邏輯投射進來。其實將請求跟服務接口加進來。
sMediaPlayerService = interface_cast(binder);
這裡的sMediaPlayerService其實就是BpMediaPlayerService,MediaPlayerService的本地帶來,至此,MediaPlayer就獲取了MediaPlayerService的本地代理。
Client請求Service服務的流程與addService類似,只不過Server端由ServiceManager變成了特定的XXXService,所以不做額外分析
Zygote進程啟動的時候,已經對Binder進行了支持,其他Application進程均是Zygote的子進程,啟動之初就已經支持Binder通信,其實這裡主要是針對Server,因為Client自身請求是在當前線程,隨時請求,隨時使用。在zygote啟動Android應用程序時,會調用zygoteInit函數來初始化應用程序運行環境、比如虛擬機堆棧大小、Binder線程的注冊等。
public static final void zygoteInit(int targetSdkVersion, String[] argv)
throws ZygoteInit.MethodAndArgsCaller {
redirectLogStreams();
commonInit();
//啟動Binder線程池以支持Binder通信
nativeZygoteInit();
applicationInit(targetSdkVersion, argv);
}
nativeZygoteInit函數用於創建線程池,該函數是一個Native函數,其對應的JNI函數為frameworks\base\core\jni\AndroidRuntime.cpp
static void com_android_internal_os_RuntimeInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{
gCurRuntime->onZygoteInit();
}
變量gCurRuntime的類型是AndroidRuntime,AndroidRuntime類的onZygoteInit()函數是一個虛函數,在AndroidRuntime的子類AppRuntime中被實現frameworks\base\cmds\app_process\App_main.cpp
virtual void onZygoteInit()
{
sp proc = ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}
函數首先得到ProcessState對象,然後調用它的startThreadPool()函數來啟動線程池。
void ProcessState::startThreadPool() {
AutoMutex _l(mLock);
if (!mThreadPoolStarted) {
mThreadPoolStarted = true;
spawnPooledThread(true);
}
}
這裡跟Native Service的開啟自身Loop機制類似,其實就是啟動一個線程去監聽Binder字符設備,阻塞等待Client請求,如下:
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
在開發Android應用程序的時候,需要和其它進程中進行通信時,只要定義Binder對象,然後把這個Binder對象的遠程代理通過其它途徑傳給其它進程後,其它進程就可以通過這個Binder對象的遠程接口來調用我們的應用程序進程的函數了,它不像C++層實現Binder進程間通信機制的Server時,必須要手動調用IPCThreadState.joinThreadPool函數來進入一個無限循環中與Binder驅動程序交互以便獲得Client端的請求。
其實並不是所有的進程都需要Binder通信,這個命令是為了將當前線程注冊到驅動,其實線程池的意思也是相對驅動而言,在驅動創建binder_thread結構體。 BC_ENTER_LOOPER命令下的處理非常簡單,僅僅是將當前線程binder_thread的狀態標志位設置為BINDER_LOOPER_STATE_ENTERED,binder_thread_write函數執行完後,由於bwr.read_size > 0,因此binder_ioctl()函數還會執行Binder驅動讀. 這樣就將當前線程注冊到了Binder驅動中,同時該線程進入睡眠等待客戶端請求。這樣就將當前線程注冊到了Binder驅動中,同時該線程進入睡眠等待客戶端請求,當有客戶端請求到來時,該Binder線程被喚醒,接收並處理客戶端的請求。因此Android應用程序通過注冊Binder線程來支持Binder進程間通信機制,joinThreadPool進入循環,就是開啟Server的Binder路徑。 線程退出時,也會告訴Binder驅動程序,它退出了,這樣Binder驅動程序就不會再在Client端的進程間調用分發給它了:
mOut.writeInt32(BC_EXIT_LOOPER);
talkWithDriver(false);
Java層Application使用binder的是通過AIDL語言來進行的,為什麼AIDL定義接口與服務就足夠了呢? 編譯系統進行了怎麼樣的轉換,使得接口支持binder通信?AIDL(Android Interface Definition Language)其實就是基於Binder框架的一種實現語言,在進行編譯的時候,就已經對Binder的實現進行一系列的封裝,生成的IxxxxService.stub以及IxxxxService.Proxy類都是對Binder封裝的一種體現。AIDL的最終效果就是讓IPC的通訊就像調用函數那樣簡單,系統自動幫你完成了發送請求數據的序列化,以及返回結果數據的解析。開發者需要做的就是寫上一個接口文件,然後利用aidl工具轉化一下得到另一個java文件,這個文件在服務和客戶端程序各放一份,服務程序繼承IxxxxService.Stub 然後將函數接口裡面的邏輯代碼實現,這裡可以看到明顯的Server與Client架構模型。Service只是個容器,其中基於aidl實現的IXXService才是真正的服務,Service中主要就是初始化,提供綁定操作,已經解綁,結束一些服務的操作。
//server 端
public interface ICatService extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements org.crazyit.service.ICatService {
private static final java.lang.String DESCRIPTOR = "org.crazyit.service.ICatService";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
//client 端
private static class Proxy implements org.crazyit.service.ICatService {
private android.os.IBinder mRemote;
//mRemote其實就是binder的代理類,其實這個類的實例化是在Native創建的,在java層看不到實例代碼,其實這樣也是為了更好的封裝。
final class BinderProxy implements IBinder {
public native boolean pingBinder();
public native boolean isBinderAlive();
public IInterface queryLocalInterface(String descriptor) {
return null;
}
當bindService之後,客戶端會得到一個Binder引用,是Binder,不是IxxxxService.Proxy也不是IxxxxService.Stub實例,必須基於Binder實例化出一個IxxxxService.Proxy或者stub。如果服務端和客戶端都是在同一個進程,直接將IxxxxService當做普通的對象調用就成了。Google的同志利用IxxxxService.Stub.asInterface函數對這兩種不同的情況進行了統一,也就是不管你是在同一進程還是不同進程,那麼在拿到Binder引用後,調用IxxxxService.Stub.asInterface(IBinder obj) 即可得到一個IxxxxService實例,只管調用IxxxxService裡的函數就成了。
/**
* Cast an IBinder object into an org.crazyit.service.ICatService
* interface, generating a proxy if needed.
*/
public static org.crazyit.service.ICatService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof org.crazyit.service.ICatService))) {
return ((org.crazyit.service.ICatService) iin);
}
return new org.crazyit.service.ICatService.Stub.Proxy(obj);
}
AIDL的最終效果就是讓 IPC的通訊就像調用函數那樣簡單。自動的幫你完成了參數序列化發送以及解析返回數據的那一系列麻煩。而你所需要做的就是寫上一個接口文件,然後利用aidl工具轉化一下得到另一個java文件,這個文件在服務和客戶端程序各放一份。服務程序繼承IxxxxService.Stub 然後將函數接口裡面的邏輯代碼實現一下。
bindService與onServiceConnected執行是異步的,首先看一下binderService的源碼,
@Override
public boolean bindService(Intent service, ServiceConnection conn,
....
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(),
service, service.resolveTypeIfNeeded(getContentResolver()),
sd, flags);
if (res < 0) {
throw new SecurityException(
"Not allowed to bind to service " + service);
}
...
}
IServiceConnection.Stub 其實也是一條Binder體系,InnerConnection對象是一個Binder對象,一會是要傳遞給ActivityManagerService的,ActivityManagerServic後續就是要通過這個Binder對象和ServiceConnection通信的。
private static class InnerConnection extends IServiceConnection.Stub {
final WeakReference mDispatcher;
......
InnerConnection(LoadedApk.ServiceDispatcher sd) {
mDispatcher = new WeakReference(sd);
}
......
} 來看看bind函數
public int bindService(IApplicationThread caller, IBinder token,
Intent service, String resolvedType, IServiceConnection connection,
int flags
data.writeStrongBinder(connection.asBinder());
接著通過retrieveServiceLocked函數,得到一個ServiceRecord,這個ServiceReocrd描述的是一個Service對象,這裡就是CounterService了,這是根據傳進來的參數service的內容獲得的。回憶一下在MainActivity.onCreate函數綁定服務的語句:
Intent bindIntent = new Intent(MainActivity.this, CounterService.class);
bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
這裡的參數service,就是上面的bindIntent了,它裡面設置了Service類的信息,因此,這裡可以通過它來把Service的信息取出來,並且保存在ServiceRecord對象s中。 接下來,就是把傳進來的參數connection封裝成一個ConnectionRecord對象。注意,這裡的參數connection是一個Binder對象,它的類型是LoadedApk.ServiceDispatcher.InnerConnection,是在Step 4中創建的,後續ActivityManagerService就是要通過它來告訴MainActivity,CounterService已經啟動起來了,因此,這裡要把這個ConnectionRecord變量c保存下來,它保在在好幾個地方,都是為了後面要用時方便地取回來的,這裡就不仔細去研究了,只要知道ActivityManagerService要使用它時就可以方便地把它取出來就可以了. 我們先沿著app.thread.scheduleCreateService這個路徑分析下去,然後再回過頭來分析requestServiceBindingsLocked的調用過程。這裡的app.thread是一個Binder對象的遠程接口,類型為ApplicationThreadProxy。每一個Android應用程序進程裡面都有一個ActivtyThread對象和一個ApplicationThread對象,其中是ApplicationThread對象是ActivityThread對象的一個成員變量,是ActivityThread與ActivityManagerService之間用來執行進程間通信的,
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
IServiceConnection如何完成在AMS端口的轉換
sp Parcel::readStrongBinder() const
{
sp val;
unflatten_binder(ProcessState::self(), *this, &val);
return val;
}
沒什麼,再向下看,不是什麼東東都可以向下看的,否則別人會罵的。
[cpp] view plain copy 在CODE上查看代碼片派生到我的代碼片
status_t unflatten_binder(const sp& proc,
const Parcel& in, sp* out)
{
const flat_binder_object* flat = in.readObject(false);
if (flat) {
switch (flat->type) {
case BINDER_TYPE_BINDER:
*out = static_cast(flat->cookie);
return finish_unflatten_binder(NULL, *flat, in);
case BINDER_TYPE_HANDLE: //因為我們是Client,當然會調用這個
*out = proc->getStrongProxyForHandle(flat->handle);
return finish_unflatten_binder(
static_cast(out->get()), *flat, in);
}
}
return BAD_TYPE;
}
這個返回的就是一個BpBinder,其handle為傳入的handle.現在已經看到reply.readStrongBinder()的返回值為一個BpBinder,即interface_cast(reply.readStrongBinder());的參數為一個BpBinder.
case BIND_SERVICE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app = ApplicationThreadNative.asInterface(b);
IBinder token = data.readStrongBinder();
Intent service = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
//這個轉換可以吧b轉換成代理
b = data.readStrongBinder();
int fl = data.readInt();
IServiceConnection conn = IServiceConnection.Stub.asInterface(b);
int res = bindService(app, token, service, resolvedType, conn, fl);
reply.writeNoException();
reply.writeInt(res);
return true;
}
接著通過retrieveServiceLocked函數,得到一個ServiceRecord,這個ServiceReocrd描述的是一個Service對象,這裡就是CounterService了,這是根據傳進來的參數service的內容獲得的。回憶一下在MainActivity.onCreate函數綁定服務的語句:
Intent bindIntent = new Intent(MainActivity.this, CounterService.class);
bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
這裡的參數service,就是上面的bindIntent了,它裡面設置了CounterService類的信息(CounterService.class),因此,這裡可以通過它來把CounterService的信息取出來,並且保存在ServiceRecord對象s中。接下來,就是把傳進來的參數connection封裝成一個ConnectionRecord對象。注意,這裡的參數connection是一個Binder對象,它的類型是LoadedApk.ServiceDispatcher.InnerConnection,是在Step 4中創建的,後續ActivityManagerService就是要通過它來告訴MainActivity,CounterService已經啟動起來了,因此,這裡要把這個ConnectionRecord變量c保存下來,它保在在好幾個地方,都是為了後面要用時方便地取回來的,這裡就不仔細去研究了,只要知道ActivityManagerService要使用它時就可以方便地把它取出來就可以了. 我們先沿著app.thread.scheduleCreateService這個路徑分析下去,然後再回過頭來分析requestServiceBindingsLocked的調用過程。這裡的app.thread是一個Binder對象的遠程接口,類型為ApplicationThreadProxy。每一個Android應用程序進程裡面都有一個ActivtyThread對象和一個ApplicationThread對象,其中是ApplicationThread對象是ActivityThread對象的一個成員變量,是ActivityThread與ActivityManagerService之間用來執行進程間通信的,
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
接下來
class ActivityManagerProxy implements IActivityManager
{
......
public void publishService(IBinder token,
Intent intent, IBinder service) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
intent.writeToParcel(data, 0);
data.writeStrongBinder(service);
mRemote.transact(PUBLISH_SERVICE_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
}
......
}
IServiceConnection如何完成在AMS端口的轉換
sp Parcel::readStrongBinder() const
{
sp val;
unflatten_binder(ProcessState::self(), *this, &val);
return val;
}
[cpp] view plain copy 在CODE上查看代碼片派生到我的代碼片
status_t unflatten_binder(const sp& proc,
const Parcel& in, sp* out)
{
const flat_binder_object* flat = in.readObject(false);
if (flat) {
switch (flat->type) {
case BINDER_TYPE_BINDER:
*out = static_cast(flat->cookie);
return finish_unflatten_binder(NULL, *flat, in);
case BINDER_TYPE_HANDLE: //因為我們是Client,當然會調用這個
*out = proc->getStrongProxyForHandle(flat->handle);
return finish_unflatten_binder(
static_cast(out->get()), *flat, in);
}
}
return BAD_TYPE;
}
這個返回的就是一個BpBinder,其handle為傳入的handle.現在已經看到reply.readStrongBinder()的返回值為一個BpBinder,即interface_cast(reply.readStrongBinder());的參數為一個BpBinder.
case BIND_SERVICE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app = ApplicationThreadNative.asInterface(b);
IBinder token = data.readStrongBinder();
Intent service = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
//這個轉換可以吧b轉換成代理
b = data.readStrongBinder();
int fl = data.readInt();
IServiceConnection conn = IServiceConnection.Stub.asInterface(b);
int res = bindService(app, token, service, resolvedType, conn, fl);
reply.writeNoException();
reply.writeInt(res);
return true;
}
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (DEBUG_SERVICE)
Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
try {
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
} else {
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, 0, 0, 0);
}
ensureJitEnabled();
} catch (RemoteException ex) {
}
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to bind to service " + s
+ " with " + data.intent + ": " + e.toString(), e);
}
}
}
}
public void publishService(IBinder token,
Intent intent, IBinder service) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
intent.writeToParcel(data, 0);
data.writeStrongBinder(service);
mRemote.transact(PUBLISH_SERVICE_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
}
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
struct binder_ref *ref = binder_get_ref(proc, fp->handle);
if (ref == NULL) {
binder_user_error("binder: %d:%d got "
"transaction with invalid "
"handle, %ld\n", proc->pid,
thread->pid, fp->handle);
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_failed;
}
if (ref->node->proc == target_proc) {
if (fp->type == BINDER_TYPE_HANDLE)
fp->type = BINDER_TYPE_BINDER;
else
fp->type = BINDER_TYPE_WEAK_BINDER;
fp->binder = ref->node->ptr;
fp->cookie = ref->node->cookie;
binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);
if (binder_debug_mask & BINDER_DEBUG_TRANSACTION)
printk(KERN_INFO " ref %d desc %d -> node %d u%p\n",
ref->debug_id, ref->desc, ref->node->debug_id, ref->node->ptr);
} else {
struct binder_ref *new_ref;
new_ref = binder_get_ref_for_node(target_proc, ref->node);
if (new_ref == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_for_node_failed;
}
fp->handle = new_ref->desc;
binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);
if (binder_debug_mask & BINDER_DEBUG_TRANSACTION)
printk(KERN_INFO " ref %d desc %d -> ref %d desc %d (node %d)\n",
ref->debug_id, ref->desc, new_ref->debug_id, new_ref->desc, ref->node->debug_id);
}
} break;
Java層的通信是經過封裝。in與to 就是個例子
Java層客戶端的Binder代理都是BinderProxy,而且他們都是在native層生成的,因此,在上層看不到BinderProxy實例化。BinderProxy位於Binder.java中,
final class BinderProxy implements IBinder {
public native boolean pingBinder();
public native boolean isBinderAlive();
其創建位於Native代碼/frameworks/base/core/jni/android_util_Binder.cpp中
const char* const kBinderProxyPathName = "android/os/BinderProxy";
clazz = env->FindClass(kBinderProxyPathName);
gBinderProxyOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
jobject javaObjectForIBinder(JNIEnv* env, const sp& val)
{
if (val == NULL) return NULL;
if (val->checkSubclass(&gBinderOffsets)) {
// One of our own!
jobject object = static_cast(val.get())->object();
//printf("objectForBinder %p: it's our own %p!\n", val.get(), object);
return object;
}
// For the rest of the function we will hold this lock, to serialize
// looking/creation of Java proxies for native Binder proxies.
AutoMutex _l(mProxyLock);
// Someone else's... do we know about it?
jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
if (object != NULL) {
jobject res = env->CallObjectMethod(object, gWeakReferenceOffsets.mGet);
if (res != NULL) {
LOGV("objectForBinder %p: found existing %p!\n", val.get(), res);
return res;
}
LOGV("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get());
android_atomic_dec(&gNumProxyRefs);
val->detachObject(&gBinderProxyOffsets);
env->DeleteGlobalRef(object);
}
object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
if (object != NULL) {
LOGV("objectForBinder %p: created new %p!\n", val.get(), object);
// The proxy holds a reference to the native object.
env->SetIntField(object, gBinderProxyOffsets.mObject, (int)val.get());
val->incStrong(object);
// The native object needs to hold a weak reference back to the
// proxy, so we can retrieve the same proxy if it is still active.
jobject refObject = env->NewGlobalRef(
env->GetObjectField(object, gBinderProxyOffsets.mSelf));
val->attachObject(&gBinderProxyOffsets, refObject,
jnienv_to_javavm(env), proxy_cleanup);
// Note that a new object reference has been created.
android_atomic_inc(&gNumProxyRefs);
incRefsCreated(env);
}
return object;
}
接下去是進入AMS的bindService,再調用ActiveServices.java 的bindServiceLocked,它會把IServiceConnection實例存放到ConnectionRecord裡面,並執行bringUpServiceLocked,
int bindServiceLocked(IApplicationThread caller, IBinder token,
Intent service, String resolvedType,
IServiceConnection connection, int flags, int userId) {
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
IBinder binder = connection.asBinder();
...
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null){
return 0;
}
bringUpServiceLocked會調用realStartServiceLocked,調用scheduleCreateService,完成service的創建和Oncreate()的執行,然後執行requestServiceBindingsLocked,這個是bind服務相關處理,最後是sendServiceArgsLocked,這個是Start服務的處理。
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo), app.repProcState);
requestServiceBindingsLocked(r, execInFg);
sendServiceArgsLocked(r, execInFg, true);
}
繼續往下看requestServiceBindingsLocked再調用ActivityThread的方法scheduleBindService,在ActivityThread.java 中,它發出一個BIND_SERVICE事件,被handleBindService處理,
private void handleBindService(BindServiceData data) {
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
} else {
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, 0, 0, 0);
}
這裡先調用Service服務的onBind方法,因為服務是重載的,所以會執行具體服務類的方法,並返回服務裡的binder實例,被轉換後返回到AMS中,AMS繼續調用publishService方法,進而調用ActiveServices.java的publishServiceLocked,
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
for (int conni=r.connections.size()-1; conni>=0; conni--) {
ArrayList clist = r.connections.valueAt(conni);
for (int i=0; i
這裡主要調用到c.conn.connected,c就是ConnectionRecord,其成員conn是一個IServiceConnection類型實例,connected則是其實現類的方法,這裡其實也是一套基於binder通信proxy與Stub,IServiceConnection是采用aidl定義的一個接口,位置在core/java/Android/app/IServiceConnection.aidl,aidl定義如下,只有一個接口方法connected:
oneway interface IServiceConnection {
void connected(in ComponentName name, IBinder service);
}
其服務端的實現在LoadedApk.java,InnerConnection類是在ServiceDispatcher的內部類,並在ServiceDispatcher的構造函數裡面實例化的,其方法connected也是調用的ServiceDispatcher的方法connected,
private static class InnerConnection extendsIServiceConnection.Stub {
final WeakReference mDispatcher;
InnerConnection(LoadedApk.ServiceDispatcher sd) {
mDispatcher = new WeakReference(sd);
}
public void connected(ComponentName name, IBinder service) throws RemoteException {
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if (sd != null) {
sd.connected(name, service);
}
}
}
ServiceDispatcher(ServiceConnection conn,
Context context, Handler activityThread, int flags) {
mIServiceConnection = new InnerConnection(this);
mConnection = conn;
mContext = context;
mActivityThread = activityThread;
mLocation = new ServiceConnectionLeaked(null);
mLocation.fillInStackTrace();
mFlags = flags;
}
這裡就再回到我們前面的ContextImpl裡面bindServiceCommon方法裡面,這裡進行ServiceConnection轉化為IServiceConnection時,調用了mPackageInfo.getServiceDispatcher,mPackageInfo就是一個LoadedApk實例,
/*package*/ LoadedApk mPackageInfo;
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
UserHandle user) {
IServiceConnection sd;
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
mMainThread.getHandler(), flags);
}
所以,getServiceDispatcher會創建一個ServiceDispatcher實例,並將ServiceDispatcher實例和ServiceConnection實例形成KV對,並在ServiceDispatcher的構造函數裡將ServiceConnection實例c賦值給ServiceConnection的成員變量mConnection,
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
Context context, Handler handler, int flags) {
synchronized (mServices) {
LoadedApk.ServiceDispatcher sd = null;
ArrayMap map = mServices.get(context);
if (map != null) {
sd = map.get(c);
}
if (sd == null) {
sd = new ServiceDispatcher(c, context, handler, flags);
if (map == null) {
map = new ArrayMap();
mServices.put(context, map);
}
map.put(c, sd);
}
在執行ServiceDispatcher的connected方法時,就會調用到ServiceConnection的onServiceConnected,完成綁定ServiceConnection的觸發。
public void doConnected(ComponentName name, IBinder service) {
if (old != null) {
mConnection.onServiceDisconnected(name);
}
// If there is a new service, it is now connected.
if (service != null) {
mConnection.onServiceConnected(name, service);
}
}
AIDL總結
binderService其實是通過AMS進行中轉,如果Service沒啟動,就啟動Service,之後進行Publish將新進程的Bidner的代理轉發給各個端口,誰需要發給誰,具體流程如下圖:
1、Activity調用bindService函數通知ActivityManagerService,要啟動Service這個服務 2、ActivityManagerService創建Servicerecord,並且ApplicationThreadProxy回調,在MainActivity所在的進程內部把Service啟動起來,並且調用它的onCreate函數; 3、ActivityManagerService把Service啟動起來後,繼續調用onBind函數,讓Service返回一個Binder對象給它,以便AMS傳遞給Activity 4、ActivityManagerService把從Service處得到這個Binder對象傳給Activity,即把這個Binder對象作為參數傳遞給Activity內部定義的ServiceConnection對象的onServiceConnected函數;這裡是通過IserviceConnection binder實現。 5、Activity內部定義的ServiceConnection對象的onServiceConnected函數在得到這個Binder對象後,就通過它的getService成同函數獲得CounterService接口,封裝跟拆解 6、Java層的mRemote本身都是BinderProxy
Binder面試應
Binder面試問題–來說說Binder
原則由點及面,由面及裡:Java AIDL原理,binder驅動,掛起與喚醒等
如何理解這個Binder:從用法理解原理,而不是從原理理解用法,大概也是比較合理的學習方式。先會說話,再學語法。 Binder用來干嘛的,通信,既然是通信,就是把數據傳輸給目標。具體到Android就是將當前進程的數據發送給目標進程。 Android的實現基本算是C/S架構,所有的核心服務有自己的獨立進程。Android基於Linux內核,其進程模型就是Linux進程模型,Linux的進程通信方式完全適用於Android,或者說,就是一模一樣的。
一,系統啟動Android設備的開機流程總得來分可以分為三部分:加載引導程序引導程序bootloader是開機運行的第一個小程序,因此它是針對特定的主板與芯片的。boot
在實際項目開發中,會出現很多的異常直接導致程序crash掉,在開發中我們可以通過logcat查看錯誤日志,Debug出現的異常,讓程序安全的運行,但是在開發
谷歌最近更新了Support Library 24.2.0,而DiffUtil就是在這個版本添加的一個工具類。DiffUtil是一個查找集合變化的工具類,是搭配Recyc
背景最近的項目中用到了類似美團中的下拉多選菜單,在實際開發過程中,也發現了一些問題,主要歸納如下:1.當菜單較為復雜時,如果不能設計好代碼邏輯,將造成控件難於維護2.美團