Android中在native层对java层应用程序发送广播方法及原理详解
Android从native发送数据给上层应用接收,常用的有JNI的形式,同样用广播也可以实现,本文正是基于广播实现native与上层应用通信,参考Android的Native方式广播intent,但是仅仅知道方法是不够的,需要更深层次的去了解数据是如何传输的原理,本文分两个部分,先讲发送广播的方法,再讲为何要按照这种格式发送。
本地广播发送方法
发送端
这是vendor\mediatek\proprietary\packages\apps\MTKThermalManager\jni\thermald.cpp中的源码,发送一个指定Action,并携带一个key为type,值为value的数据的广播。
bool sendBroadcastMessage(String16 action, int value)
{
TM_INFO_LOG("sendBroadcastMessage(): Action: %s, Value: %d ", action.string(), value);
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> am = sm->getService(String16("activity"));
if (am != NULL) {
Parcel data, reply;
data.writeInterfaceToken(String16("android.app.IActivityManager"));
data.writeStrongBinder(NULL);
// intent begin
data.writeString16(action); // action
data.writeInt32(0); // URI data type
data.writeString16(NULL, 0); // type
data.writeInt32(0); // flags
data.writeString16(NULL, 0); // package name
data.writeString16(NULL, 0); // component name
data.writeInt32(0); // source bound - size
data.writeInt32(0); // categories - size
data.writeInt32(0); // selector - size
data.writeInt32(0); // clipData - size
data.writeInt32(-2); // contentUserHint: -2 -> UserHandle.USER_CURRENT
data.writeInt32(-1); // bundle extras length
data.writeInt32(0x4C444E42); // 'B' 'N' 'D' 'L'
int oldPos = data.dataPosition();
data.writeInt32(1); // size
// data.writeInt32(0); // VAL_STRING, need to remove because of analyze common intent
data.writeString16(String16("type"));
data.writeInt32(1); // VAL_INTEGER
data.writeInt32(value);
int newPos = data.dataPosition();
data.setDataPosition(oldPos - 8);
data.writeInt32(newPos - oldPos); // refill bundle extras length
data.setDataPosition(newPos);
// intent end
data.writeString16(NULL, 0); // resolvedType
data.writeStrongBinder(NULL); // resultTo
data.writeInt32(0); // resultCode
data.writeString16(NULL, 0); // resultData
data.writeInt32(-1); // resultExtras
data.writeString16(NULL, 0); // permission
data.writeInt32(0); // appOp
data.writeInt32(-1); // option
data.writeInt32(1); // serialized: != 0 -> ordered
data.writeInt32(0); // sticky
data.writeInt32(-2); // userId: -2 -> UserHandle.USER_CURRENT
status_t ret = am->transact(IBinder::FIRST_CALL_TRANSACTION + 13, data, &reply); // BROADCAST_INTENT_TRANSACTION
if (ret == NO_ERROR) {
int exceptionCode = reply.readExceptionCode();
if (exceptionCode) {
TM_INFO_LOG("sendBroadcastMessage(%s) caught exception %d\n",
action.string(), exceptionCode);
return false;
}
} else {
return false;
}
} else {
TM_INFO_LOG("getService() couldn't find activity service!\n");
return false;
}
return true;
}
接收端
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter filter = new IntentFilter();
filter.addAction(action);
registerReceiver(dynamicReceiver,filter);
}
private BroadcastReceiver dynamicReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(action)){
Bundle bundle = intent.getExtras();
int number = bundle.getInt("type");
String test = String.valueOf(number);
Toast.makeText(context, test, Toast.LENGTH_LONG).show();
}
}
};
原理解析
首先,需要获取java层的IActivityManager接口,然后将需要传递的intent和token,caller等信息写入到一个parcel对象中,parcel是Binder常用的数据传输格式,最后再通过获取到的接口将这个parcel发送出去。这里重点分析传输的这个parcel中所要求的数据格式,不清楚这个数据格式,当需要携带多个数据时,必然会引发错误。
java层中转站
发送端通过binder传输的数据是在ActivityManagerNative.java中接收。
frameworks\base\core\java\android\app\ActivityManagerNative.java
case BROADCAST_INTENT_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app =
b != null ? ApplicationThreadNative.asInterface(b) : null;
Intent intent = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
b = data.readStrongBinder();
IIntentReceiver resultTo =
b != null ? IIntentReceiver.Stub.asInterface(b) : null;
int resultCode = data.readInt();
String resultData = data.readString();
Bundle resultExtras = data.readBundle();
String perm = data.readString();
int appOp = data.readInt();
boolean serialized = data.readInt() != 0;
boolean sticky = data.readInt() != 0;
int userId = data.readInt();
int res = broadcastIntent(app, intent, resolvedType, resultTo,
resultCode, resultData, resultExtras, perm, appOp,
serialized, sticky, userId);
reply.writeNoException();
reply.writeInt(res);
return true;
}
在上面这段代码中可以看到,在发送端写入的数据分别被读取,其中重点关注Intent intent = Intent.CREATOR.createFromParcel(data);
在java层创建intent对象
frameworks\base\core\java\android\content\Intent.java
public static final Parcelable.Creator<Intent> CREATOR
= new Parcelable.Creator<Intent>() {
public Intent createFromParcel(Parcel in) {
return new Intent(in);
}
public Intent[] newArray(int size) {
return new Intent[size];
}
};
调用Intent的构造函数
protected Intent(Parcel in) {
readFromParcel(in);
}
public void readFromParcel(Parcel in) {
setAction(in.readString());
mData = Uri.CREATOR.createFromParcel(in);
mType = in.readString();
mFlags = in.readInt();
mPackage = in.readString();
mComponent = ComponentName.readFromParcel(in);
if (in.readInt() != 0) {
mSourceBounds = Rect.CREATOR.createFromParcel(in);
}
int N = in.readInt();
if (N > 0) {
mCategories = new ArraySet<String>();
int i;
for (i=0; i<N; i++) {
mCategories.add(in.readString().intern());
}
} else {
mCategories = null;
}
if (in.readInt() != 0) {
mSelector = new Intent(in);
}
if (in.readInt() != 0) {
mClipData = new ClipData(in);
}
mExtras = in.readBundle();
}
前面没什么好说的,都是按照发送时写入的数据格式,然后在这里读取,这段代码重点看mExtras = in.readBundle();
发送时携带的数据正是存放在这个Bundle中的。
public final Bundle readBundle() {
return readBundle(null);
}
public final Bundle readBundle(ClassLoader loader) {
int length = readInt();
if (length < 0) {
if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length);
return null;
}
final Bundle bundle = new Bundle(this, length);
if (loader != null) {
bundle.setClassLoader(loader);
}
return bundle;
}
这里的readInt()
就是读取发送时data.writeInt32(-1); // bundle extras length
设置的length,在设置时由于不确定长度,因此先置为-1,然后再用oldPos记录下当时的位置,在数据写入完毕后,用newPos-oldPos得出数据长度,在用data.setDataPosition(oldPos - 8)
将写入位置重置到写入bundle extras length的位置,data.writeInt32(newPos - oldPos); // refill bundle extras length
写入真实的长度。
继续往下看,读取完长度之后实际上是调用了Bundle的构造函数。
Bundle(Parcel parcelledData, int length) {
readFromParcelInner(parcelledData, length);
}
void readFromParcelInner(Parcel parcel, int length) {
if (length == 0) {
// Empty Bundle or end of data.
mParcelledData = EMPTY_PARCEL;
mHasFds = false;
mFdsKnown = true;
return;
}
int magic = parcel.readInt();
if (magic != BUNDLE_MAGIC) {
//noinspection ThrowableInstanceNeverThrown
throw new IllegalStateException("Bad magic number for Bundle: 0x"
+ Integer.toHexString(magic));
}
// Advance within this Parcel
int offset = parcel.dataPosition();
parcel.setDataPosition(offset + length);
Parcel p = Parcel.obtain();
p.setDataPosition(0);
p.appendFrom(parcel, offset, length);
if (DEBUG) Log.d(TAG, "Retrieving " + Integer.toHexString(System.identityHashCode(this))
+ ": " + length + " bundle bytes starting at " + offset);
p.setDataPosition(0);
mParcelledData = p;
mHasFds = p.hasFileDescriptors();
mFdsKnown = true;
}
static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
实际就是与之前写入的data.writeInt32(0x4C444E42); // 'B' 'N' 'D' 'L'
做一个校对,而Parcel p = Parcel.obtain();
这个对象,实际上就是从传入的parcel对象中截取出写有bundle相关数据的一段来然后赋值给p,再将p的值传给Bundle对象持有的mParcelledData,此时bundle对象就被创建完成了。
data.writeInt32(1); // size
data.writeInt32(0); // VAL_STRING
data.writeString16(String16("type"));
data.writeInt32(1); // VAL_INTEGER
data.writeInt32(value);
而上面发送时写入bundle的数据事实上是保存在mParcelledData对象中,这里就要从接收端读取bundle中的数据时来分析,无论是bundle调用getInt() , getString() , 还是getchar(),首先都要调用unparcel()。
synchronized void unparcel() {
if (mParcelledData == null) {
if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ ": no parcelled data");
return;
}
if (mParcelledData == EMPTY_PARCEL) {
if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ ": empty");
if (mMap == null) {
mMap = new ArrayMap<String, Object>(1);
} else {
mMap.erase();
}
mParcelledData = null;
return;
}
int N = mParcelledData.readInt();
if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ ": reading " + N + " maps");
if (N < 0) {
return;
}
if (mMap == null) {
mMap = new ArrayMap<String, Object>(N);
} else {
mMap.erase();
mMap.ensureCapacity(N);
}
mParcelledData.readArrayMapInternal(mMap, N, mClassLoader);
mParcelledData.recycle();
mParcelledData = null;
if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ " final map: " + mMap);
}
这里的N就是之前写入的data.writeInt32(1); // size
,实际上就是bundle中键值对的个数,这里只有1个,N=1,接下来看mParcelledData.readArrayMapInternal(mMap, N, mClassLoader);
void readArrayMapInternal(ArrayMap outVal, int N,
ClassLoader loader) {
if (DEBUG_ARRAY_MAP) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Log.d(TAG, "Reading " + N + " ArrayMap entries", here);
}
int startPos;
while (N > 0) {
if (DEBUG_ARRAY_MAP) startPos = dataPosition();
String key = readString();
Object value = readValue(loader);
if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read #" + (N-1) + " "
+ (dataPosition()-startPos) + " bytes: key=0x"
+ Integer.toHexString((key != null ? key.hashCode() : 0)) + " " + key);
outVal.append(key, value);
N--;
}
}
public final Object readValue(ClassLoader loader) {
int type = readInt();
switch (type) {
case VAL_NULL:
return null;
case VAL_STRING:
return readString();
case VAL_INTEGER:
return readInt();
case VAL_MAP:
return readHashMap(loader);
case VAL_PARCELABLE:
return readParcelable(loader);
case VAL_SHORT:
return (short) readInt();
case VAL_LONG:
return readLong();
case VAL_FLOAT:
return readFloat();
case VAL_DOUBLE:
return readDouble();
case VAL_BOOLEAN:
return readInt() == 1;
case VAL_CHARSEQUENCE:
return readCharSequence();
case VAL_LIST:
return readArrayList(loader);
case VAL_BOOLEANARRAY:
return createBooleanArray();
case VAL_BYTEARRAY:
return createByteArray();
case VAL_STRINGARRAY:
return readStringArray();
case VAL_CHARSEQUENCEARRAY:
return readCharSequenceArray();
case VAL_IBINDER:
return readStrongBinder();
case VAL_OBJECTARRAY:
return readArray(loader);
case VAL_INTARRAY:
return createIntArray();
case VAL_LONGARRAY:
return createLongArray();
case VAL_BYTE:
return readByte();
case VAL_SERIALIZABLE:
return readSerializable();
case VAL_PARCELABLEARRAY:
return readParcelableArray(loader);
case VAL_SPARSEARRAY:
return readSparseArray(loader);
case VAL_SPARSEBOOLEANARRAY:
return readSparseBooleanArray();
case VAL_BUNDLE:
return readBundle(loader); // loading will be deferred
default:
int off = dataPosition() - 4;
throw new RuntimeException(
"Parcel " + this + ": Unmarshalling unknown type code " + type + " at offset " + off);
}
}
这里就很好理解了,就是前面写入的data.writeInt32(0); // VAL_STRING
正是readValue函数中int type = readInt();
type的值,这里有每个值对应的数字可以参照一下。
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;
private static final int VAL_BUNDLE = 3;
private static final int VAL_PARCELABLE = 4;
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;
private static final int VAL_SPARSEARRAY = 12;
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;
private static final int VAL_OBJECTARRAY = 17;
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;
private static final int VAL_SPARSEBOOLEANARRAY = 22;
private static final int VAL_BOOLEANARRAY = 23;
private static final int VAL_CHARSEQUENCEARRAY = 24;
最后,在4.4以前有2个特殊的地方需要注意下,readArrayMapInternal函数中是调用readValue而不是直接调用String key = readString();
而不是调用readValue,因此在写入时要注意需要写入data.writeInt32(0); // VAL_STRING
,还有一点是4.4以前并不需要写入data.writeInt32(-2); // contentUserHint: -2 -> UserHandle.USER_CURRENT
这一项。