Apk安装之——PMS安装apk过程分析

上篇文章Apk安装之——点击安装确认页面的确认按钮,到将安装过程交给PMS的过程分析分析到apk安装的流程,交给了PMS,这篇文章继续分析,PMS是如何安装apk包的。首先调用PMS的installStage进行安装。下面看看这个方法:


http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

void installStage(String packageName, File stagedDir,
        IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
        String installerPackageName, int installerUid, UserHandle user,
        PackageParser.SigningDetails signingDetails) {
    ...
    final VerificationInfo verificationInfo = new VerificationInfo(
            sessionParams.originatingUri, sessionParams.referrerUri,
            sessionParams.originatingUid, installerUid);

    final OriginInfo origin = OriginInfo.fromStagedFile(stagedDir);
    //关键代码 msg.what是INIT_COPY的消息
    final Message msg = mHandler.obtainMessage(INIT_COPY);
    final int installReason = fixUpInstallReason(installerPackageName, installerUid,
            sessionParams.installReason);
    final InstallParams params = new InstallParams(origin, null, observer,
            sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
            verificationInfo, user, sessionParams.abiOverride,
            sessionParams.grantedRuntimePermissions, signingDetails, installReason);
    params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
    //注意,这里的params是一个InstallParams类型的对象,赋值给了msg.obj
    msg.obj = params;

    ...
    //关键代码 发送一个msg.what是INIT_COPY的消息
    mHandler.sendMessage(msg);
}

这个方法内部,会发送一个msg.what是INIT_COPY的消息,下面看PackageHandler对这个消息的处理

http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
class PackageHandler extends Handler {
	...

