平台
RK3288 + Android 7.1
问题:
删除 /system/priv-app/Shell 后, 通过adb install安装应用出现:
adb shell install xxx.apk Error: java.lang.SecurityException: Permission Denial: runInstallCreate from pm command asks to run as user -1 but is calling from user 0; this requires android.permission.INTERACT_ACROSS_USERS_FULL
此时, 使用adb root 再执行adb install 则不会存在相同的问题.
分析:
首先, adb install 执行后, 会调用系统中的pm 命令来进行安装.
|-- frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java private int runInstallCreate() throws RemoteException { final InstallParams installParams = makeInstallParams(); final int sessionId = doCreateSession(installParams.sessionParams, installParams.installerPackageName, installParams.userId); // NOTE: adb depends on parsing this string System.out.println("Success: created install session [" + sessionId + "]"); return PackageInstaller.STATUS_SUCCESS; } // private InstallParams makeInstallParams() { final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL); final InstallParams params = new InstallParams(); params.sessionParams = sessionParams; ... case "--user": params.userId = UserHandle.parseUserArg(nextOptionData()); break; ... } return params; } private static class InstallParams { SessionParams sessionParams; String installerPackageName; //这里对应的是LOG中的 user -1 int userId = UserHandle.USER_ALL; } private int doCreateSession(SessionParams params, String installerPackageName, int userId) throws RemoteException { //下面是真正抛异常的函数 userId = translateUserId(userId, "runInstallCreate"); if (userId == UserHandle.USER_ALL) { userId = UserHandle.USER_SYSTEM; params.installFlags |= PackageManager.INSTALL_ALL_USERS; } final int sessionId = mInstaller.createSession(params, installerPackageName, userId); return sessionId; } private int translateUserId(int userId, String logContext) { return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, logContext, "pm command"); }
|--frameworks/base/core/java/android/app/ActivityManager.java public static int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll, boolean requireFull, String name, String callerPackage) { if (UserHandle.getUserId(callingUid) == userId) { return userId; } try { return ActivityManagerNative.getDefault().handleIncomingUser(callingPid, callingUid, userId, allowAll, requireFull, name, callerPackage); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
|--frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java @Override public int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll, boolean requireFull, String name, String callerPackage) { return mUserController.handleIncomingUser(callingPid, callingUid, userId, allowAll, requireFull ? ALLOW_FULL_ONLY : ALLOW_NON_FULL, name, callerPackage); }
|--frameworks/base/services/core/java/com/android/server/am/UserController.java int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll, int allowMode, String name, String callerPackage) { final int callingUserId = UserHandle.getUserId(callingUid); if (callingUserId == userId) { return userId; } // Note that we may be accessing mCurrentUserId outside of a lock... // shouldn't be a big deal, if this is being called outside // of a locked context there is intrinsically a race with // the value the caller will receive and someone else changing it. // We assume that USER_CURRENT_OR_SELF will use the current user; later // we will switch to the calling user if access to the current user fails. int targetUserId = unsafeConvertIncomingUserLocked(userId); if (callingUid != 0 && callingUid != SYSTEM_UID) { final boolean allow; if (mService.checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid, callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) { // If the caller has this permission, they always pass go. And collect $200. allow = true; } else if (allowMode == ALLOW_FULL_ONLY) { // We require full access, sucks to be you. allow = false; } else if (mService.checkComponentPermission(INTERACT_ACROSS_USERS, callingPid, callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) { // If the caller does not have either permission, they are always doomed. allow = false; } else if (allowMode == ALLOW_NON_FULL) { // We are blanket allowing non-full access, you lucky caller! allow = true; } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE) { // We may or may not allow this depending on whether the two users are // in the same profile. allow = isSameProfileGroup(callingUserId, targetUserId); } else { throw new IllegalArgumentException("Unknown mode: " + allowMode); } //出错LOG打印的地方: if (!allow) { if (userId == UserHandle.USER_CURRENT_OR_SELF) { // In this case, they would like to just execute as their // owner user instead of failing. targetUserId = callingUserId; } else { StringBuilder builder = new StringBuilder(128); builder.append("Permission Denial: "); builder.append(name); if (callerPackage != null) { builder.append(" from "); builder.append(callerPackage); } builder.append(" asks to run as user "); builder.append(userId); builder.append(" but is calling from user "); builder.append(UserHandle.getUserId(callingUid)); builder.append("; this requires "); builder.append(INTERACT_ACROSS_USERS_FULL); if (allowMode != ALLOW_FULL_ONLY) { builder.append(" or "); builder.append(INTERACT_ACROSS_USERS); } String msg = builder.toString(); Slog.w(TAG, msg); throw new SecurityException(msg); } } } if (!allowAll && targetUserId < 0) { throw new IllegalArgumentException( "Call does not support special user #" + targetUserId); } // Check shell permission if (callingUid == Process.SHELL_UID && targetUserId >= UserHandle.USER_SYSTEM) { if (hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, targetUserId)) { throw new SecurityException("Shell does not have permission to access user " + targetUserId + "\n " + Debug.getCallers(3)); } } return targetUserId; }
到这里, 问题比较明显跟
mService.checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid, callingUid, -1, true) == PackageManager.PERMISSION_GRANTED
这行代码有关.
|--frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java int checkComponentPermission(String permission, int pid, int uid, int owningUid, boolean exported) { if (pid == MY_PID) { return PackageManager.PERMISSION_GRANTED; } return ActivityManager.checkComponentPermission(permission, uid, owningUid, exported); }
|--frameworks/base/core/java/android/app/ActivityManager.java /** @hide */ public static int checkComponentPermission(String permission, int uid, int owningUid, boolean exported) { // Root, system server get to do everything. final int appId = UserHandle.getAppId(uid); if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) { return PackageManager.PERMISSION_GRANTED; } // Isolated processes don't get any permissions. if (UserHandle.isIsolated(uid)) { return PackageManager.PERMISSION_DENIED; } // If there is a uid that owns whatever is being accessed, it has // blanket access to it regardless of the permissions it requires. if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) { return PackageManager.PERMISSION_GRANTED; } // If the target is not exported, then nobody else can get to it. if (!exported) { /* RuntimeException here = new RuntimeException("here"); here.fillInStackTrace(); Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid, here); */ return PackageManager.PERMISSION_DENIED; } if (permission == null) { return PackageManager.PERMISSION_GRANTED; } try { return AppGlobals.getPackageManager() .checkUidPermission(permission, uid); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java @Override public int checkUidPermission(String permName, int uid) { final int userId = UserHandle.getUserId(uid); if (!sUserManager.exists(userId)) { return PackageManager.PERMISSION_DENIED; } synchronized (mPackages) { Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid)); if (obj != null) { final SettingBase ps = (SettingBase) obj; final PermissionsState permissionsState = ps.getPermissionsState(); if (permissionsState.hasPermission(permName, userId)) { return PackageManager.PERMISSION_GRANTED; } // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) { return PackageManager.PERMISSION_GRANTED; } } else { ArraySet<String> perms = mSystemPermissions.get(uid); if (perms != null) { if (perms.contains(permName)) { return PackageManager.PERMISSION_GRANTED; } if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms .contains(Manifest.permission.ACCESS_FINE_LOCATION)) { return PackageManager.PERMISSION_GRANTED; } } } } return PackageManager.PERMISSION_DENIED; }
实际上, 就是去检查调用者(shell)是否包含INTERACT_ACROSS_USERS_FULL的权限.
通过命令可以看出当前adb调用的用户信息:
adb shell id uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc) context=u:r:shell:s0
为什么会跟 Shell这个应用有关系?
|--frameworks/base/packages/Shell/AndroidManifest.xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.shell" coreApp="true" android:sharedUserId="android.uid.shell" >
继续用命令可以获得结果:
adb shell pm dump com.android.shell结果中有: Packages: Package [com.android.shell] (e456cc6): userId=2000
所以, shell用户是否包含INTERACT_ACROSS_USERS_FULL的权限是由 Shell这个应用决定的.
若删除掉Shell 则会导致权限缺失.
PS
若通过remount 后再删除Shell应用, 重启等操作, adb install 工作都是正常的.
会出问题一般是在烧录前或恢复出厂前, Shell不存在