继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Android Apk安装过程分析(下)

拉丁的传说
关注TA
已关注
手记 575
粉丝 126
获赞 789

3. dexopt操作

Apk文件其实只是一个归档zip压缩包,而我们编写的代码最终都编译成了.dex文件,但为了提高运行性能,android系统并不会直接执行.dex,而是会在安装过程中执行dexopt操作来优化.dex文件,最终android系统执行的时优化后的'odex'文件(注意:这个odex文件的后缀也是.dex,其路径在data/dalvik-cache)。对于dalvik虚拟机,dexopt就是优化操作,而对于art虚拟机,dexopt执行的则是dex2oat操作,既将.dex文件翻译成oat文件。关于artdex2oat的更多信息请看后文。

这里我们先来看看PMSdexopt操作:

3.1 performDexOptLI()

这个方法的核心是

final int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg), pkg.packageName, dexCodeInstructionSet, vmSafeMode);

其作用就是调用PMSmInstaller成员变量的dexopt操作。

3.2 Installer.dexopt

Installer类的dexopt方法又调用InstallerConnection类的dexopt方法,来看看这个方法:

public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
            String instructionSet, boolean vmSafeMode) {
        StringBuilder builder = new StringBuilder("dexopt");
        builder.append(' ');
        builder.append(apkPath);
        builder.append(' ');
        builder.append(uid);
        builder.append(isPublic ? " 1" : " 0");
        builder.append(' ');
        builder.append(pkgName);
        builder.append(' ');
        builder.append(instructionSet);
        builder.append(' ');
        builder.append(vmSafeMode ? " 1" : " 0");        return execute(builder.toString());
    } public synchronized String transact(String cmd) {        if (!connect()) {
            Slog.e(TAG, "connection failed");            return "-1";
        }        if (!writeCommand(cmd)) {            /*
             * If installd died and restarted in the background (unlikely but
             * possible) we'll fail on the next write (this one). Try to
             * reconnect and write the command one more time before giving up.
             */
            Slog.e(TAG, "write command failed? reconnect!");            if (!connect() || !writeCommand(cmd)) {                return "-1";
            }
        }        if (LOCAL_DEBUG) {
            Slog.i(TAG, "send: '" + cmd + "'");
        }        final int replyLength = readReply();        if (replyLength > 0) {
            String s = new String(buf, 0, replyLength);            if (LOCAL_DEBUG) {
                Slog.i(TAG, "recv: '" + s + "'");
            }            return s;
        } else {            if (LOCAL_DEBUG) {
                Slog.i(TAG, "fail");
            }            return "-1";
        }
    }    public int execute(String cmd) {
        String res = transact(cmd);        try {            return Integer.parseInt(res);
        } catch (NumberFormatException ex) {            return -1;
        }
    }  private boolean connect() {        if (mSocket != null) {            return true;
        }
        Slog.i(TAG, "connecting...");        try {
            mSocket = new LocalSocket();

            LocalSocketAddress address = new LocalSocketAddress("installd",
                    LocalSocketAddress.Namespace.RESERVED);

            mSocket.connect(address);

            mIn = mSocket.getInputStream();
            mOut = mSocket.getOutputStream();
        } catch (IOException ex) {
            disconnect();            return false;
        }        return true;
    }

由上面的几个方法可以知道,最终dexopt操作是通过socket的方式来跨进程通知守护进程installd,由其去执行dexopt操作。

3.3 commands::dexopt()

最终守护进程installd会调用Commands.c文件(位于/source/framework/native/cmds/installd)的dexopt方法。

