Android 应用安装流程初探

启动安装

在app代码中,如果想安装另一个应用,一般通过下列代码实现,调用后会显示系统安装界面

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType("the apkPath of app...", 
    "application/vnd.android.package-archive");
startActivity(intent);

系统安装界面是com.android.packageinstaller.PackageInstallerActivity,它在manifest里的声明是

".PackageInstallerActivity"
        android:configChanges="orientation|keyboardHidden|screenSize"
        android:excludeFromRecents="true">
    "1">
        "android.intent.action.VIEW" />
        "android.intent.action.INSTALL_PACKAGE" />
        "android.intent.category.DEFAULT" />
        "file" />
        "application/vnd.android.package-archive" />
    
    "1">
        "android.intent.action.INSTALL_PACKAGE" />
        "android.intent.category.DEFAULT" />
        "file" />
        "package" />
    
    "1">
        "android.content.pm.action.CONFIRM_PERMISSIONS" />
        "android.intent.category.DEFAULT" />
    

可以看到这个activity支持三种action,VIEW、INSTALL_PACKAGE、CONFIRM_PERMISSIONS,其中VIEW和INSTALL_PACKAGE都可以启动应用的安装。

PackageInstallerActivity根据用户设置(比如是否允许安装未知来源的应用等)进行一些检查,同时查询应用需要的权限进行显示,在用户点击“安装”以后,启动界面InstallAppProgress进行安装。这里整个流程比较简单,不再贴代码进行分析了,不过其中获取应用信息、获取权限信息的调用可以学习一下。

在流程的最后,调用了PMS.installPackageWithVerificationAndEncryption()正式进入安装流程

PMS安装流程

通过context.getPackageManager得到的PackageManager实际上是ApplicationPackageManager的实例,所以调用到的就是

/**
* packageURI -- 要安装的apk的uri
* obesrver -- 安装过程的观察者
* flags -- 这个流程里,表示是否覆盖安装,当然其他流程中会有其他值
* installerPackageName -- 安装者的包名
* verificationParams -- 一些校验参数,后续流程中看它的具体使用
* encryptionParams -- 一些加密参数,这个流程里是null
**/
@Override
public void installPackageWithVerificationAndEncryption(Uri packageURI,
        IPackageInstallObserver observer, int flags, String installerPackageName,
        VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
    installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
            installerPackageName, verificationParams, encryptionParams);
}

installCommon在校验参数之后调用了PMS.installPackage,然后调用到PMS.installPackageAsUser

@Override
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
        int installFlags, String installerPackageName, VerificationParams verificationParams,
        String packageAbiOverride, int userId) {
    // 主要做几个事情,1、检查安装者权限;2、更新flag;3、创建UserHandle实例;4、创建OriginInfo
    // ...
    // 这里的Handler是内部类PackageHandler
    final Message msg = mHandler.obtainMessage(INIT_COPY);
    msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
            null, verificationParams, user, packageAbiOverride, null);
    mHandler.sendMessage(msg);
}

PackageHandler的message处理方法如下,这里先仅列出两个事件的处理

void doHandleMessage(Message msg) {
   switch (msg.what) {
       case INIT_COPY: {
           HandlerParams params = (HandlerParams) msg.obj;
           int idx = mPendingInstalls.size();
           if (!mBound) {
               if (!connectToService()) {
                   params.serviceError();
                   return;
               } else {
                   mPendingInstalls.add(idx, params);
               }
           } else {
               mPendingInstalls.add(idx, params);
               if (idx == 0) {
                   mHandler.sendEmptyMessage(MCS_BOUND);
               }
           }
           break;
       }
       case MCS_BOUND: {
           if (msg.obj != null) {
               mContainerService = (IMediaContainerService) msg.obj;
           }
           if (mContainerService == null) {
               if (!mBound) {
                    // 异常情况处理
               }
           } else if (mPendingInstalls.size() > 0) {
               HandlerParams params = mPendingInstalls.get(0);
               if (params != null) {
                   if (params.startCopy()) {
                       if (mPendingInstalls.size() > 0) {
                           mPendingInstalls.remove(0);
                       }
                       if (mPendingInstalls.size() == 0) {
                           if (mBound) {

                               removeMessages(MCS_UNBIND);
                               Message ubmsg = obtainMessage(MCS_UNBIND);
                               sendMessageDelayed(ubmsg, 10000);
                           }
                       } else {
                           mHandler.sendEmptyMessage(MCS_BOUND);
                       }
                   }
               }
           } else {
                // 异常情况,打印log
           }
           break;
       }
   }
}

