勿忘初心

个人签名

530篇博客

Android应用程序安装过程浅析(6)

勿忘初心2018-09-25 15:16

本文来自网易云社区

作者:孙有军

 

 上面主要是调用了createDataDirsLI来进行包的安装:

private int createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo) {
    int[] users = sUserManager.getUserIds();
    int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo);
    if (res < 0) {
        return res;
    }
    for (int user : users) {
        if (user != 0) {
            res = mInstaller.createUserData(volumeUuid, packageName,
                    UserHandle.getUid(user, uid), user, seinfo);
            if (res < 0) {
                return res;
            }
        }
    }
    return res;
}

       这里最终调用了mInstaller的intall函数,mInstaller是一个InstallerConnection,InstallerConnection里面是通过输入输出流与一个LocalSocket进行安装操作的,所以这里最终调用的InstallerConnection的intall函数,执行完成后如果user不为空,创建用户数据。


       包的安装过程到此就结束了,我们再回头看看POST_INSTALL进行了什么操作?


case POST_INSTALL: {
    if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
    PostInstallData data = mRunningInstalls.get(msg.arg1);
    mRunningInstalls.delete(msg.arg1);
    boolean deleteOld = false;

    if (data != null) {
        InstallArgs args = data.args;
        PackageInstalledInfo res = data.res;

        if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
            final String packageName = res.pkg.applicationInfo.packageName;
            res.removedInfo.sendBroadcast(false, true, false);
            Bundle extras = new Bundle(1);
            extras.putInt(Intent.EXTRA_UID, res.uid);

            // Now that we successfully installed the package, grant runtime
            // permissions if requested before broadcasting the install.
            if ((args.installFlags
                    & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0) {
                grantRequestedRuntimePermissions(res.pkg, args.user.getIdentifier(),
                        args.installGrantPermissions);
            }

            // Determine the set of users who are adding this
            // package for the first time vs. those who are seeing
            // an update.
            int[] firstUsers;
            int[] updateUsers = new int[0];
            if (res.origUsers == null || res.origUsers.length == 0) {
                firstUsers = res.newUsers;
            } else {
                firstUsers = new int[0];
                for (int i=0; i<res.newUsers.length; i++) {
                    int user = res.newUsers[i];
                    boolean isNew = true;
                    for (int j=0; j<res.origUsers.length; j++) {
                        if (res.origUsers[j] == user) {
                            isNew = false;
                            break;
                        }
                    }
                    if (isNew) {
                        int[] newFirst = new int[firstUsers.length+1];
                        System.arraycopy(firstUsers, 0, newFirst, 0,
                                firstUsers.length);
                        newFirst[firstUsers.length] = user;
                        firstUsers = newFirst;
                    } else {
                        int[] newUpdate = new int[updateUsers.length+1];
                        System.arraycopy(updateUsers, 0, newUpdate, 0,
                                updateUsers.length);
                        newUpdate[updateUsers.length] = user;
                        updateUsers = newUpdate;
                    }
                }
            }
            sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                    packageName, extras, null, null, firstUsers);
            final boolean update = res.removedInfo.removedPackage != null;
            if (update) {
                extras.putBoolean(Intent.EXTRA_REPLACING, true);
            }
            sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                    packageName, extras, null, null, updateUsers);
            if (update) {
                sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
                        packageName, extras, null, null, updateUsers);
                sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
                        null, null, packageName, null, updateUsers);

                // treat asec-hosted packages like removable media on upgrade
                if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
                    if (DEBUG_INSTALL) {
                        Slog.i(TAG, "upgrading pkg " + res.pkg
                                + " is ASEC-hosted -> AVAILABLE");
                    }
                    int[] uidArray = new int[] { res.pkg.applicationInfo.uid };
                    ArrayList<String> pkgList = new ArrayList<String>(1);
                    pkgList.add(packageName);
                    sendResourcesChangedBroadcast(true, true,
                            pkgList,uidArray, null);
                }
            }
            if (res.removedInfo.args != null) {
                // Remove the replaced package's older resources safely now
                deleteOld = true;
            }

            // If this app is a browser and it's newly-installed for some
            // users, clear any default-browser state in those users
            if (firstUsers.length > 0) {
                // the app's nature doesn't depend on the user, so we can just
                // check its browser nature in any user and generalize.
                if (packageIsBrowser(packageName, firstUsers[0])) {
                    synchronized (mPackages) {
                        for (int userId : firstUsers) {
                            mSettings.setDefaultBrowserPackageNameLPw(null, userId);
                        }
                    }
                }
            }
            // Log current value of "unknown sources" setting
            EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
                getUnknownSourcesSettings());
        }
        // Force a gc to clear up things
        Runtime.getRuntime().gc();
        // We delete after a gc for applications  on sdcard.
        if (deleteOld) {
            synchronized (mInstallLock) {
                res.removedInfo.args.doPostDeleteLI(true);
            }
        }
        if (args.observer != null) {
            try {
                Bundle extras = extrasForInstallResult(res);
                args.observer.onPackageInstalled(res.name, res.returnCode,
                        res.returnMsg, extras);
            } catch (RemoteException e) {
                Slog.i(TAG, "Observer no longer exists.");
            }
        }
    } else {
        Slog.e(TAG, "Bogus post-install token " + msg.arg1);
    }
} break;

万里长征最后一步,这里主要先将安装信息从安装列表中移除,这也是在之前processPendingInstall中添加的,包安装成功之后,在发送安装成功广播之前先获取运行时权限,获取权限后发送ACTION_PACKAGE_ADDED广播,如果是更新包再发送ACTION_PACKAGE_REPLACED和ACTION_MY_PACKAGE_REPLACED广播来通知其他应用,安装的广播发送完成后发送一个资源更改的广播通知其他应用,如果该应用是一个浏览器,则先清除默认的浏览器设置,重新检查浏览器设置。


上诉几步调用完成之后,强制调用gc,来触发jvm进行垃圾回收操作。gc调用后删除旧的安装信息,如果初始传入的IPackageInstallObserver2不为空,这回调调用方安装包安装完成。


总结

到此大致分析了整个安装过程,还有很多细节可以分析,比如parsePackage,之后可以在进行解析,整篇文字可能有理解错误的地方,望指出。


网易云免费体验馆,0成本体验20+款云产品! 

更多网易研发、产品、运营经验分享请访问网易云社区