int dexopt(const char *apk_path, uid_t uid, bool is_public,           const char *pkgname, const char *instruction_set,           bool vm_safe_mode, bool is_patchoat){    struct utimbuf ut;
    struct stat input_stat, dex_stat;
    char out_path[PKG_PATH_MAX];    char persist_sys_dalvik_vm_lib[PROPERTY_VALUE_MAX];    char *end;    const char *input_file;    char in_odex_path[PKG_PATH_MAX];    int res, input_fd=-1, out_fd=-1;

    ...
    ...    pid_t pid;
    pid = fork();    if (pid == 0) {        /* child -- drop privileges before continuing */
        if (setgid(uid) != 0) {
            ALOGE("setgid(%d) failed in installd during dexopt\n", uid);            exit(64);
        }        if (setuid(uid) != 0) {
            ALOGE("setuid(%d) failed in installd during dexopt\n", uid);            exit(65);
        }        // drop capabilities
        struct __user_cap_header_struct capheader;
        struct __user_cap_data_struct capdata[2];
        memset(&capheader, 0, sizeof(capheader));        memset(&capdata, 0, sizeof(capdata));
        capheader.version = _LINUX_CAPABILITY_VERSION_3;        if (capset(&capheader, &capdata[0]) < 0) {
            ALOGE("capset failed: %s\n", strerror(errno));            exit(66);
        }        if (set_sched_policy(0, SP_BACKGROUND) < 0) {
            ALOGE("set_sched_policy failed: %s\n", strerror(errno));            exit(70);
        }        if (flock(out_fd, LOCK_EX | LOCK_NB) != 0) {
            ALOGE("flock(%s) failed: %s\n", out_path, strerror(errno));            exit(67);
        }        if (strncmp(persist_sys_dalvik_vm_lib, "libdvm", 6) == 0) {
            run_dexopt(input_fd, out_fd, input_file, out_path);
        } else if (strncmp(persist_sys_dalvik_vm_lib, "libart", 6) == 0) {            if (is_patchoat) {
                run_patchoat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set);
            } else {
                run_dex2oat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set,
                            vm_safe_mode);
            }
        } else {            exit(69);   /* Unexpected persist.sys.dalvik.vm.lib value */
        }        exit(68);   /* only get here on exec failure */
    } else {
        res = wait_child(pid);        if (res == 0) {
            ALOGV("DexInv: --- END '%s' (success) ---\n", input_file);
        } else {
            ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", input_file, res);            goto fail;
        }
    }

    ut.actime = input_stat.st_atime;
    ut.modtime = input_stat.st_mtime;
    utime(out_path, &ut);

    close(out_fd);
    close(input_fd);    return 0;

fail:    if (out_fd >= 0) {
        close(out_fd);
        unlink(out_path);
    }    if (input_fd >= 0) {
        close(input_fd);
    }    return -1;
}

由上面的代码可以发现,installd在做了些操作后,fork出了一个新的进程,根据虚拟机的类型为libdvmlibart分别执行run_dexoptrun_dex2oat(如果为is_patchoat,则是run_patchoat)操作。

4. 更新权限信息

dexopt操作执行完后,installNewPackageLI()方法就会走到updateSettingsLI()来更新设置信息,而更新设置信息主要是权限信息,所以直接来看updatePermissionsLPw();

4.1 updatePermissionsLPw