	private boolean connectToService() {
            if (DEBUG_INSTALL) Log.i(TAG, "Trying to bind to DefaultContainerService");
            Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
            Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
            if (mContext.bindServiceAsUser(service, mDefContainerConn,
                    Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                mBound = true;
                return true;
            }
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            return false;
        }
	...

	void doHandleMessage(Message msg) {
            switch (msg.what) {
                case INIT_COPY: {
                    HandlerParams params = (HandlerParams) msg.obj;
                    int idx = mPendingInstalls.size();
		    //mBound默认是false
                    if (!mBound) {
                        //关键代码1  发送连接DefaultContainerService的请求,这是system_server进程和com.android.defcontainer进程进行通信,
                        if (!connectToService()) {
                            params.serviceError();
                            ...
			    //如果绑定DefaultContainerService失败,则return
                            return;
                        } else {
                            //关键代码2    如果连接上DefaultContainerService,则将这个安装请求保存到这个mPendingInstalls这个List集合中
                            mPendingInstalls.add(idx, params);
                        }
                    } else {
		        //如果已经绑定了DefaultContainerService,则将这个安装请求保存到这个mPendingInstalls这个List集合中
                        mPendingInstalls.add(idx, params);
                        //如果是第一个安装请求,则发送绑定MCS_BOUND消息,在未给mPendingInstalls添加安装请求前,上面mPendingInstalls.size()的值是0,
			//当添加按章请求后,这是mPendingInstalls.size()的值是1,所以,这里idx==0,就是给第一个安装请求发送MCS_BOUND消息
                        if (idx == 0) {
			    //关键代码3
                            mHandler.sendEmptyMessage(MCS_BOUND);
                        }
                    }
                    break;
		    ...
		
	}
 }


 final private DefaultContainerConnection mDefContainerConn =
            new DefaultContainerConnection();
    class DefaultContainerConnection implements ServiceConnection {
        public void onServiceConnected(ComponentName name, IBinder service) {
            if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");
            final IMediaContainerService imcs = IMediaContainerService.Stub
                    .asInterface(Binder.allowBlocking(service));
            //关键代码 这里发送msg.what为MCS_BOUND的消息是,msg.obj的值是imcs,其实就是DefaultContainerService在system_server的代理对象
            mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
        }

        public void onServiceDisconnected(ComponentName name) {
            if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
        }
 }

在INIT_COPY这个case中,如果本次安装时,没有其他安装请求,则mBound的值是false,这会执行到关键代码1处,调用connectToService()方法进行绑定DefaultContainerService的连接操作,这是个进程间通信的过程,是system_server进程和com.android.defcontainer进程进行通信。如果连接上DefaultContainerService,则将安装请求添加进mPendingInstalls这个集合中,如果连接失败,则return。如果本次安装时,已经有其他安装请求绑定DefaultContainerService服务了,则将这次安装请求添加进mPendingInstalls,并通过handler发送MCS_BOUND消息。注意发送MCS_BOUND消息时,msg.obj被赋值了,这个值是DefaultContainerService在system_server进程中的代理对象.下面看看handler对MCS_BOUND消息的处理:

http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
 class PackageHandler extends Handler {
	void doHandleMessage(Message msg) {
            switch (msg.what) {
		...
		case MCS_BOUND: {
                    
                    if (msg.obj != null) {  //关键代码1 如果msg.obj不为null,则给mContainerService赋值
                        mContainerService = (IMediaContainerService) msg.obj;
                        ...
                    }
		    
                    if (mContainerService == null) {  //关键代码2  如果mContainerService为null
		        
                        if (!mBound) {  //关键代码3  如果是连接上DefaultContainerService,mBound的值会true,如果这里的mBound的值为false,则安装过程出错了,
    
                            for (HandlerParams params : mPendingInstalls) {
                                // Indicate service bind error
                                params.serviceError();
                                ...
                            }
			    //安装过程出错,清除这个mPendingInstalls集合
                            mPendingInstalls.clear();
                        } else {
			   
                            Slog.w(TAG, "Waiting to connect to media container service");
                        }
                    } else if (mPendingInstalls.size() > 0) {  //关键代码5 需要安装的请求的个数大于0
                        HandlerParams params = mPendingInstalls.get(0);
                        if (params != null) {
                            ...
                            if (params.startCopy()) {  //关键代码6 开始复制
                                ...
                                if (mPendingInstalls.size() > 0) { //复制成功,则将本次安装请求重mPendingInstalls集合中移除
                                    mPendingInstalls.remove(0);
                                }
                                if (mPendingInstalls.size() == 0) { // 如果安装请求的个数为0
                                    if (mBound) {
                                        //如果需要安装的apk的个数为0,并且是绑定状态,则发送消息和ContainerService解绑
                                        removeMessages(MCS_UNBIND);
                                        Message ubmsg = obtainMessage(MCS_UNBIND);
                                        sendMessageDelayed(ubmsg, 10000);
                                    }
                                } else {
                                    // 接着处理下一个绑定ContainerService的请求
                                    mHandler.sendEmptyMessage(MCS_BOUND);
                                }
                            }
                            ...
                        }
                    } else {
                        // Should never happen ideally.
                        Slog.w(TAG, "Empty queue");
                    }
                    break;
		    ...
                }
	}
 }

handler中处理MCS_BOUND消息的逻辑是,由于发送MCS_BOUND时msg.obj赋值过,导致关键代码1处成立,则执行关键代码5处的逻辑,这是就会执行关键代码6处的逻辑,在发送Init_copy消息时,msg.obj是InstallParams类型的对象,所以,这里mPendingInstalls中取出的就是InstallParams类型的对象,调用InstallParams类的startCopy方法进行复制工作。当复制成功后,会则将本次安装请求重mPendingInstalls集合中移除,如果需要安装的apk的个数为0,并且是绑定状态,则发送消息和ContainerService解绑,如果不为0,则继续处理下一个安装请求。这个方法中,还有另外的一个逻辑,这里也顺便阐述一下,如果关键代码1出的逻辑成立则执行关键代码2处的逻辑,关键代码2中逻辑是,如果当前状态是为绑定,则肯定是出错了,因为connectToServcie方法中如果连接成功,这个mBound这个值就复制为true了这里这个值如果是false,则if(!mBound)这个判断条件成立,这样安装过程肯定出错了,则将mPendingInstalls集合清除。正常情况下,这个mBound的值应该是true,所以不做处理。表示已经连接上了。下面接着分析,InstallParams类的startCopy方法是如何进行复制的:


http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
final boolean startCopy() {
    boolean res;
    try {
        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 {
	    // 关键代码1
            handleStartCopy();
            res = true;
        }
    } catch (RemoteException e) {
        if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
        mHandler.sendEmptyMessage(MCS_RECONNECT);
        res = false;
    }
    //关键代码2
    handleReturnCode();
    return res;
}

InstallParams是继承自HandlerParams的startCopy()这个方法是HandlerParams的方法,这个方法内部,会判断安装重试的次数是否大于4次(MAX_RETRIES是个常量,值是4),如果大于4次,则发送放弃安装。如果不大于4次,则执行handleStartCopy()方法,下面看看这个方法的具体实现:

http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

public void handleStartCopy() throws RemoteException {
    int ret = PackageManager.INSTALL_SUCCEEDED;

    // If we're already staged, we've firmly committed to an install location
    //如果是一个新的app,则staged为false
    if (origin.staged) {
        if (origin.file != null) {
            installFlags |= PackageManager.INSTALL_INTERNAL;
            installFlags &= ~PackageManager.INSTALL_EXTERNAL;
        } else {
            throw new IllegalStateException("Invalid stage location");
        }
    }
    // 确定APK的安装位置,onSd表示安装到SD卡上,
    // onInt表示安装到内部存储,即Data分区
    // ephemeral:安装到临时存储(Instant Apps安装)
    final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
    final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
    final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
    PackageInfoLite pkgLite = null;

    if (onInt && onSd) {  
        // 不能同时安装到sd卡和内部存储位置
        Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
        ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
    } else if (onSd && ephemeral) {
        //flags冲突,不能在sd卡安装Instant app
        Slog.w(TAG,  "Conflicting flags specified for installing ephemeral on external");
        ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
    } else {
        //获取apk的轻量信息
        pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
                packageAbiOverride);
        if (!origin.staged && pkgLite.recommendedInstallLocation
                == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
            ... 
	    //省略这个判断内部的代码,这块的主要逻辑是判断,如果存储空间不足,则需要释放Cache的一些空间
        }
    }

    if (ret == PackageManager.INSTALL_SUCCEEDED) {
        ...
        //省略这个判断内部的代码,这块的主要逻辑是判断安装的位置
    }
    //创建InstallArgs对象,createInstallArgs方法内部返回的是FileInstallArgs类型的对象。FileInstallArgs是InstallArgs的子类
    final InstallArgs args = createInstallArgs(this);
    mArgs = args;

    if (ret == PackageManager.INSTALL_SUCCEEDED) {
        ...
        if (!origin.existing && requiredUid != -1
                && isVerificationEnabled(
                        verifierUser.getIdentifier(), installFlags, installerUid)) {
            ...
           // 此处省略大段APK的检查逻辑。
        } else {
            //调用FileInstallArgs类的copyApk方法
            ret = args.copyApk(mContainerService, true);
        }
    }
    mRet = ret;
}

这个方法内部,根据安装方式,判断安装的位置是否冲突,在判断,安装的位置是否有足够的存储空间,如果没有,释放缓存空间,接着确定安装位置,在接着对apk进行检测,最后创建FileInstallArgs类型的对象,并调用其调用copyApk方法,copyApk方法中直接调用了doCopyApk方法,下面看看这个方法:

http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
    ...

    try {
        final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
	//创建临时文件
        final File tempDir =
                mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
        codeFile = tempDir;
        resourceFile = tempDir;
    } catch (IOException e) {
        Slog.w(TAG, "Failed to create copy file: " + e);
        return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
    }

    ...

    int ret = PackageManager.INSTALL_SUCCEEDED;
    //调用DefaultContainerService在system_server进程中的代理对象的copyPackage方法,这是进程间通信的过程,
    //跨进程调用DefaultContainerService进程中的copyPackage()函数
    ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
    if (ret != PackageManager.INSTALL_SUCCEEDED) {
        return ret;
    }
    //复制Native库文件
    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) {
        Slog.e(TAG, "Copying native libraries failed", e);
        ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
    } finally {
        IoUtils.closeQuietly(handle);
    }
    return ret;
}

FileInstallArgs类的copyApk方法中,又会调用doCopyApk方法,在doCopyApk方法中,会创建一个临时路径,这里之所以创建临时目录,命名如/data/app/vmdl29300388.tmp,其中数字部分29300388在每一次安装过程都不同,是安装时的SessionId。由于PMS监控了/data/app目录,如果该目录下有后缀名为.apk的文件生成,便会触发PMS扫描,为了避免这种情况,安装APK时,先用了临时的文件名。 并跨进程调用DefaultContainerService的copyPackage方法完成apk的复制工作,然后复制Native库。完成了apk的复制工作后,apk就在data/app目录下,不过此时还是临时文件的,后续还需要重新命名。如果复制过程为出现异常,则返回INSTALL_SUCCEEDED(1)这个整数;否则,会返回对应的错误码。到这里,分析完了PMS H a n d l e r P a r a m s 类 的 的 s t a r t C o p y ( ) 中 的 h a n d l e S t a r t C o p y 方 法 的 处 理 逻 辑 , 下 面 继 续 看 P M S HandlerParams类的的startCopy()中的handleStartCopy方法的处理逻辑,下面继续看PMS HandlerParamsstartCopy()handleStartCopyPMSHandlerParams类的的startCopy()方法的后续对复制apk的结果的处理逻辑:

http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
final boolean startCopy() {
    boolean res;
    try {
        if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);

        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 {
	    // 关键代码1
            handleStartCopy();
            res = true;
        }
    } catch (RemoteException e) {
        if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
        mHandler.sendEmptyMessage(MCS_RECONNECT);
        res = false;
    }
    //关键代码2
    handleReturnCode();
    return res;
}

