本文首發於微信公衆號「後廠村碼農」html
關聯繫列
Android AOSP基礎系列
Android系統啓動系列
應用進程啓動系列
Android深刻四大組件系列
Android深刻理解Context系列
Android深刻理解JNI系列
Android解析WindowManager
Android解析WMS系列
Android解析AMS系列
Android包管理機制系列
Android輸入系統系列前端
在本系列的此前文章中,以MediaPlayerService爲例,講解了系統服務是如何註冊的(addService),既然有註冊那確定也要有獲取,本篇文章仍舊以MediaPlayerService爲例,來說解系統服務的獲取過程(getService)。文章會分爲兩個部分進行講解,分別是客戶端MediaPlayerService請求獲取服務和服務端ServiceManager處理請求,先來學習第一部分。 此前的系列文章見:liuwangshu.cn/tags/Binder…程序員
要想獲取MediaPlayerService,須要先調用getMediaPlayerService函數,以下所示。 frameworks/av/media/libmedia/IMediaDeathNotifier.cpp微信
IMediaDeathNotifier::getMediaPlayerService()
{
ALOGV("getMediaPlayerService");
Mutex::Autolock _l(sServiceLock);
if (sMediaPlayerService == 0) {
sp<IServiceManager> sm = defaultServiceManager();//1
sp<IBinder> binder;
do {
binder = sm->getService(String16("media.player"));//2
if (binder != 0) {//3
break;
}
ALOGW("Media player service not published, waiting...");
usleep(500000); //4
} while (true);
if (sDeathNotifier == NULL) {
sDeathNotifier = new DeathNotifier();
}
binder->linkToDeath(sDeathNotifier);
sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);//5
}
ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");
return sMediaPlayerService;
}
複製代碼
註釋1處的defaultServiceManager返回的是BpServiceManager,註釋2處獲取名爲」media.player」的系統服務(MediaPlayerService),返回的值爲BpBinder。因爲這個時候MediaPlayerService可能尚未向ServiceManager註冊,那麼就不能知足註釋3的條件,在註釋4處休眠0.5s後繼續調用getService函數,直到獲取服務對應的爲止。 註釋5處的interface_cast函數用於將BpBinder轉換成BpMediaPlayerService,其原理就是經過BpBinder的handle來找到對應的服務,即BpMediaPlayerService。app
註釋2處的獲取服務是本文的重點,BpServiceManager的getService函數以下所示。 frameworks/native/libs/binder/IServiceManager.cpp::BpServiceManager函數
virtual sp<IBinder> getService(const String16& name) const
{
...
int n = 0;
while (uptimeMillis() < timeout) {
n++;
if (isVendorService) {
ALOGI("Waiting for vendor service %s...", String8(name).string());
CallStack stack(LOG_TAG);
} else if (n%10 == 0) {
ALOGI("Waiting for service %s...", String8(name).string());
}
usleep(1000*sleepTime);
sp<IBinder> svc = checkService(name);//1
if (svc != NULL) return svc;
}
ALOGW("Service %s didn't start. Returning NULL", String8(name).string());
return NULL;
}
複製代碼
getService函數中主要作的事就是循環的查詢服務是否存在,若是不存在就繼續查詢,查詢服務用到了註釋1處的checkService函數,代碼以下所示。
frameworks/native/libs/binder/IServiceManager.cpp::BpServiceManageroop
virtual sp<IBinder> checkService( const String16& name) const
{
Parcel data, reply;//1
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);//2
remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);//3
return reply.readStrongBinder();
}
複製代碼
註釋1處的data,看過上一篇文章的同窗應該很熟悉,它出如今BpServiceManager的addService函數中,data是一個數據包,後面會不斷的將數據寫入到data中。註釋2處將字符串"media.player"寫入到data中。 註釋3處的remote()指的是mRemote,也就是BpBinder,BpBinder的transact函數以下所示。學習
frameworks/native/libs/binder/BpBinder.cppui
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;
}
複製代碼
BpBinder將邏輯處理交給IPCThreadState,後面的調用鏈在=Android Binder原理(三)系統服務的註冊過程中講過,這裏再次簡單的過一遍,IPCThreadState::self()會建立建立IPCThreadState,IPCThreadState的transact函數以下所示。
frameworks/native/libs/binder/IPCThreadState.cppspa
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
status_t err;
flags |= TF_ACCEPT_FDS;
...
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);//1
if (err != NO_ERROR) {
if (reply) reply->setError(err);
return (mLastError = err);
}
if ((flags & TF_ONE_WAY) == 0) {
...
if (reply) {
err = waitForResponse(reply);//2
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
...
} else {
//不須要等待reply的分支
err = waitForResponse(NULL, NULL);
}
return err;
}
複製代碼
調用BpBinder的transact函數實際上就是調用IPCThreadState的transact函數。註釋1處的writeTransactionData函數用於傳輸數據,其中第一個參數BC_TRANSACTION表明向Binder驅動發送命令協議。 註釋1處的writeTransactionData用於準備發送的數據,其內部會將BC_TRANSACTION和binder_transaction_data結構體寫入到mOut中。 接着查看waitForResponse函數作了什麼,代碼以下所示。
frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
uint32_t cmd;
int32_t err;
while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break;//1
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
cmd = (uint32_t)mIn.readInt32();
IF_LOG_COMMANDS() {
alog << "Processing waitForResponse Command: "
<< getReturnString(cmd) << endl;
}
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
...
default:
//處理各類命令協議
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
break;
}
}
finish:
...
return err;
}
複製代碼
註釋1處的talkWithDriver函數的內部經過ioctl與Binder驅動進行通訊,代碼以下所示。 frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
if (mProcess->mDriverFD <= 0) {
return -EBADF;
}
//和Binder驅動通訊的結構體
binder_write_read bwr; //1
//mIn是否有可讀的數據,接收的數據存儲在mIn
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();//2
//這時doReceive的值爲true
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();//3
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
...
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_LOG_COMMANDS() {
alog << "About to read/write, write size = " << mOut.dataSize() << endl;
}
#if defined(__ANDROID__)
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)//4
err = NO_ERROR;
else
err = -errno;
#else
err = INVALID_OPERATION;
#endif
...
} while (err == -EINTR);
...
return err;
}
複製代碼
註釋1處的 binder_write_read是和Binder驅動通訊的結構體,在註釋2和3處將mOut、mIn賦值給binder_write_read的相應字段,最終經過註釋4處的ioctl函數和Binder驅動進行通訊。這一過程的時序圖以下所示。
這時咱們須要再次查看Android Binder原理(三)系統服務的註冊過程這篇文章第2小節給出的圖。
從這張簡化的流程圖能夠看出,咱們當前分析的是客戶端進程的流程,當MediaPlayerService向Binder驅動發送BC_TRANSACTION命令後,Binder驅動會向ServiceManager發送BR_TRANSACTION命令,接下來咱們來查看服務端ServiceManager是如何處理獲取服務這一請求的。
說到服務端ServiceManager處理請求,不得不說到ServiceManager的啓動過程,具體的請看Android Binder原理(四)ServiceManager的啓動過程 這篇文章。 這裏簡單回顧servicemanager的入口函數,以下所示。
frameworks/native/cmds/servicemanager/service_manager.c
int main(int argc, char** argv) {
...
bs = binder_open(driver, 128*1024);
...
if (binder_become_context_manager(bs)) {
ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
...
if (getcon(&service_manager_context) != 0) {
ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
abort();
}
binder_loop(bs, svcmgr_handler);//1
return 0;
}
複製代碼
main函數主要作了三件事,其中最後一件事就是調用binder_loop函數,這裏須要注意,它的第二個參數爲svcmgr_handler,後面會再次提到svcmgr_handler。 binder_loop函數以下所示。 frameworks/native/cmds/servicemanager/binder.c
void binder_loop(struct binder_state *bs, binder_handler func) {
...
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < 0) {
ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
break;
}
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
if (res == 0) {
ALOGE("binder_loop: unexpected reply?!\n");
break;
}
if (res < 0) {
ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
break;
}
}
}
複製代碼
在無限循環中不斷的調用ioctl函數,它不斷的使用BINDER_WRITE_READ指令查詢Binder驅動中是否有新的請求,若是有就交給binder_parse函數處理。若是沒有,當前線程就會在Binder驅動中睡眠,等待新的進程間通訊請求。 binder_parse函數以下所示。
frameworks/native/cmds/servicemanager/binder.c
int binder_parse(struct binder_state *bs, struct binder_io *bio, uintptr_t ptr, size_t size, binder_handler func) {
int r = 1;
uintptr_t end = ptr + (uintptr_t) size;
while (ptr < end) {
uint32_t cmd = *(uint32_t *) ptr;
ptr += sizeof(uint32_t);
#if TRACE
fprintf(stderr,"%s:\n", cmd_name(cmd));
#endif
switch(cmd) {
...
case BR_TRANSACTION: {
struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
if ((end - ptr) < sizeof(*txn)) {
ALOGE("parse: txn too small!\n");
return -1;
}
binder_dump_txn(txn);
if (func) {
unsigned rdata[256/4];
struct binder_io msg;
struct binder_io reply;
int res;
bio_init(&reply, rdata, sizeof(rdata), 4);
bio_init_from_txn(&msg, txn);
res = func(bs, txn, &msg, &reply);//1
if (txn->flags & TF_ONE_WAY) {
binder_free_buffer(bs, txn->data.ptr.buffer);
} else {
binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
}
}
ptr += sizeof(*txn);
break;
}
...
}
return r;
}
複製代碼
這裏截取了BR_TRANSACTION命令的處理部分,註釋1出的func經過一路傳遞指向的是svcmgr_handler,svcmgr_handler函數以下所示。
frameworks/native/cmds/servicemanager/service_manager.c
int svcmgr_handler(struct binder_state *bs, struct binder_transaction_data *txn, struct binder_io *msg, struct binder_io *reply) {
...
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
if (!handle)
break;
bio_put_ref(reply, handle);
return 0;
...
default:
ALOGE("unknown code %d\n", txn->code);
return -1;
}
bio_put_uint32(reply, 0);
return 0;
}
複製代碼
當要獲取服務時,會調用do_find_service函數,代碼以下所示。 frameworks/native/cmds/servicemanager/service_manager.c
uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid)
{
struct svcinfo *si = find_svc(s, len);//1
if (!si || !si->handle) {
return 0;
}
if (!si->allow_isolated) {
uid_t appid = uid % AID_USER;
if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) {
return 0;
}
}
if (!svc_can_find(s, len, spid, uid)) {
return 0;
}
return si->handle;
}
複製代碼
註釋1處的find_svc函數用於查詢服務,返回的svcinfo是一個結構體,其內部包含了服務的handle值,最終會返回服務的handle值。接着來看find_svc函數:
frameworks/native/cmds/servicemanager/service_manager.c
struct svcinfo *find_svc(const uint16_t *s16, size_t len) {
struct svcinfo *si;
for (si = svclist; si; si = si->next) {
if ((len == si->len) &&
!memcmp(s16, si->name, len * sizeof(uint16_t))) {
return si;
}
}
return NULL;
}
複製代碼
系統服務的註冊流程中,在Kernel Binder中會調用do_add_service函數,其內部會將包含服務名和handle值的svcinfo保存到svclist列表中。一樣的,在獲取服務的流程中,find_svc函數中會遍歷svclist列表,根據服務名查找對應服務是否已經註冊,若是已經註冊就會返回對應的svcinfo,若是沒有註冊就返回NULL。
這篇文章將系統服務的獲取過程分爲兩個部分,代碼涉及到了Native Binder和Kernel Binder。在下一篇文章中會繼續學習Java Binder相關的內容。
更多的內容請關注個人獨立博客的知識體系:
liuwangshu.cn/system/
這裏不只分享大前端、Android、Java等技術,還有程序員成長類文章。