private void updatePermissionsLPw(String changingPkg,
            PackageParser.Package pkgInfo, int flags) {        // Make sure there are no dangling permission trees.
        Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();        while (it.hasNext()) {            final BasePermission bp = it.next();            if (bp.packageSetting == null) {                // We may not yet have parsed the package, so just see if
                // we still know about its settings.
                bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage);
            }            if (bp.packageSetting == null) {
                Slog.w(TAG, "Removing dangling permission tree: " + bp.name
                        + " from package " + bp.sourcePackage);
                it.remove();
            } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) {                if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
                    Slog.i(TAG, "Removing old permission tree: " + bp.name
                            + " from package " + bp.sourcePackage);
                    flags |= UPDATE_PERMISSIONS_ALL;
                    it.remove();
                }
            }
        }        // Make sure all dynamic permissions have been assigned to a package,
        // and make sure there are no dangling permissions.
        it = mSettings.mPermissions.values().iterator();        while (it.hasNext()) {            final BasePermission bp = it.next();            if (bp.type == BasePermission.TYPE_DYNAMIC) {                if (DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
                        + bp.name + " pkg=" + bp.sourcePackage
                        + " info=" + bp.pendingInfo);                if (bp.packageSetting == null && bp.pendingInfo != null) {                    final BasePermission tree = findPermissionTreeLP(bp.name);                    if (tree != null && tree.perm != null) {
                        bp.packageSetting = tree.packageSetting;
                        bp.perm = new PackageParser.Permission(tree.perm.owner,                                new PermissionInfo(bp.pendingInfo));
                        bp.perm.info.packageName = tree.perm.info.packageName;
                        bp.perm.info.name = bp.name;
                        bp.uid = tree.uid;
                    }
                }
            }            if (bp.packageSetting == null) {                // We may not yet have parsed the package, so just see if
                // we still know about its settings.
                bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage);
            }            if (bp.packageSetting == null) {
                Slog.w(TAG, "Removing dangling permission: " + bp.name
                        + " from package " + bp.sourcePackage);
                it.remove();
            } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) {                if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
                    Slog.i(TAG, "Removing old permission: " + bp.name
                            + " from package " + bp.sourcePackage);
                    flags |= UPDATE_PERMISSIONS_ALL;
                    it.remove();
                }
            }
        }        // Now update the permissions for all packages, in particular
        // replace the granted permissions of the system packages.
        if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {            for (PackageParser.Package pkg : mPackages.values()) {                if (pkg != pkgInfo) {
                    grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0,
                            changingPkg);
                }
            }
        }        
        if (pkgInfo != null) {
            grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0, changingPkg);
        }
    }    private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace,
            String packageOfInterest) {        final PackageSetting ps = (PackageSetting) pkg.mExtras;        if (ps == null) {            return;
        }        final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
        HashSet<String> origPermissions = gp.grantedPermissions;        boolean changedPermission = false;        if (replace) {
            ps.permissionsFixed = false;            if (gp == ps) {
                origPermissions = new HashSet<String>(gp.grantedPermissions);
                gp.grantedPermissions.clear();
                gp.gids = mGlobalGids;
            }
        }        if (gp.gids == null) {
            gp.gids = mGlobalGids;
        }        final int N = pkg.requestedPermissions.size();        for (int i=0; i<N; i++) {            final String name = pkg.requestedPermissions.get(i);            final boolean required = pkg.requestedPermissionsRequired.get(i);            final BasePermission bp = mSettings.mPermissions.get(name);            if (DEBUG_INSTALL) {                if (gp != ps) {
                    Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp);
                }
            }            if (bp == null || bp.packageSetting == null) {                if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
                    Slog.w(TAG, "Unknown permission " + name
                            + " in package " + pkg.packageName);
                }                continue;
            }            final String perm = bp.name;            boolean allowed;            boolean allowedSig = false;            if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {                // Keep track of app op permissions.
                ArraySet<String> pkgs = mAppOpPermissionPackages.get(bp.name);                if (pkgs == null) {
                    pkgs = new ArraySet<>();
                    mAppOpPermissionPackages.put(bp.name, pkgs);
                }
                pkgs.add(pkg.packageName);
            }            final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;            if (level == PermissionInfo.PROTECTION_NORMAL
                    || level == PermissionInfo.PROTECTION_DANGEROUS) {                // We grant a normal or dangerous permission if any of the following
                // are true:
                // 1) The permission is required
                // 2) The permission is optional, but was granted in the past
                // 3) The permission is optional, but was requested by an
                //    app in /system (not /data)
                //
                // Otherwise, reject the permission.
                allowed = (required || origPermissions.contains(perm)
                        || (isSystemApp(ps) && !isUpdatedSystemApp(ps)));
            } else if (bp.packageSetting == null) {                // This permission is invalid; skip it.
                allowed = false;
            } else if (level == PermissionInfo.PROTECTION_SIGNATURE) {
                allowed = grantSignaturePermission(perm, pkg, bp, origPermissions);                if (allowed) {
                    allowedSig = true;
                }
            } else {
                allowed = false;
            }            if (DEBUG_INSTALL) {                if (gp != ps) {
                    Log.i(TAG, "Package " + pkg.packageName + " granting " + perm);
                }
            }            if (allowed) {                if (!isSystemApp(ps) && ps.permissionsFixed) {                    // If this is an existing, non-system package, then
                    // we can't add any new permissions to it.
                    if (!allowedSig && !gp.grantedPermissions.contains(perm)) {                        // Except...  if this is a permission that was added
                        // to the platform (note: need to only do this when
                        // updating the platform).
                        allowed = isNewPlatformPermissionForPackage(perm, pkg);
                    }
                }                if (allowed) {                    if (!gp.grantedPermissions.contains(perm)) {
                        changedPermission = true;
                        gp.grantedPermissions.add(perm);
                        gp.gids = appendInts(gp.gids, bp.gids);
                    } else if (!ps.haveGids) {
                        gp.gids = appendInts(gp.gids, bp.gids);
                    }
                } else {                    if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
                        Slog.w(TAG, "Not granting permission " + perm
                                + " to package " + pkg.packageName
                                + " because it was previously installed without");
                    }
                }
            } else {                if (gp.grantedPermissions.remove(perm)) {
                    changedPermission = true;
                    gp.gids = removeInts(gp.gids, bp.gids);
                    Slog.i(TAG, "Un-granting permission " + perm
                            + " from package " + pkg.packageName
                            + " (protectionLevel=" + bp.protectionLevel
                            + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
                            + ")");
                } else if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) == 0) {                    // Don't print warning for app op permissions, since it is fine for them
                    // not to be granted, there is a UI for the user to decide.
                    if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
                        Slog.w(TAG, "Not granting permission " + perm
                                + " to package " + pkg.packageName
                                + " (protectionLevel=" + bp.protectionLevel
                                + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
                                + ")");
                    }
                }
            }
        }        if ((changedPermission || replace) && !ps.permissionsFixed &&
                !isSystemApp(ps) || isUpdatedSystemApp(ps)){            // This is the first that we have heard about this package, so the
            // permissions we have now selected are fixed until explicitly
            // changed.
            ps.permissionsFixed = true;
        }
        ps.haveGids = true;
    }

