对Bundle进行序列化时,依次写入携带所有数据的长度、Bundle魔数(0x4C444E42)和键值对。见BaseBundle.writeToParcelInner方法
int lengthPos = parcel.dataPosition();
parcel.writeInt(-1); // dummy, will hold length
parcel.writeInt(BUNDLE_MAGIC);
int startPos = parcel.dataPosition();
parcel.writeArrayMapInternal(map);
int endPos = parcel.dataPosition();
// Backpatch length
parcel.setDataPosition(lengthPos);
int length = endPos - startPos;
parcel.writeInt(length);
parcel.setDataPosition(endPos);
pacel.writeArrayMapInternal方法写入键值对,先写入Hashmap的个数,然后依次写入键和值
/**
* Flatten an ArrayMap into the parcel at the current dataPosition(),
* growing dataCapacity() if needed. The Map keys must be String objects.
*/
/* package */ void writeArrayMapInternal(ArrayMap<String, Object> val) {
...
final int N = val.size();
writeInt(N);
...
int startPos;
for (int i=0; i<N; i++) {
if (DEBUG_ARRAY_MAP) startPos = dataPosition();
writeString(val.keyAt(i));
writeValue(val.valueAt(i));
...
接着,调用writeValue时依次写入Value类型和Value本身,如果是Parcelable对象,则调用writeParcelable方法,后者会调用Parcelable对象的writeToParcel方法
public final void writeValue(Object v) {
if (v == null) {
writeInt(VAL_NULL);
} else if (v instanceof String) {
writeInt(VAL_STRING);
writeString((String) v);
} else if (v instanceof Integer) {
writeInt(VAL_INTEGER);
writeInt((Integer) v);
} else if (v instanceof Map) {
writeInt(VAL_MAP);
writeMap((Map) v);
} else if (v instanceof Bundle) {
// Must be before Parcelable
writeInt(VAL_BUNDLE);
writeBundle((Bundle) v);
} else if (v instanceof PersistableBundle) {
writeInt(VAL_PERSISTABLEBUNDLE);
writePersistableBundle((PersistableBundle) v);
} else if (v instanceof Parcelable) {
// IMPOTANT: cases for classes that implement Parcelable must
// come before the Parcelable case, so that their specific VAL_*
// types will be written.
writeInt(VAL_PARCELABLE);
writeParcelable((Parcelable) v, 0);
一个典型的Bundle格式可以包含以下几个部分:
[Header]
Magic Number (4 bytes) | Total Length (4 bytes) | Number of Pairs (4 bytes)
[Key-Value Pair 1]
Key Length (4 bytes) | Key (variable length) | Padding (variable length) |
Value Length (4 bytes) | Value Type (4 bytes) | Value (variable length) | Padding (variable length)
[Key-Value Pair 2]
Key Length (4 bytes) | Key (variable length) | Padding (variable length) |
Value Length (4 bytes) | Value Type (4 bytes) | Value (variable length) | Padding (variable length)
...
[Key-Value Pair N]
Key Length (4 bytes) | Key (variable length) | Padding (variable length) |
Value Length (4 bytes) | Value Type (4 bytes) | Value (variable length) | Padding (variable length)
需要注意的是对齐规则,在Android的Bundle中,数据对齐(alignment)是一个重要的概念,特别是在序列化和反序列化过程中。Android的Parcel类用于读取和写入Bundle的数据,而Parcel类在处理数据时遵循特定的对齐规则,以确保数据在内存中正确对齐。这些对齐规则帮助提高访问效率,并确保数据在不同架构上的兼容性
在Android的Parcel和Bundle中,字符串是以UTF-16编码存储的。UTF-16编码的每个字符都占用两个字节
在往Intent的Bundle对象中添加键值对(Key Value)时,Key为String类型,而Value则可以为各种数据类型,包括int、Boolean、String和Parcelable对象等等,Parcel类中维护着这些类型信息
// Keep in sync with frameworks/native/include/private/binder/ParcelValTypes.h.
private static final int VAL_NULL = -1;
private static final int VAL_STRING = 0;
private static final int VAL_INTEGER = 1;
private static final int VAL_MAP = 2; // length-prefixed
private static final int VAL_BUNDLE = 3;
private static final int VAL_PARCELABLE = 4; // length-prefixed
private static final int VAL_SHORT = 5;
private static final int VAL_LONG = 6;
private static final int VAL_FLOAT = 7;
private static final int VAL_DOUBLE = 8;
private static final int VAL_BOOLEAN = 9;
private static final int VAL_CHARSEQUENCE = 10;
private static final int VAL_LIST = 11; // length-prefixed
private static final int VAL_SPARSEARRAY = 12; // length-prefixed
private static final int VAL_BYTEARRAY = 13;
private static final int VAL_STRINGARRAY = 14;
private static final int VAL_IBINDER = 15;
private static final int VAL_PARCELABLEARRAY = 16; // length-prefixed
private static final int VAL_OBJECTARRAY = 17; // length-prefixed
private static final int VAL_INTARRAY = 18;
private static final int VAL_LONGARRAY = 19;
private static final int VAL_BYTE = 20;
private static final int VAL_SERIALIZABLE = 21; // length-prefixed
private static final int VAL_SPARSEBOOLEANARRAY = 22;
private static final int VAL_BOOLEANARRAY = 23;
private static final int VAL_CHARSEQUENCEARRAY = 24;
private static final int VAL_PERSISTABLEBUNDLE = 25;
private static final int VAL_SIZE = 26;
private static final int VAL_SIZEF = 27;
private static final int VAL_DOUBLEARRAY = 28;
private static final int VAL_CHAR = 29;
private static final int VAL_SHORTARRAY = 30;
private static final int VAL_CHARARRAY = 31;
private static final int VAL_FLOATARRAY = 32;
// The initial int32 in a Binder call's reply Parcel header:
// Keep these in sync with libbinder's binder/Status.h.
private static final int EX_SECURITY = -1;
private static final int EX_BAD_PARCELABLE = -2;
private static final int EX_ILLEGAL_ARGUMENT = -3;
private static final int EX_NULL_POINTER = -4;
private static final int EX_ILLEGAL_STATE = -5;
private static final int EX_NETWORK_MAIN_THREAD = -6;
private static final int EX_UNSUPPORTED_OPERATION = -7;
private static final int EX_SERVICE_SPECIFIC = -8;
private static final int EX_PARCELABLE = -9;
public static final int EX_HAS_NOTED_APPOPS_REPLY_HEADER = -127;
private static final int EX_HAS_STRICTMODE_REPLY_HEADER = -128;
private static final int EX_TRANSACTION_FAILED = -129;
private static final int ARRAY_ALLOCATION_LIMIT = 1000000;
private static final int SIZE_BYTE = 1;
private static final int SIZE_CHAR = 2;
private static final int SIZE_SHORT = 2;
private static final int SIZE_BOOLEAN = 4;
private static final int SIZE_INT = 4;
private static final int SIZE_FLOAT = 4;
private static final int SIZE_DOUBLE = 8;
private static final int SIZE_LONG = 8;
我们先编写了一个这样的Java代码
static void bundle_parcel_study(){
Bundle b = new Bundle();
int[] a = {1,1};
b.putIntArray("aaa",a);
Hacktools.bundleToBytes(b);
}
然后获取到了它的Hexdump形式
Length: 40 (0x28)
0000: 20 00 00 00 42 4E 44 4C 01 00 00 00 03 00 00 00 | ...BNDL........
0010: 61 00 61 00 61 00 00 00 12 00 00 00 02 00 00 00 | a.a.a...........
0020: 01 00 00 00 01 00 00 00 | ........
根据hexdump,我们可以看到:
20 00 00 00 42 4E 44 4C 01 00 00 00
package com.anonymous.bundlemismatchtest;
import android.util.Log;
public class HexDump {
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static void printHexDump(String tag, byte[] bytes) {
if (bytes == null || bytes.length == 0) {
Log.d(tag, "Empty or null byte array");
return;
}
StringBuilder hexDump = new StringBuilder();
int length = bytes.length;
hexDump.append(String.format("length: %d (0x%X)\n", length, length));
for (int i = 0; i < length; i += 16) {
hexDump.append(String.format("%04X: ", i));
// Hex part
for (int j = 0; j < 16; j++) {
if (i + j < length) {
int v = bytes[i + j] & 0xFF;
hexDump.append(HEX_ARRAY[v >>> 4]);
hexDump.append(HEX_ARRAY[v & 0x0F]);
hexDump.append(' ');
} else {
hexDump.append(" ");
}
}
hexDump.append("| ");
// ASCII part
for (int j = 0; j < 16; j++) {
if (i + j < length) {
byte b = bytes[i + j];
if (b >= 32 && b <= 126) {
hexDump.append((char) b);
} else {
hexDump.append('.');
}
}
}
hexDump.append('\n');
}
Log.d(tag, hexDump.toString());
}
}
package com.anonymous.bundlemismatchtest;
import android.os.Bundle;
import android.os.Parcel;
import android.util.Log;
public class Hacktools {
//bundle to bytes
public static byte[] bundleToBytes(Bundle bundle) {
Parcel parcel = Parcel.obtain();
parcel.writeBundle(bundle);
byte[] bytes = parcel.marshall();
HexDump.printHexDump("MyTag", bytes);
//parcel.recycle();
return bytes;
}
// bytes to bundle
public static Bundle bytesToBundle(byte[] bytes) {
Parcel parcel = Parcel.obtain();
parcel.unmarshall(bytes, 0, bytes.length);
parcel.setDataPosition(0);
Bundle bundle = Bundle.CREATOR.createFromParcel(parcel);
//parcel.recycle();
return bundle;
}
// hexdump
public static String hexdump(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
//simulate the bundle mismatch
static void simulateBundleMismatch(Bundle b0) {
// 从exp中发送:序列化
byte[] exp_send = bundleToBytes(b0);
// system_server接收:反序列化 readFromParcel
Bundle b1 = bytesToBundle(exp_send);
// system_server发送:序列化 writeToParcel
byte[] system_server_send = bundleToBytes(b1);
// Setting接收:序列化
Bundle b2 = bytesToBundle(system_server_send);
}
}