Android O项目首次刷机PackageInstaller无法安装和卸载应用

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
24
01-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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Override
public int createSession(SessionParams params, String installerPackageName, int userId) {
try {
return createSessionInternal(params, installerPackageName, userId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}


private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
throws IOException {
//……
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
params.installFlags |= PackageManager.INSTALL_FROM_ADB;

} else {
// Only apps with INSTALL_PACKAGES are allowed to set an installer that is not the
// caller.
if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) !=
PackageManager.PERMISSION_GRANTED) {
mAppOps.checkPackage(callingUid, installerPackageName);
}
//……
}

//……
return sessionId;
}

结合log来看,出问题的就是mAppOps.checkPackage(callingUid, installerPackageName);这行代码,问题原因是mAppOps为null。mAppOps为AppOpsManager的实例,相当于AppOpsService的封装。mAppOps的初始化是在PackageInstallerService中的systemReady()中进行的:mAppOps = mContext.getSystemService(AppOpsManager.class);。Context#getSystemService()获取的服务是在SystemServiceRegistry中注册的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Manages all of the system services that can be returned by {@link Context#getSystemService}.
* Used by {@link ContextImpl}.
* @hide
*/
public final class SystemServiceRegistry {
static{
registerService(Context.APP_OPS_SERVICE, AppOpsManager.class,
new CachedServiceFetcher<AppOpsManager>() {
@Override
public AppOpsManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.APP_OPS_SERVICE);
IAppOpsService service = IAppOpsService.Stub.asInterface(b);
return new AppOpsManager(ctx, service);
}});
}
}

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
2
3
4
5
6
7
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
01-01 12:12:39.508 838 838 I AppOps : Pruning old package com.mediatek.mtklogger/com.android.server.AppOpsService$UidState@2504d14: new uid=10069
01-01 12:12:39.508 838 838 I AppOps : Pruning old package com.google.android.tts/com.android.server.AppOpsService$UidState@52519bd: new uid=10065
01-01 12:12:39.508 838 838 I AppOps : Pruning old package com.google.android.syncadapters.contacts/com.android.server.AppOpsService$UidState@3d1bbb2: new uid=10062
01-01 12:12:39.508 838 838 I AppOps : Pruning old package com.google.android.calendar/com.android.server.AppOpsService$UidState@5295703: new uid=10055
01-01 12:12:39.508 838 838 I AppOps : Pruning old package com.android.carrierdefaultapp/com.android.server.AppOpsService$UidState@a6f2680: new uid=10054
01-01 12:12:39.508 838 838 I AppOps : Pruning old package com.bootup.receiveimage/com.android.server.AppOpsService$UidState@60e94b9: new uid=10051

说明该服务的注册也是没问题的,所以问题应该就是PackageInstallerService的systemReady()没有执行引起的。而该方法是在PackageManagerService的systemReady()方法中调用的,所以可能是在该方法被调用之前出现了什么异常,导致后面的代码没有执行,PackageManagerService的systemReady()是在SystemServer的startOtherService()中调用的:

1
2
3
4
5
6
7
traceBeginAndSlog("MakePackageManagerServiceReady");
try {
mPackageManagerService.systemReady();
} catch (Throwable e) {
reportWtf("making Package Manager Service ready", e);
}
traceEnd();

也就是说PackageManagerService的systemReady()是允许出现异常的,而且会有异常信息输出,在开机log中搜索“making Package Manager Service ready”(其实最开始搜索的是PackageManager,也找到了出错信息):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
01-01 12:12:39.489   838   838 W SystemServer: ***********************************************
01-01 12:12:39.492 838 838 E SystemServer: BOOT FAILURE making Package Manager Service ready
01-01 12:12:39.492 838 838 E SystemServer: java.lang.SecurityException: Permission android.permission.MANAGE_ACTIVITY_STACKS is not a changeable permission type
01-01 12:12:39.492 838 838 E SystemServer: at com.android.server.pm.PackageManagerService.enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(PackageManagerService.java:5717)
01-01 12:12:39.492 838 838 E SystemServer: at com.android.server.pm.PackageManagerService.grantRuntimePermission(PackageManagerService.java:5761)
01-01 12:12:39.492 838 838 E SystemServer: at com.android.server.pm.PackageManagerService.grantRuntimePermission(PackageManagerService.java:5724)
01-01 12:12:39.492 838 838 E SystemServer: at com.android.server.pm.DefaultPermissionGrantPolicy.grantRuntimePermissionsLPw(DefaultPermissionGrantPolicy.java:1066)
01-01 12:12:39.492 838 838 E SystemServer: at com.android.server.pm.DefaultPermissionGrantPolicy.grantRuntimePermissionsLPw(DefaultPermissionGrantPolicy.java:1008)
01-01 12:12:39.492 838 838 E SystemServer: at com.android.server.pm.DefaultPermissionGrantPolicy.grantDefaultPermissionExceptions(DefaultPermissionGrantPolicy.java:1143)
01-01 12:12:39.492 838 838 E SystemServer: at com.android.server.pm.DefaultPermissionGrantPolicy.grantDefaultPermissions(DefaultPermissionGrantPolicy.java:224)
01-01 12:12:39.492 838 838 E SystemServer: at com.android.server.pm.PackageManagerService.systemReady(PackageManagerService.java:22721)
01-01 12:12:39.492 838 838 E SystemServer: at com.android.server.SystemServer.startOtherServices(SystemServer.java:1715)
01-01 12:12:39.492 838 838 E SystemServer: at com.android.server.SystemServer.run(SystemServer.java:412)
01-01 12:12:39.492 838 838 E SystemServer: at com.android.server.SystemServer.main(SystemServer.java:280)
01-01 12:12:39.492 838 838 E SystemServer: at java.lang.reflect.Method.invoke(Native Method)
01-01 12:12:39.492 838 838 E SystemServer: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
01-01 12:12:39.492 838 838 E SystemServer: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:837)

