分析 Package manager has died

理解:Binder 跨进程驱动时候,应该是创建了线程池去执行相对应的系统服务,而如果过多超过了1M,就会出现tooLarge...

那么会进一步导致getPackManager died,就是死了,因为进程通信Binder挂了,没人传递信息了,getPackManager.getPackInfo

或者getServiceManager.getService都没有办法完成了

记住一点:Binder挂了,驱动就关了,而manager 也就死了(驱动就是发动机)


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

先来开看一下Crash log:

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

为什么会发生Package manager has died

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

[html] view plain copy
  1. 102    @Override  
  2. 103    public PackageInfo getPackageInfo(String packageName, int flags)  
  3. 104            throws NameNotFoundException {  
  4. 105        try {  
  5. 106            PackageInfo pi = mPM.getPackageInfo(packageName, flags, mContext.getUserId());  
  6. 107            if (pi != null) {  
  7. 108                return pi;  
  8. 109            }  
  9. 110        } catch (RemoteException e) {  
  10. 111            throw new RuntimeException("Package manager has died", e);  
  11. 112        }  
  12. 113  
  13. 114        throw new NameNotFoundException(packageName);  
  14. 115    }  


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


那为什么友会发生RemoteException

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


为什么会造成TransactionTooLargeException

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

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


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

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


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

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

[html] view plain copy
  1. 44 #define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))  
这便是一个进程中binder的大小,大约1M。

给Binder分配内存的代码:

[html] view plain copy
  1. 349#if !defined(HAVE_WIN32_IPC)  
  2. 350        // mmap the binder, providing a chunk of virtual address space to receive transactions.  
  3. 351        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);  
  4. 352        if (mVMStart == MAP_FAILED) {  
  5. 353            // *sigh*  
  6. 354            ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");  
  7. 355            close(mDriverFD);  
  8. 356            mDriverFD = -1;  
  9. 357        }  


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

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


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

写了个demo来证明一下:

[java] view plain copy
  1. public class MainActivity extends Activity {  
  2.   
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.         test();  
  8.     }  
  9.   
  10.     private void test() {  
  11.         for (int i = 0; i < 2; i++) {  
  12.         new Thread() {  
  13.             @Override  
  14.             public void run() {  
  15.                 int count = 0;  
  16.                     List<PackageInfo> list = getPackageManager()  
  17.                             .getInstalledPackages(0);  
  18.                     for (PackageInfo info : list) {  
  19.                         if(count >=1000){  
  20.                             break;  
  21.                         }  
  22.                         try {  
  23.                             PackageInfo pi = getPackageManager()  
  24.                                     .getPackageInfo(info.packageName,  
  25.                                             0);  
  26.                             Log.e("yanchen""yanchen threadid:"+Thread.currentThread().getId()   
  27.                                     + ",i:" + count++);  
  28.                         } catch (NameNotFoundException e) {  
  29.                         }  
  30.                 }  
  31.             }  
  32.         }.start();  
  33.     }  
  34.     }  
  35. }  

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

运行打印的log:

[java] view plain copy
  1. E/yanchen (21180): yanchen threadid:4097,i:271  
  2. E/yanchen (21180): yanchen threadid:4097,i:272  
  3. E/yanchen (21180): yanchen threadid:4097,i:273  
  4. E/yanchen (21180): yanchen threadid:4097,i:274  
  5. E/yanchen (21180): yanchen threadid:4097,i:275  
  6. E/yanchen (21180): yanchen threadid:4097,i:276  

此时也如预期发生了Crash:
[java] view plain copy
  1. E/JavaBinder(31244): !!! FAILED BINDER TRANSACTION !!!  
  2. E/AndroidRuntime(31244): FATAL EXCEPTION: Thread-4798  
  3. E/AndroidRuntime(31244): Process: com.example.testdl, PID: 31244  
  4. E/AndroidRuntime(31244): java.lang.RuntimeException: Package manager has died  
  5. E/AndroidRuntime(31244):    at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:155)  
  6. E/AndroidRuntime(31244):    at com.example.testdl.MainActivity$1.run(MainActivity.java:40)  
  7. E/AndroidRuntime(31244): Caused by: android.os.TransactionTooLargeException  
  8. E/AndroidRuntime(31244):    at android.os.BinderProxy.transactNative(Native Method)  
  9. E/AndroidRuntime(31244):    at android.os.BinderProxy.transact(Binder.java:496)  
  10. E/AndroidRuntime(31244):    at android.content.pm.IPackageManager$Stub$Proxy.getPackageInfo(IPackageManager.java:2208)  
  11. E/AndroidRuntime(31244):    at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:150)  
  12. E/AndroidRuntime(31244):    ... 1 more  
  13. D/EnterpriseDeviceManagerService( 3021): isMana  


解决方式:

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

修改后的Demo:

[html] view plain copy
  1. synchronized(MainActivity.class){  
  2.     PackageInfo pi = getPackageManager()  
  3.             .getPackageInfo(info.packageName,  
  4.                     PackageManager.GET_ACTIVITIES);  
  5. }  

再次运行就不会Crash了。

转自:https://blog.csdn.net/xxooyc/article/details/50162523

猜你喜欢

转载自blog.csdn.net/rnzuozuo/article/details/80505945