欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-03-02 13:47:30
...

问题:

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