• [Android Pro] 分析 Package manager has died


    reference to : http://blog.csdn.net/xxooyc/article/details/50162523

    这是今天遇到的一个issue,由于Binder造成的。虽然比较简单,还是保持记录下吧。

    先来开看一下Crash log:

        E/HpnsService(24810): HPNS Version is 5.0java.lang.RuntimeException: Package manager has died  
        E/HpnsService(24810):   at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:111)  
        E/HpnsService(24810):   at com.xx.xxx.util.AppUtil.checkInstalledPackageVersionCode(AppUtil.java:568)  
        E/HpnsService(24810):   at com.xx.xxx.util.AppUtil.checkAppStatus(AppUtil.java:653)  
        E/HpnsService(24810):   at com.xx.xxx.view.AppListView$LoadingAppThread.run(AppListView.java:723)  
        E/HpnsService(24810): Caused by: android.os.TransactionTooLargeException  
        E/HpnsService(24810):   at android.os.BinderProxy.transactNative(Native Method)  
        E/HpnsService(24810):   at android.os.BinderProxy.transact(Binder.java:496)  
        E/HpnsService(24810):   at android.content.pm.IPackageManager$Stub$Proxy.getPackageInfo(IPackageManager.java:1786)  
        E/HpnsService(24810):   at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:106)  
        E/HpnsService(24810):   ... 3 more  
    

    为什么会发生Package manager has died?

    frameworks/base/core/java/Android/app/ApplicationPackageManager.java:

    102    @Override
    103    public PackageInfo getPackageInfo(String packageName, int flags)
    104            throws NameNotFoundException {
    105        try {
    106            PackageInfo pi = mPM.getPackageInfo(packageName, flags, mContext.getUserId());
    107            if (pi != null) {
    108                return pi;
    109            }
    110        } catch (RemoteException e) {
    111            throw new RuntimeException("Package manager has died", e);
    112        }
    113
    114        throw new NameNotFoundException(packageName);
    115    }
    

     这是一个Binder调用,造成这个的原因是因为发生了RemoteException。


    那为什么友会发生RemoteException?

    其实也就是下面的这句Caused by: android.os.TransactionTooLargeException造成的。


    为什么会造成TransactionTooLargeException?

    frameworks/base/core/jni/android_util_Binder.cpp:

        682        case FAILED_TRANSACTION:  
        683            ALOGE("!!! FAILED BINDER TRANSACTION !!!");  
        684            // TransactionTooLargeException is a checked exception, only throw from certain methods.  
        685            // FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION  
        686            //        but it is not the only one.  The Binder driver can return BR_FAILED_REPLY  
        687            //        for other reasons also, such as if the transaction is malformed or  
        688            //        refers to an FD that has been closed.  We should change the driver  
        689            //        to enable us to distinguish these cases in the future.  
        690            jniThrowException(env, canThrowRemoteException  
        691                    ? "android/os/TransactionTooLargeException"  
        692                            : "java/lang/RuntimeException", NULL);  
        693            break;  
    

     
    可以看出如果Binder的使用超出了一个进程的限制就会抛出TransactionTooLargeException这个异常。

    如果是其他原因造成Binder crash的话就会抛出RuntimeException。


    那一个进程的Binder内存限制是多少?

    frameworks/native/libs/binder/ProcessState.cpp:

    44 #define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))  
    

     这便是一个进程中binder的大小,大约1M。

    给Binder分配内存的代码:

        349#if !defined(HAVE_WIN32_IPC)  
        350        // mmap the binder, providing a chunk of virtual address space to receive transactions.  
        351        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);  
        352        if (mVMStart == MAP_FAILED) {  
        353            // *sigh*  
        354            ALOGE("Using /dev/binder failed: unable to mmap transaction memory.
    ");  
        355            close(mDriverFD);  
        356            mDriverFD = -1;  
        357        }  
    

    通过上面的清理,知道了如果一个进程中使用的Binder内容超过了1M,就会crash.

    而如果这时候恰巧在用getPackageManager()做事情,就会提示Package manager has died。


    可以事实真的是这样的吗?

    写了个demo来证明一下:

        public class MainActivity extends Activity {  
          
            @Override  
            protected void onCreate(Bundle savedInstanceState) {  
                super.onCreate(savedInstanceState);  
                setContentView(R.layout.activity_main);  
                test();  
            }  
          
            private void test() {  
                for (int i = 0; i < 2; i++) {  
                new Thread() {  
                    @Override  
                    public void run() {  
                        int count = 0;  
                            List<PackageInfo> list = getPackageManager()  
                                    .getInstalledPackages(10000);  
                            for (PackageInfo info : list) {  
                                if(count >=1000){  
                                    break;  
                                }  
                                try {  
                                    PackageInfo pi = getPackageManager()  
                                            .getPackageInfo(info.packageName,  
                                                    PackageManager.GET_ACTIVITIES);  
                                    Log.e("yanchen", "yanchen threadid:"+Thread.currentThread().getId()   
                                            + ",i:" + count++);  
                                } catch (NameNotFoundException e) {  
                                }  
                        }  
                    }  
                }.start();  
            }  
            }  
        }  
    

    这个Demo就是同时创建两个线程来进行Binder调用.

    运行打印的log:

        E/yanchen (21180): yanchen threadid:4097,i:271  
        E/yanchen (21180): yanchen threadid:4097,i:272  
        E/yanchen (21180): yanchen threadid:4097,i:273  
        E/yanchen (21180): yanchen threadid:4097,i:274  
        E/yanchen (21180): yanchen threadid:4097,i:275  
        E/yanchen (21180): yanchen threadid:4097,i:276  
    

     
    此时也如预期发生了Crash:

        E/JavaBinder(31244): !!! FAILED BINDER TRANSACTION !!!  
        E/AndroidRuntime(31244): FATAL EXCEPTION: Thread-4798  
        E/AndroidRuntime(31244): Process: com.example.testdl, PID: 31244  
        E/AndroidRuntime(31244): java.lang.RuntimeException: Package manager has died  
        E/AndroidRuntime(31244):    at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:155)  
        E/AndroidRuntime(31244):    at com.example.testdl.MainActivity$1.run(MainActivity.java:40)  
        E/AndroidRuntime(31244): Caused by: android.os.TransactionTooLargeException  
        E/AndroidRuntime(31244):    at android.os.BinderProxy.transactNative(Native Method)  
        E/AndroidRuntime(31244):    at android.os.BinderProxy.transact(Binder.java:496)  
        E/AndroidRuntime(31244):    at android.content.pm.IPackageManager$Stub$Proxy.getPackageInfo(IPackageManager.java:2208)  
        E/AndroidRuntime(31244):    at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:150)  
        E/AndroidRuntime(31244):    ... 1 more  
        D/EnterpriseDeviceManagerService( 3021): isMana  
    

    解决方式:

         其实只要避免多个线程同时来调用Binder就可以了,毕竟一个线程用了会释放,所以理论上是很难发生的。

    修改后的Demo:

        synchronized(MainActivity.class){  
            PackageInfo pi = getPackageManager()  
                    .getPackageInfo(info.packageName,  
                            PackageManager.GET_ACTIVITIES);  
        }  
    

    再次运行就不会Crash了。

  • 相关阅读:
    JS获取今天的日期
    领域模型vs数据模型,应该怎么用?
    如何让技术想法更容易被理解?
    如何做好技术 Team Leader
    回归分析中常见的“门槛模型”!
    有了数据湖,距离数据仓库消失还有几年?
    数据治理 VS 公司治理、IT治理、数仓治理
    Sentence-seven basic patterns 英语句子结构
    VM的Linux CentOS系统的VMTools的手动安装
    linux下IPTABLES配置详解
  • 原文地址:https://www.cnblogs.com/0616--ataozhijia/p/6478231.html
Copyright © 2020-2023  润新知