由上面两个方法可以看到,在apk的安装时PMS会将该app的所有权限都记录下来并更新到PMSmAppOpPermissionPackages成员变量里面,并判定是否授予该app请求的权限。

4.2 完成安装

还记得前面说过的在processPendingInstall方法在执行installPackageLi后会执行以下语句

 if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {                    // Pass responsibility to the Backup Manager.  It will perform a
                    // restore if appropriate, then pass responsibility back to the
                    // Package Manager to run the post-install observer callbacks
                    // and broadcasts.
                    IBackupManager bm = IBackupManager.Stub.asInterface(
                            ServiceManager.getService(Context.BACKUP_SERVICE));                    if (bm != null) {                        if (DEBUG_INSTALL) Log.v(TAG, "token " + token
                                + " to BM for possible restore");                        try {
                            bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
                        } catch (RemoteException e) {                            // can't happen; the backup manager is local
                        } catch (Exception e) {
                            Slog.e(TAG, "Exception trying to enqueue restore", e);
                            doRestore = false;
                        }
                    } else {
                        Slog.e(TAG, "Backup Manager not found!");
                        doRestore = false;
                    }
                }

我也不是很清楚为什么系统会调用IBackupManagerrestoreAtInstall方法,不过发现在BackupManagerServicerestoreAtInstall方法中会有以下代码:

... if (skip) {            // Auto-restore disabled or no way to attempt a restore; just tell the Package
            // Manager to proceed with the post-install handling for this package.
            if (DEBUG) Slog.v(TAG, "Finishing install immediately");            try {
                mPackageManagerBinder.finishPackageInstall(token);
            } catch (RemoteException e) { /* can't happen */ }
        }
...

最终restoreAtInstall方法又会调用PMSfinishPackageInstall方法,而此方法最终会发送Intent.ACTION_PACKAGE_ADDED广播,apk的安装就到到此结束了。

原文链接:http://www.apkbus.com/blog-664680-77895.html

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP