实例分析:android.process.media由于调用进程crash而退出


Log:

09-13 11:46:42.093 14778 17309 I dalvikvm: Ljava/lang/RuntimeException;: No memory in memObj
09-13 11:46:42.093 14778 17309 I dalvikvm:     at android.database.CursorWindow.native_init(Native Method)
09-13 11:46:42.093 14778 17309 I dalvikvm:     at android.database.CursorWindow.<init>(CursorWindow.java:569)
09-13 11:46:42.093 14778 17309 I dalvikvm:     at android.database.CursorWindow.<init>(CursorWindow.java:36)
09-13 11:46:42.093 14778 17309 I dalvikvm:     at android.database.CursorWindow$1.createFromParcel(CursorWindow.java:544)
09-13 11:46:42.093 14778 17309 I dalvikvm:     at android.database.CursorWindow$1.createFromParcel(CursorWindow.java:542)
09-13 11:46:42.093 14778 17309 I dalvikvm:     at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:116)
09-13 11:46:42.093 14778 17309 I dalvikvm:     at android.os.Binder.execTransact(Binder.java:336)
09-13 11:46:42.093 14778 17309 I dalvikvm:     at dalvik.system.NativeStart.run(Native Method)
09-13 11:46:42.093 14778 17309 I dalvikvm: "Binder Thread #3" prio=5 tid=10 NATIVE
09-13 11:46:42.093 14778 17309 I dalvikvm:   | group="main" sCount=0 dsCount=0 obj=0x4055e5f8 self=0x361b28
09-13 11:46:42.093 14778 17309 I dalvikvm:   | sysTid=17309 nice=10 sched=0/0 cgrp=bg_non_interactive handle=3356232
09-13 11:46:42.093 14778 17309 I dalvikvm:   | schedstat=( 1261444095 8805053706 2920 )
09-13 11:46:42.093 14778 17309 I dalvikvm:   at dalvik.system.NativeStart.run(Native Method)

09-13 11:46:42.093 14778 17309 E dalvikvm: VM aborting
09-13 11:46:42.093 14778 17309 I dalvikvm: "Binder Thread #3" prio=5 tid=10 NATIVE
09-13 11:46:42.093 14778 17309 I dalvikvm:   | group="main" sCount=0 dsCount=0 obj=0x4055e5f8 self=0x361b28
09-13 11:46:42.093 14778 17309 I dalvikvm:   | sysTid=17309 nice=10 sched=0/0 cgrp=bg_non_interactive handle=3356232
09-13 11:46:42.093 14778 17309 I dalvikvm:   | schedstat=( 1261444095 8805053706 2920 )
09-13 11:46:42.093 14778 17309 I dalvikvm:   at dalvik.system.NativeStart.run(Native Method)

分析:

检查代码位置,此Exception出现在MediaProvider Server端响应QUERY_TRANSACTION时,由于传来的Parcel指向的内存地址为空引起。


考虑整个调用流程,CursorWindow实例由ContentProviderProxy在Binder调用前时产生,故此对象产生于用户进程,并传给Server端,在处理QUERY_TRANSACTION时,由于读出的CursorWindow实例内存地址为空抛出异常引起android.process.media退出。

而仔细检查相关程序代码,并未发现再出现内存不足时在Log中应出现的那些信息,故排除掉内存不足情形。而且Log中也没有Leaked Cursor信息。


最终原因剖析:

当MediaProvider收到外部出现的query请求时,此外部程序所在进程退出,导致所传进来的CursorWindow所拥有的IMememory binder被清空,所以当MediaProvider处理QUERY_TRANSACTION时发现收到的IMemory对象指向的内存地址为NULL,最终抛出异常致android.media.process退出。


Binder调用前代码如下:

ContentProviderNative.java

final class ContentProviderProxy implements IContentProvider
{
    public Cursor query(Uri url, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) throws RemoteException {
        CursorWindow window = new CursorWindow(false /* window will be used remotely */); //生成空的CursorWindow实例
        BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
        IBulkCursor bulkCursor = bulkQueryInternal(
            url, projection, selection, selectionArgs, sortOrder,
            adaptor.getObserver(), window,
            adaptor);
        if (bulkCursor == null) {
            window.close();
            adaptor.close();
            return null;
        }
        return adaptor;
    }

    private IBulkCursor bulkQueryInternal(
        Uri url, String[] projection,
        String selection, String[] selectionArgs, String sortOrder,
        IContentObserver observer, CursorWindow window,
        BulkCursorToCursorAdaptor adaptor) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();

        data.writeInterfaceToken(IContentProvider.descriptor);
...
        data.writeString(sortOrder);
        data.writeStrongBinder(observer.asBinder());
        window.writeToParcel(data, 0); //把CursorWindow对象写入到Parcel
...
        mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); //调用到server端

        DatabaseUtils.readExceptionFromParcel(reply);
...


在query方法中,调用newCursorWindow(false) -> initBuffer(false) 生成空的CursorWindow。window.writeToParcel将把IMemory所在的Binder对象写入Parcel.