仔细看一下,安装过程是需要调用IMediaContainerService mContainerService的,在第一次调用到INIT_COPY的时候还没有bindService,所以mBound是false,mContainerService的是空,所以要先调用connectToService,然后把安装参数add到pending队列,在bindService成功后,会发送MCS_BOUND事件,mContainerService会被赋值为DefaultContainerService.IMediaContainerService.Stub实例,然后开始处理pending队列。

真正的处理从InstallParams.startCopy()开始,依次调用handleStartCopy()和handleReturnCode()

final boolean startCopy() {
    boolean res;
    try {
        if (++mRetries > MAX_RETRIES) {
            // 超过重试次数
            return false;
        } else {
            handleStartCopy();
            res = true;
        }
    } catch (RemoteException e) {
        // ...
    }
    handleReturnCode();
    return res;
}
public void handleStartCopy() throws RemoteException {
    int ret = PackageManager.INSTALL_SUCCEEDED;

    // If we're already staged, we've firmly committed to an install location
    if (origin.staged) {
        // 第一次是false,这里更新installFlags
    }

    final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
    final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;

    PackageInfoLite pkgLite = null;

    if (onInt && onSd) {
        // 异常情况
    } else {
        pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
                packageAbiOverride);
        if (!origin.staged && pkgLite.recommendedInstallLocation
                == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
            // 没有足够的安装空间
        }
    }

    if (ret == PackageManager.INSTALL_SUCCEEDED) {
        // 前面的流程没有异常就会走到这里
        int loc = pkgLite.recommendedInstallLocation;
        if (loc == ***) {
            // 各种异常的流程的处理,对ret赋值
        } else {
            // 再次对安装位置进行调整
            loc = installLocationPolicy(pkgLite);
            if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
                ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
            } else if (!onSd && !onInt) {
                // 调整installFlags
            }
        }
    }
    // 根据安装位置创建InstallArgs实例,这里以FileInstallArgs为例走流程
    final InstallArgs args = createInstallArgs(this);
    mArgs = args;

    if (ret == PackageManager.INSTALL_SUCCEEDED) {
         /*
         * ADB installs appear as UserHandle.USER_ALL, and can only be performed by
         * UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER.
         */
        int userIdentifier = getUser().getIdentifier();
        if (userIdentifier == UserHandle.USER_ALL
                && ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0)) {
            userIdentifier = UserHandle.USER_OWNER;
        }

        /*
         * Determine if we have any installed package verifiers. If we
         * do, then we'll defer to them to verify the packages.
         */
        final int requiredUid = mRequiredVerifierPackage == null ? -1
                : getPackageUid(mRequiredVerifierPackage, userIdentifier);
        if (!origin.existing && requiredUid != -1
                && isVerificationEnabled(userIdentifier, installFlags)) {
            // 如果需要验证应用(android的安全机制)
        } else {
            ret = args.copyApk(mContainerService, true);
        }
    }

    mRet = ret;
}

以FileInstallArgs为例,看一下copyApk的实现

int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
    if (origin.staged) {
        // 已经stage,直接返回
    }

    try {
        // 创建/data/app下的安装路径,同时修改为755权限
        final File tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid);
        codeFile = tempDir;
        resourceFile = tempDir;
    } catch (IOException e) {
        return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
    }

    final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
        @Override
        public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
            // ...
        }
    };

    int ret = PackageManager.INSTALL_SUCCEEDED;
    ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
    if (ret != PackageManager.INSTALL_SUCCEEDED) {
        return ret;
    }

    final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
    NativeLibraryHelper.Handle handle = null;
    try {
        handle = NativeLibraryHelper.Handle.create(codeFile);
        ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
                abiOverride);
    } catch (IOException e) {
        ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
    } finally {
        IoUtils.closeQuietly(handle);
    }

    return ret;
}

实现的逻辑比较清晰,执行copyApk的结果就是生成了以下文件

..data

….app

……com.fr.demo

……..base.apk

……..lib

……….x86 –
因为是用模拟器运行,所以是x86

…………libxxx.so

