在之前的文章中,我们对PakageManagerService启动流程分析 做了简单的介绍,并对PMS系统的启动流程做了详细的解析。上面只是说到了Android的PMS的运行流程,而对于Android apk的整个安装流程并没有过多的介绍。本篇将重点介绍下Android apk的运行启动流程。
总结一下,关于apk的安装流程主要分为以下步骤:

  1. 将apk文件复制到data/app目录
  2. 解析apk信息
  3. dexopt操作
  4. 更新权限信息
  5. 完成安装,发送Intent.ACTION_PACKAGE_ADDED广播

用一张图说明一下:

此图可见,从PakageManagerService的启动到apk的安装完成,中间还是经历了很多的流程。

1, 将apk文件copy至data/app目录

在之前的文章我们说过,安装应用 Intent 的是 PackageInstallerActivity,但这个类厂商可以随意修改,这个类也并没有在 android.jar 中。PackageInstallerActivity 在安装过程中,实际调用的是 ApplicationPackageManager 里面的代码。

private void installCommon(Uri packageURI,        PackageInstallObserver observer, int flags, String installerPackageName,        VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {    if (!"file".equals(packageURI.getScheme())) {        throw new UnsupportedOperationException("Only file:// URIs are supported");    }    if (encryptionParams != null) {        throw new UnsupportedOperationException("ContainerEncryptionParams not supported");    }    final String originPath = packageURI.getPath();    try {        mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,                verificationParams, null);    } catch (RemoteException ignored) {    }}

在 ApplicationPackageManager 中是通过 Binder 机制调用了 PackageManagerService 中的 installPackage 方法,在这个方法中有个核心的方法installPackageAsUser,也就是我们前面流程图看到的。

@Overridepublic void installPackage(String originPath, IPackageInstallObserver2 observer,        int installFlags, String installerPackageName, VerificationParams verificationParams,        String packageAbiOverride) {    installPackageAsUser(originPath, observer, installFlags, installerPackageName,            verificationParams, packageAbiOverride, UserHandle.getCallingUserId());}

installPackageAsUser

mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);        final int callingUid = Binder.getCallingUid();        ...        ...        if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {            installFlags |= PackageManager.INSTALL_FROM_ADB;        } else {            // Caller holds INSTALL_PACKAGES permission, so we're less strict            // about installerPackageName.            installFlags &= ~PackageManager.INSTALL_FROM_ADB;            installFlags &= ~PackageManager.INSTALL_ALL_USERS;        }        UserHandle user;        if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {            user = UserHandle.ALL;        } else {            user = new UserHandle(userId);        }        verificationParams.setInstallerUid(callingUid);        final File originFile = new File(originPath);        final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);        final Message msg = mHandler.obtainMessage(INIT_COPY);        msg.obj = new InstallParams(origin, observer, installFlags,                installerPackageName, verificationParams, user, packageAbiOverride);        mHandler.sendMessage(msg);

这个方法主要是判断安装来源,包括adb,shell,all_user,然后像PMS的mHandler发送INIT_COPY的消息,这个mHandler运行在一个HandlerThread中。在接受到 INIT_COPY 消息后,将要安装的参数信息加入到 PendingInstalls 中去,如果是第一个安装,还需要发送 MCS_BOUND 消息,用于触发实际安装过程。

case INIT_COPY: {    HandlerParams params = (HandlerParams) msg.obj;    int idx = mPendingInstalls.size();    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);    // If a bind was already initiated we dont really    // need to do anything. The pending install    // will be processed later on.    if (!mBound) {        // If this is the only one pending we might        // have to bind to the service again.        if (!connectToService()) {            Slog.e(TAG, "Failed to bind to media container service");            params.serviceError();            return;        } else {            // Once we bind to the service, the first            // pending request will be processed.            mPendingInstalls.add(idx, params);        }    } else {        mPendingInstalls.add(idx, params);        // Already bound to the service. Just make        // sure we trigger off processing the first request.        if (idx == 0) {            mHandler.sendEmptyMessage(MCS_BOUND);        }    }    break;}

HandlerParams.startCopy

在 MCS_BOUND 消息中取出第一个安装请求,并调用 startCopy 方法。

HandlerParams params = mPendingInstalls.get(0);if (params != null) {   if (params.startCopy()) {     // ...   }   // ...}

handleStartCopy()

在 startCopy 中调用 handleStartCopy 方法,由于这个类,需要与 MCS (MediaContainerService) 进行通信,有可能发生异常,因而这里设置了重试机制。

if (++mRetries > MAX_RETRIES) {    Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");    mHandler.sendEmptyMessage(MCS_GIVE_UP);    handleServiceError();    return false;} else {    handleStartCopy();    res = true;}handleReturnCode();

handleStartCopy的核心就是copyApk,其他的都是些存储空间检查,权限检查等等安全校验。

2, 解析apk信息

完成apk copy到data/app目录的操作后,下一步就到了 handleReturnCode,这个方法又跳转到processPendingInstall()方法,下面先来看看processPendingInstall()方法。

private void processPendingInstall(final InstallArgs args, final int currentStatus) {        // Queue up an async operation since the package installation may take a little while.        mHandler.post(new Runnable() {            public void run() {                mHandler.removeCallbacks(this);                 // Result object to be returned                PackageInstalledInfo res = new PackageInstalledInfo();                res.returnCode = currentStatus;                res.uid = -1;                res.pkg = null;                res.removedInfo = new PackageRemovedInfo();                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {                    args.doPreInstall(res.returnCode);                    synchronized (mInstallLock) {                        installPackageLI(args, res); //1.安装                    }                    args.doPostInstall(res.returnCode, res.uid);                }                // A restore should be performed at this point if (a) the install                // succeeded, (b) the operation is not an update, and (c) the new                // package has not opted out of backup participation.                final boolean update = res.removedInfo.removedPackage != null;                final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;                boolean doRestore = !update                        && ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);                // Set up the post-install work request bookkeeping.  This will be used                // and cleaned up by the post-install event handling regardless of whether                // there's a restore pass performed.  Token values are >= 1.                int token;                if (mNextInstallToken < 0) mNextInstallToken = 1;                token = mNextInstallToken++;                PostInstallData data = new PostInstallData(args, res);                mRunningInstalls.put(token, data);                if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);                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); //2.调用backup服务                        } 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;                    }                }                if (!doRestore) {                    // No restore possible, or the Backup Manager was mysteriously not                    // available -- just fire the post-install work request directly.                    if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);                    Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);                    mHandler.sendMessage(msg);                }            }        });    }

在这个方法有几个关键步骤:
一是installPackageLI(args, res);,这个方法具体执行了解析package和后续操作,
而再installPackageLI(args, res);执行完毕后会走到bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);,会调用backupservice的restoreAtInstall方法,而restoreAtInstall方法最终又会调用PMS的finishPackageInstall()方法,完成安装。

installPackageLI

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {        final int installFlags = args.installFlags;        String installerPackageName = args.installerPackageName;        File tmpPackageFile = new File(args.getCodePath());        boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);        boolean onSd = ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0);        boolean replace = false;        final int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE;        // Result object to be returned        res.returnCode = PackageManager.INSTALL_SUCCEEDED;        if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);        // Retrieve PackageSettings and parse package        final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY                | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)                | (onSd ? PackageParser.PARSE_ON_SDCARD : 0);        PackageParser pp = new PackageParser();        pp.setSeparateProcesses(mSeparateProcesses);        pp.setDisplayMetrics(mMetrics);        final PackageParser.Package pkg;        try {            pkg = pp.parsePackage(tmpPackageFile, parseFlags);        } catch (PackageParserException e) {            res.setError("Failed parse during installPackageLI", e);            return;        }        // Mark that we have an install time CPU ABI override.        pkg.cpuAbiOverride = args.abiOverride;        String pkgName = res.name = pkg.packageName;        if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {            if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {                res.setError(INSTALL_FAILED_TEST_ONLY, "installPackageLI");                return;            }        }        try {            pp.collectCertificates(pkg, parseFlags);            pp.collectManifestDigest(pkg);        } catch (PackageParserException e) {            res.setError("Failed collect during installPackageLI", e);            return;        }        /* If the installer passed in a manifest digest, compare it now. */        if (args.manifestDigest != null) {            if (DEBUG_INSTALL) {                final String parsedManifest = pkg.manifestDigest == null ? "null"                        : pkg.manifestDigest.toString();                Slog.d(TAG, "Comparing manifests: " + args.manifestDigest.toString() + " vs. "                        + parsedManifest);            }            if (!args.manifestDigest.equals(pkg.manifestDigest)) {                res.setError(INSTALL_FAILED_PACKAGE_CHANGED, "Manifest digest changed");                return;            }        } else if (DEBUG_INSTALL) {            final String parsedManifest = pkg.manifestDigest == null                    ? "null" : pkg.manifestDigest.toString();            Slog.d(TAG, "manifestDigest was not present, but parser got: " + parsedManifest);        }        // Get rid of all references to package scan path via parser.        pp = null;        String oldCodePath = null;        boolean systemApp = false;        synchronized (mPackages) {            // Check whether the newly-scanned package wants to define an already-defined perm            int N = pkg.permissions.size();            for (int i = N-1; i >= 0; i--) {                PackageParser.Permission perm = pkg.permissions.get(i);                BasePermission bp = mSettings.mPermissions.get(perm.info.name);                if (bp != null) {                    // If the defining package is signed with our cert, it's okay.  This                    // also includes the "updating the same package" case, of course.                    // "updating same package" could also involve key-rotation.                    final boolean sigsOk;                    if (!bp.sourcePackage.equals(pkg.packageName)                            || !(bp.packageSetting instanceof PackageSetting)                            || !bp.packageSetting.keySetData.isUsingUpgradeKeySets()                            || ((PackageSetting) bp.packageSetting).sharedUser != null) {                        sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures,                                pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;                    } else {                        sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg);                    }                    if (!sigsOk) {                        // If the owning package is the system itself, we log but allow                        // install to proceed; we fail the install on all other permission                        // redefinitions.                        if (!bp.sourcePackage.equals("android")) {                            res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "                                    + pkg.packageName + " attempting to redeclare permission "                                    + perm.info.name + " already owned by " + bp.sourcePackage);                            res.origPermission = perm.info.name;                            res.origPackage = bp.sourcePackage;                            return;                        } else {                            Slog.w(TAG, "Package " + pkg.packageName                                    + " attempting to redeclare system permission "                                    + perm.info.name + "; ignoring new declaration");                            pkg.permissions.remove(i);                        }                    }                }            }            // Check if installing already existing package            if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {                String oldName = mSettings.mRenamedPackages.get(pkgName);                if (pkg.mOriginalPackages != null                        && pkg.mOriginalPackages.contains(oldName)                        && mPackages.containsKey(oldName)) {                    // This package is derived from an original package,                    // and this device has been updating from that original                    // name.  We must continue using the original name, so                    // rename the new package here.                    pkg.setPackageName(oldName);                    pkgName = pkg.packageName;                    replace = true;                    if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName="                            + oldName + " pkgName=" + pkgName);                } else if (mPackages.containsKey(pkgName)) {                    // This package, under its official name, already exists                    // on the device; we should replace it.                    replace = true;                    if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);                }            }            PackageSetting ps = mSettings.mPackages.get(pkgName);            if (ps != null) {                if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);                oldCodePath = mSettings.mPackages.get(pkgName).codePathString;                if (ps.pkg != null && ps.pkg.applicationInfo != null) {                    systemApp = (ps.pkg.applicationInfo.flags &                            ApplicationInfo.FLAG_SYSTEM) != 0;                }                res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);            }        }        if (systemApp && onSd) {            // Disable updates to system apps on sdcard            res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,                    "Cannot install updates to system apps on sdcard");            return;        }        if (!args.doRename(res.returnCode, pkg, oldCodePath)) {            res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");            return;        }        if (replace) {            replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,                    installerPackageName, res);        } else {            installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,                    args.user, installerPackageName, res);        }        synchronized (mPackages) {            final PackageSetting ps = mSettings.mPackages.get(pkgName);            if (ps != null) {                res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);            }        }    }

这个方法先是解析了package包,然后做了大量签名和权限校验的工作,最终走到判断是安装新的apk还是覆盖安装的判断语句上。

 if (replace) {            replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,                    installerPackageName, res);        } else {            installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,                    args.user, installerPackageName, res);        }

installNewPackageLI()

 private void installNewPackageLI(PackageParser.Package pkg,            int parseFlags, int scanFlags, UserHandle user,            String installerPackageName, PackageInstalledInfo res) {        // Remember this for later, in case we need to rollback this install        String pkgName = pkg.packageName;        if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);        boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();        synchronized(mPackages) {            if (mSettings.mRenamedPackages.containsKey(pkgName)) {                // A package with the same name is already installed, though                // it has been renamed to an older name.  The package we                // are trying to install should be installed as an update to                // the existing one, but that has not been requested, so bail.                res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName                        + " without first uninstalling package running as "                        + mSettings.mRenamedPackages.get(pkgName));                return;            }            if (mPackages.containsKey(pkgName)) {                // Don't allow installation over an existing package with the same name.                res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName                        + " without first uninstalling.");                return;            }        }        try {            PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,                    System.currentTimeMillis(), user);            updateSettingsLI(newPackage, installerPackageName, null, null, res);            // delete the partially installed application. the data directory will have to be            // restored if it was already existing            if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {                // remove package from internal structures.  Note that we want deletePackageX to                // delete the package data and cache directories that it created in                // scanPackageLocked, unless those directories existed before we even tried to                // install.                deletePackageLI(pkgName, UserHandle.ALL, false, null, null,                        dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,                                res.removedInfo, true);            }        } catch (PackageManagerException e) {            res.setError("Package couldn't be installed in " + pkg.codePath, e);        }    }

这个方法核心的步骤有两个:

  • PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags,
    scanFlags,System.currentTimeMillis(), user);
  • updateSettingsLI(newPackage, installerPackageName, null, null, res);

scanPackageLI负责安装,而updateSettingLI则是完成安装后的设置信息更新。

scanPackageLI()

scanPackageLI()方法主要逻辑是由scanPackageDirtyLI()实现的,在这个方法中主要做了如下的操作:

  • 设置系统App的一些参数
  • 校验签名
  • 解析app的provider,校验是否与已有的provider冲突
  • 32/64位abi的一些设置
  • 四大组件的解析,注册

3,dexopt操作

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

performDexOptLI()

performDexOptLI的一个核心方法:

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

本方法的核心就是调用PMS的mInstaller成员变量的dexopt操作。

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操作。

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

4,更新权限信息

dexopt操作执行完后,installNewPackageLI()方法就会走到updateSettingsLI()来更新设置信息,而更新设置信息主要是权限信息。更新权限信息最核心的方法是:updatePermissionsLPw()。

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的所有权限都记录下来并更新到PMS的mAppOpPermissionPackages成员变量里面,并判定是否授予该app请求的权限。

5,完成安装,发广播

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

在BackupManagerService的restoreAtInstall方法中会有以下代码:

... 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方法最终会调用PMS的finishPackageInstall方法,而此方法最终会发送Intent.ACTION_PACKAGE_ADDED广播,apk的安装就到到此结束。

更多相关文章

  1. 旅行青蛙(旅かえる)逆向笔记
  2. Android逆向之旅—Hook神器Frida使用详解
  3. Android开发-AndroidStudio3.x修改应用图标无法在真机上显示
  4. Androd开发艺术探索 第10章 Android的消息机制 读书笔记
  5. android 使用Lottie实现Android动画
  6. Android导出文件位置讨论
  7. Android使用TabHost程序异常终止could not create tab content b
  8. 使用mapbar 地图sdk要关闭硬件加速
  9. Android事件机制之一:事件传递和消费

随机推荐

  1. Android 界面滑动实现---Scroller类 从源
  2. Android之场景桌面(一)
  3. Android 职业路上--只要还有一丝希望,不
  4. 两个Activity之间跳转问题之activity的四
  5. 安装应用android批量安装APK
  6. 【腾讯Bugly干货分享】Android(安卓)进程
  7. 20个经典Android游戏源码下载地址(持续更
  8. Android通讯录字母排序城市列表展示效果
  9. 实现Android(安卓)动态加载APK(Fragment o
  10. android中反射的应用