1.问题现象
Android O的项目,刷机后首次开机,在文件管理器中选择apk进行安装,packageinstaller闪退,可卸载应用也无法卸载。重启手机后问题消失。
2.问题log
应用安装的log:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2401-01 12:08:06.781 6599 6599 E AndroidRuntime: FATAL EXCEPTION: main
01-01 12:08:06.781 6599 6599 E AndroidRuntime: Process: com.google.android.packageinstaller, PID: 6599
01-01 12:08:06.781 6599 6599 E AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.google.android.packageinstaller/com.android.packageinstaller.InstallInstalling}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.app.AppOpsManager.checkPackage(int, java.lang.String)' on a null object reference
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2805)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2883)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at android.app.ActivityThread.-wrap11(Unknown Source:0)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1613)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at android.os.Looper.loop(Looper.java:164)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6523)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:857)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.app.AppOpsManager.checkPackage(int, java.lang.String)' on a null object reference
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at android.os.Parcel.readException(Parcel.java:2019)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at android.os.Parcel.readException(Parcel.java:1959)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at android.content.pm.IPackageInstaller$Stub$Proxy.createSession(IPackageInstaller.java:254)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at android.content.pm.PackageInstaller.createSession(PackageInstaller.java:324)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at com.android.packageinstaller.InstallInstalling.onCreate(InstallInstalling.java:147)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at android.app.Activity.performCreate(Activity.java:7022)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at android.app.Activity.performCreate(Activity.java:7013)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2758)
01-01 12:08:06.781 6599 6599 E AndroidRuntime: ... 9 more
3.问题分析
从来log上看问题的原因是在InstallInstalling启动过程中出现了NPE,问题出现在performLaunchActivity()方法进行binder通信的过程中,即IPackageInstaller$Stub$Proxy.createSession
,对端代码为PackageInstallerService,问题出现在其createSession()方法:
1 |
|
结合log来看,出问题的就是mAppOps.checkPackage(callingUid, installerPackageName);
这行代码,问题原因是mAppOps为null。mAppOps为AppOpsManager的实例,相当于AppOpsService的封装。mAppOps的初始化是在PackageInstallerService中的systemReady()中进行的:mAppOps = mContext.getSystemService(AppOpsManager.class);
。Context#getSystemService()获取的服务是在SystemServiceRegistry
中注册的:
1 | /** |
AppOpsManager是在SystemServiceRegistry的静态代码块中注册的,所以目前来看造成mAppOps为null的原因有以下几种:
- 有可能是SystemServiceRegistry加载出了问题;
- 服务或者注册出了问题
- mAppOps没有初始化也就是PackageInstallerService的systemReady()没有执行(mAppOps的初始化是该方法的第一行代码)。
第一种可能基本可以排除,因为SystemServiceRegistry中同时注册了很过系统服务,如果这个类加载出了问题,表现出来的现象应该要严重得多。第三种可能性最大,为了进一步分析mAppOps为null的原因,需要抓下首次开机启动时的log,并开启MTK的PMS调试开关(adb shell dumpsys package log a on)。
在抓到的开机log中,发现了大量AppOpsService的log:
1 | 01-01 12:12:39.507 838 838 I AppOps : Pruning old package com.google.android.inputmethod.latin/com.android.server.AppOpsService$UidState@85b6b67: new uid=10068 |
说明该服务的注册也是没问题的,所以问题应该就是PackageInstallerService的systemReady()没有执行引起的。而该方法是在PackageManagerService的systemReady()方法中调用的,所以可能是在该方法被调用之前出现了什么异常,导致后面的代码没有执行,PackageManagerService的systemReady()是在SystemServer的startOtherService()中调用的:
1 | traceBeginAndSlog("MakePackageManagerServiceReady"); |
也就是说PackageManagerService的systemReady()是允许出现异常的,而且会有异常信息输出,在开机log中搜索“making Package Manager Service ready”(其实最开始搜索的是PackageManager,也找到了出错信息):
1 | 01-01 12:12:39.489 838 838 W SystemServer: *********************************************** |
从log上看,问题出在PackageManagerService的enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission()方法:
1 | private static void enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission( |
从代码上看,满足条件!bp.isRuntime() && !bp.isDevelopment()
时会抛出上面log中的异常。来看下isRuntime()和isDevelopment()是什么含义:
1 | public boolean isRuntime() {//protectionLevel是dangerous |
从adb shell dumpsys package中可以看到android.permission.MANAGE_ACTIVITY_STACKS权限的信息如下:
1 | Permission [android.permission.MANAGE_ACTIVITY_STACKS] (c6783bc): |
从上面的信息来看,这个权限是在包名为android的package,也就是framework-res.apk中定义的,其protectionLevel为signature|privileged,不是runtime也不是development,所以会出现异常。
从dump信息中找到使用了该权限的应用一共有三个:
- android.uid.shell
- com.android.systemui
- com.emoji.keyboard.touchpal.go
前两个应用都是原生就有的,未做修改,所以可以排除。第三个是项目中预置的应用,而且用到了MANAGE_ACTIVITY_STACKS权限。
为什么用到这个权限会引起异常呢?继续看异常的调用栈,分析下出现异常时代码在干什么。从上面的调用栈可以看到,在PackageManagerService#systemReady()方法中调用了DefaultPermissionGrantPolicy#grantDefaultPermissions():
1 | // If we upgraded grant all default permissions before kicking off. |
而该代码正好在mInstallerService.systemReady();
前面执行,所以该部分代码出异常后,mInstallerService的systemReady()方法未得到调用。继续看grantDefaultPermissions()代码:
1 | public void grantDefaultPermissions(int userId) { |
问题出在grantDefaultPermissionExceptions()这个方法中,该方法用于从/system/etc/default-permissions/下的xml文件中读取应用的默认运行时权限,并授予应用。其写法如下:
1 |
|
其中的注释很好的说明了该文件的作用:默认权限有两种,一种被授予特殊的平台组件,比如默认dialer,默认email等,这些权限的授予是平台控制的(上面的grantDefaultSystemHandlerPermissions()方法);另一种是厂商指定的,也就是在项目中配置的。这个文件只包含后者。
从代码上来看,授予默认权限指的都是运行时权限,/system/etc/default-permissions/的文件中也只能添加运行时权限或者development权限,否则就会出现异常,但是前面以及了解到android.permission.MANAGE_ACTIVITY_STACKS并不是运行时权限或者development权限,而com.emoji.keyboard.touchpal.go在/system/etc/default-permissions/下添加了该权限所以会出现异常。至于为什么重启后该问题会消失,是因为第一次启动虽然产生了异常,但是该权限还是被授予了应用,之后启动就不会走到相同的流程。
4.问题修改
只需要将/system/etc/default-permissions/中添加的android.permission.MANAGE_ACTIVITY_STACKS权限去掉即可,该权限需要在/system/etc/permissions/下进行配置,该目录下xml文件的作用在privapp-permissions-platform.xml文件中的注释有清楚的说明:
1 | <!-- |
5.总结
关于默认权限目前已知有以下几种情况:
- priv-app 系统默认授予运行时权限;
- 系统核心应用,系统默认授予运行时权限;
- 项目预置应用的默认运行时权限在/system/etc/default-permissions进行配置;
- signature|privileged权限在/system/etc/permissions/下进行配置。
另外。Android P中android.permission.MANAGE_ACTIVITY_STACKS的权限变成了下面这样:
1 | Permission [android.permission.MANAGE_ACTIVITY_STACKS] (69ec6d4): |
多了个development,所以Android P上不存在该问题。
详细的应用权限授予过程还需要进一步了解……