在关键代码2处,就是对复制apk后的返回结果的处理,下面看看这个handleReturnCode()方法的具体实现

http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
 @Override
 void handleReturnCode() {
     if (mArgs != null) {
         processPendingInstall(mArgs, mRet);
     }
 }


private void processPendingInstall(final InstallArgs args, final int currentStatus) {

    mHandler.post(new Runnable() {
        public void run() {
            mHandler.removeCallbacks(this);
            PackageInstalledInfo res = new PackageInstalledInfo();
            res.setReturnCode(currentStatus);
            res.uid = -1;
            res.pkg = null;
            res.removedInfo = null;
            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
	        //安装前的准备工作,检查包状态,确保环境ok,如果环境不ok,那么会清理拷贝的文件
                args.doPreInstall(res.returnCode);
                synchronized (mInstallLock) {
		    //安装apk,调用installPackageTracedLI进行安装
                    installPackageTracedLI(args, res);
                }
		//安装完后的处理
                args.doPostInstall(res.returnCode, res.uid);
            }

            ...

            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
    
                IBackupManager bm = IBackupManager.Stub.asInterface(
                        ServiceManager.getService(Context.BACKUP_SERVICE));
                ...
		//这块省略的逻辑,是对apk的备份过程
            }

            ...
        }
    });
}

