Android7.0的静默安装失败问题研究

来源:互联网 发布:怎样申请新的淘宝账号 编辑:程序博客网 时间:2024/05/27 06:17

Android7.0的静默安装失败问题研究

最近遇到了在Android7.0上静默安装失败的问题。应用程序放到系统分区(/system/priv-app/)执行pm命令实现静默安装的方式在其他版本上都可以实现静默安装,在7.0上就安装失败,报这个异常:

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

至于安装失败肯定是pm instll的执行逻辑做了修改,我们直接从Pm的源码开始研究。
网上关于7.0静默安装的资料确实很少,没办法了只能先去查看源码看为啥报这个异常了。手头上也没有7.0的源码,只能去下载了,20个G的源码下载了三个小时左右。7.0源码下载,清华大学Android源码镜像站,在解压的aosp目录下面执行repo sync即可得到完整目录。
看frameworks的,源码查看工具我用的是understand,全局搜索runInstallCreate from pm command asks to run as user没搜到,说明这句话是组装成的,所以拆开搜索,全局搜索asks to run as user,这次搜到了。该方法在UserController类中

int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,            int allowMode, String name, String callerPackage) {        //测试发现这个getUserId的返回值应该为0        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);            }            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;    }

研究发现callingUserId = UserHandle.getUserId(callingUid)的返回值为0,如果userId的值和callingUserId的值相同就不会执行下面的方法,也就不会抛出异常了。那我们就继续追踪看看userId是什么。这次我们Pm的源码开始,pm install就是在Pm这个类中执行的,调用的是runInstall方法,runInstall的源码就不贴出来了,我们这里只关心两个方法
final InstallParams params = makeInstallParams();和final int sessionId = doCreateSession(params.sessionParams,params.installerPackageName, params.userId);
makeInstallParams的源码:该方法是解析命令中的参数的,这里只看主要的

private InstallParams makeInstallParams() {        final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL);        final InstallParams params = new InstallParams();        params.sessionParams = sessionParams;        String opt;        while ((opt = nextOption()) != null) {            switch (opt) {               ....                case "-i":                    params.installerPackageName = nextArg();                    if (params.installerPackageName == null) {                        throw new IllegalArgumentException("Missing installer package");                    }                    break;               ...               ...                case "--user":                    params.userId = UserHandle.parseUserArg(nextOptionData());                    break;                ....                default:                    throw new IllegalArgumentException("Unknown option " + opt);            }        }        return params;    }

doCreateSession方法中回去调用userId = translateUserId(userId, “runInstallCreate”);
translateUserId中回去调用ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, logContext, “pm command”);看ActivityManagerService中的handleIncomingUser方法

@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);    }

找到了,就是在这里调用的mUserController.handleIncomingUser方法,抛出的异常。这个userId就是我们调用doCreateSession方法传入的params.userId,我们继续看makeInstallParams的源码,params.userId是–user指定的。
我们在代码中执行Runtime.getRuntime().exec(“pm install –user 0 apkpath”),log又抛出一个空指针异常。
调用链:doCreateSession—>mInstaller.createSession(params, installerPackageName, userId)–>createSessionInternal–>mAppOps.checkPackage(callingUid, installerPackageName);
checkPackageName会判断installerPackageName是不是为空,为空就抛出异常。installerPackageNameye也是调用doCreateSession时传入params.installerPackageName 该值也是pm命令指定的,为当前调用执行pm命令的包名
所以修改pm命令为:Runtime.getRuntime().exec(“pm install -i 包名 –user 0 apkpath”),静默安装成功!!!
到这里就结束了,后面的源码没有贴出来,但是写出了调用过程,大家可以自己去追踪一下源码!

1 0
原创粉丝点击