从log上看,问题出在PackageManagerService的enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission()方法:

1
2
3
4
5
6
7
8
9
10
11
12
private static void enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(
PackageParser.Package pkg, BasePermission bp) {
int index = pkg.requestedPermissions.indexOf(bp.name);
if (index == -1) {
throw new SecurityException("Package " + pkg.packageName
+ " has not requested permission " + bp.name);
}
if (!bp.isRuntime() && !bp.isDevelopment()) {
throw new SecurityException("Permission " + bp.name
+ " is not a changeable permission type");
}
}

从代码上看,满足条件!bp.isRuntime() && !bp.isDevelopment()时会抛出上面log中的异常。来看下isRuntime()和isDevelopment()是什么含义:

1
2
3
4
5
6
7
8
9
10
public boolean isRuntime() {//protectionLevel是dangerous
return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_DANGEROUS;
}

public boolean isDevelopment() {//protectionLevel是PROTECTION_SIGNATURE | PROTECTION_FLAG_DEVELOPMENT
return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_SIGNATURE
&& (protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0;
}

从adb shell dumpsys package中可以看到android.permission.MANAGE_ACTIVITY_STACKS权限的信息如下:

1
2
3
4
5
Permission [android.permission.MANAGE_ACTIVITY_STACKS] (c6783bc):
sourcePackage=android
uid=1000 gids=null type=0 prot=signature|privileged
perm=Permission{19e8f45 android.permission.MANAGE_ACTIVITY_STACKS}
packageSetting=PackageSetting{2fcec10 android/1000}

从上面的信息来看,这个权限是在包名为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
2
3
4
// If we upgraded grant all default permissions before kicking off.
for (int userId : grantPermissionsUserIds) {
mDefaultPermissionPolicy.grantDefaultPermissions(userId);//用于授予应用默认权限
}

而该代码正好在mInstallerService.systemReady();前面执行,所以该部分代码出异常后,mInstallerService的systemReady()方法未得到调用。继续看grantDefaultPermissions()代码:

1
2
3
4
5
6
7
8
9
public void grantDefaultPermissions(int userId) {
if (mService.hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
grantAllRuntimePermissions(userId);
} else {//走这个分支
grantPermissionsToSysComponentsAndPrivApps(userId);//授予priv-app默认运行时权限
grantDefaultSystemHandlerPermissions(userId);//授予一些系统核心应用默认的运行时权限,比如拨号,联系人等
grantDefaultPermissionExceptions(userId);//从xml中读取默认运行时权限,可fix,让用户无法修改,权限配置页面会看不到该权限
}
}

问题出在grantDefaultPermissionExceptions()这个方法中,该方法用于从/system/etc/default-permissions/下的xml文件中读取应用的默认运行时权限,并授予应用。其写法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<!--
This file contains permissions to be granted by default. Default
permissions are granted to special platform components and to apps
that are approved to get default grants. The special components
are apps that are expected to work out-of-the-box as they provide
core use cases such as default dialer, default email, etc. These
grants are managed by the platform. The apps that are additionally
approved for default grants are ones that provide carrier specific
functionality, ones legally required at some location, ones providing
alternative disclosure and opt-out UI, ones providing highlight features
of a dedicated device, etc. This file contains only the latter exceptions.
Fixed permissions cannot be controlled by the user and need a special
approval. Typically these are to ensure either legally mandated functions
or the app is considered a part of the OS.
-->

<exceptions>
<exception package="com.emoji.keyboard.touchpal.go">
<permission name="android.permission.MANAGE_ACTIVITY_STACKS" fixed="false"/>
</exception>
</exceptions>

其中的注释很好的说明了该文件的作用:默认权限有两种,一种被授予特殊的平台组件,比如默认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
2
3
4
<!--
This XML file declares which signature|privileged permissions should be granted to privileged
applications that come with the platform
-->

5.总结

关于默认权限目前已知有以下几种情况:

  • priv-app 系统默认授予运行时权限;
  • 系统核心应用,系统默认授予运行时权限;
  • 项目预置应用的默认运行时权限在/system/etc/default-permissions进行配置;
  • signature|privileged权限在/system/etc/permissions/下进行配置。

另外。Android P中android.permission.MANAGE_ACTIVITY_STACKS的权限变成了下面这样:

1
2
3
4
5
Permission [android.permission.MANAGE_ACTIVITY_STACKS] (69ec6d4):
sourcePackage=android
uid=1000 gids=null type=0 prot=signature|privileged|development
perm=Permission{fbebcd2 android.permission.MANAGE_ACTIVITY_STACKS}
packageSetting=PackageSetting{5c62591 android/1000}

多了个development,所以Android P上不存在该问题。

详细的应用权限授予过程还需要进一步了解……

Powered by Hexo and Hexo-theme-hiker

Copyright © 2018 - 2022 得一 All Rights Reserved.

访客数 : | 访问量 :