handleReturnCode方法内部,直接调用了processPendingInstall来处理具体的安装逻辑。processPendingInstall方法中,是将安装过程放在一个线程中的,这个方法内部,主要做了三件事,第一安装前的检查,检查安装环境是否正常,如果不正常,则清理复制的apk文件,如果安装环境正常,则进行进行安装工作,安装完后,处理一些收尾的事情。installPackageTracedLI方法内部直接调用了installPackageLI方法,下面具体看看这个方法的具体实现:

http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
        ...
	PackageParser pp = new PackageParser();
	...
        final PackageParser.Package pkg;
        try {
	    //关键代码1
            pkg = pp.parsePackage(tmpPackageFile, parseFlags);
            DexMetadataHelper.validatePackageDexMetadata(pkg);
        } catch (PackageParserException e) {
            res.setError("Failed parse during installPackageLI", e);
            return;
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }

        ...

        try {
            //收集签名信息
            if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
                pkg.setSigningDetails(args.signingDetails);
            } else {
                PackageParser.collectCertificates(pkg, false /* skipVerify */);
            }
        } catch (PackageParserException e) {
            res.setError("Failed collect during installPackageLI", e);
            return;
        }
	...

        pp = null;
        String oldCodePath = null;
        boolean systemApp = false;
        synchronized (mPackages) {
            // 检查安装包是否存在
            if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                String oldName = mSettings.getRenamedPackageLPr(pkgName);
                if (pkg.mOriginalPackages != null
                        && pkg.mOriginalPackages.contains(oldName)
                        && mPackages.containsKey(oldName)) {
     
                    pkg.setPackageName(oldName);
                    pkgName = pkg.packageName;
		    //替换安装,也就是升级app
                    replace = true;
                } else if (mPackages.containsKey(pkgName)) {  
                    //替换安装,也就是升级app
                    replace = true;
                    if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);
                }
              ...
            }
            //如果Settings中已经记录了待安装的APK信息,需要验证APK的签名
            PackageSetting ps = mSettings.mPackages.get(pkgName);
            if (ps != null) {
                PackageSetting signatureCheckPs = ps;
                ...
		//省略的代码是 验证apk的签名信息
            }

            int N = pkg.permissions.size();
            for (int i = N-1; i >= 0; i--) {
                final PackageParser.Permission perm = pkg.permissions.get(i);
                final BasePermission bp =
                        (BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name);
                ...
		//省略的代码部分处理安装的apk中的权限的逻辑
            }
        }

        if (systemApp) {
            if (onExternal) {
		//系统app不能被sd卡上的app替换,return
                res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
                        "Cannot install updates to system apps on sdcard");
                return;
            } else if (instantApp) {
		//系统app不能被instant app替换
                res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
                        "Cannot update a system app with an instant app");
                return;
            }
        }

	...
        
	//将之前的临时文件名重新命名
        if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
            res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
            return;
        }

        ...

        try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
                "installPackageLI")) {
            if (replace) {
                if (pkg.applicationInfo.isStaticSharedLibrary()) {
                    PackageParser.Package existingPkg = mPackages.get(pkg.packageName);
                    if (existingPkg != null &&
                            existingPkg.getLongVersionCode() != pkg.getLongVersionCode()) {
                        res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring "
                                + "static-shared libs cannot be updated");
                        return;
                    }
                }
		//替换安装,也就是升级app
                replacePackageLIF(pkg, parseFlags, scanFlags, args.user,
                        installerPackageName, res, args.installReason);
            } else {
	        //安装一个新的app
                installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                        args.user, installerPackageName, volumeUuid, res, args.installReason);
            }
        }


        mArtManagerService.prepareAppProfiles(pkg, resolveUserIds(args.user.getIdentifier()));

    
	// 检查是否需要优化app中的dex文件,默认是不给instant app做dex优化的,只有满足下面4个条件,才做dex的优化
        //   1) it is not forward locked.
        //   2) it is not on on an external ASEC container.
        //   3) it is not an instant app or if it is then dexopt is enabled via gservices.
        //   4) it is not debuggable.
        final boolean performDexopt = (res.returnCode == PackageManager.INSTALL_SUCCEEDED)
                && !forwardLocked
                && !pkg.applicationInfo.isExternalAsec()
                && (!instantApp || Global.getInt(mContext.getContentResolver(),
                Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
                && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);

        if (performDexopt) {
            DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName,
                    REASON_INSTALL,
                    DexoptOptions.DEXOPT_BOOT_COMPLETE |
                    DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
            mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
                    null /* instructionSets */,
                    getOrCreateCompilerPackageStats(pkg),
                    mDexManager.getPackageUseInfoOrDefault(pkg.packageName),
                    dexoptOptions);
        }

        ...

        synchronized (mPackages) {
            final PackageSetting ps = mSettings.mPackages.get(pkgName);
            if (ps != null) {
	    //更新APK的所属用户
                res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
                ps.setUpdateAvailable(false /*updateAvailable*/);
            }
            ...
        }
}

