關聯絡列
Android AOSP基礎系列
Android系統啟動系列
應用程式啟動系列
Android深入四大元件系列
Android深入理解Context系列
Android深入理解JNI系列
Android解析WindowManager
Android解析WMS系列
Android解析AMS系列
Android包管理機制系列
Android輸入系統系列
本文首發於微信公眾號「後廠技術官」
前言
在上一篇文章中,我們學習了ServiceManager中的Binder機制,有一個問題由於篇幅問題沒有講完,那就是MediaPlayerService是如何註冊的。通過了解MediaPlayerService是如何註冊的,可以得知系統服務的註冊過程。
1.從呼叫鏈角度說明MediaPlayerService是如何註冊的
我們先來看MediaServer的入口函式,程式碼如下所示。 frameworks/av/media/mediaserver/main_mediaserver.cpp
int main(int argc __unused, char **argv __unused)
{
signal(SIGPIPE, SIG_IGN);
//獲取ProcessState例項
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm(defaultServiceManager());
ALOGI("ServiceManager: %p", sm.get());
InitializeIcuOrDie();
//註冊MediaPlayerService
MediaPlayerService::instantiate();//1
ResourceManagerService::instantiate();
registerExtensions();
//啟動Binder執行緒池
ProcessState::self()->startThreadPool();
//當前執行緒加入到執行緒池
IPCThreadState::self()->joinThreadPool();
}
複製程式碼
這段程式碼中的很多內容都在上一篇文章介紹過了,接著分析註釋1處的程式碼。
frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
void MediaPlayerService::instantiate() {
defaultServiceManager()->addService(
String16("media.player"), new MediaPlayerService,());
}
複製程式碼
defaultServiceManager返回的是BpServiceManager,不清楚的看[Android Binder原理(二)ServiceManager中的Binder機制][1]這篇文章。引數是一個字串和MediaPlayerService,看起來像是Key/Value的形式來完成註冊,接著看addService函式。
frameworks/native/libs/binder/IServiceManager.cpp
virtual status_t addService(const String16& name, const sp<IBinder>& service,
bool allowIsolated, int dumpsysPriority) {
Parcel data, reply;//資料包
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name); //name值為"media.player"
data.writeStrongBinder(service); //service值為MediaPlayerService
data.writeInt32(allowIsolated ? 1 : 0);
data.writeInt32(dumpsysPriority);
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);//1
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
複製程式碼
data是一個資料包,後面會不斷的將資料寫入到data中, 註釋1處的remote()指的是mRemote,也就是BpBinder。addService函式的作用就是將請求資料打包成data,然後傳給BpBinder的transact函式,程式碼如下所示。 frameworks/native/libs/binder/BpBinder.cpp
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,先來看IPCThreadState::self()幹了什麼? frameworks/native/libs/binder/IPCThreadState.cpp
IPCThreadState* IPCThreadState::self()
{
//首次進來gHaveTLS的值為false
if (gHaveTLS) {
restart:
const pthread_key_t k = gTLS;//1
IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);//2
if (st) return st;
return new IPCThreadState;//3
}
...
pthread_mutex_unlock(&gTLSMutex);
goto restart;
}
複製程式碼
註釋1處的TLS的全稱為Thread local storage,指的是執行緒本地儲存空間,在每個執行緒中都有TLS,並且執行緒間不共享。註釋2處用於獲取TLS中的內容並賦值給IPCThreadState*指標。註釋3處會新建一個IPCThreadState,這裡可以得知IPCThreadState::self()實際上是為了建立IPCThreadState,它的建構函式如下所示。 frameworks/native/libs/binder/IPCThreadState.cpp
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0)
{
pthread_setspecific(gTLS, this);//1
clearCaller();
mIn.setDataCapacity(256);
mOut.setDataCapacity(256);
}
複製程式碼
註釋1處的pthread_setspecific函式用於設定TLS,將IPCThreadState::self()獲得的TLS和自身傳進去。IPCThreadState中還包含mIn、一個mOut,其中mIn用來接收來自Binder驅動的資料,mOut用來儲存發往Binder驅動的資料,它們預設大小都為256位元組。 知道了IPCThreadState的建構函式,再回來檢視IPCThreadState的transact函式。 frameworks/native/libs/binder/IPCThreadState.cpp
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驅動傳送命令協議,向Binder裝置傳送的命令協議都以BC_開頭,而Binder驅動返回的命令協議以BR_開頭。這個命令協議我們先記住,後面會再次提到他。
現在分別來分析註釋1的writeTransactionData函式和註釋2處的waitForResponse函式。
1.1 writeTransactionData函式分析
frameworks/native/libs/binder/IPCThreadState.cpp
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;//1
tr.target.ptr = 0;
tr.target.handle = handle;//2
tr.code = code; //code=ADD_SERVICE_TRANSACTION
tr.flags = binderFlags;
tr.cookie = 0;
tr.sender_pid = 0;
tr.sender_euid = 0;
const status_t err = data.errorCheck();//3
if (err == NO_ERROR) {
tr.data_size = data.ipcDataSize();
tr.data.ptr.buffer = data.ipcData();
tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
tr.data.ptr.offsets = data.ipcObjects();
} else if (statusBuffer) {
tr.flags |= TF_STATUS_CODE;
*statusBuffer = err;
tr.data_size = sizeof(status_t);
tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);
tr.offsets_size = 0;
tr.data.ptr.offsets = 0;
} else {
return (mLastError = err);
}
mOut.writeInt32(cmd); //cmd=BC_TRANSACTION
mOut.write(&tr, sizeof(tr));
return NO_ERROR;
}
複製程式碼
註釋1處的binder_transaction_data結構體(tr結構體)是向Binder驅動通訊的資料結構,註釋2處將handle傳遞給target的handle,用於標識目標,這裡的handle的值為0,代表了ServiceManager。 註釋3處對資料data進行錯誤檢查,如果沒有錯誤就將資料賦值給對應的tr結構體。最後會將BC_TRANSACTION和tr結構體寫入到mOut中。 上面程式碼呼叫鏈的時序圖如下所示。
1.2 waitForResponse函式分析
接著回過頭來檢視waitForResponse函式做了什麼,waitForResponse函式中的case語句很多,這裡擷取部分程式碼。
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;
case BR_DEAD_REPLY:
err = DEAD_OBJECT;
goto finish;
...
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驅動進行通訊,這一部分涉及到Kernel Binder的內容 了,就不再詳細介紹了,只需要知道在Kernel Binder中會記錄服務名和handle,用於後續的服務查詢。
1.3 小節
從呼叫鏈的角度來看,MediaPlayerService是如何註冊的貌似並不複雜,因為這裡只是簡單的介紹了一個呼叫鏈分支,可以簡單的總結為以下幾個步驟:
- addService函式將資料打包傳送給BpBinder來進行處理。
- BpBinder新建一個IPCThreadState物件,並將通訊的任務交給IPCThreadState。
- IPCThreadState的writeTransactionData函式用於將命令協議和資料寫入到mOut中。
- IPCThreadState的waitForResponse函式主要做了兩件事,一件事是通過ioctl函式操作mOut和mIn來與Binder驅動進行資料互動,另一件事是處理各種命令協議。
2.從程式角度說明MediaPlayerService是如何註冊的
實際上MediaPlayerService的註冊還涉及到了程式,如下圖所示。
從圖中看出是以C/S架構為基礎,addService是在MediaPlayerService進行的,它是Client端,用於請求新增系統服務。而Server端則是指的是ServiceManager,用於完成系統服務的新增。 Client端和Server端分別執行在兩個程式中,通過向Binder來進行通訊。更詳細點描述,就是兩端通過向Binder驅動傳送命令協議來完成系統服務的新增。這其中命令協議非常多,過程也比較複雜,這裡對命令協議進行了簡化,只涉及到了四個命令協議,其中 BC_TRANSACTION和BR_TRANSACTION過程是一個完整的事務,BC_REPLY和BR_REPLY是一個完整的事務。 Client端和Server端向Binder驅動傳送命令協議以BC開頭,而Binder驅動向Client端和Server端返回的命令協議以BR_開頭。
步驟如下所示: 1.Client端向Binder驅動傳送BC_TRANSACTION命令。 2.Binder驅動接收到請求後生成BR_TRANSACTION命令,喚醒Server端的執行緒後將BR_TRANSACTION命令傳送給ServiceManager。 3.Server端中的服務註冊完成後,生成BC_REPLY命令傳送給Binder驅動。 4.Binder驅動生成BR_REPLY命令,喚醒Client端的執行緒後將BR_REPLY命令傳送個Client端。
通過這些協議命令來驅動並完成系統服務的註冊。
3.總結
本文分別從呼叫鏈角度和程式角度來講解MediaPlayerService是如何註冊的,間接的得出了服務是如何註冊的 。這兩個角度都比較複雜,因此這裡分別對這兩個角度做了簡化,作為應用開發,我們不需要注重太多的過程和細節,只需要瞭解大概的步驟即可。