android 9 adb安装过程学习(三)

PackageManagerService

一、PackageManagerService.installStage

接下来,进入 PackageManagerService 阶段。从PackageInstallerSession.java的commitLocked调用
这里的 IPackageInstallObserver2 observer 是前面创建的本次 localObserver:
位置:./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) {if (DEBUG_INSTANT) {if ((sessionParams.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {Slog.d(TAG, "Ephemeral install of " + packageName);}}//【1.1】创建一个 VerificationInfo 对象,用于校验final VerificationInfo verificationInfo = new VerificationInfo(sessionParams.originatingUri, sessionParams.referrerUri,sessionParams.originatingUid, installerUid);//【1.2】创建了一个 OriginInfo 对象final OriginInfo origin = OriginInfo.fromStagedFile(stagedDir);//【1】创建了一个 INIT_COPY 消息final Message msg = mHandler.obtainMessage(INIT_COPY);final int installReason = fixUpInstallReason(installerPackageName, installerUid,sessionParams.installReason);//【1.3】创建一个 InstallParams 安装参数对象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));msg.obj = params;Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",System.identityHashCode(msg.obj));Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",System.identityHashCode(msg.obj));//【2】发送 INIT_COPY 消息mHandler.sendMessage(msg);}

这里的 mHandler 是在 PackageManagerService 的构造器中创建的:

	synchronized (mPackages) {mHandlerThread = new ServiceThread(TAG,Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);mHandlerThread.start();mHandler = new PackageHandler(mHandlerThread.getLooper());//...}

是一个 PackageHandler 实例,其绑定了一个子线程 ServiceThread

1.1 new VerificationInfo

VerificationInfo 类定义在 PackageManagerService 中,用于信息校验:

    static class VerificationInfo {/** A constant used to indicate that a uid value is not present. */public static final int NO_UID = -1;/** URI referencing where the package was downloaded from. */final Uri originatingUri;/** HTTP referrer URI associated with the originatingURI. */final Uri referrer;/** UID of the application that the install request originated from. */final int originatingUid;/** UID of application requesting the install */final int installerUid;	// 安装者应用的 uidVerificationInfo(Uri originatingUri, Uri referrer, int originatingUid, int installerUid) {this.originatingUri = originatingUri;this.referrer = referrer;this.originatingUid = originatingUid;this.installerUid = installerUid;}}

1.2 new OriginInfo

这里创建了一个 OriginInfo 实例,封装安装目录相关信息

    static class OriginInfo {/*** Location where install is coming from, before it has been* copied/renamed into place. This could be a single monolithic APK* file, or a cluster directory. This location may be untrusted.*/final File file;	// 安装到内置存储,不为 null/*** Flag indicating that {@link #file} or {@link #cid} has already been* staged, meaning downstream users don't need to defensively copy the* contents.*/final boolean staged;/*** Flag indicating that {@link #file} or {@link #cid} is an already* installed app that is being moved.*/final boolean existing;final String resolvedPath;final File resolvedFile;static OriginInfo fromNothing() {return new OriginInfo(null, false, false);}static OriginInfo fromUntrustedFile(File file) {return new OriginInfo(file, false, false);}static OriginInfo fromExistingFile(File file) {return new OriginInfo(file, false, true);}static OriginInfo fromStagedFile(File file) {	//【1】安装到内置存储会调用return new OriginInfo(file, true, false);}private OriginInfo(File file, boolean staged, boolean existing) {this.file = file;	// 内置时为:/data/app/vmdl[sessionId].tmpthis.staged = staged;	// 安装到内置存储时为 truethis.existing = existing;	// 安装到内置存储时为 falseif (file != null) {resolvedPath = file.getAbsolutePath();resolvedFile = file;} else {resolvedPath = null;resolvedFile = null;}}}

这里我们先关注安装到内置存储中的逻辑

1.3 new PMS.InstallParams

InstallParams 类定义在 PackageManagerService 中,封装了安装参数:

    class InstallParams extends HandlerParams {final OriginInfo origin;final MoveInfo move;	// move package 才会传入,安装时为 nullfinal IPackageInstallObserver2 observer;	// 本地传观察者int installFlags;final String installerPackageName;final String volumeUuid;private InstallArgs mArgs;private int mRet;final String packageAbiOverride;final String[] grantedRuntimePermissions;	// 安装时授予的运行时权限列表final VerificationInfo verificationInfo;final PackageParser.SigningDetails signingDetails;final int installReason;InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,int installFlags, String installerPackageName, String volumeUuid,VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,String[] grantedPermissions, PackageParser.SigningDetails signingDetails, int installReason) {super(user);this.origin = origin;this.move = move;this.observer = observer;this.installFlags = installFlags;this.installerPackageName = installerPackageName;this.volumeUuid = volumeUuid;this.verificationInfo = verificationInfo;this.packageAbiOverride = packageAbiOverride;this.grantedRuntimePermissions = grantedPermissions;this.signingDetails = signingDetails;this.installReason = installReason;}//...}

InstallParams 继承了 HandlerParams
这里涉及到一个 MoveInfo move,在 movePackageInternal 也就是移动 package 时才会调用,这里是安装,我们先不关注

二、PackageHandler.doHandleMessage[INIT_COPY]

位置:./frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
installStage最后发送了INIT_COPY消息,它会在子线程的PackageHandler 中处理 INIT_COPY 消息:

				case INIT_COPY: {//【1】取出 InstallParamsHandlerParams params = (HandlerParams) msg.obj;// mPendingInstalls 中会保存所有正在等待的安装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.//【2】mBound 用来判断是否已经绑定到了 DefaultContainerService,该服务用于安装if (!mBound) {Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",System.identityHashCode(mHandler));// If this is the only one pending we might// have to bind to the service again.//【2.1】尝试去 bind 服务,bind 成功后,mBound 置为 trueif (!connectToService()) {Slog.e(TAG, "Failed to bind to media container service");//【2.2】如果不能 bind 成功,那就触发 HandlerParams 的 serviceError 方法params.serviceError();Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",System.identityHashCode(mHandler));if (params.traceMethod != null) {Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod,params.traceCookie);}return;} else {// Once we bind to the service, the first// pending request will be processed.//【3】将本次的安装参数添加到等待集合中mPendingInstalls.add(idx, params);}} else {//【4】如果之前已经 bind 了,那就直接将安装参数添加到等待集合中mPendingInstalls.add(idx, params);// Already bound to the service. Just make// sure we trigger off processing the first request.//【3】如果是第一次添加,发送 MCS_BOUND 消息if (idx == 0) {mHandler.sendEmptyMessage(MCS_BOUND);}}break;}

这里逻辑很清晰了,我们继续看

2.1 PackageHandler.connectToService

        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);//【1】开始 bind 服务,传入了 mDefContainerConn 对象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;}

这里 bind 的服务是 DefaultContainerService

    public static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(DEFAULT_CONTAINER_PACKAGE,"com.android.defcontainer.DefaultContainerService");

PackageManagerService 内部持有一个 DefaultContainerConnection 实例

    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");//【1】获得 DefaultContainerConnection 代理对象final IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(Binder.allowBlocking(service));//【5.3】发送 MCS_BOUND 消息mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));}public void onServiceDisconnected(ComponentName name) {if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");}}

最后会发送 MCS_BOUND 消息给 PackageHandler 对象

2.2 InstallParams.serviceError

serviceError 方法是从 HandlerParams 中继承到的

        final void serviceError() {if (DEBUG_INSTALL) Slog.i(TAG, "serviceError");//【4.2】安装异常,保存结果handleServiceError();handleReturnCode();}

三、PackageHandler.doHandleMessage[MCS_BOUND] - 绑定服务

继续来看下 PackageHandler 对 MCS_BOUND 的处理

				case MCS_BOUND: {if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");//【1】这里的 msg.obj 是前面 bind 获得的代理对象,将其保存到 mContainerService 中if (msg.obj != null) {mContainerService = (IMediaContainerService) msg.obj;Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",System.identityHashCode(mHandler));}//【2】异常处理,如果 mContainerService 为 null,且 mBound 为 ture,那么这种情况是异常if (mContainerService == null) {if (!mBound) {// Something seriously wrong since we are not bound and we are not// waiting for connection. Bail out.Slog.e(TAG, "Cannot bind to media container service");//【2.1】遍历所有的 HandlerParams 安装参数,回调 serviceErrorfor (HandlerParams params : mPendingInstalls) {// Indicate service bind errorparams.serviceError();Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",System.identityHashCode(params));if (params.traceMethod != null) {Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER,params.traceMethod, params.traceCookie);}}//【2.2】清空 mPendingInstalls 集合mPendingInstalls.clear();} else {Slog.w(TAG, "Waiting to connect to media container service");}} else if (mPendingInstalls.size() > 0) {//【3】正常情况下,bind 是有效的,那么会进入这里HandlerParams params = mPendingInstalls.get(0);if (params != null) {Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",System.identityHashCode(params));Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");//【5.4】调用了 HandlerParams 的 startCopy 方法if (params.startCopy()) {// We are done...  look for more work or to// go idle.if (DEBUG_SD_INSTALL) Log.i(TAG,"Checking for more work or unbind...");// Delete pending install//【3.1】本次安装完成,删除本次处理的安装参数if (mPendingInstalls.size() > 0) {mPendingInstalls.remove(0);}if (mPendingInstalls.size() == 0) {//【3.2】如果所有的安装参数都处理玩了,unbind 服务// 这里的 unbind 延迟了 10sif (mBound) {if (DEBUG_SD_INSTALL) Log.i(TAG,"Posting delayed MCS_UNBIND");removeMessages(MCS_UNBIND);Message ubmsg = obtainMessage(MCS_UNBIND);// Unbind after a little delay, to avoid// continual thrashing.sendMessageDelayed(ubmsg, 10000);}} else {// There are more pending requests in queue.// Just post MCS_BOUND message to trigger processing// of next pending install.//【3.3】在安装队列中有其他的安装项,我们发送 MCS_BOUND 消息继续处理if (DEBUG_SD_INSTALL) Log.i(TAG,"Posting MCS_BOUND for next work");mHandler.sendEmptyMessage(MCS_BOUND);}}Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}} else {// Should never happen ideally.Slog.w(TAG, "Empty queue");}break;}

四、InstallParams.startCopy

InstallParams 继承了 HandlerParams

        final boolean startCopy() {boolean res;try {if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);//【1】如果重试次数大于 4 ,那么就放弃本次安装if (++mRetries > MAX_RETRIES) {Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");//【4.1】发送 MCS_GIVE_UP 消息mHandler.sendEmptyMessage(MCS_GIVE_UP);//【4.2】处理失败结果handleServiceError();return false;} else {//【5】继续安装handleStartCopy();res = true;}} catch (RemoteException e) {if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");//【4.3】发送 MCS_RECONNECT 消息mHandler.sendEmptyMessage(MCS_RECONNECT);res = false;}//【4.4】处理返回码handleReturnCode();return res;}

这里定义了安装尝试次数:

        private static final int MAX_RETRIES = 4;

4.1 PackageHandler.doHandleMessage[MCS_GIVE_UP] - 放弃安装

重试次数大于 4 ,那么就放弃本次安装

                case MCS_GIVE_UP: {if (DEBUG_INSTALL) Slog.i(TAG, "mcs_giveup too many retries");//【1】移除这个安装参数HandlerParams params = mPendingInstalls.remove(0);Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",System.identityHashCode(params));break;}

4.2 InstallParams.handleServiceError

处理本次安装失败的结果:

        @Overridevoid handleServiceError() {//【2.3】创建安装参数 InstallArgsmArgs = createInstallArgs(this);mRet = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;}

这里的 createInstallArgs 方法我们后面再分析

4.3 PackageHandler.doHandleMessage[MCS_RECONNECT] - 重连服务

MCS_RECONNECT 消息会尝试重连服务

                case MCS_RECONNECT: {if (DEBUG_INSTALL) Slog.i(TAG, "mcs_reconnect");if (mPendingInstalls.size() > 0) {//【1】如果 mBound 为 true,先尝试断开连接if (mBound) {disconnectService();}//【2】开始重新连接服务if (!connectToService()) {Slog.e(TAG, "Failed to bind to media container service");for (HandlerParams params : mPendingInstalls) {// Indicate service bind error//【2.2】返回安装异常params.serviceError();Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",System.identityHashCode(params));}//【3】清空 mPendingInstalls 集合mPendingInstalls.clear();}}break;}

4.4 InstallParams.handleReturnCode

当 mArgs 为 null 的时候,此时该 package 正在被校验,所以需要等到校验成功后才能安装,所以不会进入这里

        @Overridevoid handleReturnCode() {// If mArgs is null, then MCS couldn't be reached. When it// reconnects, it will try again to install. At that point, this// will succeed.//【1】如果 mArgs 这里为 null,可能是不需要校验if (mArgs != null) {processPendingInstall(mArgs, mRet);	//【7】继续安装}}

五、InstallParams.handleStartCopy

在startCopy里,如果正常就继续copy,调用handleStartCopy()

        public void handleStartCopy() throws RemoteException {//【1】保存安装结果,默认为成功int ret = PackageManager.INSTALL_SUCCEEDED;// If we're already staged, we've firmly committed to an install location//【2】我们知道 origin 中存储了安装目录相关的信息if (origin.staged) {if (origin.file != null) {//【2.1】安装到内置存储中installFlags |= PackageManager.INSTALL_INTERNAL;installFlags &= ~PackageManager.INSTALL_EXTERNAL;} else {throw new IllegalStateException("Invalid stage location");}}//【3】判断安装位置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;//【4】判断安装位置是否满足条件if (onInt && onSd) {// Check if both bits are set.	//【4.1】不能同时设置安装在 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) {Slog.w(TAG,  "Conflicting flags specified for installing ephemeral on external");//【4.2】不能设置短暂安装在 sd 中ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;} else {//【5.1】利用 ContainerService 获取 PackageInfoLite,同时判断空间是否合适pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,packageAbiOverride);if (DEBUG_INSTANT && ephemeral) {Slog.v(TAG, "pkgLite for install: " + pkgLite);}/** If we have too little free space, try to free cache* before giving up.*/	//【4.3】空间不足,尝试释放空间if (!origin.staged && pkgLite.recommendedInstallLocation== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {// TODO: focus freeing disk space on the target devicefinal StorageManager storage = StorageManager.from(mContext);final long lowThreshold = storage.getStorageLowBytes(Environment.getDataDirectory());final long sizeBytes = mContainerService.calculateInstalledSize(origin.resolvedPath, packageAbiOverride);try {//【4.4】通过 installd 释放存储mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);//【5.1】再次利用 ContainerService 获取 PackageInfoLite,同时判断空间是否合适pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,installFlags, packageAbiOverride);} catch (InstallerException e) {Slog.w(TAG, "Failed to free cache", e);}/** The cache free must have deleted the file we* downloaded to install.** TODO: fix the "freeCache" call to not delete*       the file we care about.*/	//【4.5】依然无法安装,设置结果为 RECOMMEND_FAILED_INSUFFICIENT_STORAGEif (pkgLite.recommendedInstallLocation== PackageHelper.RECOMMEND_FAILED_INVALID_URI) {pkgLite.recommendedInstallLocation= PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;}}}//【5】处理前面的空间判断后的结果if (ret == PackageManager.INSTALL_SUCCEEDED) {int loc = pkgLite.recommendedInstallLocation;if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;} else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;} else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {ret = PackageManager.INSTALL_FAILED_INVALID_APK;} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {ret = PackageManager.INSTALL_FAILED_INVALID_URI;} else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;} else {// Override with defaults if needed.//【5.2】调用 installLocationPolicy 方法,针对于降级安装和替换安装做处理loc = installLocationPolicy(pkgLite);if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;	// 处理无法降级的结果} else if (!onSd && !onInt) {// Override install location with flags// 如果 flags 没有指定内置还是外置,那么由 installLocationPolicy 的返回值指定if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {// Set the flag to install on external media.installFlags |= PackageManager.INSTALL_EXTERNAL;	// 外置installFlags &= ~PackageManager.INSTALL_INTERNAL;} else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) { 	// 内置并且是短暂安装if (DEBUG_INSTANT) {Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");}installFlags |= PackageManager.INSTALL_INSTANT_APP;	// 内置installFlags &= ~(PackageManager.INSTALL_EXTERNAL	|PackageManager.INSTALL_INTERNAL);} else {// Make sure the flag for installing on external// media is unsetinstallFlags |= PackageManager.INSTALL_INTERNAL;	// 内置并且是短暂安装installFlags &= ~PackageManager.INSTALL_EXTERNAL;}}}}//【5.3】创建一个 InstallArgs 对象,和 InstallParams 相互引用final InstallArgs args = createInstallArgs(this);mArgs = args;if (ret == PackageManager.INSTALL_SUCCEEDED) {// TODO: http://b/22976637// Apps installed for "all" users use the device owner to verify the app//【6】如果该应用是安装给 all users 的,那么需要校验应用UserHandle verifierUser = getUser();if (verifierUser == UserHandle.ALL) {verifierUser = UserHandle.SYSTEM;}/** Determine if we have any installed package verifiers. If we* do, then we'll defer to them to verify the packages.*///【7】尝试找到系统中安装的 pacakge 校验器,如果可以找到,那就要做校验啦//【7.1】获得校验器的 uid final int requiredUid = mRequiredVerifierPackage == null ? -1: getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,verifierUser.getIdentifier());//【7.1】如果是安装到内置存储(existing 为 true),并且系统中有校验器,并且系统打开了校验功能,那么就尝试校验//【5.4】通过 isVerificationEnabled 判断是否打开校验功能final int installerUid =verificationInfo == null ? -1 : verificationInfo.installerUid;if (!origin.existing && requiredUid != -1&& isVerificationEnabled(verifierUser.getIdentifier(), installFlags, installerUid)) {//【7.1.1】准备发送校验广播final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),PACKAGE_MIME_TYPE);		// 设置 DataAndType 属性,包含了 apk 路径对应的 uriverification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);	// 增加了临时的权限授予// Query all live verifiers based on current user state//【7.1.2】查询校验器final List<ResolveInfo> receivers = queryIntentReceiversInternal(verification,PACKAGE_MIME_TYPE, 0, verifierUser.getIdentifier(),false /*allowDynamicSplits*/);if (DEBUG_VERIFY) {Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent "+ verification.toString() + " with " + pkgLite.verifiers.length+ " optional verifiers");}// 计算本次校验的唯一标识 tokenfinal int verificationId = mPendingVerificationToken++;verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);// 安装器(packageInstaller)verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE,installerPackageName);// 本次安装的 installFlagsverification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS,installFlags);// 要校验的应用verification.putExtra(PackageManager.EXTRA_VERIFICATION_PACKAGE_NAME,pkgLite.packageName);// 版本号verification.putExtra(PackageManager.EXTRA_VERIFICATION_VERSION_CODE,pkgLite.versionCode);verification.putExtra(PackageManager.EXTRA_VERIFICATION_LONG_VERSION_CODE,pkgLite.getLongVersionCode());// 如果指定了校验信息,将其写入 intentif (verificationInfo != null) {if (verificationInfo.originatingUri != null) {verification.putExtra(Intent.EXTRA_ORIGINATING_URI,verificationInfo.originatingUri);}if (verificationInfo.referrer != null) {verification.putExtra(Intent.EXTRA_REFERRER,verificationInfo.referrer);}if (verificationInfo.originatingUid >= 0) {verification.putExtra(Intent.EXTRA_ORIGINATING_UID,verificationInfo.originatingUid);}if (verificationInfo.installerUid >= 0) {verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID,verificationInfo.installerUid);}}//【5.5.5】创建一个 PackageVerificationState 对象,用于保存应用校验状态,同时将创建的// installArgs 作为参数传入,并加入 mPendingVerification 集合中final PackageVerificationState verificationState = new PackageVerificationState(requiredUid, args);mPendingVerification.append(verificationId, verificationState);//【7.1.3】如果应用指定了校验者,那么我们要尝试先用指定的校验器校验final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,receivers, verificationState);DeviceIdleController.LocalService idleController = getDeviceIdleController();final long idleDuration = getVerificationTimeout();/** If any sufficient verifiers were listed in the package* manifest, attempt to ask them.*/if (sufficientVerifiers != null) {final int N = sufficientVerifiers.size();if (N == 0) {Slog.i(TAG, "Additional verifiers required, but none installed.");ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;} else {for (int i = 0; i < N; i++) {final ComponentName verifierComponent = sufficientVerifiers.get(i);// 使用前面创建的 verification 意图,再创建一个 intent!// 指定组件为应用指定的校验器,并发送广播idleController.addPowerSaveTempWhitelistApp(Process.myUid(),verifierComponent.getPackageName(), idleDuration,verifierUser.getIdentifier(), false, "package verifier");final Intent sufficientIntent = new Intent(verification);sufficientIntent.setComponent(verifierComponent);mContext.sendBroadcastAsUser(sufficientIntent, verifierUser);}}}//【7.1.4】找到和 mRequiredVerifierPackage(系统指定的默认校验器)匹配的接收者final ComponentName requiredVerifierComponent = matchComponentForVerifier(mRequiredVerifierPackage, receivers);if (ret == PackageManager.INSTALL_SUCCEEDED&& mRequiredVerifierPackage != null) {Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);/** Send the intent to the required verification agent,* but only start the verification timeout after the* target BroadcastReceivers have run.*/	//【7.1.5】指定目标应用为 mRequiredVerifierPackage 的内部组件// 发送 verification 意图verification.setComponent(requiredVerifierComponent);idleController.addPowerSaveTempWhitelistApp(Process.myUid(),mRequiredVerifierPackage, idleDuration,verifierUser.getIdentifier(), false, "package verifier");mContext.sendOrderedBroadcastAsUser(verification, verifierUser,android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {//【7.1.6】当 mRequiredVerifierPackage 接收到广播后,会回调// 该 BroadcastReceiver 的 onReceive 方法!//【5.6】此时校验完成,这里会发送 CHECK_PENDING_VERIFICATION 给 PackageHandler// 携带校验标识符final Message msg = mHandler.obtainMessage(CHECK_PENDING_VERIFICATION);msg.arg1 = verificationId;mHandler.sendMessageDelayed(msg, getVerificationTimeout());}}, null, 0, null, null);/** We don't want the copy to proceed until verification* succeeds, so null out this field.*/mArgs = null;}} else {/** No package verification is enabled, so immediately start* the remote call to initiate copy using temporary file.*///【5.7】没有合适的校验器,那么会调用 InstallArgs 的 copyApk 方法ret = args.copyApk(mContainerService, true);}}mRet = ret;}

5.1 DefaultContainerService.getMinimalPackageInfo

getMinimalPackageInfo 方法会解析 apk,返回 apk 的解析信息,同时判断空间状态
位置:"./frameworks/base/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java"

		public PackageInfoLite getMinimalPackageInfo(String packagePath, int flags,String abiOverride) {final Context context = DefaultContainerService.this;//【5.1.1】创建了一个 PackageInfoLite 实例PackageInfoLite ret = new PackageInfoLite();if (packagePath == null) {Slog.i(TAG, "Invalid package file " + packagePath);ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;	// 该 apk 无效return ret;}final File packageFile = new File(packagePath);final PackageParser.PackageLite pkg;final long sizeBytes;try {//【2】解析 apk,获得其 PackageLite 对象,并计算安装所需空间pkg = PackageParser.parsePackageLite(packageFile, 0);sizeBytes = PackageHelper.calculateInstalledSize(pkg, abiOverride);} catch (PackageParserException | IOException e) {Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e);if (!packageFile.exists()) {ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;	// 该 apk 无效} else {ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;	// 该 apk 无效}return ret;}final int recommendedInstallLocation;final long token = Binder.clearCallingIdentity();try {//【5.1.2】解析安装位置状态信息recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,pkg.packageName, pkg.installLocation, sizeBytes, flags);} finally {Binder.restoreCallingIdentity(token);}//【3】将解析到的参数保存到 PackageInfoLite 中ret.packageName = pkg.packageName;ret.splitNames = pkg.splitNames;ret.versionCode = pkg.versionCode;ret.versionCodeMajor = pkg.versionCodeMajor;ret.baseRevisionCode = pkg.baseRevisionCode;ret.splitRevisionCodes = pkg.splitRevisionCodes;ret.installLocation = pkg.installLocation;ret.verifiers = pkg.verifiers;ret.recommendedInstallLocation = recommendedInstallLocation;ret.multiArch = pkg.multiArch;//【4】返回该 PackageInfoLite 实例return ret;}

5.1.1 new PackageInfoLite

PackageInfoLite 用来保存解析到的 apk 的一些信息
位置:./frameworks/base/core/java/android/content/pm/PackageInfoLite.java

public class PackageInfoLite implements Parcelable {/*** The name of this package.  From the &lt;manifest&gt; tag's "name"* attribute.*/public String packageName;	// 应用包名/** Names of any split APKs, ordered by parsed splitName */public String[] splitNames;	// split apk 的名字/*** The android:versionCode of the package.* @deprecated Use {@link #getLongVersionCode()} instead, which includes both* this and the additional* {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor} attribute.*/@Deprecatedpublic int versionCode;	// 版本号/*** @hide* The android:versionCodeMajor of the package.*/public int versionCodeMajor;/*** Return {@link #versionCode} and {@link #versionCodeMajor} combined together as a* single long value.  The {@link #versionCodeMajor} is placed in the upper 32 bits.*/public long getLongVersionCode() {return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);}/** Revision code of base APK */public int baseRevisionCode;/** Revision codes of any split APKs, ordered by parsed splitName */public int[] splitRevisionCodes;/*** The android:multiArch flag from the package manifest. If set,* we will extract all native libraries for the given app, not just those* from the preferred ABI.*/public boolean multiArch;/*** Specifies the recommended install location. Can be one of* {@link PackageHelper#RECOMMEND_INSTALL_INTERNAL} to install on internal storage,* {@link PackageHelper#RECOMMEND_INSTALL_EXTERNAL} to install on external media,* {@link PackageHelper#RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors,* or {@link PackageHelper#RECOMMEND_FAILED_INVALID_APK} for parse errors.*/public int recommendedInstallLocation;public int installLocation;public VerifierInfo[] verifiers;public PackageInfoLite() {}//...
}

这里的 recommendedInstallLocation 可以取下面四个值:

PackageHelper.RECOMMEND_INSTALL_INTERNAL // 内置
PackageHelper.RECOMMEND_INSTALL_EXTERNAL // 外置
PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE // 存储异常
PackageHelper.RECOMMEND_FAILED_INVALID_APK // apk解析异常

5.1.2 PackageHelper.resolveInstallLocation

resolveInstallLocation 方法用于计算一个合适的安装位置给 apk

    @Deprecatedpublic static int resolveInstallLocation(Context context, String packageName,int installLocation, long sizeBytes, int installFlags) {final SessionParams params = new SessionParams(SessionParams.MODE_INVALID);params.appPackageName = packageName;params.installLocation = installLocation;params.sizeBytes = sizeBytes;params.installFlags = installFlags;try {return resolveInstallLocation(context, params);} catch (IOException e) {throw new IllegalStateException(e);}}/*** Given a requested {@link PackageInfo#installLocation} and calculated* install size, pick the actual location to install the app.*/public static int resolveInstallLocation(Context context, SessionParams params)throws IOException {//【1】如果该应用已经安装了,那么我们获得上一次安装后的信息ApplicationInfo existingInfo = null;try {existingInfo = context.getPackageManager().getApplicationInfo(params.appPackageName,PackageManager.MATCH_ANY_USER);} catch (NameNotFoundException ignored) {}final int prefer;final boolean checkBoth;boolean ephemeral = false;//【2】根据安装参数 installFlags,来选择合适的安装位置,按照优先级依次解析if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {//【2.1】如果指定了 PackageManager.INSTALL_EPHEMERAL,优先内置prefer = RECOMMEND_INSTALL_INTERNAL;ephemeral = true;	// 表示短暂安装checkBoth = false;} else if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {//【2.2】如果指定了 PackageManager.INSTALL_INTERNAL,优先内置prefer = RECOMMEND_INSTALL_INTERNAL;checkBoth = false;} else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {//【2.3】如果指定了 PackageManager.INSTALL_EXTERNAL,优先外置prefer = RECOMMEND_INSTALL_EXTERNAL;checkBoth = false;} else if (params.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {//【2.4】如果指定了 PackageManager.INSTALL_LOCATION_INTERNAL_ONLY,优先内置prefer = RECOMMEND_INSTALL_INTERNAL;checkBoth = false;} else if (params.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {//【2.5】如果指定了 PackageManager.INSTALL_LOCATION_PREFER_EXTERNAL,优先外置prefer = RECOMMEND_INSTALL_EXTERNAL;checkBoth = true;} else if (params.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {// When app is already installed, prefer same medium//【2.6】如果指定了 PackageManager.INSTALL_LOCATION_AUTO,那么我们自动调整if (existingInfo != null) {// TODO: distinguish if this is external ASEC//【2.6.1】如果之前已经安装过该应用,那么就和之前安装的位置保持一致if ((existingInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {prefer = RECOMMEND_INSTALL_EXTERNAL;} else {prefer = RECOMMEND_INSTALL_INTERNAL;}} else {//【2.6.2】否则,自动安装到内置prefer = RECOMMEND_INSTALL_INTERNAL;}checkBoth = true;} else {//【2.7】其他情况,默认是内置prefer = RECOMMEND_INSTALL_INTERNAL;checkBoth = false;}//【3】再次校验内置和外置是否合适boolean fitsOnInternal = false;if (checkBoth || prefer == RECOMMEND_INSTALL_INTERNAL) {fitsOnInternal = fitsOnInternal(context, params);}boolean fitsOnExternal = false;if (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL) {fitsOnExternal = fitsOnExternal(context, params);}//【4】最后,返回合适的安装位置if (prefer == RECOMMEND_INSTALL_INTERNAL) {// The ephemeral case will either fit and return EPHEMERAL, or will not fit// and will fall through to return INSUFFICIENT_STORAGE//【4.1】如果优先安装到内置,且内置存储是合适的,根据是否是 ephemeral 返回不同的值if (fitsOnInternal) {return (ephemeral)? PackageHelper.RECOMMEND_INSTALL_EPHEMERAL: PackageHelper.RECOMMEND_INSTALL_INTERNAL;}} else if (prefer == RECOMMEND_INSTALL_EXTERNAL) {//【4.2】如果优先安装到外置,且外置存储是合适的,返回结果if (fitsOnExternal) {return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;}}//【4.3】其他情况if (checkBoth) {if (fitsOnInternal) {return PackageHelper.RECOMMEND_INSTALL_INTERNAL;} else if (fitsOnExternal) {return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;}}//【5】异常情况,返回 PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGEreturn PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;}

5.2 PackageManagerS.installLocationPolicy

PackageInfoLite pkgLite 保存了本次要安装的应用的信息
installLocationPolicy 方法会对降级安装和替换安装做一个校验和判断

        private int installLocationPolicy(PackageInfoLite pkgLite) {String packageName = pkgLite.packageName;int installLocation = pkgLite.installLocation;boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;// readersynchronized (mPackages) {// Currently installed package which the new package is attempting to replace or// null if no such package is installed.	//【1】如果该应用已经安装过的话,那就获得上次安装后的解析信息PackageParser.Package installedPkg = mPackages.get(packageName);// Package which currently owns the data which the new package will own if installed.// If an app is unstalled while keeping data (e.g., adb uninstall -k), installedPkg// will be null whereas dataOwnerPkg will contain information about the package// which was uninstalled while keeping its data.// 下面这段代码主要是处理卸载但是保留了数据的情况,比如 adb uninstall -kPackageParser.Package dataOwnerPkg = installedPkg;if (dataOwnerPkg  == null) {PackageSetting ps = mSettings.mPackages.get(packageName);if (ps != null) {dataOwnerPkg = ps.pkg;}}//【2】如果 dataOwnerPkg 不为 nulkl,说明之前已经安装了if (dataOwnerPkg != null) {// If installed, the package will get access to data left on the device by its// predecessor. As a security measure, this is permited only if this is not a// version downgrade or if the predecessor package is marked as debuggable and// a downgrade is explicitly requested.//// On debuggable platform builds, downgrades are permitted even for// non-debuggable packages to make testing easier. Debuggable platform builds do// not offer security guarantees and thus it's OK to disable some security// mechanisms to make debugging/testing easier on those builds. However, even on// debuggable builds downgrades of packages are permitted only if requested via// installFlags. This is because we aim to keep the behavior of debuggable// platform builds as close as possible to the behavior of non-debuggable// platform builds.//【2.1】如果安装标志位设置了 INSTALL_ALLOW_DOWNGRADE,表示允许降级安装final boolean downgradeRequested =(installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) != 0;//【2.2】判断应用是否允许 debugfinal boolean packageDebuggable =(dataOwnerPkg.applicationInfo.flags& ApplicationInfo.FLAG_DEBUGGABLE) != 0;//【2.3】如果要能够降级安装,必须满足 2 个条件:1、安装标志位设置了 allow downgrade// 2、系统允许 debug 或者该应用可以 debugfinal boolean downgradePermitted =(downgradeRequested) && ((Build.IS_DEBUGGABLE) || (packageDebuggable));if (!downgradePermitted) {try {//【5.2.1】如果,安装不允许降级,那就需要做检查checkDowngrade(dataOwnerPkg, pkgLite);} catch (PackageManagerException e) {Slog.w(TAG, "Downgrade detected: " + e.getMessage());return PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE;}}}//【3】接着处理覆盖安装的情况,即相同包名的应用已经存在if (installedPkg != null) {//【3.1】覆盖安装的情况,必须携带 PackageManager.INSTALL_REPLACE_EXISTING 安装标志位if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {// Check for updated system application.if ((installedPkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {//【3.1.1】对于系统应用,不能覆盖安装到 sd 卡上if (onSd) {Slog.w(TAG, "Cannot install update to system app on sdcard");return PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION;}return PackageHelper.RECOMMEND_INSTALL_INTERNAL;} else {//【3.1.2】对于非系统应用,如果是安装到 sdcard,直接返回对应值if (onSd) {// Install flag overrides everything.return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;}// If current upgrade specifies particular preference//【3.1.2】对于非系统应用,如果只安装到内置,直接返回对应值if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {// Application explicitly specified internal.return PackageHelper.RECOMMEND_INSTALL_INTERNAL;} else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {// App explictly prefers external. Let policy decide//【3.1.3】对于非系统应用,如果优先安装到外置,那么安装位置由 // pkgLite.recommendedInstallLocation 决定} else {// Prefer previous location	//【3.1.4】对于非系统应用,其他情况进入这里if (isExternal(installedPkg)) {return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;}return PackageHelper.RECOMMEND_INSTALL_INTERNAL;}}} else {// Invalid install. Return error code	//【3.2】异常情况return PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS;}}}// All the special cases have been taken care of.// Return result based on recommended install location.//【4】如果上面的条件都不满足,并且指定了在 sdcard,那么就返回 RECOMMEND_INSTALL_EXTERNALif (onSd) {return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;}//【5】其他情况,均由 pkgLite.recommendedInstallLocation 决定return pkgLite.recommendedInstallLocation;}

5.3 PackageManagerS.createInstallArgs

其实就是针对不同的安装方式,创建不同的 InstallArgs

    private InstallArgs createInstallArgs(InstallParams params) {if (params.move != null) {//【1】如果是 move package,那么会创建 MoveInstallArgs 实例return new MoveInstallArgs(params);} else {//【5.3.1】对于一般安装,创建 FileInstallArgs 实例return new FileInstallArgs(params);}}

这里我们先关注一般情况,即创建 FileInstallArgs 实例的情况
5.3.1 new FileInstallArgs - 要安装的 apk

    class FileInstallArgs extends InstallArgs {private File codeFile;private File resourceFile;// Example topology:// /data/app/com.example/base.apk// /data/app/com.example/split_foo.apk// /data/app/com.example/lib/arm/libfoo.so// /data/app/com.example/lib/arm64/libfoo.so// /data/app/com.example/dalvik/arm/base.apk@classes.dex/** New install */	//【1】安装一个新的 apkFileInstallArgs(InstallParams params) {super(params.origin, params.move, params.observer, params.installFlags,params.installerPackageName, params.volumeUuid,params.getUser(), null /*instructionSets*/, params.packageAbiOverride,params.grantedRuntimePermissions,params.traceMethod, params.traceCookie, params.signingDetails,params.installReason);//【1.1】这里校验了下是否是  Forward Locked 的if (isFwdLocked()) {throw new IllegalArgumentException("Forward locking only supported in ASEC");}}/** Existing install */		//【2】用于描述已存在的一个安装FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {super(OriginInfo.fromNothing(), null, null, 0, null, null, null, instructionSets,null, null, null, 0, PackageParser.SigningDetails.UNKNOWN,PackageManager.INSTALL_REASON_UNKNOWN);this.codeFile = (codePath != null) ? new File(codePath) : null;this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;}//...}

我们看到 FileInstallArgs 有两个构造器

  • 一参数构造器用于创建安装过程中的 InstallArgs
  • 三参数构造器,用于描述一个已存在的安装,主要用于清除旧的安装,或者作为移动应用的时候的源数据,我们在 pms 开机初始化的过程中就已经看到过了
    当然,这里我们重点关注安装过程

5.4 PackageManagerService.isVerificationEnabled

isVerificationEnabled 用于校验系统是否打开了校验功能

    private boolean isVerificationEnabled(int userId, int installFlags, int installerUid) {if (!DEFAULT_VERIFY_ENABLE) {	//【1】如果默认没有打开校验,falsereturn false;}//【3】判断该用户下是否允许校验应用boolean ensureVerifyAppsEnabled = isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS);// Check if installing from ADB		//【4】如果安装指定了 INSTALL_FROM_ADB,进入这里if ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0) {// Do not run verification in a test harness environment	//【4.1】test harness environment 不校验if (ActivityManager.isRunningInTestHarness()) {return false;}//【4.2】如果同时该用户下能校验应用,返回 trueif (ensureVerifyAppsEnabled) {return true;}// Check if the developer does not want package verification for ADB installs//【4.3】对于 adb install,如果系统属性 PACKAGE_VERIFIER_INCLUDE_ADB 为 0 ,那就不用校验if (android.provider.Settings.Global.getInt(mContext.getContentResolver(),android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) == 0) {return false;}} else {	//【2】如果不是INSTALL_FROM_ADB方式的安装,不校验,false// only when not installed from ADB, skip verification for instant apps when// the installer and verifier are the same.if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {if (mInstantAppInstallerActivity != null&& mInstantAppInstallerActivity.packageName.equals(mRequiredVerifierPackage)) {try {mContext.getSystemService(AppOpsManager.class).checkPackage(installerUid, mRequiredVerifierPackage);if (DEBUG_VERIFY) {Slog.i(TAG, "disable verification for instant app");}return false;} catch (SecurityException ignore) { }}}}//【5】如果该用户下能校验应用,返回 trueif (ensureVerifyAppsEnabled) {return true;}//【6】判断系统属性 PACKAGE_VERIFIER_ENABLE 如果为 0,那就不用校验return android.provider.Settings.Global.getInt(mContext.getContentResolver(),android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) == 1;}

5.5 new PackageVerificationState

PackageVerificationState 用于保存被安装的应用的校验状态信息:

class PackageVerificationState {private final InstallArgs mArgs;	// InstallArgs 实例private final SparseBooleanArray mSufficientVerifierUids;private final int mRequiredVerifierUid;	// 校验者的 uidprivate boolean mSufficientVerificationComplete;private boolean mSufficientVerificationPassed;private boolean mRequiredVerificationComplete;private boolean mRequiredVerificationPassed;private boolean mExtendedTimeout;/*** Create a new package verification state where {@code requiredVerifierUid}* is the user ID for the package that must reply affirmative before things* can continue.** @param requiredVerifierUid user ID of required package verifier* @param args*/public PackageVerificationState(int requiredVerifierUid, InstallArgs args) {mRequiredVerifierUid = requiredVerifierUid;mArgs = args;mSufficientVerifierUids = new SparseBooleanArray();mExtendedTimeout = false;	// 表示是否超时}
}

六、PackageHandler.doHandleMessage[CHECK_PENDING_VERIFICATION] - 校验完成

                case CHECK_PENDING_VERIFICATION: {//【1】获得校验唯一标识final int verificationId = msg.arg1;//【2】获得本次校验的 PackageVerificationState 实例final PackageVerificationState state = mPendingVerification.get(verificationId);if ((state != null) && !state.timeoutExtended()) {//【3】获得 InstallArgs 实例final InstallArgs args = state.getInstallArgs();final Uri originUri = Uri.fromFile(args.origin.resolvedFile);Slog.i(TAG, "Verification timed out for " + originUri);//【4】从 mPendingVerification 中移除校验状态对象mPendingVerification.remove(verificationId);int ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;final UserHandle user = args.getUser();//【5】处理校验结果if (getDefaultVerificationResponse(user)== PackageManager.VERIFICATION_ALLOW) {//【5.1】校验成功Slog.i(TAG, "Continuing with installation of " + originUri);state.setVerifierResponse(Binder.getCallingUid(),PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);//【5.6.1】发送 Intent.ACTION_PACKAGE_VERIFIED 广播broadcastPackageVerified(verificationId, originUri,PackageManager.VERIFICATION_ALLOW, user);try {//【5.6.2】校验成功后,调用 installArgs.copyApk 继续处理ret = args.copyApk(mContainerService, true);} catch (RemoteException e) {Slog.e(TAG, "Could not contact the ContainerService");}} else {//【5.6.1】校验失败,发送 Intent.ACTION_PACKAGE_VERIFIED 广播broadcastPackageVerified(verificationId, originUri,PackageManager.VERIFICATION_REJECT, user);}Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);//【5.7】继续安装processPendingInstall(args, ret);// 安装过程结束,发送 MCS_UNBIND 消息mHandler.sendEmptyMessage(MCS_UNBIND);}Slog.d(TAG, "Tuo CHECK_PENDING_VERIFICATION end");break;}

这里我们通过 getDefaultVerificationResponse 方法返回校验结果:

    /*** Get the default verification agent response code.** @return default verification response code*/private int getDefaultVerificationResponse(UserHandle user) {if (sUserManager.hasUserRestriction(UserManager.ENSURE_VERIFY_APPS, user.getIdentifier())) {return PackageManager.VERIFICATION_REJECT;}return android.provider.Settings.Global.getInt(mContext.getContentResolver(),android.provider.Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE,DEFAULT_VERIFICATION_RESPONSE);}

默认返回的是 PackageManager.VERIFICATION_ALLOW,比如在校验超时的情况下

6.1 PackageManagerS.broadcastPackageVerified

    private void broadcastPackageVerified(int verificationId, Uri packageUri,int verificationCode, UserHandle user) {final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED);intent.setDataAndType(packageUri, PACKAGE_MIME_TYPE);intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);intent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);intent.putExtra(PackageManager.EXTRA_VERIFICATION_RESULT, verificationCode);mContext.sendBroadcastAsUser(intent, user,android.Manifest.permission.PACKAGE_VERIFICATION_AGENT);}

6.2 FileInstallArgs.(do)copyApk

        int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");try {return doCopyApk(imcs, temp);} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}}

copyApk 调用了 doCopyApk 方法:

        private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {//【1】如果 origin.staged 为 true,那么说明应用已经 copy 到目标目录了,// 那就直接返回 PackageManager.INSTALL_SUCCEEDEDif (origin.staged) {if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");//【1.1】设置 FileInstallArgs 的 codeFile 属性codeFile = origin.file;resourceFile = origin.file;return PackageManager.INSTALL_SUCCEEDED;}//【2】如果 origin.staged 为 false,说明应用没有拷贝到目标目录try {final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;//【5.6.2.1】获得要拷贝的目标目录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;}//【3】回调接口final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {@Overridepublic ParcelFileDescriptor open(String name, int mode) throws RemoteException {if (!FileUtils.isValidExtFilename(name)) {throw new IllegalArgumentException("Invalid filename: " + name);}try {//【3.1】访问 codeFile 目录下的 name 文件,设置权限,并返回其文件描述符final File file = new File(codeFile, name);final FileDescriptor fd = Os.open(file.getAbsolutePath(),O_RDWR | O_CREAT, 0644);Os.chmod(file.getAbsolutePath(), 0644);return new ParcelFileDescriptor(fd);} catch (ErrnoException e) {throw new RemoteException("Failed to open: " + e.getMessage());}}};int ret = PackageManager.INSTALL_SUCCEEDED;//【6.2.2】将应用程序拷贝到目标目录ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);if (ret != PackageManager.INSTALL_SUCCEEDED) {Slog.e(TAG, "Failed to copy package");return ret;}//【4】解压本地 lib 库文件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;}

根据前面创建 OriginInfo 时知道:

if (stagedDir != null) {origin = OriginInfo.fromStagedFile(stagedDir);
} else {origin = OriginInfo.fromStagedContainer(stagedCid);
}

通过 adb 安装时,安装到内置存储,会调用 OriginInfo.fromStagedFile 方法,此时 OriginInfo.staged 为 true,安装到外置存储时, OriginInfo.staged 也会为 true

这是因为在进行 adb install 过程中时,我们通过 Session,已经拷贝到目标目录了:/data/app/vml[sessionId].tmp/,所以这里 doCopyApk 无需在继续进行下去

对于其他的安装方式,OriginInfo.staged 为 false 的,那么会进入 doCopyApk 的下一步

6.2.1 PackageInstallerService.allocateStageDirLegacy

    @Deprecatedpublic File allocateStageDirLegacy(String volumeUuid, boolean isEphemeral) throws IOException {synchronized (mSessions) {try {//【3.1.2】分配一个 sessionId,保存到 mLegacySessions 中final int sessionId = allocateSessionIdLocked();mLegacySessions.put(sessionId, true);//【3.1.2】创建目标目录final File stageDir = buildStageDir(volumeUuid, sessionId, isEphemeral);//【2】创建目录,设置权限prepareStageDir(stageDir);return stageDir;} catch (IllegalStateException e) {throw new IOException(e);}}}

6.2.2 DefaultContainerService.copyPackage(Inner)

        public int copyPackage(String packagePath, IParcelFileDescriptorFactory target) {if (packagePath == null || target == null) {return PackageManager.INSTALL_FAILED_INVALID_URI;}PackageLite pkg = null;try {final File packageFile = new File(packagePath);//【1】解析应用pkg = PackageParser.parsePackageLite(packageFile, 0);//【2】继续处理 copyPackageInnerreturn copyPackageInner(pkg, target);} catch (PackageParserException | IOException | RemoteException e) {Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;}}private int copyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target)throws IOException, RemoteException {//【1】对 base apk 和 split apk 分别执行 copycopyFile(pkg.baseCodePath, target, "base.apk");if (!ArrayUtils.isEmpty(pkg.splitNames)) {for (int i = 0; i < pkg.splitNames.length; i++) {copyFile(pkg.splitCodePaths[i], target, "split_" + pkg.splitNames[i] + ".apk");}}return PackageManager.INSTALL_SUCCEEDED;}
//最终,调用了 copyFile 执行 copyprivate void copyFile(String sourcePath, IParcelFileDescriptorFactory target, String targetName)throws IOException, RemoteException {Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);InputStream in = null;OutputStream out = null;try {in = new FileInputStream(sourcePath);out = new ParcelFileDescriptor.AutoCloseOutputStream(target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE));FileUtils.copy(in, out);} finally {IoUtils.closeQuietly(out);IoUtils.closeQuietly(in);}}

七、PackageManagerS.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//【5.7.1】创建 PackageInstalledInfo 实例,封装安装结果PackageInstalledInfo res = new PackageInstalledInfo();res.setReturnCode(currentStatus);	// 保存当前的返回码res.uid = -1;res.pkg = null;res.removedInfo = null;//【1】如果返回码为 PackageManager.INSTALL_SUCCEEDEDif (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {//【×5.7.2】安装前处理args.doPreInstall(res.returnCode);synchronized (mInstallLock) {//【×5.7.3】执行安装installPackageTracedLI(args, res);}//【×5.7.4】安装后处理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.//【2】判断是否执行备份,要备份必须满足 3 个条件// 1、安装正常;2、本次安装并不是更新操作!3、应用允许备份final boolean update = res.removedInfo != null&& 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.//【3】计算本次安装的 tokenint token;if (mNextInstallToken < 0) mNextInstallToken = 1;token = mNextInstallToken++;//【*5.7.5】创建一个 PostInstallData 对象,并将其加入 mRunningInstalls 中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.//【4】如果安装成功,并且需要备份,那就获得 BackupManager 执行备份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");Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);try {// TODO: http://b/22388012if (bm.isBackupServiceActive(UserHandle.USER_SYSTEM)) {bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);} else {doRestore = false;}} 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;}}//【5】如果备份失败或者不需要备份,那就进入这个阶段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);Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "postInstall", token);Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);mHandler.sendMessage(msg);}}});}

7.1 new PackageInstalledInfo

PackageInstalledInfo 用于保存该应用的安装结果信息

    static class PackageInstalledInfo {String name;int uid;// The set of users that originally had this package installed.int[] origUsers;	// 该 pacakge 之前安装后所属的 user// The set of users that now have this package installed.int[] newUsers;		// 该 pacakge 现在安装后所属的 userPackageParser.Package pkg;int returnCode;		// 返回码String returnMsg;String installerPackageName;PackageRemovedInfo removedInfo;	//【7.1.1】用于封装要移除的 apk 的信息ArrayMap<String, PackageInstalledInfo> addedChildPackages;	// split apk 的安装结果信息//...// In some error cases we want to convey more info back to the observerString origPackage;String origPermission;}

7.1.1 new PackageRemovedInfo

封装要移除的 apk 的信息:

    static class PackageRemovedInfo {final PackageSender packageSender;String removedPackage;String installerPackageName;int uid = -1;int removedAppId = -1;int[] origUsers;int[] removedUsers = null;int[] broadcastUsers = null;int[] instantUserIds = null;SparseArray<Integer> installReasons;boolean isRemovedPackageSystemUpdate = false;boolean isUpdate;boolean dataRemoved;boolean removedForAllUsers;boolean isStaticSharedLib;// Clean up resources deleted packages.	//【1】InstallArgs 用于清除 apk 的相关数据,后面会看到InstallArgs args = null;ArrayMap<String, PackageRemovedInfo> removedChildPackages;ArrayMap<String, PackageInstalledInfo> appearedChildPackages;//...}

7.2 FileInstallArgs.doPreInstall

安装前执行清理操作,正常情况不会触发

		int doPreInstall(int status) {//【1】如果安装前的状态不是 PackageManager.INSTALL_SUCCEEDEDif (status != PackageManager.INSTALL_SUCCEEDED) {//【5.7.2.1】执行清理操作cleanUp();}return status;}

7.2.1 FileInstallArgs.cleanUp(ResourcesLI)

清理操作

        private boolean cleanUp() {if (codeFile == null || !codeFile.exists()) {return false;}//【1】删除目标目录下的所有文件removeCodePathLI(codeFile);if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {resourceFile.delete();}return true;}
//可以看到,最后调用了 removeCodePathLI 方法void removeCodePathLI(File codePath) {if (codePath.isDirectory()) {try {mInstaller.rmPackageDir(codePath.getAbsolutePath());} catch (InstallerException e) {Slog.w(TAG, "Failed to remove code path", e);}} else {codePath.delete();}}

7.3 PackageManagerS.installPackageTracedLI - 核心安装入口

    private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {try {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage");//【1】调用了 installPackageLI 方法installPackageLI(args, res);} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}}
//这里调用了 installPackageLI 方法:private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {//【1】获得安装时传入的参数和标志位final int installFlags = args.installFlags;final String installerPackageName = args.installerPackageName;final String volumeUuid = args.volumeUuid;final File tmpPackageFile = new File(args.getCodePath());final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)|| (args.volumeUuid != null));final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);final boolean forceSdk = ((installFlags & PackageManager.INSTALL_FORCE_SDK) != 0);final boolean virtualPreload =((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);boolean replace = false;//【2】重新设置扫描参数@ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;if (args.move != null) {// moving a complete application; perform an initial scan on the new install location//【2.1】如果 args.move 不为 null,表示正在移动一个 app,我们会对其进行一个初始化的扫描// 增加 SCAN_INITIAL 位scanFlags |= SCAN_INITIAL;}if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {//【2.2】如果安装参数指定了 INSTALL_DONT_KILL_APP,那么增加 SCAN_DONT_KILL_APP 位scanFlags |= SCAN_DONT_KILL_APP;}if (instantApp) {scanFlags |= SCAN_AS_INSTANT_APP;}if (fullApp) {scanFlags |= SCAN_AS_FULL_APP;}if (virtualPreload) {scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;}// Result object to be returned		//【3】更新结果码res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);res.installerPackageName = installerPackageName;if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);// Sanity check	//【4】检查 instantApp 是否和 forwardLocked/onExternal 共存,共存则报错if (instantApp && (forwardLocked || onExternal)) {Slog.i(TAG, "Incompatible ephemeral install; fwdLocked=" + forwardLocked+ " external=" + onExternal);res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);return;}// Retrieve PackageSettings and parse package	//【5】设置解析参数 parseFlags@ParseFlags final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY| PackageParser.PARSE_ENFORCE_CODE| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)| (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)| (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0);PackageParser pp = new PackageParser();		//【6】创建解析对象pp.setSeparateProcesses(mSeparateProcesses);pp.setDisplayMetrics(mMetrics);pp.setCallback(mPackageParserCallback);Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");final PackageParser.Package pkg;try {//【*7.3.1】解析 apk,获得其对应的 Package 对象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);}// Instant apps have several additional install-time checks.if (instantApp) {if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {Slog.w(TAG,"Instant app package " + pkg.packageName + " does not target at least O");res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,"Instant app package must target at least O");return;}if (pkg.applicationInfo.targetSandboxVersion != 2) {Slog.w(TAG, "Instant app package " + pkg.packageName+ " does not target targetSandboxVersion 2");res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,"Instant app package must use targetSandboxVersion 2");return;}if (pkg.mSharedUserId != null) {Slog.w(TAG, "Instant app package " + pkg.packageName+ " may not declare sharedUserId.");res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,"Instant app package may not declare a sharedUserId");return;}}if (pkg.applicationInfo.isStaticSharedLibrary()) {// Static shared libraries have synthetic package namesrenameStaticSharedLibraryPackage(pkg);// No static shared libs on external storageif (onExternal) {Slog.i(TAG, "Static shared libs can only be installed on internal storage.");res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,"Packages declaring static-shared libs cannot be updated");return;}}// If we are installing a clustered package add results for the children//【7】如果该应用有子包的话,那么对于每个子包,也会创建安装结果对象if (pkg.childPackages != null) {synchronized (mPackages) {final int childCount = pkg.childPackages.size();for (int i = 0; i < childCount; i++) {PackageParser.Package childPkg = pkg.childPackages.get(i);//【*5.7.1】针对子包,创建 PackageInstalledInfo 对象!// 设置返回码,子包包名PackageInstalledInfo childRes = new PackageInstalledInfo();childRes.setReturnCode(PackageManager.INSTALL_SUCCEEDED);childRes.pkg = childPkg;childRes.name = childPkg.packageName;//【7.1】获得子包的源 userPackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);if (childPs != null) {childRes.origUsers = childPs.queryInstalledUsers(sUserManager.getUserIds(), true);}//【7.2】如果子包之前被扫描到了(安装过),创建 PackageRemovedInfo 对象if ((mPackages.containsKey(childPkg.packageName))) {childRes.removedInfo = new PackageRemovedInfo(this);childRes.removedInfo.removedPackage = childPkg.packageName;childRes.removedInfo.installerPackageName = childPs.installerPackageName;}if (res.addedChildPackages == null) {res.addedChildPackages = new ArrayMap<>();}//【7.3】将子包的安装结果对象保存到 base apk 的信息中res.addedChildPackages.put(childPkg.packageName, childRes);}}}// If package doesn't declare API override, mark that we have an install// time CPU ABI override.	// 如果应用没有指定 abi,我们通过安装参数指定if (TextUtils.isEmpty(pkg.cpuAbiOverride)) {pkg.cpuAbiOverride = args.abiOverride;}// 如果应用指定了 ApplicationInfo.FLAG_TEST_ONLY,那么安装参数也需要指定这个参数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 {//【8】收集证书信息// either use what we've been given or parse directly from the APKif (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;}if (instantApp && pkg.mSigningDetails.signatureSchemeVersion< SignatureSchemeVersion.SIGNING_BLOCK_V2) {Slog.w(TAG, "Instant app package " + pkg.packageName+ " is not signed with at least APK Signature Scheme v2");res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,"Instant app package must be signed with APK Signature Scheme v2 or greater");return;}// Get rid of all references to package scan path via parser.pp = null;String oldCodePath = null;boolean systemApp = false;synchronized (mPackages) {// Check if installing already existing package//【9】如果安装参数指定了 INSTALL_REPLACE_EXISTING,那么我们要尝试判断是否存在已安装的应用!// 如果存在,那就要 replaceif ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {String oldName = mSettings.getRenamedPackageLPr(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.	//【9.1】如果有源包(系统应用才会有),要命名为源包,replace 为 truepkg.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.//【9.2】如果没有源包(系统应用才会有),但是已经有相同包名的应用存在,replace 为 truereplace = true;if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);}// Child packages are installed through the parent package//【9.2】对于子包,只能通过父包更新,这里不处理子包的 replaceif (pkg.parentPackage != null) {res.setError(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,"Package " + pkg.packageName + " is child of package "+ pkg.parentPackage.parentPackage + ". Child packages "+ "can be updated only through the parent package.");return;}//【9.3】如果需要替换已存在的 apk,那么需要做 sdk 的校验!// 如果旧应用支持运行时,不允许新的应用不支持运行时if (replace) {// Prevent apps opting out from runtime permissionsPackageParser.Package oldPackage = mPackages.get(pkgName);final int oldTargetSdk = oldPackage.applicationInfo.targetSdkVersion;final int newTargetSdk = pkg.applicationInfo.targetSdkVersion;if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1&& newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) {res.setError(PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,"Package " + pkg.packageName + " new target SDK " + newTargetSdk+ " doesn't support runtime permissions but the old"+ " target SDK " + oldTargetSdk + " does.");return;}// Prevent persistent apps from being updated	//【9.4】如果旧包也是子包,也不安装if ((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0) {res.setError(PackageManager.INSTALL_FAILED_INVALID_APK,"Package " + oldPackage.packageName + " is a persistent app. "+ "Persistent apps are not updateable.");return;}// Prevent installing of child packages//【9.4】如果旧包也是子包,也不安装if (oldPackage.parentPackage != null) {res.setError(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,"Package " + pkg.packageName + " is child of package "+ oldPackage.parentPackage + ". Child packages "+ "can be updated only through the parent package.");return;}}}//【10】获得上一次的安装数据PackageSetting ps = mSettings.mPackages.get(pkgName);if (ps != null) {if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);// Static shared libs have same package with different versions where// we internally use a synthetic package name to allow multiple versions// of the same package, therefore we need to compare signatures against// the package setting for the latest library version.PackageSetting signatureCheckPs = ps;if (pkg.applicationInfo.isStaticSharedLibrary()) {SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);if (libraryEntry != null) {signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);}}// Quick sanity check that we're signed correctly if updating;// we'll check this again later when scanning, but we want to// bail early here before tripping over redefined permissions.//【10.1】如果安装过旧版本,那就要校验签名!//【×3.7.2】shouldCheckUpgradeKeySetLP 用于判断是否检查签名更新;final KeySetManagerService ksms = mSettings.mKeySetManagerService;if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {//【×3.7.3】checkUpgradeKeySetLP 用于检查签名更新;if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "+ pkg.packageName + " upgrade keys do not match the "+ "previously installed version");return;}} else {try {final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg);final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg);// We don't care about disabledPkgSetting on install for now.//【×3.7.4】校验签名,如果签名不相等,那就禁止安装final boolean compatMatch = verifySignatures(signatureCheckPs, null, pkg.mSigningDetails, compareCompat,compareRecover);// The new KeySets will be re-added later in the scanning process.if (compatMatch) {synchronized (mPackages) {ksms.removeAppKeySetDataLPw(pkg.packageName);}}} catch (PackageManagerException e) {res.setError(e.error, e.getMessage());return;}}//【10.2】获得旧的 apk 的路径,判断旧应用是否是系统应用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);}//【11】检查权限是否被重新定义,如果重新定义,那就要校验权限int N = pkg.permissions.size();for (int i = N-1; i >= 0; i--) {//【11.1】获得该应用定义的权限final PackageParser.Permission perm = pkg.permissions.get(i);//【11.2】尝试从系统中获得该权限已有的信息final BasePermission bp =(BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name);// Don't allow anyone but the system to define ephemeral permissions.if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0&& !systemApp) {Slog.w(TAG, "Non-System package " + pkg.packageName+ " attempting to delcare ephemeral permission "+ perm.info.name + "; Removing ephemeral.");perm.info.protectionLevel &= ~PermissionInfo.PROTECTION_FLAG_INSTANT;}// Check whether the newly-scanned package wants to define an already-defined permif (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.//【11.3】如果该权限已经被定义过了,那就要校验下签名final boolean sigsOk;final String sourcePackageName = bp.getSourcePackageName();final PackageSettingBase sourcePackageSetting = bp.getSourcePackageSetting();final KeySetManagerService ksms = mSettings.mKeySetManagerService;if (sourcePackageName.equals(pkg.packageName)&& (ksms.shouldCheckUpgradeKeySetLocked(sourcePackageSetting, scanFlags))) {//【11.3.1】检查签名更新sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, pkg);} else {	//【11.3.2】如果不检查签名更新,那就直接比较签名// in the event of signing certificate rotation, we need to see if the// package's certificate has rotated from the current one, or if it is an// older certificate with which the current is ok with sharing permissionsif (sourcePackageSetting.signatures.mSigningDetails.checkCapability(pkg.mSigningDetails,PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {sigsOk = true;} else if (pkg.mSigningDetails.checkCapability(sourcePackageSetting.signatures.mSigningDetails,PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {// the scanned package checks out, has signing certificate rotation// history, and is newer; bring it oversourcePackageSetting.signatures.mSigningDetails = pkg.mSigningDetails;sigsOk = true;} else {sigsOk = false;}}//【11.4】如果签名发生了变化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 (!sourcePackageName.equals("android")) {//【11.4.1】如果权限的定义者不是系统,那么不允许重新定义,同时不允许继续安装res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "+ pkg.packageName + " attempting to redeclare permission "+ perm.info.name + " already owned by " + sourcePackageName);res.origPermission = perm.info.name;res.origPackage = sourcePackageName;return;} else {Slog.w(TAG, "Package " + pkg.packageName+ " attempting to redeclare system permission "+ perm.info.name + "; ignoring new declaration");pkg.permissions.remove(i);}} else if (!PLATFORM_PACKAGE_NAME.equals(pkg.packageName)) {//【11.4.2】如果权限的定义不是系统,那么允许安装,但是会忽视掉新的定义// Prevent apps to change protection level to dangerous from any other// type as this would allow a privilege escalation where an app adds a// normal/signature permission in other app's group and later redefines// it as dangerous leading to the group auto-grant.if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)== PermissionInfo.PROTECTION_DANGEROUS) {if (bp != null && !bp.isRuntime()) {Slog.w(TAG, "Package " + pkg.packageName + " trying to change a "+ "non-runtime permission " + perm.info.name+ " to runtime; keeping old protection level");perm.info.protectionLevel = bp.getProtectionLevel();}}}}}}//【12】如果覆盖更新的是系统应用,要针对安装位置做判断!// 新的 apk 不能是 onExternal / ephemeralif (systemApp) {if (onExternal) {// Abort update; system app can't be replaced with app on sdcardres.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,"Cannot install updates to system apps on sdcard");return;} else if (instantApp) {// Abort update; system app can't be replaced with an instant appres.setError(INSTALL_FAILED_INSTANT_APP_INVALID,"Cannot update a system app with an instant app");return;}}//【13】根据安装参数做不同的处理if (args.move != null) {// We did an in-place move, so dex is ready to roll//【13.1】如果是 move package,进入这里// 设置以下标签,无需做 odex,我们需要已有的移动过去即可scanFlags |= SCAN_NO_DEX;scanFlags |= SCAN_MOVE;synchronized (mPackages) {final PackageSetting ps = mSettings.mPackages.get(pkgName);if (ps == null) {res.setError(INSTALL_FAILED_INTERNAL_ERROR,"Missing settings for moved package " + pkgName);}// We moved the entire application as-is, so bring over the// previously derived ABI information.	//【13.1.1】对于 abi,和移动前的保持一致pkg.applicationInfo.primaryCpuAbi = ps.primaryCpuAbiString;pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;}} else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {// Enable SCAN_NO_DEX flag to skip dexopt at a later stage//【13.2】如果不是 forward lock 模式安装且没有安装到外置存储上,进入这里// 扫描参数设置 SCAN_NO_DEX,意味着后面不做 odex,因为这里会做scanFlags |= SCAN_NO_DEX;try {String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?args.abiOverride : pkg.cpuAbiOverride);final boolean extractNativeLibs = !pkg.isLibrary();derivePackageAbi(pkg, abiOverride, extractNativeLibs);} catch (PackageManagerException pme) {Slog.e(TAG, "Error deriving application ABI", pme);res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");return;}// Shared libraries for the package need to be updated.//【13.2.1】更新共享库文件synchronized (mPackages) {try {updateSharedLibrariesLPr(pkg, null);} catch (PackageManagerException e) {Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());}}}//【×5.7.3.5】重命名文件目录,此时文件目录由 /data/app/tmpl[SessionId].tmp 变为 /data/app/packageName-X// 如果 X 存在,那么会 X + 1if (!args.doRename(res.returnCode, pkg, oldCodePath)) {res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");return;}if (PackageManagerServiceUtils.isApkVerityEnabled()) {String apkPath = null;synchronized (mPackages) {// Note that if the attacker managed to skip verify setup, for example by tampering// with the package settings, upon reboot we will do full apk verification when// verity is not detected.final PackageSetting ps = mSettings.mPackages.get(pkgName);if (ps != null && ps.isPrivileged()) {apkPath = pkg.baseCodePath;}}if (apkPath != null) {final VerityUtils.SetupResult result =VerityUtils.generateApkVeritySetupData(apkPath);if (result.isOk()) {if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling apk verity to " + apkPath);FileDescriptor fd = result.getUnownedFileDescriptor();try {final byte[] signedRootHash = VerityUtils.generateFsverityRootHash(apkPath);mInstaller.installApkVerity(apkPath, fd, result.getContentSize());mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash);} catch (InstallerException | IOException | DigestException |NoSuchAlgorithmException e) {res.setError(INSTALL_FAILED_INTERNAL_ERROR,"Failed to set up verity: " + e);return;} finally {IoUtils.closeQuietly(fd);}} else if (result.isFailed()) {res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Failed to generate verity");return;} else {// Do nothing if verity is skipped. Will fall back to full apk verification on// reboot.}}}//【*7.3.6】开始 intentFilter 校验if (!instantApp) {startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);} else {if (DEBUG_DOMAIN_VERIFICATION) {Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName);}}//【*7.3.7】冻结应用try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,"installPackageLI")) {if (replace) {if (pkg.applicationInfo.isStaticSharedLibrary()) {// Static libs have a synthetic package name containing the version// and cannot be updated as an update would get a new package name,// unless this is the exact same version code which is useful for// development.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;}}//【*6】如果是覆盖安装,进入这里replacePackageLIF(pkg, parseFlags, scanFlags, args.user,installerPackageName, res, args.installReason);} else {//【*7】如果是全新安装,进入这里installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,args.user, installerPackageName, volumeUuid, res, args.installReason);}}// Prepare the application profiles for the new code paths.// This needs to be done before invoking dexopt so that any install-time profile// can be used for optimizations.mArtManagerService.prepareAppProfiles(pkg, resolveUserIds(args.user.getIdentifier()));// Check whether we need to dexopt the app.//// NOTE: it is IMPORTANT to call dexopt://   - after doRename which will sync the package data from PackageParser.Package and its//     corresponding ApplicationInfo.//   - after installNewPackageLIF or replacePackageLIF which will update result with the//     uid of the application (pkg.applicationInfo.uid).//     This update happens in place!//// We only need to dexopt if the package meets ALL of the following conditions://   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.//// Note that we do not dexopt instant apps by default. dexopt can take some time to// complete, so we skip this step during installation. Instead, we'll take extra time// the first time the instant app starts. It's preferred to do it this way to provide// continuous progress to the useur instead of mysteriously blocking somewhere in the// middle of running an instant app. The default behaviour can be overridden// via gservices.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);//【13.2.2】执行 odex 优化if (performDexopt) {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");// Do not run PackageDexOptimizer through the local performDexOpt// method because `pkg` may not be in `mPackages` yet.//// Also, don't fail application installs if the dexopt step fails.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);Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}// Notify BackgroundDexOptService that the package has been changed.// If this is an update of a package which used to fail to compile,// BackgroundDexOptService will remove it from its blacklist.// TODO: Layering violation//【13.2.3】将该应用从 odex fail list 中删除BackgroundDexOptService.notifyPackageChanged(pkg.packageName);//【14】为本次安装的 apk 更新目标用户信息synchronized (mPackages) {final PackageSetting ps = mSettings.mPackages.get(pkgName);if (ps != null) {res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);ps.setUpdateAvailable(false /*updateAvailable*/);}final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;for (int i = 0; i < childCount; i++) {PackageParser.Package childPkg = pkg.childPackages.get(i);PackageInstalledInfo childRes = res.addedChildPackages.get(childPkg.packageName);PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);if (childPs != null) {childRes.newUsers = childPs.queryInstalledUsers(sUserManager.getUserIds(), true);}}if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {updateSequenceNumberLP(ps, res.newUsers);updateInstantAppInstallerLocked(pkgName);}}}

7.3.1 PackageParser.parsePackage

PackageParser.parsePackage 其实我们在 PMS 启动扫描的过程中已经分析过了,这里我们重点关注其对 installFlags 的处理

    public boolean shouldCheckUpgradeKeySetLocked(PackageSettingBase oldPs, int scanFlags) {//【1】以下情况无需检查签名更新:// 1、本次是全新安装;2、扫描设置了 SCAN_INITIAL(move pkg);3、本次是覆盖安装,但是应用是 sharedUser 的// 4、应用不使用 upgradeKeySets// Can't rotate keys during boot or if sharedUser.if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || oldPs.isSharedUser()|| !oldPs.keySetData.isUsingUpgradeKeySets()) {return false;}// app is using upgradeKeySets; make sure all are valid//【2】如果应用是覆盖安装,且不是 move pkg,且不是共享 uid 的,且使用 upgradeKeySets,那么就要检查// upgradeKeySets 有效性,只有有效的 upgradeKeySets 才能检查更新long[] upgradeKeySets = oldPs.keySetData.getUpgradeKeySets();for (int i = 0; i < upgradeKeySets.length; i++) {//【2.1】判断 upgradeKeySets 是否有效if (!isIdValidKeySetId(upgradeKeySets[i])) {Slog.wtf(TAG, "Package "+ (oldPs.name != null ? oldPs.name : "<null>")+ " contains upgrade-key-set reference to unknown key-set: "+ upgradeKeySets[i]+ " reverting to signatures check.");return false;}}return true;}

7.3.3 checkUpgradeKeySetLP

进行签名更新检查:

    public boolean checkUpgradeKeySetLocked(PackageSettingBase oldPS,PackageParser.Package newPkg) {//【1】检查 key set 更新,更新有效的前提是新的 apk 持有的 keyset 至少包含旧应用的 keyset// Upgrade keysets are being used.  Determine if new package has a superset of the// required keys.long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets();for (int i = 0; i < upgradeKeySets.length; i++) {Set<PublicKey> upgradeSet = getPublicKeysFromKeySetLPr(upgradeKeySets[i]);if (upgradeSet != null && newPkg.mSigningDetails.publicKeys.containsAll(upgradeSet)) {return true;}}return false;}

7.3.4 verifySignaturesLP

校验签名:

    public static boolean verifySignatures(PackageSetting pkgSetting,PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures,boolean compareCompat, boolean compareRecover)throws PackageManagerException {final String packageName = pkgSetting.name;boolean compatMatch = false;if (pkgSetting.signatures.mSigningDetails.signatures != null) {// Already existing package. Make sure signatures matchboolean match = parsedSignatures.checkCapability(pkgSetting.signatures.mSigningDetails,PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)|| pkgSetting.signatures.mSigningDetails.checkCapability(parsedSignatures,PackageParser.SigningDetails.CertCapabilities.ROLLBACK);if (!match && compareCompat) {match = matchSignaturesCompat(packageName, pkgSetting.signatures,parsedSignatures);compatMatch = match;}if (!match && compareRecover) {match = matchSignaturesRecover(packageName,pkgSetting.signatures.mSigningDetails,parsedSignatures,PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)|| matchSignaturesRecover(packageName,parsedSignatures,pkgSetting.signatures.mSigningDetails,PackageParser.SigningDetails.CertCapabilities.ROLLBACK);}if (!match && isApkVerificationForced(disabledPkgSetting)) {match = matchSignatureInSystem(pkgSetting, disabledPkgSetting);}if (!match) {throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,"Package " + packageName +" signatures do not match previously installed version; ignoring!");}}// Check for shared user signaturesif (pkgSetting.sharedUser != null&& pkgSetting.sharedUser.signatures.mSigningDetails!= PackageParser.SigningDetails.UNKNOWN) {// Already existing package. Make sure signatures match.  In case of signing certificate// rotation, the packages with newer certs need to be ok with being sharedUserId with// the older ones.  We check to see if either the new package is signed by an older cert// with which the current sharedUser is ok, or if it is signed by a newer one, and is ok// with being sharedUser with the existing signing cert.boolean match =parsedSignatures.checkCapability(pkgSetting.sharedUser.signatures.mSigningDetails,PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)|| pkgSetting.sharedUser.signatures.mSigningDetails.checkCapability(parsedSignatures,PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);if (!match && compareCompat) {match = matchSignaturesCompat(packageName, pkgSetting.sharedUser.signatures, parsedSignatures);}if (!match && compareRecover) {match =matchSignaturesRecover(packageName,pkgSetting.sharedUser.signatures.mSigningDetails,parsedSignatures,PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)|| matchSignaturesRecover(packageName,parsedSignatures,pkgSetting.sharedUser.signatures.mSigningDetails,PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);compatMatch |= match;}if (!match) {throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,"Package " + packageName+ " has no signatures that match those in shared user "+ pkgSetting.sharedUser.name + "; ignoring!");}}return compatMatch;}

7.3.5 FileInstallArgs.doRename - 重命名临时目录

doRename 重命名文件,之前我们的临时目录为 /data/app/tmpl[SessionId].tmp,这里会将其重命名为 /data/app/packageName-X

        boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {if (status != PackageManager.INSTALL_SUCCEEDED) {cleanUp();return false;}//【1】获得改名前的临时目录 targetDir 就是 /data/app/tmpl[SessionId].tmpfinal File targetDir = codeFile.getParentFile();final File beforeCodeFile = codeFile;//【7.3.5.1】获得改名后的目录:/data/app/packageName-X,策略取决为 getNextCodePathfinal File afterCodeFile = getNextCodePath(targetDir, pkg.packageName);if (DEBUG_INSTALL) Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);try {Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());} catch (ErrnoException e) {Slog.w(TAG, "Failed to rename", e);return false;}//【2】恢复默认的安全上下文if (!SELinux.restoreconRecursive(afterCodeFile)) {Slog.w(TAG, "Failed to restorecon");return false;}//【3】更新 FileInstallArgs 的目录为最终目录// Reflect the rename internallycodeFile = afterCodeFile;resourceFile = afterCodeFile;// Reflect the rename in scanned detailstry {pkg.setCodePath(afterCodeFile.getCanonicalPath());} catch (IOException e) {Slog.e(TAG, "Failed to get path: " + afterCodeFile, e);return false;}//【4】更新扫描到的应用的 Package 中的目录数据// 以及 Application 中的数据pkg.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,afterCodeFile, pkg.baseCodePath));pkg.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,afterCodeFile, pkg.splitCodePaths));// Reflect the rename in app infopkg.setApplicationVolumeUuid(pkg.volumeUuid);pkg.setApplicationInfoCodePath(pkg.codePath);pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);pkg.setApplicationInfoResourcePath(pkg.codePath);pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);return true;}
7.3.5.1 getNextCodePath

getNextCodePath 方法用于获得最终的目录

    private File getNextCodePath(File targetDir, String packageName) {File result;SecureRandom random = new SecureRandom();byte[] bytes = new byte[16];do {//【1】最终目录为 /data/app/packageName-X(X=1,2,3...)// suffix 从 1 开始,如果 suffix 已存在,那就 +1random.nextBytes(bytes);String suffix = Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP);result = new File(targetDir, packageName + "-" + suffix);} while (result.exists());return result;}

7.3.7 freezePackageForInstall - 冻结应用

该方法用于冻结应用:

    public PackageFreezer freezePackageForInstall(String packageName, int installFlags,String killReason) {return freezePackageForInstall(packageName, UserHandle.USER_ALL, installFlags, killReason);}public PackageFreezer freezePackageForInstall(String packageName, int userId, int installFlags,String killReason) {if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {//【1】如果指定了卸载时不 kill app,就用默认的参数(卸载时才会进入)return new PackageFreezer();} else {//【2】默认是需要 kill app 的,调用 freezePackagereturn freezePackage(packageName, userId, killReason);}}

继续调用 freezePackage 方法:

    public PackageFreezer freezePackage(String packageName, int userId, String killReason) {//【×7.3.7.1】创建了一个 PackageFreezer 实例return new PackageFreezer(packageName, userId, killReason);}

7.3.7.1 new PackageFreezer

如果不杀进程,那就只创建一个 PackageFreezer 实例,并返回,不做任何事情

        public PackageFreezer() {mPackageName = null;mChildren = null;mWeFroze = false;mCloseGuard.open("close");}
//如果要 kill app,那么会创建 PackageFreezer(父包和子包),并且会杀掉进程public PackageFreezer(String packageName, int userId, String killReason) {synchronized (mPackages) {mPackageName = packageName;mWeFroze = mFrozenPackages.add(mPackageName);final PackageSetting ps = mSettings.mPackages.get(mPackageName);if (ps != null) {//【1】杀掉父包的进程killApplication(ps.name, ps.appId, userId, killReason);}final PackageParser.Package p = mPackages.get(packageName);if (p != null && p.childPackages != null) {final int N = p.childPackages.size();mChildren = new PackageFreezer[N];for (int i = 0; i < N; i++) {//【2】为子包创建 PackageFreezer,并杀掉子包的进程mChildren[i] = new PackageFreezer(p.childPackages.get(i).packageName,userId, killReason);}} else {mChildren = null;}}mCloseGuard.open("close");}

7.4 FileInstallArgs.doPostInstall

清理操作,正常情况不会触发

        int doPostInstall(int status, int uid) {//【1】如果安装前的状态不是 PackageManager.INSTALL_SUCCEEDEDif (status != PackageManager.INSTALL_SUCCEEDED) {//【7.2.1】执行清理操作cleanUp();}return status;}

7.5 new PostInstallData

又创建了一个 PostInstallData 对象,对 PackageInstalledInfo 做了再次封装:

    static class PostInstallData {public InstallArgs args;public PackageInstalledInfo res;PostInstallData(InstallArgs _a, PackageInstalledInfo _r) {args = _a;res = _r;}}

八、PackageHandler.doHandleMessage[POST_INSTALL] - 安装收尾

PackageHandler 会处理 POST_INSTALL 消息,此时已经处于安装收尾阶段:

                case POST_INSTALL: {if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);//【1】我们在安装开始前,会用 PostInstallData 封装安装结果,并保存到 mRunningInstalls 中// 在安装结束后,会处理本次的安装结果PostInstallData data = mRunningInstalls.get(msg.arg1);final boolean didRestore = (msg.arg2 != 0);mRunningInstalls.delete(msg.arg1);		// 删除掉if (data != null) {InstallArgs args = data.args;PackageInstalledInfo parentRes = data.res;//【2】本次安装是否授予运行时权限final boolean grantPermissions = (args.installFlags& PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;//【2】本次安装是否 kill appfinal boolean killApp = (args.installFlags& PackageManager.INSTALL_DONT_KILL_APP) == 0;final boolean virtualPreload = ((args.installFlags& PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);final String[] grantedPermissions = args.installGrantPermissions;// Handle the parent package//【×8.1】调用 handlePackagePostInstall 继续处理收尾工作handlePackagePostInstall(parentRes, grantPermissions, killApp,virtualPreload, grantedPermissions, didRestore,args.installerPackageName, args.observer);// Handle the child packages//【3】处理子包final int childCount = (parentRes.addedChildPackages != null)? parentRes.addedChildPackages.size() : 0;for (int i = 0; i < childCount; i++) {PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i);//【×8.1】调用 handlePackagePostInstall 继续处理收尾工作handlePackagePostInstall(childRes, grantPermissions, killApp,virtualPreload, grantedPermissions, false /*didRestore*/,args.installerPackageName, args.observer);}// Log tracing if neededif (args.traceMethod != null) {Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.traceMethod,args.traceCookie);}} else {Slog.e(TAG, "Bogus post-install token " + msg.arg1);}Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);} break;

8.1 PackageManagerS.handlePackagePostInstall

处理安装收尾工作:

    private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,boolean killApp, boolean virtualPreload, String[] grantedPermissions,boolean launchedForRestore, String installerPackage,IPackageInstallObserver2 installObserver) {//【1】当安装结果为 success 后,会进入后续的处理if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {// Send the removed broadcasts//【1.1】如果是 move pacakge,那么发送 removed 广播if (res.removedInfo != null) {res.removedInfo.sendPackageRemovedBroadcasts(killApp);}// Now that we successfully installed the package, grant runtime// permissions if requested before broadcasting the install. Also// for legacy apps in permission review mode we clear the permission// review flag which is used to emulate runtime permissions for// legacy apps.//【1.2】如果安装时指定了授予运行时权限,并且应用的目标 sdk 支持运行时权限,那就授予运行时权限if (grantPermissions) {final int callingUid = Binder.getCallingUid();mPermissionManager.grantRequestedRuntimePermissions(res.pkg, res.newUsers, grantedPermissions, callingUid,mPermissionCallback);}//【1.3】判断安装方式,是更新安装,还是全新安装!// 我们知道,如果 res.removedInfo 不为 null 的话,一定是覆盖更新final boolean update = res.removedInfo != null&& res.removedInfo.removedPackage != null;final String installerPackageName =res.installerPackageName != null? res.installerPackageName: res.removedInfo != null? res.removedInfo.installerPackageName: null;// If this is the first time we have child packages for a disabled privileged// app that had no children, we grant requested runtime permissions to the new// children if the parent on the system image had them already granted.//【1.4】如果被 disable 的特权应用之前没有子包,这是第一次有子包,那么我们会授予新的子包// 运行时权限,如果旧的特权应用之前已经授予if (res.pkg.parentPackage != null) {final int callingUid = Binder.getCallingUid();mPermissionManager.grantRuntimePermissionsGrantedToDisabledPackage(res.pkg, callingUid, mPermissionCallback);}synchronized (mPackages) {mInstantAppRegistry.onPackageInstalledLPw(res.pkg, res.newUsers);}final String packageName = res.pkg.applicationInfo.packageName;// Determine the set of users who are adding this package for// the first time vs. those who are seeing an update.//【1.5】决定在那些 user 下是第一次安装,那些用户下是覆盖更新int[] firstUserIds = EMPTY_INT_ARRAY;int[] firstInstantUserIds = EMPTY_INT_ARRAY;int[] updateUserIds = EMPTY_INT_ARRAY;int[] instantUserIds = EMPTY_INT_ARRAY;final boolean allNewUsers = res.origUsers == null || res.origUsers.length == 0;final PackageSetting ps = (PackageSetting) res.pkg.mExtras;for (int newUser : res.newUsers) {final boolean isInstantApp = ps.getInstantApp(newUser);if (allNewUsers) {if (isInstantApp) {firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);} else {firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);}continue;}// res.newUsers 表示本次安装新增加的目标 user// res.origUsers 标志之前安装的目标 userboolean isNew = true;for (int origUser : res.origUsers) {if (origUser == newUser) {isNew = false;break;}}if (isNew) {if (isInstantApp) {firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);} else {firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);}} else {if (isInstantApp) {instantUserIds = ArrayUtils.appendInt(instantUserIds, newUser);} else {updateUserIds = ArrayUtils.appendInt(updateUserIds, newUser);}}}// Send installed broadcasts if the package is not a static shared lib.if (res.pkg.staticSharedLibName == null) {mProcessLoggingHandler.invalidateProcessLoggingBaseApkHash(res.pkg.baseCodePath);// Send added for users that see the package for the first time// sendPackageAddedForNewUsers also deals with system appsint appId = UserHandle.getAppId(res.uid);boolean isSystem = res.pkg.applicationInfo.isSystemApp();sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload,virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds);// Send added for users that don't see the package for the first time//【1.5.1】给第一次安装的用户发送 ACTION_PACKAGE_ADDED 广播Bundle extras = new Bundle(1);extras.putInt(Intent.EXTRA_UID, res.uid);if (update) {//【1.5.2】给覆盖更新的用户发送 ACTION_PACKAGE_ADDED 广播,带 EXTRA_REPLACING 属性extras.putBoolean(Intent.EXTRA_REPLACING, true);}sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,extras, 0 /*flags*/,null /*targetPackage*/, null /*finishedReceiver*/,updateUserIds, instantUserIds);if (installerPackageName != null) {sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,extras, 0 /*flags*/,installerPackageName, null /*finishedReceiver*/,updateUserIds, instantUserIds);}// if the required verifier is defined, but, is not the installer of record// for the package, it gets notifiedfinal boolean notifyVerifier = mRequiredVerifierPackage != null&& !mRequiredVerifierPackage.equals(installerPackageName);if (notifyVerifier) {sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,extras, 0 /*flags*/,mRequiredVerifierPackage, null /*finishedReceiver*/,updateUserIds, instantUserIds);}// Send replaced for users that don't see the package for the first time//【1.5.3】给覆盖更新的用户发送 ACTION_PACKAGE_REPLACED / ACTION_MY_PACKAGE_REPLACED 广播if (update) {sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,packageName, extras, 0 /*flags*/,null /*targetPackage*/, null /*finishedReceiver*/,updateUserIds, instantUserIds);if (installerPackageName != null) {sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,extras, 0 /*flags*/,installerPackageName, null /*finishedReceiver*/,updateUserIds, instantUserIds);}if (notifyVerifier) {sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,extras, 0 /*flags*/,mRequiredVerifierPackage, null /*finishedReceiver*/,updateUserIds, instantUserIds);}sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,null /*package*/, null /*extras*/, 0 /*flags*/,packageName /*targetPackage*/,null /*finishedReceiver*/, updateUserIds, instantUserIds);} else if (launchedForRestore && !isSystemApp(res.pkg)) {// First-install and we did a restore, so we're responsible for the// first-launch broadcast.//【1.5.4】如果是第一次安装,同时我们要做一个恢复操作,并且 apk 不是系统应用// 那么我们会发送 ACTION_PACKAGE_FIRST_LAUNCH 广播if (DEBUG_BACKUP) {Slog.i(TAG, "Post-restore of " + packageName+ " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));}sendFirstLaunchBroadcast(packageName, installerPackage,firstUserIds, firstInstantUserIds);}// Send broadcast package appeared if forward locked/external for all users// treat asec-hosted packages like removable media on upgradeif (res.pkg.isForwardLocked() || isExternal(res.pkg)) {if (DEBUG_INSTALL) {Slog.i(TAG, "upgrading pkg " + res.pkg+ " is ASEC-hosted -> AVAILABLE");}final int[] uidArray = new int[]{res.pkg.applicationInfo.uid};ArrayList<String> pkgList = new ArrayList<>(1);pkgList.add(packageName);sendResourcesChangedBroadcast(true, true, pkgList, uidArray, null);}}// Work that needs to happen on first install within each user//【1.6】针对 firstUsers 做一些权限恢复和默认浏览器设置if (firstUserIds != null && firstUserIds.length > 0) {synchronized (mPackages) {for (int userId : firstUserIds) {// If this app is a browser and it's newly-installed for some// users, clear any default-browser state in those users. The// app's nature doesn't depend on the user, so we can just check// its browser nature in any user and generalize.//【1.6.1】默认浏览器设置if (packageIsBrowser(packageName, userId)) {mSettings.setDefaultBrowserPackageNameLPw(null, userId);}// We may also need to apply pending (restored) runtime// permission grants within these users.//【1.6.2】处理那些正在等待或者需要恢复的运行时权限授予mSettings.applyPendingPermissionGrantsLPw(packageName, userId);}}}if (allNewUsers && !update) {notifyPackageAdded(packageName);}// Log current value of "unknown sources" settingEventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,getUnknownSourcesSettings());// Remove the replaced package's older resources safely now// We delete after a gc for applications  on sdcard.if (res.removedInfo != null && res.removedInfo.args != null) {// 触发 GC 回收资源Runtime.getRuntime().gc();synchronized (mInstallLock) {//【5.8.1.1】移除掉更新后的旧 apkres.removedInfo.args.doPostDeleteLI(true);}} else {// Force a gc to clear up things. Ask for a background one, it's fine to go on// and not block here.VMRuntime.getRuntime().requestConcurrentGC();}// Notify DexManager that the package was installed for new users.// The updated users should already be indexed and the package code paths// should not change.// Don't notify the manager for ephemeral apps as they are not expected to// survive long enough to benefit of background optimizations.for (int userId : firstUserIds) {PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId);// There's a race currently where some install events may interleave with an uninstall.// This can lead to package info being null (b/36642664).if (info != null) {mDexManager.notifyPackageInstalled(info, userId);}}}// If someone is watching installs - notify them//【*8.2】通知观察者安装的结果,这里的 installObserver 是我们之前创建的 localObseverif (installObserver != null) {try {Bundle extras = extrasForInstallResult(res);installObserver.onPackageInstalled(res.name, res.returnCode,res.returnMsg, extras);} catch (RemoteException e) {Slog.i(TAG, "Observer no longer exists.");}}}

我们看到,这里涉及到了几个重要的广播:

  • Intent.ACTION_PACKAGE_ADDED:当有应用程序第一次安装时,会发送该广播给对应的 firstUsers!
    • 携带数据:Intent.EXTRA_UID,值为 apk uid;
  • Intent.ACTION_PACKAGE_REPLACED:当有应用程序被覆盖安装时,会发送该广播给对应的 updateUsers!
    • 携带数据:Intent.EXTRA_UID,
    • 携带数据:Intent.EXTRA_REPLACING,置为 true;
  • Intent.ACTION_MY_PACKAGE_REPLACED:当有应用程序被覆盖安装时,会发送该广播给对应的 updateUsers 下被更新的 pkg!
    • 携带数据:packageName,被更新的应用包;

8.1.1 FileInstallArgs.doPostDeleteLI - 删除被更新的旧 apk

        boolean doPostDeleteLI(boolean delete) {// XXX err, shouldn't we respect the delete flag?//【*8.1.2】清除 apk 文件 和 dex 文件cleanUpResourcesLI();return true;}

8.1.2 FileInstallArgs.cleanUpResourcesLI

        void cleanUpResourcesLI() {// Try enumerating all code paths before deletingList<String> allCodePaths = Collections.EMPTY_LIST;if (codeFile != null && codeFile.exists()) {try {//【1】收集 apk pathfinal PackageLite pkg = PackageParser.parsePackageLite(codeFile, 0);allCodePaths = pkg.getAllCodePaths();} catch (PackageParserException e) {// Ignored; we tried our best}}//【2】清除 apkcleanUp();//【3】清除 dex filesremoveDexFiles(allCodePaths, instructionSets);}

8.2 IPackageInstallObserver2.onPackageInstalled

这里的 installObserver 是我们在 4.3 PackageInstallerSession.commitLocked 中创建的另一个 Observer
位置:./frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java

final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {@Overridepublic void onUserActionRequired(Intent intent) {throw new IllegalStateException();}@Overridepublic void onPackageInstalled(String basePackageName, int returnCode, String msg,Bundle extras) {destroyInternal();dispatchSessionFinished(returnCode, msg, extras);}};

8.2.1 PackageInstallerSession.destroyInternal

关闭文件桥,删除文件拷贝:

    private void destroyInternal() {synchronized (mLock) {mSealed = true;mDestroyed = true;// Force shut down all bridgesfor (RevocableFileDescriptor fd : mFds) {fd.revoke();}//【1】关闭之前打开的文件桥对象for (FileBridge bridge : mBridges) {bridge.forceClose();}}if (stageDir != null) {try {//【2】删除之前的文件拷贝目录mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());} catch (InstallerException ignored) {}}}

8.2.2 PackageInstallerSession.dispatchSessionFinished

处理回调,通知监听者:

    private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {final IPackageInstallObserver2 observer;final String packageName;synchronized (mLock) {mFinalStatus = returnCode;mFinalMessage = msg;observer = mRemoteObserver;packageName = mPackageName;}if (observer != null) {// Execute observer.onPackageInstalled on different tread as we don't want callers// inside the system server have to worry about catching the callbacks while they are// calling into the sessionfinal SomeArgs args = SomeArgs.obtain();args.arg1 = packageName;args.arg2 = msg;args.arg3 = extras;args.arg4 = observer;args.argi1 = returnCode;//【*4.1.2】触发 mRemoteObserver 回调mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();}final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);// Send broadcast to default launcher only if it's a new installfinal boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);if (success && isNewInstall) {mPm.sendSessionCommitBroadcast(generateInfo(), userId);}//【*8.2.2.2】回调触发mCallback.onSessionFinished(this, success);}

在前面 4.1 commit 事务的时候,我们创建了一个 PackageInstallObserverAdapter,并将其保存到了 PackageInstallerSession.mRemoteObserver 中,这里首先会触发 mRemoteObserver 的回调
在 3.1.4.1 new InternalCallback 的时候,我们在创建 PackageInstallerSession 时,传入了一个回调对象 InternalCallback:

private final InternalCallback mInternalCallback = new InternalCallback();

InternalCallback 类定义在 PackageInstallerService 中,该对象的引用会被保存到 PackageInstallerSession.mCallback 变量中

8.2.2.2 InternalCallback.onSessionFinished

这里我们重点关于 onSessionFinished 方法:

        public void onSessionFinished(final PackageInstallerSession session, boolean success) {//【×8.2.2.2.1】注册者回调mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);mInstallHandler.post(new Runnable() {@Overridepublic void run() {synchronized (mSessions) {//【1】从 PackageInstallerService 中的 mSessions 移除了该 Session;mSessions.remove(session.sessionId);//【2】将该 Sesssion 添加到历史中;addHistoricalSessionLocked(session);final File appIconFile = buildAppIconFile(session.sessionId);if (appIconFile.exists()) {appIconFile.delete();}//【×3.1.6.1】持久化事务记录文件writeSessionsLocked();}}});}

我们看到,在 InternalCallback 中又回调了另外一个 Callback mCallbacks,它也是 PackageInstallerService 的内部类:

8.2.2.2.1 Callback.notifySessionFinished

前面我们分析过,Callback 本质上就是一个 Handler,这里就是向其所在的线程发送消息:

        public void notifySessionFinished(int sessionId, int userId, boolean success) {obtainMessage(MSG_SESSION_FINISHED, sessionId, userId, success).sendToTarget();}

在 3.1.4.2 Callbacks.notifySessionXXXX 中,我们分析过,最终其实是很将安装的结果分发给了注册在 Callback 中的所有远程回调:

private final RemoteCallbackList<IPackageInstallerCallback>mCallbacks = new RemoteCallbackList<>();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/175117.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

详解FreeRTOS:互斥信号量和递归互斥信号量(高级篇—3)

目录 1、互斥信号量 1.1、互斥信号量运作机制 1.2、创建互斥信号量

[SpringCloud] SpringCloud配置中心的核心原理

SpringCloud是什么时候去拉取配置中心的配置中心客户端的配置信息为什么要写在bootstrap文件中对象中注入的属性是如何动态刷新的一些开源的配置中心是如何整合SpringCloud的 文章目录 1.从SpringBoot的启动过程说起1.1 大致过程 2.准备Environment的核心操作2.1 前置操作 3.pr…

NOI / 1.10编程基础之简单排序 提问05:分数线划定 c语言 结构体

描述 世博会志愿者的选拔工作正在 A 市如火如荼的进行。为了选拔最合适的人才&#xff0c;A市对所有报名的选手进行了笔试&#xff0c;笔试分数达到面试分数线的选手方可进入面试。面试分数线根据计划录取人数的150%划定&#xff0c;即如果计划录取m名志愿者&#xff0c;则面试…

SEOAI每周资讯和Linus思考 231127

欢迎查看 Linus筋斗云SEO 的每周资讯整理&#xff0c;本周的一些要点&#xff1a; Google11月核心更新和评论更新仍然没有结束9个搜索结果页的主要变化&#xff1a;图标、品牌、粉丝数、新模块GSC已索引页面狂掉&#xff1f;Google的问题&#xff0c;已修复黑五网一期间的搜索…

Doris物化视图

物化视图就是包含了查询结果的数据库对象,可能是对远程数据的本地copy,也可能是一个表或多表join后结果的行或列的子集,也可能是聚合后的结果。说白了,就是预先存储查询结果的一种数据库对象。 在Doris中的物化视图,就是查询结果预先存储起来的特殊的表。 物化视图的出现主…

《100 Java Mistakes and How to Avoid Them》笔记 2

继续阅读本书&#xff0c;编程语言处理数值都有可能出现问题&#xff0c;如溢出&#xff0c;整数的最大最小值不对称&#xff0c;Double.NaN 等。 由于 Java 学了 C&#xff0c;也用 0 开始的数字来表示 8 进制数&#xff0c;如 037, 010 分别是十进制的 31 和 8&#xff0c;这…

强化学习中的Q学习

Q学习&#xff08;Q-Learning&#xff09;是强化学习中的一种基于值的学习方法&#xff0c;用于在有限马尔可夫决策过程&#xff08;MDP&#xff09;中学习最优的动作策略。Q学习主要用于离散状态和离散动作的问题。 以下是Q学习的基本概念和步骤&#xff1a; Q-Value&#xf…

Android控件全解手册 - 实现抽奖大转盘

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列

pkl文件的简介(Python中的Pickle)

文章目录 Pickle模块简介Pickle模块的使用 最近从Github上下载了一个预训练好的Faster-RCNN模型用于科研任务&#xff0c;突然对该文件的格式&#xff0c;.pkl文件产生了一丝疑惑&#xff0c;便去特意了解了一下该格式的文件的含义&#xff0c;下面与大家共享。 Pickle模块简介…

修改element的抽屉<el-drawer的宽度

Drawer 抽屉 有些时候, Dialog 组件并不满足我们的需求, 比如你的表单很长, 亦或是你需要临时展示一些文档, Drawer 拥有和 Dialog 几乎相同的 API, 在 UI 上带来不一样的体验. 我们通过el-button来显示Drawer&#xff0c;通过visible.sync来控制Drawer的显示状态。通过设置si…

在Spring Boot中隔离@Async异步任务的线程池

在异步任务执行的时候&#xff0c;我们知道其背后都有一个线程池来执行任务&#xff0c;但是为了控制异步任务的并发不影响到应用的正常运作&#xff0c;我们需要对线程池做好相关的配置&#xff0c;以防资源过度使用。这个时候我们就考虑将线程池进行隔离了。 那么我们为啥要…

【数据结构初阶】单链表

各位读者老爷好&#xff0c;鼠鼠我又来了哈。鼠鼠我呀现在来基于C语言实现以下单链表&#xff0c;希望对你有所帮助&#xff01; 目录 1.链表的概念及结构 2.链表的分类 3.无头单向非循环链表的实现 3.1.单链表打印 3.2.单链表尾插 3.3.单链表头插 3.4.单链表尾删 3.5…

在OpenCV中基于深度学习的边缘检测

引言 如何在OpenCV中使用基于深度学习的边缘检测&#xff0c;它比目前流行的canny边缘检测器更精确。边缘检测在许多用例中是有用的&#xff0c;如视觉显著性检测&#xff0c;目标检测&#xff0c;跟踪和运动分析&#xff0c;结构从运动&#xff0c;3D重建&#xff0c;自动驾驶…

MyBatis-Plus及多数据源入门教程

开发环境配置 JDK 1.8、Maven 3.8.8、 IDEA CE 2023.2、MySQL 8.0.34 框架介绍 MyBatis-Plus&#xff08;简称 MP&#xff09;是一个 MyBatis的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。MyBatis 是一款非常优秀的开源…

训练 CNN 对 CIFAR-10 数据中的图像进行分类

1. 加载 CIFAR-10 数据库 import keras from keras.datasets import cifar10# 加载预先处理的训练数据和测试数据 (x_train, y_train), (x_test, y_test) cifar10.load_data() 2. 可视化前 24 个训练图像 import numpy as np import matplotlib.pyplot as plt %matplotlib …

leetcode:506. 相对名次

一、题目 函数原型&#xff1a;char** findRelativeRanks(int* score, int scoreSize, int* returnSize) 二、思路 创建一个新的数组newscore&#xff0c;将原数组数据拷贝到新数组&#xff0c;降序排序新数组。 遍历原数组&#xff0c;在新数组中找到原数组中数据在新数组中的…

优雅使用docker-compose部署Skywalking

Skywalking使用docker-compose部署 version: 3.1 services: // 部署elasetic search 用于存储获取的应用信息与日志elasticsearch:image: elasticsearch:7.13.3container_name: elasticsearchprivileged: trueenvironment:- "cluster.nameelasticsearch" #设置集群名…

算法通关村第十二关-黄金挑战字符串冲刺题

最长公共前缀 描述 : 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 ""。 题目 : LeetCode 14.最长公共前缀 : 分析 : 第一种方式&#xff0c;我们可以竖着比较&#xff0c;如图所示&#xff0c;每前进一个位置就…

【论文解读】基于生成式面部先验的真实世界盲脸修复

论文地址&#xff1a;https://arxiv.org/pdf/2101.04061.pdf 代码地址&#xff1a;https://github.com/TencentARC/GFPGAN 图片解释&#xff1a; 与最先进的面部修复方法的比较&#xff1a;HiFaceGAN [67]、DFDNet [44]、Wan 等人。[61] 和 PULSE [52] 在真实世界的低质量图像…

CocosCreator 之 Tween缓动系统的使用

版本&#xff1a; 3.4.0 语言&#xff1a; TypeScript 环境&#xff1a; Mac 简介 在CocosCreator 3.x版本后&#xff0c; Tween缓动系统代替了原有的Action动作。官方使用缓动系统的主要目的之一是用于解决离线动画无法满足需求时的动态动画问题。 简单的示例&#xff1a; …