这个方法内部,处理的逻辑还是很复杂的,下面列出主要的处理流程:
1.通过PackageParser的parsePackage方法对apk进行解析,具体的解析工作,很复杂,主要是解析apk文件中的manifest文件,并将manifest文件中的application标签,activity,service,broadcast,contentprovider,这四大组件,intentfileter等标签解析成对应的数据结构,比如activity标签就解析为Activity类,这里的这个Activity类不是我们平时开发时继承的Activity,这个Activity是继承Component类的,将这些解析的标签对应的数据保存到PackageParser$Package类的各个集合中。这里由于篇幅的原因,省略了这个解析过程的具体分析,但是读者一定要仔细去分析这个过程,如果做插件化开发,这个方法是很重要的,必须要了解。

2.收集apk中的签名信息和验证签名信息,如果是替换安装,则要和原来的apk的签名信息进行对比,主要是为了安全升级app

3.判断需要安装的apk的信息,是否有记录,如果有则是替换安装,如果没有,则是安装一个新的app

4.处理apk中的权限

5.将原来的临时文件重命名

6.如果是替换安装,则走具体的替换安装的逻辑,如果需要替换的是系统APP,则调用Settings$disableSystemPackageLPw来disable旧的APK;如果替换的是非系统APP,则调用deletePackageLI删除旧的APK。不管是更新系统还是非系统apk,都会先清除之前的packages信息,然后通过scanPackageTracedLI去安装apk,安装完后更新permissions和setting,最后通过writeLPr更新packages.xml.如果是一个安装一个新的app,则执行installNewPackageLIF方法,这个方法,后面会进行分析。

7.对满足条件的apk进行dex文件的优化处理

8.晚装完成后,更新apk所属的用户

下面下分析installNewPackageLIF方法:

http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

private void installNewPackageLIF(PackageParser.Package pkg, final @ParseFlags int parseFlags,
        final @ScanFlags int scanFlags, UserHandle user, String installerPackageName,
        String volumeUuid, PackageInstalledInfo res, int installReason) {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage");

    // Remember this for later, in case we need to rollback this install
    String pkgName = pkg.packageName;


    synchronized(mPackages) {
        final String renamedPackage = mSettings.getRenamedPackageLPr(pkgName);
        if (renamedPackage != null) {
            // 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 "
                    + renamedPackage);
            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 {
        //扫描apk
        PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags,
                System.currentTimeMillis(), user);
	//更新Settings中的信息
        updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);

        if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
	    //如果安装成功,则为新安装的应用准备数据
            prepareAppDataAfterInstallLIF(newPackage);

        } else {
            // 安装失败,则删除apk
            deletePackageLIF(pkgName, UserHandle.ALL, false, null,
                    PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
        }
    } catch (PackageManagerException e) {
        res.setError("Package couldn't be installed in " + pkg.codePath, e);
    }

    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}

这个方法内部,通过调用scanPackageLI()函数,这样一来APK的各种信息都会记录在PMS中;描完以后,会调用updateSettingsLI()函数来更新APK的权限,设置安装状态等。如果安装成功,则为新安装的app准备数据,如果安装失败,则将apk删除。
以上便是PMS安装apk的过程,下面对这个过程进行总结:

总结:PMS处理apk的安装过程,是从installStage方法开始的,这个方法内部,会通过handler发送一个msg.what是INIT_COPY的消息,handler内部处理这个消息,发起apk文件的复制,这个工作是在com.android.defcontainer进程中进行的,但是复制apk文件时,会先判断是否绑定了DefaultContainerService这个服务,因为只有绑定了DefaultContainerService这个服务,才能和com.android.defcontainer进程进行通信,如果未绑定DefaultContainerService这个服务,则会发起绑定这个服务的请求,绑定完成后,调用InstallParams类的startCopy方法开始复制,这个方法内部会先判断安装的重试次数是否超过了4次,如果超过了,就放弃安装,如果未超过,就开始安装,安装的过程中,根据安装方式,判断安装的位置是否冲突,在判断,安装的位置是否有足够的存储空间,如果没有,释放缓存空间,接着确定安装位置,在接着对apk进行检测。接着会创建一个临时文件路径, 然后在通过进程间通信的方式,在com.android.defcontainer进程中进行apk文件的复制工作。然后复制Native库。完成了apk的复制工作后,apk就在data/app目录下,不过此时还是临时文件的,后续还需要重新命名。如果复制过程为出现异常,则返回INSTALL_SUCCEEDED(1)这个整数;否则,会返回对应的错误码。接着处理返回的复制结果,如果复制正常,则会进入到预安装,预安装这个步骤中,主要是检测包的状态,安装环境,比如sd卡的上的apk安装,如果在安装时,sd卡被拔出了或者sd卡上的文件不存了,所以这里要再次确认下安装的环境,如果安装环境不正常,则会清除复制的apk,如果安装环境正常,则进入实质的安装阶段,这个阶段,会解析apk,并将信息保存到PackageParser$Package类的成员变量中,在接着验证apk的签名信息,处理apk中的权限,接着将临时文件重命名,判断是安装一个新的app还是升级app,然后进行相应的安装和升级,如果安装过程中,安装失败,会删除apk,安装的本质是对apk中文件manifest文件进行解析,并将保存到PackageParser.Package中的信息赋值给Settings这个对象中,完成对Settings对象中对apk信息的更新。在接着是对满足一定条件的apk中的dex文件进行优化,完成安装后,更新apk所属的用户。

参考:
包管理机制分析

Android包管理机制(三)PMS处理APK的安装

应用程序安装流程

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值