700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > android adb install apk的安装流程

android adb install apk的安装流程

时间:2021-10-05 16:31:28

相关推荐

android adb install apk的安装流程

目录:

一、PackageManagerService启动流程

二、apk adb安装流程

一、简介

1.Android上应用安装可以分为以下几种方式:

通过adb命令安装:adb 命令包括adb push/install用户下载的Apk,通过系统安装器packageinstaller安装该Apk。packageinstaller是系统内置的应用程序,用于安装和卸载应用程序。系统开机时安装系统应用。电脑或者手机上的应用商店自动安装

第三种系统安装我们在上个章节PackageManagerService启动流程已经说过,剩余的三种其实没什么太大的区别,主要的流程都是:1)PackageInstallerService.createSession创建sessionId。

2)session = new PackageInstaller.Session(mInterface.getPackageInstaller().openSession(sessionId)),splitName=base.apk

out = session.openWrite(splitName, 0, sizeBytes);创建一个临时的apk :/data/app/vmd+sessionId+tmp/base.apk ,把原apkt保存到刚刚创建的apk文件中

3)mit

他们的差别是:第一种是通过adb的方式安装,第二中是通过packageinstaller.apk的方式安装,第三种是oem厂商自己写,可以满足静默安装,默认赋予动态权限等。

2.APK文件结构:

apk的安装主要会解析上面哪几个文件呢?assert,lib,META-INF,res,AndroidManifest.xml。至于classes.dex是在apk启动的时候加载的。

3.adb install 安装时序图

上面图片不清晰的,可以查看/download/Bill_xiao/12328139

二、adb 安装流程

1.adb install +apk,这个命令会通过adb程序调用shell脚本,shell脚本会根据传递的参数来选择调用cmd程序,cmd在system/bin下面。

cmd.cpp的main函数会执行 status_t err = IBinder::shellCommand(service, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, args, cb, result);而它会调用PackageManagerServcie.onShellCommand方法

public void onShellCommand(FileDescriptor in, FileDescriptor out,FileDescriptor err, String[] args, ShellCallback callback,ResultReceiver resultReceiver) {(new PackageManagerShellCommand(this)).exec(this, in, out, err, args, callback, resultReceiver);}

PackageManagerShellCommand 继承ShellCommand ShellCommand.exec方法如下,可以看出会回调子类的onCommand方法

