--- title: 'Android Parcel' disqus: kyleAlien --- Android Parcel === ## OverView of Content [TOC] ## Parcel 概述 同一進程之間要傳遞對象只需要傳遞對象的內存地址,**不同進程之間若要傳遞對象就不能用內存地址了**(**由於每個進程空間的內存地址都是 ++虛擬內存機制++ 的關係**) **Parcel 是一種數據的載體**,**用於承載希望通過 IBinder 發送的訊息,它會打包需要的數據傳送到另外一個進程** > ![](https://i.imgur.com/JniT2Fa.png) ## Parcel 類 詳細 Java 層 Parcel 使用可以參考 官方 [**Parcel**](https://developer.android.com/reference/android/os/Parcel) 介紹 ### [Parcel](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/Parcel.cpp) 控制 * 說明幾個常見函數 | 函數 | 功能 | | -------- | -------- | | int dataSize() | 獲取當前已儲存的數據大小 | | int dataAvail() |獲取當前已儲存 **可讀** 的數據大小 | | int dataCapacity() | Parcel 可容納的儲存大小 | | int dataPosition() | 類似滑鼠標,代表當前處理到哪 | | void setDataPosition(int pos) | 設定讀寫位置,`0 ~ dataSize` 之間 | | void setDataCapacity(int size) | 設定儲存空間大小 | > ![](https://i.imgur.com/YW9SvQk.png) ### Parcel - Primitive * Primitive 就是針對原始數據 (對於 Java 來說就是原始的八大數據 `char`、`byte`、`int`、`short`、`long`...) 的讀寫操作,這些數據有 read 就會有 write * 在寫入數據時,從 `dataPosition` 的位子寫入數據(接續 Pos 指標寫),若有需要就會擴充 Parcel 大小 > 會動態擴展 Parcel 包空間 以下列出幾個 Function | 函數 | 功能 | | -------- | -------- | | writeBtye(byte) | 寫入一個 Byte 數據 | | readByte() | 讀取一個 Byte 數據 | | writeDouble(double) | 寫入一個 double 數據 | | readDouble() | 讀取一個 double 數據 | ### Parcel - Primitive array * **原始數據類型數組**,通常是 **++先寫入 4 Byte 表示數據大小++**,接著再寫數據本身 > 下面就會分析到 writeString... 它就會先寫入 wirteInt32 代表了 string 的長度 * 在寫入數據時,從 `dataPosition` 的位子寫入數據(接續 Pos 指標寫),若有需要就會擴充 Parcel 大小 | 函數 | 功能 | | -------- | -------- | | writeBooleanArray(boolean[]) | 寫 Boolean 數組 | | readBooleanArray() | 讀 Boolean 數組 | | createBooleanArray() | 返回一個 Boolean 數組,可能為 null | | writeByteArray(byte[]) | 寫 byte 數組 | | writeByteArray(byte[], int, int) | 同上,但是多了數據要被寫入的起點、終點 | | readByteArray() | 讀 byte 數組 | | createByteArray() | 返回一個 byte 數組,可能為 null | ### Parcel - [Parcelable](https://developer.android.com/reference/android/os/Parcelable) ```java= // Parcelable.java public interface Parcelable { ... public interface Creator<T> { public T createFromParcel(Parcel source); public T[] newArray(int size); } } ``` * Parcelable 協議的對象可以通過 Parcel 來存取 (eg. Bundle 就實現了 Parcelable 接口) | 函數 | 功能 | | -------- | -------- | | writeParcelable | 將實現 Parcelable 類的名子 & 內容寫入 Parcel 中,實際上它是通過回調 writeToParcel(Parcelable 的方法) 方法來寫入新數據 | | readParcelable | 讀取並返回 Parcelable 方法 | | writeParcelableArray | 同上,不過是寫入 Array 數據 | | readParcelableArray | 讀取並返回數組 | ### Parcel - Bundles * Bundle 實現 Parcelable 接口,**它是一種特殊的 `type-safe` 容器**,**Bundle 最大的特點是它採用 ++key-value 的方式儲存數據++,並在 ++優化了讀取效率++** | 函數 | 功能 | | -------- | -------- | | writeBundle(Bundle) | 將 Bundle 寫入 parcel | | readBundle() | 讀取並返回 **新的 Bundle 對象** | | readBundle(ClassLoader) | 同上,ClassLoader 用於 Bundle 獲取對應的 Parcelable 對象 | ### Parcel - Untyped Containers * 主要用於讀寫 **表準的 java 容器** | 函數 | 補充 | | -------- | -------- | | writeArray(Object[]) | | | readArray(ClassLoader) | | | writeList(List) | | | readList(List, ClassLoader) | | ### Parcel 特性 1. **無關性** 不排斥傳輸何類型的數據 2. **分類打包** 不同類型使用不同方式打包、解開 3. **自動組裝** Parcel 會 **依照協議來為接收方提供完整還原出原始數據的業務** ## [Parcel](https://cs.android.com/android/platform/superproject/+/master:system/libhwbinder/Parcel.cpp;l=621;drc=652335ea7c2f8f281a1b93a1e1558960b6ad1b6f;bpv=1;bpt=1) - ActiveObject * 一般來說 Parcel 作為一個包裹數據的載體,而 **Active Object 寫入的則是它們的特殊標誌 ++引用++**,在讀取對象時一般來說會創建一個新的對象,但是 **Active Object 則是 ++原先的對象++** * 能以這種方式傳輸的對象不多,主要有兩類 1. **Binder**: Android IPC 的核心機制之一,Binder 也是一個類,利用 Parcel 將 Binder 對象寫入,**取讀時可以讀取到原先的 Binder 對象** | 函數 | 功能 | | -------- | -------- | | writeStrongBinder(IBinder) | 寫入一個實現 IBinder 接口的類 | | writeStrongInterface(Interface) | 寫入一個實現 Interface 接口的類 | | readStrongBinder() | 讀取 Binder | | readStrongInterface() | 讀取 Interface | ```cpp= // Parcel.cpp status_t Parcel::writeStrongBinder(const sp<IBinder>& val) { // @ 追蹤 flattenBinder 方法 return flatten_binder(ProcessState::self(), val, this); } status_t Parcel::flattenBinder(const sp<IBinder>& binder) { flat_binder_object obj = {}; if (binder != nullptr) { // 判別傳入的對象是否是 Local Service (目前只有 BBinder 會複寫它) BHwBinder *local = binder->localBinder(); if (!local) { // 非本地 BpHwBinder *proxy = binder->remoteBinder(); if (proxy == nullptr) { ALOGE("null proxy"); } const int32_t handle = proxy ? proxy->handle() : 0; // 這個標誌很重要,在 Binder Kernel 會判斷 obj.hdr.type = BINDER_TYPE_HANDLE; obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS; obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */ obj.handle = handle; obj.cookie = 0; } else { ... 省略部份 // 本地服務 // // 這個標誌很重要,在 Binder Kernel 會判斷 obj.hdr.type = BINDER_TYPE_BINDER; obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs()); obj.cookie = reinterpret_cast<uintptr_t>(local); } } else { obj.hdr.type = BINDER_TYPE_BINDER; obj.binder = 0; obj.cookie = 0; } // @ 查看 finish_flatten_binder return finish_flatten_binder(binder, obj, out); } inline static status_t finish_flatten_binder( const sp<IBinder>& /*binder*/, const flat_binder_object& flat, Parcel* out) { // @ 分析 writeObject return out->writeObject(flat); } ``` :::info * 看似序列化轉換,其實不然,傳送的一直是相同物件,但這個行為有 Kernel space 完成 ::: 2. **FileDescriptor**:Linux 中的文件描述符 | 函數 | 功能 | | -------- | -------- | | writeFileDescriptor(FileDescriptor) | 寫入文件描述符 | | readFileDescriptor() | 讀取文件描述符 | ### Parcel 寫入 Binder 物件 - writeObject * [**Parcel**](https://cs.android.com/android/platform/superproject/+/master:system/libhwbinder/Parcel.cpp)**#`writeObject` 函數**:將 BinderServer 資料填入 Parcel,這裡的 **重點是使用到 `mObjects`、`mObjectsSize` 兩個變量來記錄 Service** ```cpp= // Parcel.h class Parcel { ... 省略部分 private: binder_size_t* mObjects; // 偏移陣列 (儲存 Service 地址) size_t mObjectsSize; // Service 的數量 } // ------------------------------------------------------------ // Parcel.cpp status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData) { // 空間檢查,查看是否有足夠的空間可以寫入 const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity; // 判斷是否有足夠空間寫入一個 Binder 物件 const bool enoughObjects = mObjectsSize < mObjectsCapacity; if (enoughData && enoughObjects) { restart_write: // 將 val 寫入 Parcel 空間 *reinterpret_cast<T*>(mData+mDataPos) = val; const binder_object_header* hdr = reinterpret_cast<binder_object_header*>(mData+mDataPos); switch (hdr->type) { case BINDER_TYPE_BINDER: case BINDER_TYPE_WEAK_BINDER: case BINDER_TYPE_HANDLE: case BINDER_TYPE_WEAK_HANDLE: { const flat_binder_object *fbo = reinterpret_cast<const flat_binder_object*>(hdr); if (fbo->binder != 0) { // 調整 mObjects、mObjectsSize mObjects[mObjectsSize++] = mDataPos; acquire_binder_object(ProcessState::self(), *fbo, this); } break; } case BINDER_TYPE_FD: { // 是否同意攜帶 FD (檔案描述符號) if (!mAllowFds) { // fail before modifying our object index return FDS_NOT_ALLOWED; } mHasFds = mFdsKnown = true // 調整 mObjects、mObjectsSize mObjects[mObjectsSize++] = mDataPos; break; } case BINDER_TYPE_FDA: // 調整 mObjects、mObjectsSize mObjects[mObjectsSize++] = mDataPos; break; case BINDER_TYPE_PTR: { const binder_buffer_object *buffer_obj = reinterpret_cast< const binder_buffer_object*>(hdr); if ((void *)buffer_obj->buffer != nullptr) { // 調整 mObjects、mObjectsSize mObjects[mObjectsSize++] = mDataPos; } break; } default: { ALOGE("writeObject: unknown type %d", hdr->type); break; } } return finishWrite(sizeof(val)); } // 以下為擴充 Parcel // 擴充 Data 空間 if (!enoughData) { const status_t err = growData(sizeof(val)); if (err != NO_ERROR) return err; } // 擴充 Object 空間 if (!enoughObjects) { if (mObjectsSize > SIZE_MAX - 2) return NO_MEMORY; // overflow if (mObjectsSize + 2 > SIZE_MAX / 3) return NO_MEMORY; // overflow size_t newSize = ((mObjectsSize+2)*3)/2; if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t)); if (objects == nullptr) return NO_MEMORY; mObjects = objects; mObjectsCapacity = newSize; } goto restart_write; // 重試 } ``` ### Parcel 讀取 Binder 物件 - readStrongBinder * [**Parcel**](https://cs.android.com/android/platform/superproject/+/master:system/libhwbinder/Parcel.cpp)#`readStrongBinder` 函數:創建本地 Proxy,並讀取 BinderDriver 的數據,並 **透過 Parcel#`readObject` 來讀取 BinderServer** ```cpp= // Parcel.cpp status_t Parcel::readStrongBinder(sp<IBinder>* val) const { // @ 查看 readNullableStrongBinder status_t status = readNullableStrongBinder(val); ... return status; } status_t Parcel::readNullableStrongBinder(sp<IBinder>* val) const { // @ 查看 unflatten_binder return unflatten_binder(ProcessState::self(), *this, val); } status_t unflatten_binder(const sp<ProcessState>& proc, const Parcel& in, sp<IBinder>* out) { const flat_binder_object* flat = in.readObject<flat_binder_object>(); if (flat) { switch (flat->hdr.type) { case BINDER_TYPE_BINDER: *out = reinterpret_cast<IBinder*>(flat->cookie); return finish_unflatten_binder(nullptr, *flat, in); // 使用者註冊 Service 時,傳入會進這個 casee case BINDER_TYPE_HANDLE: // 創建一個本地代理,對應到 Binder ref *out = proc->getStrongProxyForHandle(flat->handle); return finish_unflatten_binder( static_cast<BpHwBinder*>(out->get()), *flat, in); } } return BAD_TYPE; } ``` ### Parcel 讀取 Binder 物件 - readObject * [**Parcel**](https://cs.android.com/android/platform/superproject/+/master:system/libhwbinder/Parcel.cpp)#`readObject` 函數:會讀取 Parcel 包資料,並將其中的 Object 物件轉為 IBinder 物件 ```cpp= // Parcel.cpp template<typename T> const T* Parcel::readObject(size_t *objects_offset) const { const size_t DPOS = mDataPos; if (objects_offset != nullptr) { *objects_offset = 0; } if ((DPOS+sizeof(T)) <= mDataSize) { // 抓取物件,並強制轉型 const T* obj = reinterpret_cast<const T*>(mData+DPOS); // 更新 Pos mDataPos = DPOS + sizeof(T); const binder_object_header *hdr = reinterpret_cast<const binder_object_header*>(obj); switch (hdr->type) { case BINDER_TYPE_BINDER: case BINDER_TYPE_WEAK_BINDER: case BINDER_TYPE_HANDLE: case BINDER_TYPE_WEAK_HANDLE: { const flat_binder_object *flat_obj = reinterpret_cast<const flat_binder_object*>(hdr); if (flat_obj->cookie == 0 && flat_obj->binder == 0) { // 空物件 // // When transferring a NULL binder object, we don't write it into // the object list, so we don't want to check for it when // reading. ALOGV("readObject Setting data pos of %p to %zu", this, mDataPos); return obj; } break; } ... 省略其他 case } ... 省略部分 } return nullptr; } ``` ## Parcel 內部實現 Parcel 的實現會掉用到 Native 層 & Java 層的交互,所以這邊分開講 ### Parcel - [Java](https://android.googlesource.com/platform/frameworks/base/+/27f592d/core/java/android/os/Parcel.java) 層 1. 應用程序可以透過 `Parcel.obtain()` 獲取一個 Parcel 實例(透過這個方法就可以覆用) ```java= // /android/os/Parcel.java private static final int POOL_SIZE = 6; private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE]; public static Parcel obtain() { // 使用預設 Parcel 池 final Parcel[] pool = sOwnedPool; // 預設有 6 個 Parcel 對象 synchronized (pool) { Parcel p; for (int i=0; i<POOL_SIZE; i++) { p = pool[i]; if (p != null) { pool[i] = null; // 使用後將 pool 內選中的 Parcel 置為 null if (DEBUG_RECYCLE) { p.mStack = new RuntimeException(); } return p; } } } return new Parcel(0); // Parcel 池為空,就創建一個 } ``` 2. Parcel constructor:呼叫 Native 層創建 Parcel 物件 ```java= // Parcel.java // 保存 Native parcel 指針 private long mNativePtr; // used by native code // 在 obtain function 創建時傳入 0 private Parcel(int nativePtr) { ... init(nativePtr); } private void init(int nativePtr) { if (nativePtr != 0) { ... } else { // 走這裡 mNativePtr = nativeCreate(); mOwnsNativeParcelObject = true; } } // Native 實現的方法 private static native int nativeCreate(); ``` ### Parcel - [JNI Native](https://android.googlesource.com/platform/frameworks/base/+/master/core/jni/android_os_Parcel.cpp) - [Cpp](https://android.googlesource.com/platform/frameworks/native/+/jb-dev/libs/binder/Parcel.cpp) 1. JNI Native 實現: [**android_os_Parcel.cpp**](https://android.googlesource.com/platform/frameworks/base/+/master/core/jni/android_os_Parcel.cpp),可以看到他在這創建了一個 Parcel 對象 ```cpp= // /core/jni/android_os_Parcel.cpp // 對應 Java & Native 的方法,應該是 JNI 的 ++動態註冊++ static const JNINativeMethod gParcelMethods[] = { ... {"nativeCreate", "()J", (void*)android_os_Parcel_create}, ... } static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz) { // 創建 Native 層的 Parcel 對象 Parcel* parcel = new Parcel(); return reinterpret_cast<jlong>(parcel); } ``` 2. Cpp Native 實現:[**Parcel.cpp**](https://android.googlesource.com/platform/frameworks/native/+/jb-dev/libs/binder/Parcel.cpp),可以看到 Parcel 類是遵循 **==動態擴展==**,在這主要 **初使化成員變量,但不分配內存** ```cpp= // /libs/binder/Parcel.cpp Parcel::Parcel() { initState(); } void Parcel::initState() { mError = NO_ERROR; // 錯誤碼 mData = 0; // Parcel 中儲存的數據 mDataSize = 0; // Parcel 中儲存的大小 mDataCapacity = 0; // Parcel 容量 mDataPos = 0; // 數據指針 ... } ``` ## Parcel 實際使用 ### [ServiceManagerNative](https://android.googlesource.com/platform/frameworks/base/+/android-5.0.0_r1/core/java/android/os/ServiceManagerNative.java) - 寫入數據 * 實際是透過 ServiceManagerProxy 的 Binder 機制來寫入數據 :::warning 1. 之後 ServiceManagerNative 的實現又不同了,這邊先以研究 Parcel 為目的,看 Androd 5.0.0 的源碼 2. 這裡先不關注 Binder 的實現,先關注 Parcel 的使用 ::: * ServiceManagerNative:看 Parcel 的 writeString 方法(ServiceManagerProxy 的 getService 方法) ```java= // /java/android/os/IServiceManager.java public interface IServiceManager extends IInterface { public IBinder getService(String name) throws RemoteException; ... static final String descriptor = "android.os.IServiceManager"; } // --------------------------------------------------------------------- // /android-5.0.0_r1/core/java/android/os/ServiceManagerNative.java public abstract class ServiceManagerNative extends Binder implements IServiceManager { ... } class ServiceManagerProxy implements IServiceManager { public ServiceManagerProxy(IBinder remote) { mRemote = remote; } public IBinder asBinder() { return mRemote; } public IBinder getService(String name) throws RemoteException { // 1. 取 Parcel 對象 Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); // 2. writeInterfaceToken 用於寫入 IBinder 接口標誌 data.writeInterfaceToken(IServiceManager.descriptor); // 3. 通過 writeString 在 Parcel 中寫入需要像 ServiceManager 查詢的 Service 名稱 data.writeString(name); mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0); IBinder binder = reply.readStrongBinder(); // 回收 Parcel 對象 reply.recycle(); data.recycle(); return binder; } ... } ``` * Parcel:跳轉 Parcel 的實現 (包括 Parcel's JNI & Parcel's cpp) 1. Java & JNI ```java= // /java/android/os/Parcel.java public final void writeInterfaceToken(String interfaceName) { nativeWriteInterfaceToken(mNativePtr, interfaceName); } public final void writeString(String val) { nativeWriteString(mNativePtr, val); } private static native void nativeWriteInterfaceToken(int nativePtr, String interfaceName); private static native void nativeWriteString(int nativePtr, String val); // --------------------------------------------------------------------- // /master/core/jni/android_os_Parcel.cpp // 對應 nativeWriteInterfaceToken 方法 static void android_os_Parcel_writeInterfaceToken(JNIEnv* env, jclass clazz, jlong nativePtr, jstring name) { Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr); if (parcel != nullptr) { InterfaceDescriptorString descriptor(env, name); parcel->writeInterfaceToken(reinterpret_cast<const char16_t*>(descriptor.str()), descriptor.size()); } } ``` 2. writeInterfaceToken Cpp 實現:當時現 writeString16 這個方法時,^1^ 先寫入 4 byte 的數據長度,^2^ 用單位 * 長度,^3^ writeInplace 計算最終位置,^4^ 使用 memcpy 複製 str 到 data 位置中,^5^ 寫入結尾字符串 `\0` ```cpp= // /libs/binder/Parcel.cpp //有關 writeInterfaceToken 的實現先理解為 writeInt32 + writeString16 就可以 status_t Parcel::writeInterfaceToken(const String16& interface) { // 先不關心 getStrictModePolicy 的實踐 writeInt32(IPCThreadState::self()->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER); // currently the interface identification token is just its name as a string return writeString16(interface); } status_t Parcel::writeString16(const String16& str) { return writeString16(str.string(), str.size()); } status_t Parcel::writeString16(const char16_t* str, size_t len) { if (str == NULL) return writeInt32(-1); // 1. 先寫入 4 byte 的數據長度 status_t err = writeInt32(len); if (err == NO_ERROR) { len *= sizeof(char16_t); // 2. len = len * 單位 (由於是 string 16 所以就是 char_16) // 3. writeInplace 計算最終位置 uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t)); // // len + 2 byte 的原因是以 `\0` 的關係 if (data) { // 4. 使用 memcpy 複製 str 到 data 位置中 memcpy(data, str, len); // 5. 寫入字符串 0 *reinterpret_cast<char16_t*>(data+len) = 0; return NO_ERROR; } err = mError; } return err; } ``` 4. writeInplace 計算複製數據的的目的:^1^ **PAD_SIZE** 用於計算 **當以 4 對齊,容納 len 大小的數據需要多少空間**,^2^ 判斷是否需要填充尾部數據,^3^ 若有溢位則擴容(growData),^4^ 填充尾部數據 ```cpp= // /libs/binder/Parcel.cpp #define PAD_SIZE(s) (((s)+3)&~3) void* Parcel::writeInplace(size_t len) // 傳入的 len, len *= sizeof(char16_t) { // 1. PAD_SIZE // eg. len = 3, padding = 4 // len = 4, padding = 4 // len = 5, padding = 8 // len = 52, padding = 52 const size_t padded = PAD_SIZE(len); // 計算是否溢位 if (mDataPos+padded < mDataPos) { return NULL; // 超出大小 or padded 是負的 } // 2. 判斷是否需要填充尾部數據 if ((mDataPos+padded) <= mDataCapacity) { restart_write: // 計算最後位置 uint8_t* const data = mData+mDataPos; // 判斷是否有需要補充尾部數據 if (padded != len) { // 需要填充尾部的情況 #if BYTE_ORDER == BIG_ENDIAN static const uint32_t mask[4] = { 0x00000000, 0xffffff00, 0xffff0000, 0xff000000 }; #endif #if BYTE_ORDER == LITTLE_ENDIAN // Android 目前都使用 LSB static const uint32_t mask[4] = { 0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff }; #endif // 4. 填充尾部數據 *reinterpret_cast<uint32_t*>(data+padded-4) &= mask[padded-len]; } // 更新 mDataPos 鼠標位子 finishWrite(padded); return data; } // 3. 若是超出容量則需要擴容 status_t err = growData(padded); if (err == NO_ERROR) goto restart_write; // 擴容成功後再次判斷是否需要填充數據 return NULL; } ``` :::info * BIG_ENDIAN & LITTLE_ENDIAN 差異,可以 [**參考**](https://www.twblogs.net/a/5b8ff3b62b71776722163349) ,BIG_ENDIAN 簡單來說就是低地址存放最高有效位元,反知 LITTLE_ENDIAN > ![](https://i.imgur.com/vPszqJP.png) ::: ### [Service_manager](https://android.googlesource.com/platform/frameworks/base/+/6215d3ff4b5dfa52a5d8b9a42e343051f31066a5/cmds/servicemanager/service_manager.c) - 讀取數據 ```c= // /cmds/servicemanager/service_manager.c uint16_t svcmgr_id[] = { 'a','n','d','r','o','i','d','.','o','s','.', 'I','S','e','r','v','i','c','e','M','a','n','a','g','e','r' }; int svcmgr_handler(struct binder_state *bs, struct binder_txn *txn, struct binder_io *msg, struct binder_io *reply) { ... uint32_t strict_policy; if (txn->target != svcmgr_handle) return -1; strict_policy = bio_get_uint32(msg); // 取得 policy 值 s = bio_get_string16(msg, &len); // 取得 String16 也就是上寫入的 interface token // 判斷是否是取到 android.os.IServiceManager 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; ... default: LOGE("unknown code %d\n", txn->code); return -1; } bio_put_uint32(reply, 0); return 0; } ``` ## Appendix & FAQ :::info ::: ###### tags: `Android 系統` `進程`