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