---
title: 'Android Parcel'
disqus: kyleAlien
---
Android Parcel
===
## OverView of Content
[TOC]
## Parcel 概述
同一進程之間要傳遞對象只需要傳遞對象的內存地址,**不同進程之間若要傳遞對象就不能用內存地址了**(**由於每個進程空間的內存地址都是 ++虛擬內存機制++ 的關係**)
**Parcel 是一種數據的載體**,**用於承載希望通過 IBinder 發送的訊息,它會打包需要的數據傳送到另外一個進程**
> 
## 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) | 設定儲存空間大小 |
> 
### 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
> 
:::
### [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 系統` `進程`