public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,String[] args, ShellCallback callback, ResultReceiver resultReceiver) {String cmd;int start;if (args != null && args.length > 0) {cmd = args[0];start = 1;} else {cmd = null;start = 0;}init(target, in, out, err, args, callback, start);// Log.i(" PackageManagerService-xiao", "ShellCommand exec cmd:"+cmd);mCmd = cmd;mResultReceiver = resultReceiver;if (DEBUG) Log.i(TAG, "Starting command " + mCmd + " on " + mTarget);int res = -1;try {res = onCommand(mCmd);if (DEBUG) Slog.d(TAG, "Executed command " + mCmd + " on " + mTarget);} catch (SecurityException e) {PrintWriter eout = getErrPrintWriter();eout.println("Security exception: " + e.getMessage());eout.println();e.printStackTrace(eout);} catch (Throwable e) {// Unlike usual calls, in this case if an exception gets thrown// back to us we want to print it back in to the dump data, since// that is where the caller expects all interesting information to// go.PrintWriter eout = getErrPrintWriter();eout.println();eout.println("Exception occurred while executing:");e.printStackTrace(eout);} finally {if (DEBUG) Slog.d(TAG, "Flushing output streams on " + mTarget);if (mOutPrintWriter != null) {mOutPrintWriter.flush();}if (mErrPrintWriter != null) {mErrPrintWriter.flush();}if (DEBUG) Slog.d(TAG, "Sending command result on " + mTarget);mResultReceiver.send(res, null);}if (DEBUG) Slog.d(TAG, "Finished command " + mCmd + " on " + mTarget);return res;}

@Overridepublic int onCommand(String cmd) {if (cmd == null) {return handleDefaultCommands(cmd);}final PrintWriter pw = getOutPrintWriter();try {switch(cmd) {case "install":return runInstall();case "install-abandon":case "install-destroy":return runInstallAbandon();case "install-commit":return runInstallCommit();case "install-create":return runInstallCreate();case "install-remove":return runInstallRemove();case "install-write":return runInstallWrite();case "install-existing":return runInstallExisting();case "compile":return runCompile();case "reconcile-secondary-dex-files":return runreconcileSecondaryDexFiles();case "bg-dexopt-job":return runDexoptJob();case "dump-profiles":return runDumpProfiles();case "list":return runList();case "uninstall":return runUninstall();case "resolve-activity":return runResolveActivity();case "query-activities":return runQueryIntentActivities();case "query-services":return runQueryIntentServices();case "query-receivers":return runQueryIntentReceivers();case "suspend":return runSuspend(true);case "unsuspend":return runSuspend(false);case "set-home-activity":return runSetHomeActivity();case "get-privapp-permissions":return runGetPrivappPermissions();case "get-privapp-deny-permissions":return runGetPrivappDenyPermissions();case "get-instantapp-resolver":return runGetInstantAppResolver();case "has-feature":return runHasFeature();default:return handleDefaultCommands(cmd);}} catch (RemoteException e) {pw.println("Remote exception: " + e);}return -1;}

从上面函数就能看出,不同的参数会执行不同的方法,我们的install,所以会执行runInstall()方法:

1.doCreateSession :创建PackageInstaller.Session函数。

2.doWriteSplit :创建一个临时的apk :/data/app/vmd+sessionId+tmp/base.apk

3.doCommitSession :执行mit方法,这一步才开始apk的安装。等待系统安装完成会发送一个广播带有PackageInstaller.EXTRA_STATUS字段的信息,里面回保存安装结果success或者Failure的原因

private int runInstall() throws RemoteException {final PrintWriter pw = getOutPrintWriter();final InstallParams params = makeInstallParams();final String inPath = getNextArg();setParamsSize(params, inPath);//创建sessionId mSessions.put(sessionId, session);final int sessionId = doCreateSession(params.sessionParams,params.installerPackageName, params.userId);boolean abandonSession = true;try {if (inPath == null && params.sessionParams.sizeBytes == -1) {pw.println("Error: must either specify a package size or an APK file");return 1;}//创建一个临时的apk :/data/app/vmd+sessionId+tmp/base.apk ,把原apk保存到刚刚创建的apk文件中if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {return 1;}if (doCommitSession(sessionId, false /*logSuccess*/)!= PackageInstaller.STATUS_SUCCESS) {return 1;}abandonSession = false;pw.println("Success");return 0;} finally {if (abandonSession) {try {doAbandonSession(sessionId, false /*logSuccess*/);} catch (Exception ignore) {}}}}

我们来看看sessionId到底是什么?

如下代码可以得出:

1.会检查用户是否具有安装apk的权限,添加一些安装flag

2.随机产生一个sessionId值,创建stageDir目录:/data/app/vmd+sessionId+tmp

3.创建一个新的session,并且保存值到mSessions,mSessions.put(sessionId, session);

PackageInstallerService.createSession

public int createSession(SessionParams params, String installerPackageName, int userId) {try {return createSessionInternal(params, installerPackageName, userId);} catch (IOException e) {throw ExceptionUtils.wrap(e);}}private int createSessionInternal(SessionParams params, String installerPackageName, int userId)throws IOException {final int callingUid = Binder.getCallingUid();mPm.enforceCrossUserPermission(callingUid, userId, true, true, "createSession");//检查APK安装权限//检查用户权限if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {throw new SecurityException("User restriction prevents installing");}//如果是root权限或者shell脚本安装(adb 安装)的赋值INSTALL_FROM_ADBif ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {params.installFlags |= PackageManager.INSTALL_FROM_ADB;} else {mAppOps.checkPackage(callingUid, installerPackageName);//检查包名跟uid是否匹配params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;if ((params.installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0&& !mPm.isCallerVerifier(callingUid)) {params.installFlags &= ~PackageManager.INSTALL_VIRTUAL_PRELOAD;}}// Only system components can circumvent runtime permissions when installing.//检查运行权限 adb安装就没有这个检查if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0&& mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {throw new SecurityException("You need the "+ "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "+ "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");}//检查是否安装到外部存储,新版本不允许。if ((params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0|| (params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {throw new IllegalArgumentException("New installs into ASEC containers no longer supported");}// Defensively resize giant app iconsif (params.appIcon != null) {final ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);final int iconSize = am.getLauncherLargeIconSize();if ((params.appIcon.getWidth() > iconSize * 2)|| (params.appIcon.getHeight() > iconSize * 2)) {params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,true);}}switch (params.mode) {case SessionParams.MODE_FULL_INSTALL:case SessionParams.MODE_INHERIT_EXISTING:break;default:throw new IllegalArgumentException("Invalid install mode: " + params.mode);}// If caller requested explicit location, sanity check it, otherwise// resolve the best internal or adopted location.//检测apk需要安装到指定文件夹挂载情况,/data/appif ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {if (!PackageHelper.fitsOnInternal(mContext, params)) {throw new IOException("No suitable internal storage available");}} else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {if (!PackageHelper.fitsOnExternal(mContext, params)) {throw new IOException("No suitable external storage available");}} else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {// For now, installs to adopted media are treated as internal from// an install flag point-of-view.params.setInstallFlagsInternal();} else {// For now, installs to adopted media are treated as internal from// an install flag point-of-view.params.setInstallFlagsInternal();// Resolve best location for install, based on combination of// requested install flags, delta size, and manifest settings.final long ident = Binder.clearCallingIdentity();try {params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, params);} finally {Binder.restoreCallingIdentity(ident);}}final int sessionId;final PackageInstallerSession session;synchronized (mSessions) {// Sanity check that installer isn't going crazy//表示由callingUid进程所执行安装的apk有多少个final int activeCount = getSessionCount(mSessions, callingUid);if (activeCount >= MAX_ACTIVE_SESSIONS) {throw new IllegalStateException("Too many active sessions for UID " + callingUid);}//表示由callingUid进程所已经安装的apk有多少个final int historicalCount = mHistoricalSessionsByInstaller.get(callingUid);if (historicalCount >= MAX_HISTORICAL_SESSIONS) {throw new IllegalStateException("Too many historical sessions for UID " + callingUid);}//随机产生一个数字,并且放到mAllocatedSessionssessionId = allocateSessionIdLocked();final long createdMillis = System.currentTimeMillis();// We're staging to exactly one locationFile stageDir = null;String stageCid = null;if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {final boolean isInstant =(params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;//params.volumeUuid=null stageDir:/data/app/vmd+sessionId+tmpstageDir = buildStageDir(params.volumeUuid, sessionId, isInstant);} else {stageCid = buildExternalStageCid(sessionId);//"smdl" + sessionId + ".tmp";}session = new PackageInstallerSession(mInternalCallback, mContext, mPm,mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,params, createdMillis, stageDir, stageCid, false, false);synchronized (mSessions) {mSessions.put(sessionId, session);}mCallbacks.notifySessionCreated(session.sessionId, session.userId);//通知回调session创建成功writeSessionsAsync();return sessionId;}

doWriteSplit比较简单,可以自行研究。那我们来看看doCommitSession 的mit做了哪些东西?

public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {Preconditions.checkNotNull(statusReceiver);final boolean wasSealed;synchronized (mLock) {assertCallerIsOwnerOrRootLocked();assertPreparedAndNotDestroyedLocked("commit");final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext, statusReceiver, sessionId, isInstallerDeviceOwnerLocked(), userId);//获取IPackageInstallObserver2.StubmRemoteObserver = adapter.getBinder();if (forTransfer) {mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);if (mInstallerUid == mOriginalInstallerUid) {throw new IllegalArgumentException("Session has not been transferred");}} else {//uid检查if (mInstallerUid != mOriginalInstallerUid) {throw new IllegalArgumentException("Session has been transferred");}}//没有调用sealAndValidateLocked之前都是falsewasSealed = mSealed;if (!mSealed) {try {sealAndValidateLocked();} catch (IOException e) {throw new IllegalArgumentException(e);} catch (PackageManagerException e) {destroyInternal();// Cannot call dispatchFinal synchronous as this might be called from inside the// system server on the main thread. Hence the call back scheduled in// dispachFinal has to be scheduled on a different thread.mHandler.obtainMessage(MSG_SESSION_FINISHED_WITH_EXCEPTION, e).sendToTarget();return;}}// Client staging is fully done at this pointmClientProgress = 1f;computeProgressLocked(true);// This ongoing commit should keep session active, even though client// will probably close their end.mActiveCount.incrementAndGet();mCommitted = true;mHandler.obtainMessage(MSG_COMMIT).sendToTarget();}if (!wasSealed) {// Persist the fact that we've sealed ourselves to prevent// mutations of any hard links we create. We do this without holding// the session lock, since otherwise it's a lock inversion.mCallback.onSessionSealedBlocking(this);}}

主要做了:1.创建PackageInstallObserverAdapter实例,从名字就能知道是用于apk的安装结果观察。

2.调用sealAndValidateLocked()方法

3.发送MSG_COMMIT广播。

先重点查看sealAndValidateLocked做了什么事

private void sealAndValidateLocked() throws PackageManagerException, IOException {assertNoWriteFileTransfersOpenLocked();assertPreparedAndNotDestroyedLocked("sealing of session");//如果此apk已经安装过,或者存在的话,从mPackages中获取packageInfo,如果没有就是nullfinal PackageInfo pkgInfo = mPm.getPackageInfo(params.appPackageName, PackageManager.GET_SIGNATURES| PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);File mFile= resolveStageDirLocked();mSealed = true;// Verify that stage looks sane with respect to existing application.// This currently only ensures packageName, versionCode, and certificate// consistency.try {validateInstallLocked(pkgInfo);} catch (PackageManagerException e) {throw e;} catch (Throwable e) {// Convert all exceptions into package manager exceptions as only those are handled// in the code abovethrow new PackageManagerException(e);}// Read transfers from the original owner stay open, but as the session's data// cannot be modified anymore, there is no leak of information.}

从上述的代码看:1.查询此apk是否安装过,如果安装过的话pkgInfo不为空

2.resolveStageDirLocked 获取apk源文件目录,这个目录是在createSession中创建的:/data/app/vmd+sessionId+tmp

3.执行validateInstallLocked方法,扫描/data/app/vmd+sessionId+tmp 目录下的文件,清空缓存文件,对以.apk结尾的文件进行扫描。获取一些基本的apk信息,主要解析mainfest,获取包名及其他属性信息coreApp ,multiArch, use32bitAbi,extractNativeLibs ,versionCode 。

private void validateInstallLocked(@Nullable PackageInfo pkgInfo)throws PackageManagerException {mPackageName = null;mVersionCode = -1;mSignatures = null;mResolvedBaseFile = null;mResolvedStagedFiles.clear();mResolvedInheritedFiles.clear();try {resolveStageDirLocked();} catch (IOException e) {throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,"Failed to resolve stage location", e);}//移除/data/app/vmd+sessionId+tmp下面.removed结尾的文件final File[] removedFiles = mResolvedStageDir.listFiles(sRemovedFilter);final List<String> removeSplitList = new ArrayList<>();if (!ArrayUtils.isEmpty(removedFiles)) {for (File removedFile : removedFiles) {final String fileName = removedFile.getName();final String splitName = fileName.substring(0, fileName.length() - REMOVE_SPLIT_MARKER_EXTENSION.length());removeSplitList.add(splitName);}}//添加/data/app/vmd+lsessionId+tmp下面,不是.removed结尾的文件final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) {throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");}// Verify that all staged packages are internally consistentfinal ArraySet<String> stagedSplits = new ArraySet<>();for (File addedFile : addedFiles) {final ApkLite apk;try {int flags = PackageParser.PARSE_COLLECT_CERTIFICATES;if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {flags |= PackageParser.PARSE_IS_EPHEMERAL;}//解析apk,主要解析mainfest,获取包名及其他属性信息coreApp ,multiArch, use32bitAbi, extractNativeLibs ,versionCode apk = PackageParser.parseApkLite(addedFile, flags);} catch (PackageParserException e) {throw PackageManagerException.from(e);}if (!stagedSplits.add(apk.splitName)) {throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,"Split " + apk.splitName + " was defined multiple times");}// Use first package to define unknown valuesif (mPackageName == null) {mPackageName = apk.packageName;mVersionCode = apk.versionCode;}if (mSignatures == null) {mSignatures = apk.signatures;mCertificates = apk.certificates;}assertApkConsistentLocked(String.valueOf(addedFile), apk);// Take this opportunity to enforce uniform namingfinal String targetName;if (apk.splitName == null) {targetName = "base.apk";} else {targetName = "split_" + apk.splitName + ".apk";}if (!FileUtils.isValidExtFilename(targetName)) {throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,"Invalid filename: " + targetName);}final File targetFile = new File(mResolvedStageDir, targetName);if (!addedFile.equals(targetFile)) {addedFile.renameTo(targetFile);}// Base is coming from sessionif (apk.splitName == null) {mResolvedBaseFile = targetFile;}mResolvedStagedFiles.add(targetFile);}if (removeSplitList.size() > 0) {if (pkgInfo == null) {throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,"Missing existing base package for " + mPackageName);}// validate split names marked for removalfor (String splitName : removeSplitList) {if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) {throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,"Split not found: " + splitName);}}// ensure we've got appropriate package name, version code and signaturesif (mPackageName == null) {mPackageName = pkgInfo.packageName;mVersionCode = pkgInfo.versionCode;}if (mSignatures == null) {mSignatures = pkgInfo.signatures;}}//PackageManagerShellCommand makeInstallParams 在初始化就赋值SessionParams.MODE_FULL_INSTALLif (params.mode == SessionParams.MODE_FULL_INSTALL) {// Full installs must include a base packageif (!stagedSplits.contains(null)) {throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,"Full install must include a base package");}} else {// Partial installs must be consistent with existing installif (pkgInfo == null || pkgInfo.applicationInfo == null) {throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,"Missing existing base package for " + mPackageName);}final PackageLite existing;final ApkLite existingBase;ApplicationInfo appInfo = pkgInfo.applicationInfo;try {existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0);existingBase = PackageParser.parseApkLite(new File(appInfo.getBaseCodePath()),PackageParser.PARSE_COLLECT_CERTIFICATES);} catch (PackageParserException e) {throw PackageManagerException.from(e);}assertApkConsistentLocked("Existing base", existingBase);//包名版本的信息检测// Inherit base if not overriddenif (mResolvedBaseFile == null) {mResolvedBaseFile = new File(appInfo.getBaseCodePath());mResolvedInheritedFiles.add(mResolvedBaseFile);}// Inherit splits if not overriddenif (!ArrayUtils.isEmpty(existing.splitNames)) {for (int i = 0; i < existing.splitNames.length; i++) {final String splitName = existing.splitNames[i];Log.i(" PackageManagerService-xiao", "PackageInstallerSession validateInstallLocked splitName :"+splitName);final File splitFile = new File(existing.splitCodePaths[i]);final boolean splitRemoved = removeSplitList.contains(splitName);if (!stagedSplits.contains(splitName) && !splitRemoved) {mResolvedInheritedFiles.add(splitFile);}}}// Inherit compiled oat directory.final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile();mInheritedFilesBase = packageInstallDir;final File oatDir = new File(packageInstallDir, "oat");//解析odxif (oatDir.exists()) {final File[] archSubdirs = oatDir.listFiles();// Keep track of all instruction sets we've seen compiled output for.// If we're linking (and not copying) inherited files, we can recreate the// instruction set hierarchy and link compiled output.if (archSubdirs != null && archSubdirs.length > 0) {final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets();for (File archSubDir : archSubdirs) {// Skip any directory that isn't an ISA subdir.if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {continue;}mResolvedInstructionSets.add(archSubDir.getName());List<File> oatFiles = Arrays.asList(archSubDir.listFiles());// Only add compiled files associated with the base.// Once b/62269291 is resolved, we can add all compiled files again.for (File oatFile : oatFiles) {if (oatFile.getName().equals("base.art")|| oatFile.getName().equals("base.odex")|| oatFile.getName().equals("base.vdex")) {mResolvedInheritedFiles.add(oatFile);}}}}}}}

现在我们来看看MSG_COMMIT做什么?就是直接调用commitLocked

private final Handler.Callback mHandlerCallback = new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {switch (msg.what) {case MSG_COMMIT:synchronized (mLock) {try {commitLocked();} catch (PackageManagerException e) {final String completeMsg = ExceptionUtils.getCompleteMessage(e);Slog.e(TAG,"Commit of session " + sessionId + " failed: " + completeMsg);destroyInternal();dispatchSessionFinished(e.error, completeMsg, null);}}

private void commitLocked()throws PackageManagerException {if (mDestroyed) {throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");}if (!mSealed) {throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");}Preconditions.checkNotNull(mPackageName);Preconditions.checkNotNull(mSignatures);Preconditions.checkNotNull(mResolvedBaseFile);//needToAskForPermissionsLocked 是否具有静默安装权限if (needToAskForPermissionsLocked()) {// User needs to accept permissions; give installer an intent they// can use to involve user.//发送广播权限已经被确认好了final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS);intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);try {mRemoteObserver.onUserActionRequired(intent);} catch (RemoteException ignored) {}// Commit was keeping session marked as active until now; release// that extra refcount so session appears idle.closeInternal(false);return;}if (stageCid != null) {// Figure out the final installed size and resize the container once// and for all. Internally the parser handles straddling between two// locations when inheriting.final long finalSize = calculateInstalledSize();resizeContainer(stageCid, finalSize);}// Inherit any packages and native libraries from existing install that// haven't been overridden.if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {try {final List<File> fromFiles = mResolvedInheritedFiles;final File toDir = resolveStageDirLocked();if (LOGD) Log.d("PackageManagerService-xiao", "Inherited files: " + mResolvedInheritedFiles);if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {throw new IllegalStateException("mInheritedFilesBase == null");}if (isLinkPossible(fromFiles, toDir)) {if (!mResolvedInstructionSets.isEmpty()) {final File oatDir = new File(toDir, "oat");createOatDirs(mResolvedInstructionSets, oatDir);}linkFiles(fromFiles, toDir, mInheritedFilesBase);} else {// TODO: this should delegate to DCS so the system process// avoids holding open FDs into containers.copyFiles(fromFiles, toDir);}} catch (IOException e) {throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,"Failed to inherit existing install", e);}}// TODO: surface more granular state from dexoptmInternalProgress = 0.5f;computeProgressLocked(true);// Unpack native librariesextractNativeLibraries(mResolvedStageDir, params.abiOverride);//拷贝外部的库// Container is ready to go, let's seal it up!if (stageCid != null) {finalizeAndFixContainer(stageCid);}// We've reached point of no return; call into PMS to install the stage.// Regardless of success or failure we always destroy session.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();//删除stageDir临时文件dispatchSessionFinished(returnCode, msg, extras);}};final UserHandle user;if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {user = UserHandle.ALL;} else {user = new UserHandle(userId);}mRelinquished = true;mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,mInstallerPackageName, mInstallerUid, user, mCertificates);}

确认安装权限和安装位置之后调用PackageManagerService.installStage。大家有没有一种恍然大悟的感觉,之前所做的所有事情最后都是会回到PackageManagerService,由它来扫描安装apk。那之后的过程跟我们之前讲述的系统扫描是否会相同呢?

void installStage(String packageName, File stagedDir, String stagedCid,IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,String installerPackageName, int installerUid, UserHandle user,Certificate[][] certificates) {if (true) {if ((sessionParams.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {Log.d(TAG, "Ephemeral install of " + packageName);}}final VerificationInfo verificationInfo = new VerificationInfo(sessionParams.originatingUri, sessionParams.referrerUri,sessionParams.originatingUid, installerUid);final OriginInfo origin;if (stagedDir != null) {origin = OriginInfo.fromStagedFile(stagedDir);} else {origin = OriginInfo.fromStagedContainer(stagedCid);}final Message msg = mHandler.obtainMessage(INIT_COPY);final int installReason = fixUpInstallReason(installerPackageName, installerUid,sessionParams.installReason);//安装原因final InstallParams params = new InstallParams(origin, null, observer,sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,verificationInfo, user, sessionParams.abiOverride,sessionParams.grantedRuntimePermissions, certificates, 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));mHandler.sendMessage(msg);}

创建安装参数InstallParams,发送安装广播INIT_COPY。

void doHandleMessage(Message msg) {switch (msg.what) {case INIT_COPY: {HandlerParams params = (HandlerParams) msg.obj;// idx为当前需要安装的APK个数,mPendingInstalls里面保存所有需要安装的APK解析出来的HandlerParams参数int idx = mPendingInstalls.size();if (DEBUG_INSTALL) Log.i(TAG, "doHandleMessage init_copy idx=" + idx + ": " + params);// If a bind was already initiated we dont really// need to do anything. The pending install// will be processed later on.if (!mBound) {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./*APK的安装居然需要使用另外一个APK提供的服务,该服务就是DefaultContainerService,由DefaultCotainerService.apk提供,下面的connectToService函数将调用bindService来启动该服务*/if (!connectToService()) {Log.e(TAG, "Failed to bind to media container service");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.mPendingInstalls.add(idx, params);}} else {mPendingInstalls.add(idx, params);// Already bound to the service. Just make// sure we trigger off processing the first request.//启动安装if (idx == 0) {mHandler.sendEmptyMessage(MCS_BOUND);}}break;}case MCS_BOUND: {if (DEBUG_INSTALL) Log.i(TAG, "mcs_bound");if (msg.obj != null) {mContainerService = (IMediaContainerService) msg.obj;Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",System.identityHashCode(mHandler));}if (mContainerService == null) {if (!mBound) {// Something seriously wrong since we are not bound and we are not// waiting for connection. Bail out.Log.e(TAG, "Cannot bind to media container service");for (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);}return;}mPendingInstalls.clear();//如果没法启动该service,则不能安装程序} else {Log.w(TAG, "Waiting to connect to media container service");}} else if (mPendingInstalls.size() > 0) {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");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 installif (mPendingInstalls.size() > 0) {mPendingInstalls.remove(0);//删除队列头}if (mPendingInstalls.size() == 0) {if (mBound) {if (DEBUG_SD_INSTALL) Log.i(TAG,"Posting delayed MCS_UNBIND");//如果安装请求都处理完了,则需要和Service断绝联系,//通过发送MSC_UNB消息处理断交请求。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.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.Log.w(TAG, "Empty queue");}break;}

doHandleMessage 处理INIT_COPY广播主要做了哪些事情。

1.连接DefaultCotainerService服务,把刚才send的信息添加到mPendingInstalls.add(idx, params)。发送mHandler.sendEmptyMessage(MCS_BOUND)广播。

2.执行params.startCopy(),params就是之前InstallParams。

HandlerParams和InstallArgs介绍

除了HandlerParams家族外,这里提前请出另外一个家族InstallArgs及其成员,如图4-8所示。

:-:

图4-8 HandlerParams及InstallArgs家族成员

由图4-8可知:

HandlerParams和InstallArgs均为抽象类。HandlerParams有三个子类,分别是InstallParams、MoveParams和MeasureParams。其中,InstallParams用于处理APK的安装,MoveParams用于处理某个已安装APK的搬家请求(例如从内部存储移动到SD卡上),MeasureParams用于查询某个已安装的APK占据存储空间的大小(例如在设置程序中得到的某个APK使用的缓存文件的大小)。对于InstallParams来说,它还有两个伴儿,即InstallArgs的派生类FileInstallArgs和SdInstallArgs。其中,FileInstallArgs针对的是安装在内部存储的APK,而SdInstallArgs针对的是那些安装在SD卡上的APK。

final boolean startCopy() {boolean res;try {if (DEBUG_INSTALL) Log.i(TAG, "HandlerParams startCopy " + mUser + ": " + this);//MAX_RETIRES目前为4,表示尝试4次安装,如果还不成功,则认为安装失败if (++mRetries > MAX_RETRIES) {Log.w(TAG, "Failed to invoke remote methods on default container service. Giving up");mHandler.sendEmptyMessage(MCS_GIVE_UP);handleServiceError();return false;} else {handleStartCopy();res = true;}} catch (RemoteException e) {if (DEBUG_INSTALL) Log.i(TAG, "Posting install MCS_RECONNECT");mHandler.sendEmptyMessage(MCS_RECONNECT);res = false;}handleReturnCode();return res;}

在上述代码中,基类的startCopy将调用子类实现的handleStartCopy和handleReturnCode函数。下面来看InstallParams是如何实现这两个函数的。先来看派生类InstallParams的handleStartCopy函数

public void handleStartCopy() throws RemoteException {int ret = PackageManager.INSTALL_SUCCEEDED;Log.i(" PackageManagerService-xiao", "PackageManagerService handleStartCopy origin.staged:"+origin.staged);// If we're already staged, we've firmly committed to an install location// 是安装在手机内部存储空间还是sdcard中,设置对应标志位// 新安装的情况下stage为true,也就是默认内部存储if (origin.staged) {if (origin.file != null) {installFlags |= PackageManager.INSTALL_INTERNAL;installFlags &= ~PackageManager.INSTALL_EXTERNAL;} else if (origin.cid != null) {installFlags |= PackageManager.INSTALL_EXTERNAL;installFlags &= ~PackageManager.INSTALL_INTERNAL;} else {throw new IllegalStateException("Invalid stage location");}}final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;//安装到sd卡final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;//安装到内部存储final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;PackageInfoLite pkgLite = null;// 检查APK的安装位置是否正确if (onInt && onSd) {//不能同时安装到内部和外部// Check if both bits are set.Log.w(TAG, "Conflicting flags specified for installing on both internal and external");ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;} else if (onSd && ephemeral) {Log.w(TAG, "Conflicting flags specified for installing ephemeral on external");ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;} else {//一般是这个pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,packageAbiOverride);//获取一个合适的安装位置,包名等if (DEBUG_EPHEMERAL && ephemeral) {Log.v(TAG, "pkgLite for install: " + pkgLite);}/** If we have too little free space, try to free cache* before giving up.*/if (!origin.staged && pkgLite.recommendedInstallLocation== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {// TODO: focus freeing disk space on the target devicefinal StorageManager storage = StorageManager.from(mContext);//从DSMS查询内部空间最小余量,默认是总空间的10%final long lowThreshold = storage.getStorageLowBytes(Environment.getDataDirectory());final long sizeBytes = mContainerService.calculateInstalledSize(origin.resolvedPath, isForwardLocked(), packageAbiOverride);try {// 释放存储空间mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,installFlags, packageAbiOverride);} catch (InstallerException e) {Log.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.*/if (pkgLite.recommendedInstallLocation== PackageHelper.RECOMMEND_FAILED_INVALID_URI) {pkgLite.recommendedInstallLocation= PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;}}}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 {/// M: Removable system app supportinstallFlags = sPmsExt.customizeInstallPkgFlags(installFlags, pkgLite,mSettings.mPackages, getUser());// Override with defaults if needed.//②根据DCS返回的安装路径,还需要调用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 flagsif (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_EPHEMERAL) {Log.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;}}}}//③创建一个安装参数对象,对于安装位置为内部存储的情况,args的真实类型为FileInstallArgsfinal 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 appUserHandle 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.*/final int requiredUid = mRequiredVerifierPackage == null ? -1: getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,verifierUser.getIdentifier());final int installerUid =verificationInfo == null ? -1 : verificationInfo.installerUid;if (!origin.existing && requiredUid != -1&& isVerificationEnabled(verifierUser.getIdentifier(), installFlags, installerUid)) {// 对当前包做验证操作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);verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);// Query all live verifiers based on current user statefinal List<ResolveInfo> receivers = queryIntentReceiversInternal(verification,PACKAGE_MIME_TYPE, 0, verifierUser.getIdentifier(),false /*allowDynamicSplits*/);if (DEBUG_VERIFY) {Log.d(TAG, "Found " + receivers.size() + " verifiers for intent "+ verification.toString() + " with " + pkgLite.verifiers.length+ " optional verifiers");}final int verificationId = mPendingVerificationToken++;verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE,installerPackageName);verification.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);if (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);}}final PackageVerificationState verificationState = new PackageVerificationState(requiredUid, args);mPendingVerification.append(verificationId, verificationState);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) {Log.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);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);}}}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.*/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) {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.*///调用args的copyApk函数ret = args.copyApk(mContainerService, true);}}mRet = ret;}

在以上代码中,一共列出了五个关键点,总结如下:

调用DCS的getMinimalPackageInfo函数,将得到一个PackageLite对象,该对象是一个轻量级的用于描述APK的结构(相比PackageParser.Package来说)。在这段代码逻辑中,主要想取得其recommendedInstallLocation的值。此值表示该APK推荐的安装路径。调用installLocationPolicy检查推荐的安装路径。例如系统Package不允许安装在SD卡上。createInstallArgs将根据安装位置创建不同的InstallArgs。如果是内部存储,则返回FileInstallArgs,否则为SdInstallArgs。在正式安装前,应先对该APK进行必要的检查。这部分代码后续再介绍。调用InstallArgs的copyApk。对本例来说,将调用FileInstallArgs的copyApk函数。

DefaultContainerService.java::getMinimalPackageInfo函数

public PackageInfoLite getMinimalPackageInfo(String packagePath, int flags,String abiOverride) {final Context context = DefaultContainerService.this;final boolean isForwardLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;PackageInfoLite ret = new PackageInfoLite();if (packagePath == null) {Slog.i(TAG, "Invalid package file " + packagePath);ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;return ret;}final File packageFile = new File(packagePath);final PackageParser.PackageLite pkg;final long sizeBytes;try {pkg = PackageParser.parsePackageLite(packageFile, 0);sizeBytes = PackageHelper.calculateInstalledSize(pkg, isForwardLocked, 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;} else {ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;}return ret;}final int recommendedInstallLocation;final long token = Binder.clearCallingIdentity();try {//调用recommendAppInstallLocation,取得一个合理的安装位置PackageHelper.RECOMMEND_INSTALL_INTERNAL 或者RECOMMEND_INSTALL_EXTERNALrecommendedInstallLocation = PackageHelper.resolveInstallLocation(context,pkg.packageName, pkg.installLocation, sizeBytes, flags);} finally {Binder.restoreCallingIdentity(token);}ret.packageName = pkg.packageName;ret.splitNames = pkg.splitNames;ret.versionCode = pkg.versionCode;ret.baseRevisionCode = pkg.baseRevisionCode;ret.splitRevisionCodes = pkg.splitRevisionCodes;ret.installLocation = pkg.installLocation;ret.verifiers = pkg.verifiers;ret.recommendedInstallLocation = recommendedInstallLocation;ret.multiArch = pkg.multiArch;return ret;}@Overridepublic ObbInfo getObbInfo(String filename) {try {return ObbScanner.getObbInfo(filename);} catch (IOException e) {Slog.d(TAG, "Couldn't get OBB info for " + filename);return null;}}

APK可在AndroidManifest.xml中声明一个安装位置,不过DCS除了解析该位置外,还需要做进一步检查,这个工作由recommendAppInstallLocation函数完成,代码如下

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

public static int resolveInstallLocation(Context context, SessionParams params)throws IOException {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;if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {prefer = RECOMMEND_INSTALL_INTERNAL;ephemeral = true;checkBoth = false;} else if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {prefer = RECOMMEND_INSTALL_INTERNAL;checkBoth = false;} else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {prefer = RECOMMEND_INSTALL_EXTERNAL;checkBoth = false;} else if (params.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {prefer = RECOMMEND_INSTALL_INTERNAL;checkBoth = false;} else if (params.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {prefer = RECOMMEND_INSTALL_EXTERNAL;checkBoth = true;} else if (params.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {//一般设定的位置为AUTO,默认是内部空间// When app is already installed, prefer same mediumif (existingInfo != null) {// TODO: distinguish if this is external ASECif ((existingInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {prefer = RECOMMEND_INSTALL_EXTERNAL;} else {prefer = RECOMMEND_INSTALL_INTERNAL;}} else {prefer = RECOMMEND_INSTALL_INTERNAL;}checkBoth = true;} else {prefer = RECOMMEND_INSTALL_INTERNAL;checkBoth = false;}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);}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_STORAGEif (fitsOnInternal) {return (ephemeral)? PackageHelper.RECOMMEND_INSTALL_EPHEMERAL: PackageHelper.RECOMMEND_INSTALL_INTERNAL;}} else if (prefer == RECOMMEND_INSTALL_EXTERNAL) {if (fitsOnExternal) {return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;}}if (checkBoth) {if (fitsOnInternal) {return PackageHelper.RECOMMEND_INSTALL_INTERNAL;} else if (fitsOnExternal) {return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;}}return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;}

DCS的getMinimalPackageInfo函数为了得到一个推荐的安装路径做了不少工作,其中,各种安装策略交叉影响。这里总结一下相关的知识点:

APK在AndroidManifest.xml中设置的安装点默认为AUTO,在具体对应时倾向内部空间。用户在Settings数据库中设置的安装位置。检查外部存储或内部存储是否有足够空间

PackageManagerService.java::InstallArgs.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);}}

/*本例中temp参数为true,createCopyFile将在/data/app下创建一个临时文件。临时文件名为vmdl-随机数.tmp。为什么会用这样的文件名呢?因为PKMS通过Linux的inotify机制监控了/data/app,目录,如果新复制生成的文件名后缀为apk,将触发PKMS扫描。为了防止发生这种情况,这里复制生成的文件才有了如此奇怪的*///文件以及拷贝完成就不需要再拷贝if (origin.staged) {if (DEBUG_INSTALL) Log.d(TAG, origin.file + " already staged; skipping copy temp:"+temp);codeFile = origin.file;resourceFile = origin.file;return PackageManager.INSTALL_SUCCEEDED;}try {final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;// 创建目录 /data/app/vmdl1054430479.tmp/final File tempDir =mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);Log.i("PackageManagerService-xiao", "PackageManagerService FileInstallArgs tempDir:"+tempDir.getAbsolutePath());codeFile = tempDir;resourceFile = tempDir;} catch (IOException e) {Log.w(TAG, "Failed to create copy file: " + e);return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;}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 {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;// 真正的文件拷贝ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);if (ret != PackageManager.INSTALL_SUCCEEDED) {Log.e(TAG, "Failed to copy package");return ret;}// 获取库的跟目录final File libraryRoot = new File(codeFile, LIB_DIR_NAME);NativeLibraryHelper.Handle handle = null;try {handle = NativeLibraryHelper.Handle.create(codeFile);// 拷贝 Native代码 即so文件ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,abiOverride);} catch (IOException e) {Log.e(TAG, "Copying native libraries failed", e);ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;} finally {IoUtils.closeQuietly(handle);}return ret;}

4. handleReturnCode分析

在HandlerParams的startCopy函数中,handleStartCopy执行完之后,将调用handleReturnCode开展后续工作,代码如下:

PackageManagerService.java::InstallParams.HandleParams

void 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.if (mArgs != null) {processPendingInstall(mArgs, mRet);}}

private void processPendingInstall(final InstallArgs args, final int currentStatus) {// Queue up an async operation since the package installation may take a little while.// 向mHandler中发送一个Runnable对象,这里是异步操作,因为安装一个程序包可能需要一些时间//一般currentStatus=1mHandler.post(new Runnable() {public void run() {mHandler.removeCallbacks(this);// 清除任务// Result object to be returnedPackageInstalledInfo res = new PackageInstalledInfo(); // 创建一个PackageInstalledInfo对象res.setReturnCode(currentStatus);res.uid = -1;res.pkg = null;res.removedInfo = null;if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {// 预安装阶段,主要是检查安装包的状态,确保安装环境正常,如果安装环境有问题会清理拷贝文件args.doPreInstall(res.returnCode);synchronized (mInstallLock) {// 安装阶段,调用installPackageLI进行安装installPackageTracedLI(args, res);}args.doPostInstall(res.returnCode, res.uid); // 清除一些临时文件}// A restore should be performed at this point if (a) the install// succeeded, (b) the operation is not an update, and (c) the new// package has not opted out of backup participation.final boolean update = res.removedInfo != 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.int token;//计算一个ID号if (mNextInstallToken < 0) mNextInstallToken = 1;token = mNextInstallToken++;PostInstallData data = new PostInstallData(args, res);mRunningInstalls.put(token, data);//保存到mRunningInstalls结构中,以token为keyif (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);// 安装成功,且需要备份的情况if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {// Pass responsibility to the Backup Manager. It will perform a// restore if appropriate, then pass responsibility back to the// Package Manager to run the post-install observer callbacks// and broadcasts.IBackupManager bm = IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE));if (bm != null) {if (DEBUG_INSTALL) Log.v(TAG, "token " + token+ " to BM for possible restore");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) {Log.e(TAG, "Exception trying to enqueue restore", e);doRestore = false;}} else {Log.e(TAG, "Backup Manager not found!");doRestore = false;}}if (!doRestore) {// No restore possible, or the Backup Manager was mysteriously not// available -- just fire the post-install work request directly.if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "postInstall", token);//抛一个POST_INSTALL消息给mHandler进行处理 adb 等待安装结果Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);mHandler.sendMessage(msg);}}});}

由上面代码可知,handleReturnCode主要做了4件事情:

调用InstallArgs的doPreInstall函数,在本例中是FileInstallArgs的doPreInstall函数。调用PKMS的installPackageLI函数进行APK安装,该函数内部将调用InstallArgs的doRename对临时文件进行改名。另外,还需要扫描此APK文件。此过程和之前介绍的“扫描系统Package”一节的内容类似。至此,该APK中的私有财产就全部被登记到PKMS内部进行保存了。调用InstallArgs的doPostInstall函数,在本例中是FileInstallArgs的doPostInstall函数。此时,该APK已经安装完成(不论失败还是成功),继续向mHandler抛送一个POST_INSTALL消息,该消息携带一个token,通过它可从mRunningInstalls数组中取得一个PostInstallData对象

private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {try {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage");installPackageLI(args, res);} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}}

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {Log.i(" PackageManagerService-xiao", "PackageManagerService installPackageLI");//获取 installFlags属性,这个属性表明APP安装到哪里final int installFlags = args.installFlags;// 安装包应用程序的 包名final String installerPackageName = args.installerPackageName;// volume的Uuidfinal 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;int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;if (args.move != null) {// moving a complete application; perform an initial scan on the new install locationscanFlags |= SCAN_INITIAL;}if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {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 returnedres.setReturnCode(PackageManager.INSTALL_SUCCEEDED);res.installerPackageName = installerPackageName;if (DEBUG_INSTALL) Log.d(TAG, "installPackageLI: path=" + tmpPackageFile);// Sanity checkif (instantApp && (forwardLocked || onExternal)) {Log.i(TAG, "Incompatible ephemeral install; fwdLocked=" + forwardLocked+ " external=" + onExternal);res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);return;}// Retrieve PackageSettings and parse package// 获取解析包配置的标志位final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY| PackageParser.PARSE_ENFORCE_CODE| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)| (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)| (instantApp ? PackageParser.PARSE_IS_EPHEMERAL : 0)| (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0);PackageParser pp = new PackageParser();pp.setSeparateProcesses(mSeparateProcesses);// 设置解析包的独立进程属性pp.setDisplayMetrics(mMetrics);// 设置解析包的屏幕属性pp.setCallback(mPackageParserCallback);Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");final PackageParser.Package pkg;try {// 解析APK,主要是解析AndroidManifest.xml文件,将结果记录在PackageParser.Package中pkg = pp.parsePackage(tmpPackageFile, parseFlags);} catch (PackageParserException e) {res.setError("Failed parse during installPackageLI", e);return;} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}// Instant apps must have target SDK >= O and have targetSanboxVersion >= 2//sdk版本检查if (instantApp && pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {Log.w(TAG, "Instant app package " + pkg.packageName + " does not target O");res.setError(INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE,"Instant app package must target O");return;}if (instantApp && pkg.applicationInfo.targetSandboxVersion != 2) {Log.w(TAG, "Instant app package " + pkg.packageName+ " does not target targetSandboxVersion 2");res.setError(INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE,"Instant app package must use targetSanboxVersion 2");return;}if (pkg.applicationInfo.isStaticSharedLibrary()) {// Static shared libraries have synthetic package namesrenameStaticSharedLibraryPackage(pkg);// No static shared libs on external storageif (onExternal) {Log.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 childrenLog.i(" PackageManagerService-xiao", "PackageManagerService installPackageLI pkg.childPackages:"+pkg.childPackages);//一个APK拆分成几个apk的情况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);PackageInstalledInfo childRes = new PackageInstalledInfo();childRes.setReturnCode(PackageManager.INSTALL_SUCCEEDED);childRes.pkg = childPkg;childRes.name = childPkg.packageName;PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);if (childPs != null) {childRes.origUsers = childPs.queryInstalledUsers(sUserManager.getUserIds(), true);}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<>();}res.addedChildPackages.put(childPkg.packageName, childRes);}}}// If package doesn't declare API override, mark that we have an install// time CPU ABI override.if (TextUtils.isEmpty(pkg.cpuAbiOverride)) {pkg.cpuAbiOverride = args.abiOverride;}String pkgName = res.name = pkg.packageName;// 如果这个待安装的APP不是测试包,但是如果环境为仅允许测试包则返回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 {// either use what we've been given or parse directly from the APKif (args.certificates != null) {try {PackageParser.populateCertificates(pkg, args.certificates);} catch (PackageParserException e) {// there was something wrong with the certificates we were given;// try to pull them from the APKPackageParser.collectCertificates(pkg, parseFlags);}} else {PackageParser.collectCertificates(pkg, parseFlags);}} catch (PackageParserException e) {res.setError("Failed collect during installPackageLI", e);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// 如果安装的升级应用,继续使用以前的老包名if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {Log.i(" PackageManagerService-xiao", "PackageManagerService installPackageLI PackageManager.INSTALL_REPLACE_EXISTING");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.// 如果要进行安装的应用,已经存在,将是替换安装,则设置replace=truepkg.setPackageName(oldName);pkgName = pkg.packageName;replace = true;if (DEBUG_INSTALL) Log.d(TAG, "Replacing existing renamed package: oldName="+ oldName + " pkgName=" + pkgName);} else if (mPackages.containsKey(pkgName)) {// This package, under its official name, already exists// on the device; we should replace it.replace = true;if (DEBUG_INSTALL) Log.d(TAG, "Replace existing pacakge: " + pkgName);}// Child packages are installed through the parent packageif (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;}if (replace) {// Prevent apps opting out from runtime permissions// 如果是替换,即升级安装PackageParser.Package oldPackage = mPackages.get(pkgName);final int oldTargetSdk = oldPackage.applicationInfo.targetSdkVersion;final int newTargetSdk = pkg.applicationInfo.targetSdkVersion;// 如果老的TargetSdk 大于android 5.1 而新的TargetSdk 小于5.1,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 apps from downgrading their targetSandbox.final int oldTargetSandbox = oldPackage.applicationInfo.targetSandboxVersion;final int newTargetSandbox = pkg.applicationInfo.targetSandboxVersion;if (oldTargetSandbox == 2 && newTargetSandbox != 2) {res.setError(PackageManager.INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE,"Package " + pkg.packageName + " new target sandbox "+ newTargetSandbox + " is incompatible with the previous value of"+ oldTargetSandbox + ".");return;}// Prevent installing of child packagesif (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;}}}PackageSetting ps = mSettings.mPackages.get(pkgName);// 如果 ps 不为null,同样说明,已经存在一个具有相同安装包包名的程序,被安装,所以还是处理覆盖安装的问题。// 这里主要验证包名的签名,不一致的话,是不能覆盖安装的,另外版本号也不能比安装的地,否则不能替换安装if (ps != null) {if (DEBUG_INSTALL) Log.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.//检查密钥集合是否一致if (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) {if (!checkUpgradeKeySetLP(signatureCheckPs, pkg)) {res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "+ pkg.packageName + " upgrade keys do not match the "+ "previously installed version");return;}} else {try {verifySignaturesLP(signatureCheckPs, pkg);} catch (PackageManagerException e) {res.setError(e.error, e.getMessage());return;}}// 判断安装的应用是否存在同名的应用,如果存在,判断应用是否带有系统应用的标志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);}// 检查APK中定义的所有的权限是否已经被其他应用定义了,如果重定义的是系统应用定义的权限,//那么忽略本app定义的这个权限。如果重定义的是非系统引用的权限,那么本次安装就以失败返回。int N = pkg.permissions.size();for (int i = N-1; i >= 0; i--) {PackageParser.Permission perm = pkg.permissions.get(i);BasePermission bp = mSettings.mPermissions.get(perm.info.name);// Don't allow anyone but the system to define ephemeral permissions.if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0&& !systemApp) {Log.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.final boolean sigsOk;if (bp.sourcePackage.equals(pkg.packageName)&& (bp.packageSetting instanceof PackageSetting)&& (shouldCheckUpgradeKeySetLP((PackageSetting) bp.packageSetting,scanFlags))) {sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg);} else {sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures,pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;}if (!sigsOk) {// If the owning package is the system itself, we log but allow// install to proceed; we fail the install on all other permission// redefinitions.if (!bp.sourcePackage.equals("android")) {res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "+ pkg.packageName + " attempting to redeclare permission "+ perm.info.name + " already owned by " + bp.sourcePackage);res.origPermission = perm.info.name;res.origPackage = bp.sourcePackage;return;} else {Log.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)) {// 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()) {Log.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.protectionLevel;}}}}}}/// M: Operator APP support// 如果是带带有系统应用标志的应用,却要安装在SD卡上,则报错返回,安装失败原因错误的安装路径if (systemApp || sPmsExt.isOperatorApp(mPackages, mSettings.mPackages, pkgName)) {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;}}// 如果是移动APPLog.i(" PackageManagerService-xiao", "installPackageLIargs.move"+args.move);if (args.move != null) {// We did an in-place move, so dex is ready to rollscanFlags |= 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.pkg.applicationInfo.primaryCpuAbi = ps.primaryCpuAbiString;pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;}} else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {//安装apk的情况// Enable SCAN_NO_DEX flag to skip dexopt at a later stagescanFlags |= SCAN_NO_DEX;try {String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?args.abiOverride : pkg.cpuAbiOverride);final boolean extractNativeLibs = !pkg.isLibrary();derivePackageAbi(pkg, new File(pkg.codePath), abiOverride,extractNativeLibs, mAppLib32InstallDir);} catch (PackageManagerException pme) {Log.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.synchronized (mPackages) {try {updateSharedLibrariesLPr(pkg, null);//更新sharedlib库} catch (PackageManagerException e) {Log.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());}}}// 重命名,将/data/app/vmdl{安装会话}.tmp重命名为data/app/包名-suffix// /data/app/vmdl636355084.tmp to /data/app/kr.hwangti.batterylog-D9rVNrKDGBnxt7CIXwcJyg==if (!args.doRename(res.returnCode, pkg, oldCodePath)) {res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");return;}Log.d(TAG, " installPackageLI instantApp: " + instantApp);if (!instantApp) {startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);} else {if (DEBUG_DOMAIN_VERIFICATION) {Log.d(TAG, "Not verifying instant app install for app links: " + pkgName);}}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.mVersionCode != pkg.mVersionCode) {res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring "+ "static-shared libs cannot be updated");return;}}// 覆盖安装replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,installerPackageName, res, args.installReason);} else {// 首次安装installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,args.user, installerPackageName, volumeUuid, res, args.installReason);}}// 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.//// 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);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);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 violationBackgroundDexOptService.notifyPackageChanged(pkg.packageName);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);}}}

POST_INSTALL处理

case POST_INSTALL: {if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);// 我们知道msg的arg1是token,arg2是0PostInstallData 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;// 如果已经成功的安装了应用,在发送广播之前先授予一些必要的权限// 这些权限在 installPackageAsUser 中创建 InstallParams 时传递的,为null。//INSTALL_GRANT_RUNTIME_PERMISSIONS 这个属性在PackageManagerShellCommand.java 参数是-g给予的final boolean grantPermissions = (args.installFlags& PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;final 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 packagehandlePackagePostInstall(parentRes, grantPermissions, killApp,virtualPreload, grantedPermissions, didRestore,args.installerPackageName, args.observer);// Handle the child packagesfinal int childCount = (parentRes.addedChildPackages != null)? parentRes.addedChildPackages.size() : 0;for (int i = 0; i < childCount; i++) {PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i);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 {Log.e(TAG, "Bogus post-install token " + msg.arg1);}Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);} break;

private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,boolean killApp, boolean virtualPreload, String[] grantedPermissions,boolean launchedForRestore, String installerPackage,IPackageInstallObserver2 installObserver) {if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {// Send the removed broadcastsif (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.//动态权限的检查 if (grantPermissions) {grantRequestedRuntimePermissions(res.pkg, res.newUsers, grantedPermissions);}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;/// M: CTA requirement - permission control @{grantCtaRuntimePerm(update, res);//@}// 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.if (res.pkg.parentPackage != null) {synchronized (mPackages) {grantRuntimePermissionsGrantedToDisabledPrivSysPackageParentLPw(res.pkg);}}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.int[] firstUsers = EMPTY_INT_ARRAY;int[] updateUsers = 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) {if (ps.getInstantApp(newUser)) {continue;}if (allNewUsers) {firstUsers = ArrayUtils.appendInt(firstUsers, newUser);continue;}boolean isNew = true;for (int origUser : res.origUsers) {if (origUser == newUser) {isNew = false;break;}}if (isNew) {firstUsers = ArrayUtils.appendInt(firstUsers, newUser);} else {updateUsers = ArrayUtils.appendInt(updateUsers, 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, firstUsers);// Send added for users that don't see the package for the first timeBundle extras = new Bundle(1);extras.putInt(Intent.EXTRA_UID, res.uid);if (update) {extras.putBoolean(Intent.EXTRA_REPLACING, true);}//发送一个广播,apk安装完成sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,extras, 0 /*flags*/,null /*targetPackage*/, null /*finishedReceiver*/, updateUsers);///M: MTK Power: Disable installation boostif (mPowerHalManager != null) {mPowerHalManager.setInstallationBoost(false);}if (installerPackageName != null) {//发送一个更新的广播sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,extras, 0 /*flags*/,installerPackageName, null /*finishedReceiver*/, updateUsers);}// Send replaced for users that don't see the package for the first timeif (update) {//apk升级sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,packageName, extras, 0 /*flags*/,null /*targetPackage*/, null /*finishedReceiver*/,updateUsers);if (installerPackageName != null) {sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,extras, 0 /*flags*/,installerPackageName, null /*finishedReceiver*/, updateUsers);}sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,null /*package*/, null /*extras*/, 0 /*flags*/,packageName /*targetPackage*/,null /*finishedReceiver*/, updateUsers);} else if (launchedForRestore && !isSystemApp(res.pkg)) {// First-install and we did a restore, so we're responsible for the// first-launch broadcast.if (DEBUG_BACKUP) {Log.i(TAG, "Post-restore of " + packageName+ " sending FIRST_LAUNCH in " + Arrays.toString(firstUsers));}//启动广播sendFirstLaunchBroadcast(packageName, installerPackage, firstUsers);}// 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) {Log.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 userif (firstUsers != null && firstUsers.length > 0) {synchronized (mPackages) {for (int userId : firstUsers) {// 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.if (packageIsBrowser(packageName, userId)) {mSettings.setDefaultBrowserPackageNameLPw(null, userId);}// We may also need to apply pending (restored) runtime// permission grants within these users.mSettings.applyPendingPermissionGrantsLPw(packageName, userId);}}}// 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) {Runtime.getRuntime().gc();synchronized (mInstallLock) {res.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 : firstUsers) {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);/// M: Removable system app supportsPmsExt.onPackageAdded(packageName, userId);}}}// If someone is watching installs - notify themif (installObserver != null) {try {Bundle extras = extrasForInstallResult(res);installObserver.onPackageInstalled(res.name, res.returnCode,res.returnMsg, extras);} catch (RemoteException e) {Log.i(TAG, "Observer no longer exists.");}}}

运行到这里整个apk的安装就完成了,如果需要增加app的动态权限可以在handlePackagePostInstall里面实现。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。