Android中在native层对java层应用程序发送广播方法及原理

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这一项。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值