CursorWindow.java

    public void writeToParcel(Parcel dest, int flags) {
        dest.writeStrongBinder(native_getBinder()); //把IMemory所在的Binder对象写入Parcel
        dest.writeInt(mStartPos);
    }

bool CursorWindow::initBuffer(bool localOnly)
{
    sp<MemoryHeapBase> heap;
    heap = new MemoryHeapBase(mMaxSize, 0, "CursorWindow");
    if (heap != NULL) {
        mMemory = new MemoryBase(heap, 0, mMaxSize);
        if (mMemory != NULL) {
            mData = (uint8_t *) mMemory->pointer();
            if (mData) {
                mHeader = (window_header_t *) mData;
                mSize = mMaxSize;

                // Put the window into a clean state
                clear();
            LOG_WINDOW("Created CursorWindow with new MemoryDealer: mFreeOffset = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mSize, mMaxSize, mData);
                return true;                
            }
        } 
        LOGE("CursorWindow heap allocation failed"); //如果mData为空,由于此Log未出现,因此其必不为空
        return false;
    } else { //如果分配堆内存失败
        LOGE("failed to create the CursorWindow heap");
        return false;
    }
}
由于Log中上述Log均未出现,因此内存分配成功。


这里调用到Server端:

abstract public class ContentProviderNative extends Binder implements IContentProvider {
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
...
            switch (code) {
                case QUERY_TRANSACTION:
                {
                    data.enforceInterface(IContentProvider.descriptor);

                    Uri url = Uri.CREATOR.createFromParcel(data);

                    // String[] projection
                    int num = data.readInt();
                    String[] projection = null;   
                    if (num > 0) {
                        projection = new String[num];
                        for (int i = 0; i < num; i++) {
                            projection[i] = data.readString();
                        }
                    }

                    // String selection, String[] selectionArgs...
                    String selection = data.readString();
                    num = data.readInt();
                    String[] selectionArgs = null;
                    if (num > 0) {
                        selectionArgs = new String[num];
                        for (int i = 0; i < num; i++) {
                            selectionArgs[i] = data.readString();
                        }
                    }

                    String sortOrder = data.readString();
                    IContentObserver observer = IContentObserver.Stub.
                        asInterface(data.readStrongBinder());
                    CursorWindow window = CursorWindow.CREATOR.createFromParcel(data); //这里调用产生Exception,即从此Parcel中读取CursorWindow对象时,由于该对象的内存指针为空引起异常

 


以下为从Parcel中重建CursorWindow的代码:


CursorWindow.java

public class CursorWindow extends SQLiteClosable implements Parcelable {
    private CursorWindow(Parcel source) { //从Parcel中重建
        IBinder nativeBinder = source.readStrongBinder();
        mStartPos = source.readInt();

        native_init(nativeBinder); //Exception here ----
    }

    // Creates a new empty window.
    public CursorWindow(boolean localWindow) {
        mStartPos = 0;
        native_init(localWindow);
    }
    public static final Parcelable.Creator<CursorWindow> CREATOR
            = new Parcelable.Creator<CursorWindow>() {        //从Parcel中重建CursorWindow实例
        public CursorWindow createFromParcel(Parcel source) {
            return new CursorWindow(source);
        }

        public CursorWindow[] newArray(int size) {
            return new CursorWindow[size];
        }
    };


android_database_CursorWindow.cpp

static void native_init_memory(JNIEnv * env, jobject object, jobject memObj)
{
    sp<IMemory> memory = interface_cast<IMemory>(ibinderForJavaObject(env, memObj)); //将Java对象转化为IMemory实例
    if (memory == NULL) {
        jniThrowException(env, "java/lang/IllegalStateException", "Couldn't get native binder");
        return;
    }

    CursorWindow * window = new CursorWindow();
    if (!window) {
        jniThrowException(env, "java/lang/RuntimeException", "No memory for native window object");
        return;
    }
    if (!window->setMemory(memory)) {  //异常抛出点
        jniThrowException(env, "java/lang/RuntimeException", "No memory in memObj");
        delete window;
        return;
    }

LOG_WINDOW("native_init_memory: numRows = %d, numColumns = %d, window = %p", window->getNumRows(), window->getNumColumns(), window);
    SET_WINDOW(env, object, window);
}


CursorWindow.cpp

bool CursorWindow::setMemory(const sp<IMemory>& memory)
{
    mMemory = memory;
    mData = (uint8_t *) memory->pointer();
    if (mData == NULL) {       //显然此处为NULL导致Exception
        return false;
    }
    mHeader = (window_header_t *) mData;

    // Make the window read-only
    ssize_t size = memory->size();
    mSize = size;
    mMaxSize = size;
    mFreeOffset = size;
LOG_WINDOW("Created CursorWindow from existing IMemory: mFreeOffset = %d, numRows = %d, numColumns = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mHeader->numRows, mHeader->numColumns, mSize, mMaxSize, mData);
    return true;
}


由此可见,当Binder设备唤醒Server端处理Query请求时,发送的CursorWindow实例所拥有的IMemory对象已经无效,极可能由于调用者进程crash导致该对象被清除。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值