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; }