TransactionTooLargeException导致的Package manager has died源码分析

问题:

try {
        pakinfo = pm.getPackageInfo("com.tencent.mm", PackageManager.GET_ACTIVITIES);
	PLog.i("isWXCanPay pakinfo = " + pakinfo);
	if (pakinfo != null) {
		versionName = pakinfo.versionName.trim();
		PLog.i("isWXCanPay versionName = " + versionName);
		int v = Integer.parseInt(versionName.substring(0, 1));
		if (v >= 5) {
		    ret = true;
		}
	}
} catch (Exception e) {
    PLog.w("isWXCanPay error = ", e);
}

对于体量比较小的应用出现异常的概率比较小,但是在遇到体量较大的应用(比如QQ,微信)就很容易出现如下异常


异常日志:

05-02 11:03:45.105: W/lbs(5017): Caused by: android.os.TransactionTooLargeException
05-02 11:03:45.105: W/lbs(5017): 	at android.os.BinderProxy.transactNative(Native Method)
05-02 11:03:45.105: W/lbs(5017): 	at android.os.BinderProxy.transact(Binder.java:504)
05-02 11:03:45.105: W/lbs(5017): 	at android.content.pm.IPackageManager$Stub$Proxy.getPackageInfo(IPackageManager.java:1818)
05-02 11:03:45.105: W/lbs(5017): 	at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:118)

问题分析:

调用流程分析
PackageManager.getPackageInfo(String packageName, int flags)


-->ApplicationPackageManager.getPackageInfo(String packageName, int flags)


userId = context.getUserId
-->ApplicationPackageManager.getPackageInfoAsUser(String packageName, int flags, int userId)

IPackageManager是一个.aidl
-->IPackageManager.getPackageInfo(packageName, flags, userId)

分析 IPacakgeManager.java,这个java文件是编译时转好 AIDL 文件生成的,这个AIDL文件位置:
base/core/java/android/content/pm/IPackageManager.aidl


/**
 *  See {@link PackageManager} for documentation on most of the APIs
 *  here.
 *
 *  {@hide}
 */
interface IPackageManager {
/**
 *  See {@link PackageManager} for documentation on most of the APIs
 *  here.
 *
 *  {@hide}
 */
interface IPackageManager {
    void checkPackageStartable(String packageName, int userId);
    boolean isPackageAvailable(String packageName, int userId);
    PackageInfo getPackageInfo(String packageName, int flags, int userId);
    ...
    }
编译后转换成.java文件后(IPackageManager.java)
@Override public android.content.pm.PackageInfo getPackageInfo(java.lang.String packageName, int flags, int userId) throws android.os.RemoteException  
{  
	android.os.Parcel _data = android.os.Parcel.obtain();  
	android.os.Parcel _reply = android.os.Parcel.obtain();  
	android.content.pm.PackageInfo _result;  
	try {  
	_data.writeInterfaceToken(DESCRIPTOR);  
	_data.writeString(packageName);  
	_data.writeInt(flags);  
	_data.writeInt(userId);  
	mRemote.transact(Stub.TRANSACTION_getPackageInfo, _data, _reply, 0);  
	_reply.readException();  
	if ((0!=_reply.readInt())) {  
		_result = android.content.pm.PackageInfo.CREATOR.createFromParcel(_reply);  
	}  
	else {  
		_result = null;  
	}  
	}  
	finally {  
		_reply.recycle();  
		_data.recycle();  
	}  
	return _result;  
} 

这个 mRemote 是个代理类,在 IPacakgeManager.java 实例化时就被创建: 

/** 
 * Cast an IBinder object into an android.content.pm.IPackageManager interface, 
 * generating a proxy if needed. 
 */  
public static android.content.pm.IPackageManager asInterface(android.os.IBinder obj)  
{  
	if ((obj==null)) {  
	return null;  
}  
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);  
if (((iin!=null)&&(iin instanceof android.content.pm.IPackageManager))) {  
	return ((android.content.pm.IPackageManager)iin);  
}  
	return new android.content.pm.IPackageManager.Stub.Proxy(obj);  
} 


...
创建代理类

private static class Proxy implements android.content.pm.IPackageManager  
{  
private android.os.IBinder mRemote;  
Proxy(android.os.IBinder remote)  
{  
mRemote = remote;  
} 

...

这个 transact 函数是在 frameworks/base/core/java/android/os/Binder.java 中由 BinderProxy 类实现的:
final class BinderProxy implements IBinder {  
    public native boolean pingBinder();  
    public native boolean isBinderAlive();  
  
    public IInterface queryLocalInterface(String descriptor) {  
        return null;  
    }  
  
    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
        ...
        }
        try {
            return transactNative(code, data, reply, flags);
        } finally {
            if (tracingEnabled) {
                Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
            }
        }
    }
    
这个transactNative开始调用android_util_Binder.cpp-->android_os_BinderProxy_transact

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
        jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
    ...
    signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/, data->dataSize());
    return JNI_FALSE;
}   
这个signalExceptionForError就是做各种异常处理的


void signalExceptionForError(JNIEnv* env, jobject obj, status_t err,
        bool canThrowRemoteException, int parcelSize)
{
    switch (err) {
        ...
        case FAILED_TRANSACTION: {
            ALOGE("!!! FAILED BINDER TRANSACTION !!!  (parcel size = %d)", parcelSize);
            const char* exceptionToThrow;
            char msg[128];
            // TransactionTooLargeException is a checked exception, only throw from certain methods.
            // FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
            //        but it is not the only one.  The Binder driver can return BR_FAILED_REPLY
            //        for other reasons also, such as if the transaction is malformed or
            //        refers to an FD that has been closed.  We should change the driver
            //        to enable us to distinguish these cases in the future.
            if (canThrowRemoteException && parcelSize > 200*1024) {
                // bona fide large payload
                exceptionToThrow = "android/os/TransactionTooLargeException";
                snprintf(msg, sizeof(msg)-1, "data parcel size %d bytes", parcelSize);
            } else {
                // Heuristic: a payload smaller than this threshold "shouldn't" be too
                // big, so it's probably some other, more subtle problem.  In practice
                // it seems to always mean that the remote process died while the binder
                // transaction was already in flight.
                exceptionToThrow = (canThrowRemoteException)
                        ? "android/os/DeadObjectException"
                        : "java/lang/RuntimeException";
                snprintf(msg, sizeof(msg)-1,
                        "Transaction failed on small parcel; remote process probably died");
            }
            jniThrowException(env, exceptionToThrow, msg);
        } break;
       ...
    }
}
由上面可以看出如果Binder的使用超出了一个进程的限制就会抛出TransactionTooLargeException这个异常。

一个进程的Binder内存限制是大约1M左右,如果超过这个限制就会报TransactionTooLargeException,导致Package manager has died。

如果仅仅想获取包名,版本号等简单的信息,可以将flag修改为GET_SIGNATURES,这样的获取信息量会大大减少,防止了TransactionTooLargeException。

参考:

    1.https://blog.csdn.net/zhbpd/article/details/78895603

    2.https://blog.csdn.net/gqlovelj/article/details/79386334


谢谢

你可能感兴趣的:(android)