然后执行handleReturnCode方法,该方法直接调用processPendingInstall,所以直接看一下processPendingInstall的实现,如下列代码中的注释,一共5步

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() {
            // ...
            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                // 1. preInsall
                args.doPreInstall(res.returnCode);
                synchronized (mInstallLock) {
                    // 2. install
                    installPackageLI(args, res);
                }
                // 3. postInstall
                args.doPostInstall(res.returnCode, res.uid);
            }
            // 4. do restore or not
            // ...
            // 5. handler, handle(POST_INSTALL)
            Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
            mHandler.sendMessage(msg);

        }
    });
}

还是以FileInstallArgs为例,分别看一下上述5步的实现

preInstall

正常流程下,可以看到,什么都不做直接返回

int doPreInstall(int status) {
    if (status != PackageManager.INSTALL_SUCCEEDED) {
        cleanUp();
    }
    return status;
}
````




"se-preview-section-delimiter">
#### installPackage 首先调用的是installPackageLI方法,这个方法有很多行,主要做的是收集包信息,检查是否覆盖安装、manifest签名是否正确等,最后的安装调用的是installNewPackageLI方法,如果是覆盖安装就调用replacePackageLI,这里看一下新安装的情况
"se-preview-section-delimiter">
```java private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user, String installerPackageName, String volumeUuid, PackageInstalledInfo res) { // 可能存在的重复包的检查 // ... try { PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags, System.currentTimeMillis(), user); updateSettingsLI(newPackage, installerPackageName, volumeUuid, null, null, res, user); // ... } catch (PackageManagerException e) { } }

一共会调用两个方法

scanPackageLI会调用,这个方法实在太长,下面的代码中做了大量精简,主要是把处理系统app和覆盖安装的部分删掉了,即使这样,还是有很多细节看不太明白

private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
        int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
    final File scanFile = new File(pkg.codePath);


    pkg.coreApp = false;

    if ((parseFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {
        pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
    }

    if (mCustomResolverComponentName != null &&
            mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {
        setUpCustomResolverActivity(pkg);
    }

    // Initialize package source and resource directories
    File destCodeFile = new File(pkg.applicationInfo.getCodePath());
    File destResourceFile = new File(pkg.applicationInfo.getResourcePath());

    SharedUserSetting suid = null;
    PackageSetting pkgSetting = null;

    if (!isSystemApp(pkg)) {
        // Only system apps can use these features.
        pkg.mOriginalPackages = null;
        pkg.mRealPackage = null;
        pkg.mAdoptPermissions = null;
    }

    // writer
    synchronized (mPackages) {
        // Check if we are renaming from an original package name.
        PackageSetting origPackage = null;
        String realName = null;


        // 1、创建PackageSetting,PackageSetting保存了包从一些信息
        pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
                destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,
                pkg.applicationInfo.primaryCpuAbi,
                pkg.applicationInfo.secondaryCpuAbi,
                pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
                user, false);



        if (realName != null) {
            // Make a note of it.
            mTransferedPackages.add(pkg.packageName);
        }


        if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
            // Check all shared libraries and map to their actual file path.
            // We only do this here for apps not on a system dir, because those
            // are the only ones that can fail an install due to this.  We
            // will take care of the system apps by updating all of their
            // library paths after the scan is done.
            updateSharedLibrariesLPw(pkg, null);
        }

        if (mFoundPolicyFile) {
            SELinuxMMAC.assignSeinfoValue(pkg);
        }

        pkg.applicationInfo.uid = pkgSetting.appId;
        pkg.mExtras = pkgSetting;
        if (shouldCheckUpgradeKeySetLP(pkgSetting, scanFlags)) {
            // ...
        } else {
            try {
                verifySignaturesLP(pkgSetting, pkg);
                // We just determined the app is signed correctly, so bring
                // over the latest parsed certs.
                pkgSetting.signatures.mSignatures = pkg.mSignatures;
            } catch (PackageManagerException e) {
                // ...
            }
        }
        // Verify that this new package doesn't have any content providers
        // that conflict with existing packages.  Only do this if the
        // package isn't already installed, since we don't want to break
        // things that are installed.
        // 2、检查将要安装的apk的content provider是否跟已有的冲突
        if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
            final int N = pkg.providers.size();
            int i;
            for (i=0; iif (p.info.authority != null) {
                    String names[] = p.info.authority.split(";");
                    for (int j = 0; j < names.length; j++) {
                        if (mProvidersByAuthority.containsKey(names[j])) {
                            throw new PackageManagerException("....");
                        }
                    }
                }
            }
        }

        // 权限管理?
        if (pkg.mAdoptPermissions != null) {
            // This package wants to adopt ownership of permissions from
            // another package.
            for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) {
                // ...
            }
        }
    }

    final String pkgName = pkg.packageName;

    final long scanFileTime = scanFile.lastModified();
    final boolean forceDex = (scanFlags & SCAN_FORCE_DEX) != 0;
    pkg.applicationInfo.processName = fixProcessName(
            pkg.applicationInfo.packageName,
            pkg.applicationInfo.processName,
            pkg.applicationInfo.uid);

    File dataPath;
    if (mPlatformPackage == pkg) {
        // The system package is special.
        // ...
    } else {
        // This is a normal package, need to make its data directory.
        dataPath = Environment.getDataUserPackageDirectory(pkg.volumeUuid,
                UserHandle.USER_OWNER, pkg.packageName);

        boolean uidError = false;
        if (dataPath.exists()) {
            // 如果已经存在,表示跟当前安装的包跟以前的包存在相同的data路径,做异常处理
        } else {
            // 3、正常流程,创建data目录
            int ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
                    pkg.applicationInfo.seinfo);
            if (dataPath.exists()) {
                pkg.applicationInfo.dataDir = dataPath.getPath();
            } else {
                pkg.applicationInfo.dataDir = null;
            }
        }

        pkgSetting.uidError = uidError;
    }

    final String path = scanFile.getPath();
    final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);

    // 4、设置native lib path
    if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
        // 不是新装包

    } else {
        if ((scanFlags & SCAN_MOVE) != 0) {
            // We haven't run dex-opt for this move (since we've moved the compiled output too)
            // but we already have this packages package info in the PackageSetting. We just
            // use that and derive the native library path based on the new codepath.
            pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString;
            pkg.applicationInfo.secondaryCpuAbi = pkgSetting.secondaryCpuAbiString;
        }

        // Set native library paths again. For moves, the path will be updated based on the
        // ABIs we've determined above. For non-moves, the path will be updated based on the
        // ABIs we determined during compilation, but the path will depend on the final
        // package path (after the rename away from the stage path).
        setNativeLibraryPaths(pkg);
    }

    final int[] userIds = sUserManager.getUserIds();
    synchronized (mInstallLock) {
        // Make sure all user data directories are ready to roll; we're okay
        // if they already exist
        if (!TextUtils.isEmpty(pkg.volumeUuid)) {
            for (int userId : userIds) {
                if (userId != 0) {
                    mInstaller.createUserData(pkg.volumeUuid, pkg.packageName,
                            UserHandle.getUid(userId, pkg.applicationInfo.uid), userId,
                            pkg.applicationInfo.seinfo);
                }
            }
        }

        // Create a native library symlink only if we have native libraries
        // and if the native libraries are 32 bit libraries. We do not provide
        // this symlink for 64 bit libraries.
        if (pkg.applicationInfo.primaryCpuAbi != null &&
                !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
            // 处理32位lib
        }
    }

    // This is a special case for the "system" package, where the ABI is
    // dictated by the zygote configuration (and init.rc). We should keep track
    // of this ABI so that we can deal with "normal" applications that run under
    // the same UID correctly.
    if (mPlatformPackage == pkg) {
        // system app
    }

    // If there's a mismatch between the abi-override in the package setting
    // and the abiOverride specified for the install. Warn about this because we
    // would've already compiled the app without taking the package setting into
    // account.
    if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
        if (cpuAbiOverride == null && pkgSetting.cpuAbiOverrideString != null) {
            Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
                    " for package: " + pkg.packageName);
        }
    }

    pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
    pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
    pkgSetting.cpuAbiOverrideString = cpuAbiOverride;

    // Copy the derived override back to the parsed package, so that we can
    // update the package settings accordingly.
    pkg.cpuAbiOverride = cpuAbiOverride;

    // Push the derived path down into PackageSettings so we know what to
    // clean up at uninstall time.
    pkgSetting.legacyNativeLibraryPathString = pkg.applicationInfo.nativeLibraryRootDir;

    if ((scanFlags&SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
        // We don't do this here during boot because we can do it all
        // at once after scanning all existing packages.
        //
        // We also do this *before* we perform dexopt on this package, so that
        // we can avoid redundant dexopts, and also to make sure we've got the
        // code and package path correct.
        adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages,
                pkg, forceDex, (scanFlags & SCAN_DEFER_DEX) != 0);
    }

    // 5、做dexOpt
    if ((scanFlags & SCAN_NO_DEX) == 0) {
        int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instruction sets */,
                forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, false /* inclDependencies */);
        if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
            throw new PackageManagerException(INSTALL_FAILED_DEXOPT, "scanPackageLI");
        }
    }
    if (mFactoryTest && pkg.requestedPermissions.contains(
            android.Manifest.permission.FACTORY_TEST)) {
        // ...
    }

    ArrayList clientLibPkgs = null;

    // writer
    synchronized (mPackages) {
        if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
            // 只有system app会走到这里
        }
    }

    // We also need to dexopt any apps that are dependent on this library.  Note that
    // if these fail, we should abort the install since installing the library will
    // result in some apps being broken.
    if (clientLibPkgs != null) {
        // 只有system app会走到这里
    }

    if ((scanFlags & SCAN_REPLACING) != 0) {
        // 覆盖安装
    }

    // Also need to kill any apps that are dependent on the library.
    if (clientLibPkgs != null) {
        // 只有system app会走到这里
    }

    // Make sure we're not adding any bogus keyset info
    KeySetManagerService ksms = mSettings.mKeySetManagerService;
    ksms.assertScannedPackageValid(pkg);

    // writer
    synchronized (mPackages) {
        // We don't expect installation to fail beyond this point

        // Add the new setting to mSettings
        mSettings.insertPackageSettingLPw(pkgSetting, pkg);
        // Add the new setting to mPackages
        mPackages.put(pkg.applicationInfo.packageName, pkg);
        // Make sure we don't accidentally delete its data.
        final Iterator iter = mSettings.mPackagesToBeCleaned.iterator();
        while (iter.hasNext()) {
            PackageCleanItem item = iter.next();
            if (pkgName.equals(item.packageName)) {
                iter.remove();
            }
        }

        // 更新pkgSetting的firstInstallTime、lastUpdateTime
        // ...

        // Add the package's KeySets to the global KeySetManagerService
        ksms.addScannedPackageLPw(pkg);

        // 处理应用中的provider,把provider信息存储在mProviders和mProvidersByAuthority
        int N = pkg.providers.size();
        StringBuilder r = null;
        int i;
        for (i=0; i// ...
        }

        // 处理应用中的service,把provider信息存储在mServices
        N = pkg.services.size();
        r = null;
        for (i=0; i// ...
        }

        // 处理应用中的receiver,把provider信息存储在mReceivers
        N = pkg.receivers.size();
        r = null;
        for (i=0; i// ...
        }

        // 处理应用中的activity,把provider信息存储在mActivities
        N = pkg.activities.size();
        r = null;
        for (i=0; i// ...
        }

        // 处理应用中的permissson,把provider信息存储在mPermissionGroups
        N = pkg.activities.size();
        N = pkg.permissionGroups.size();
        r = null;
        for (i=0; i// ...
        }

        // 处理应用中的permissson,把provider信息存储在mPermissionGroups
        N = pkg.activities.size();
        N = pkg.permissions.size();
        r = null;
        for (i=0; i// 处理Instrumentation,注意这里有各种路径的赋值,包括nativeLibraryDir
        N = pkg.instrumentation.size();
        r = null;
        for (i=0; i// TODO: Update instrumentation.nativeLibraryDir as well ? Does it
            // need other information about the application, like the ABI and what not ?
            a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;
            mInstrumentation.put(a.getComponentName(), a);
        }

        if (pkg.protectedBroadcasts != null) {
            N = pkg.protectedBroadcasts.size();
            for (i=0; i// Create idmap files for pairs of (packages, overlay packages).
        // Note: "android", ie framework-res.apk, is handled by native layers.
        if (pkg.mOverlayTarget != null) {
            // This is an overlay package.
            if (pkg.mOverlayTarget != null && !pkg.mOverlayTarget.equals("android")) {
                if (!mOverlays.containsKey(pkg.mOverlayTarget)) {
                    mOverlays.put(pkg.mOverlayTarget,
                            new ArrayMap());
                }
                ArrayMap map = mOverlays.get(pkg.mOverlayTarget);
                map.put(pkg.packageName, pkg);
                PackageParser.Package orig = mPackages.get(pkg.mOverlayTarget);
                if (orig != null && !createIdmapForPackagePairLI(orig, pkg)) {
                    throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                            "scanPackageLI failed to createIdmap");
                }
            }
        } else if (mOverlays.containsKey(pkg.packageName) &&
                !pkg.packageName.equals("android")) {
            // This is a regular package, with one or more known overlay packages.
            createIdmapsForPackageLI(pkg);
        }
    }

    return pkg;
}
  1. 创建PackageSetting,PackageSetting保存了包从一些信息
  2. 检查将要安装的apk的content provider是否跟已有的冲突
  3. 正常流程,创建data目录
  4. 设置native lib path
  5. 做dexOpt
  6. 扫描apk里provider/service/receiver/activity/permission/Instrumentation等各种信息

上面是总结出来的个人认为比较主要的流程

之后,更新PackageSetting

private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
        String volumeUuid, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res,
        UserHandle user) {
    String pkgName = newPackage.packageName;
    synchronized (mPackages) {
        // 1. 状态设置为未完成
        mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE);
        mSettings.writeLPr();
    }

    synchronized (mPackages) {
        // 2. 更新权限信息,同时申请必要的权限(6.0以下的所有权限和6.0以上的基本权限)
        updatePermissionsLPw(newPackage.packageName, newPackage,
                UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
                        ? UPDATE_PERMISSIONS_ALL : 0));

        // 3. 更新状态
        PackageSetting ps = mSettings.mPackages.get(pkgName);
        if (ps != null) {
            if (isSystemApp(newPackage)) {
                // ...
            }
            // It's implied that when a user requests installation, they want the app to be
            // installed and enabled.
            int userId = user.getIdentifier();
            if (userId != UserHandle.USER_ALL) {
                ps.setInstalled(true, userId);
                ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
            }
        }
        res.name = pkgName;
        res.uid = newPackage.applicationInfo.uid;
        res.pkg = newPackage;
        mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
        mSettings.setInstallerPackageName(pkgName, installerPackageName);
        res.returnCode = PackageManager.INSTALL_SUCCEEDED;
        //to update install status
        mSettings.writeLPr();
    }
}

postInstall

int doPostInstall(int status, int uid) {
    if (status != PackageManager.INSTALL_SUCCEEDED) {
        cleanUp();
    }
    return status;
}

restore

调用BackupManager进行restore,即应用的备份,为以后的恢复做准备

POST_INSTALL

根据需要处理动态权限申请,之后发送广播,最后通知观察者,也就是之前的安装界面

整个安装流程上看比较清晰,不过细节很多,还是有很多地方不太明白。其中包的解析,权限的处理,provider等的处理可以仔细再看一看。dexopt也可以看一下。

POST_INSTALL

根据需要处理动态权限申请,之后发送广播,最后通知观察者,也就是之前的安装界面

整个安装流程上看比较清晰,不过细节很多,还是有很多地方不太明白。其中包的解析,权限的处理,provider等的处理可以仔细再看一看。dexopt也可以看一下。

最后,附一下odex的内容

  1. odex的文件格式解析,一篇不错的博客
  2. dex文件做的优化,android中有专门的文档说明,源码目录 /dalvik/docs/dexopt.hml,其中关于优化部分的介绍

The Dalvik optimizer does the following:

  • For virtual method calls, replace the method index with a vtable index.
  • 虚方法索引用虚表索引代替
  • For instance field get/put, replace the field index with a byte offset. Also, merge the boolean / byte / char / short variants into a single 32-bit form (less code in the interpreter means more room in the CPU I-cache).
  • field的get/put方法用byte偏移来寻址?boolean / byte / char / short都用32-bit存储
  • Replace a handful of high-volume calls, like String.length(), with “inline” replacements. This skips the usual method call overhead, directly switching from the interpreter to a native implementation.
  • 频率高的调用用内联方法替换
  • Prune empty methods. The simplest example is Object., which does nothing, but must be called whenever any object is allocated. The instruction is replaced with a new version that acts as a no-op unless a debugger is attached.
  • 裁剪掉空方法
  • Append pre-computed data. For example, the VM wants to have a hash table for lookups on class name. Instead of computing this when the DEX file is loaded, we can compute it now, saving heap space and computation time in every VM where the DEX is loaded.
  • 这里说的应该是DexClassLookup,可以参考Dalvik虚拟机中DexClassLookup结构解析

你可能感兴趣的:(android,package,manager)