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
谢谢