-
书接上回
上文我们分析到,安装过程好像是把一个apk文件写入到了某个地方,通过PackageInstaller.Session的openWrite方法获取到一个OutputStream,然后写入apk文件,这里再贴一下代码:
try{ File file = new File(mPackageURI.getPath()); try (InputStream in = new FileInputStream(file)) { long sizeBytes = file.length(); //获取目标文件写入流 try (OutputStream out = session .openWrite("PackageInstaller", 0, sizeBytes)) { byte[] buffer = new byte[1024 * 1024]; while (true) { int numRead = in.read(buffer); //文件已写入(写入到缓冲区)完毕 if (numRead == -1) { //这里调用其实是IoBridge.write方法,就是真正的写入到磁盘,可以理解成flush操作 session.fsync(out); break; } if (isCancelled()) { session.close(); break; } //写入到缓冲区 out.write(buffer, 0, numRead); if (sizeBytes > 0) { float fraction = ((float) numRead / (float) sizeBytes); session.addProgress(fraction); } } } } return session; } catch (IOException | SecurityException e) { session.close(); return null; }
-
构造session和写入
想要知道session是什么,首先要知道它的真实身份,它是由getPackageManager().getPackageInstaller().createSession方法获取的。
getPackageManager().getPackageInstaller()获取的是PackageInstaller,它的createSession方法内部调用的是IPackageInstaller的createSession方法,IPackageInstaller的实现类是PackageInstallerService,它的createSession方法调用了doWriteInternal方法:
private int createSessionInternal(SessionParams params, String installerPackageName, String installerAttributionTag, int userId) throws IOException { ... //sessionId其实就是一个唯一的随机数 int sessionId = allocateSessionIdLocked(); session = new PackageInstallerSession(...); synchronized (mSessions) { //保存起来,openSession方法就是从mSessions中按照sessionId获取的session mSessions.put(sessionId, session); } ... return sessionId; }
我们看到,session的真实身份是PackageInstallerSession,但这个方法并没有返回session,而是返回了一个id,我们需要通过PackageInstaller的openSession(int sessionId)来获取,这个方法中会把PackageInstallerSession包装成PackageInstaller.Session返回,创建的PackageInstallerSession在PackageInstaller.Session中被保存为mSession字段。
所以session的openWrite调用的是PackageInstaller.Session内的这个方法:
public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes, long lengthBytes) throws IOException { try { if (ENABLE_REVOCABLE_FD) { //自动关闭输出流(即需要调用close方法刷新已写入的数据),我们这里没用到,因为用的是fsync方法直接刷新缓冲区,所以不需要close刷新 return new ParcelFileDescriptor.AutoCloseOutputStream( mSession.openWrite(name, offsetBytes, lengthBytes)); } else { //此次流程会走这里 final ParcelFileDescriptor clientSocket = mSession.openWrite(name, offsetBytes, lengthBytes); return new FileBridge.FileBridgeOutputStream(clientSocket); } } ... }
可以看到,这里返回了一个FileBridge.FileBridgeOutputStream写入流,它持有了一个ParcelFileDescriptor,通过mSession的openWrite方法获取,前面我们知道,mSession就是PackageInstallerSession,它的openWrite调用了doWriteInternal方法:
private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd) throws IOException { ... final FileBridge bridge = new FileBridge(); ... try { ... final File target = new File(stageDir, name); final FileDescriptor fd = Os.open(target.getAbsolutePath(), O_CREAT | O_WRONLY, 0644); ParcelFileDescriptor targetPfd = new ParcelFileDescriptor(fd); ... if(...){ ... }else{ //会走这 bridge.setTargetFile(targetPfd); bridge.start(); } return bridge.getClientSocket(); ... } catch (ErrnoException e) { throw e.rethrowAsIOException(); } }
stageDir是在PackageInstallerService内指定的,有兴趣可以去查找一下,有内部存储和外部存储两条路径,内部存储的stageDir路径获取如下:
private File buildSessionDir(int sessionId, SessionParams params) { ... final File result = buildTmpSessionDir(sessionId, params.volumeUuid); ... return result; } private File buildTmpSessionDir(int sessionId, String volumeUuid) { final File sessionStagingDir = getTmpSessionDir(volumeUuid); return new File(sessionStagingDir, "vmdl" + sessionId + ".tmp"); } private File getTmpSessionDir(String volumeUuid) { return Environment.getDataAppDirectory(volumeUuid); } //Environment: public static File getDataAppDirectory(String volumeUuid) { return new File(getDataDirectory(volumeUuid), "app"); } public static String getDataDirectoryPath(String volumeUuid) { if (TextUtils.isEmpty(volumeUuid)) { return DIR_ANDROID_DATA_PATH; } else { return getExpandDirectory().getAbsolutePath() + File.separator + volumeUuid; } } private static final String DIR_ANDROID_DATA_PATH = getDirectoryPath(ENV_ANDROID_DATA, "/data"); private static final String ENV_ANDROID_DATA = "ANDROID_DATA"; static String getDirectoryPath(@NonNull String variableName, @NonNull String defaultPath) { //经测试,这里获取的是“/data” String path = System.getenv(variableName); return path == null ? defaultPath : path; }
因为之前在InstallInstalling中构造的SessionParams并没有设置volumeUuid,因此此属性值为空,所以最终获取的stageDir是
/data/app/vmdl[sessionId].tmp/
([]括号表示是变量)。
name是在Install中通过openWrite方法传入的,值为“PackageInstaller”,因此target文件就是/data/app/vmdl[sessionId].tmp/PackageInstaller
。FileBridge是一个Thread子类,它的run方法如下:
@Override public void run() { final ByteBuffer tempBuffer = ByteBuffer.allocateDirect(8192); final byte[] temp = tempBuffer.hasArray() ? tempBuffer.array() : new byte[8192]; try { //从server端读取写入数据 while (IoBridge.read(mServer.getFileDescriptor(), temp, 0, MSG_LENGTH) == MSG_LENGTH) { final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN); if (cmd == CMD_WRITE) { // Shuttle data into local file int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN); while (len > 0) { int n = IoBridge.read(mServer.getFileDescriptor(), temp, 0, Math.min(temp.length, len)); if (n == -1) { throw new IOException( "Unexpected EOF; still expected " + len + " bytes"); } //写入mTarget指向的文件 IoBridge.write(mTarget.getFileDescriptor(), temp, 0, n); len -= n; } } else if (cmd == CMD_FSYNC) { // Sync and echo back to confirm Os.fsync(mTarget.getFileDescriptor()); IoBridge.write(mServer.getFileDescriptor(), temp, 0, MSG_LENGTH); } else if (cmd == CMD_CLOSE) { // Close and echo back to confirm Os.fsync(mTarget.getFileDescriptor()); mTarget.close(); mClosed = true; IoBridge.write(mServer.getFileDescriptor(), temp, 0, MSG_LENGTH); break; } } } catch (ErrnoException | IOException e) { Log.wtf(TAG, "Failed during bridge", e); } finally { forceClose(); } }
可见这里会将mServer中读取的数据写入到mTarget指向的文件,也就是
/data/app/vmdl[sessionId].tmp/PackageInstaller
,那么mServer中的数据从哪来的,mServer又是什么?在FileBridge的构造方法中:
public FileBridge() { try { ParcelFileDescriptor[] fds = ParcelFileDescriptor.createSocketPair(SOCK_STREAM); mServer = fds[0]; mClient = fds[1]; } catch (IOException e) { throw new RuntimeException("Failed to create bridge"); } }
原来是建立了一对socket通道,那么mServer读取的数据一定来自于mClient,还记得上面doWriteInternal方法返回的是bridge.getClientSocket()嘛,就是返回的mClient,因此在FileBridgeOutputStream构造方法中:
public FileBridgeOutputStream(ParcelFileDescriptor clientPfd) { mClientPfd = clientPfd; mClient = clientPfd.getFileDescriptor(); }
clientPfd就是前面传入的FileBridge的mClient,这里InstallInstalling的mClient是FileDescriptor,也就是FileBridge的mClient指向的socket文件,当我们在InstallInstalling的AsyncTask的任务中调用FileBridgeOutputStream的write写入时:
@Override public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException { ArrayUtils.throwsIfOutOfBounds(buffer.length, byteOffset, byteCount); Memory.pokeInt(mTemp, 0, CMD_WRITE, ByteOrder.BIG_ENDIAN); Memory.pokeInt(mTemp, 4, byteCount, ByteOrder.BIG_ENDIAN); IoBridge.write(mClient, mTemp, 0, MSG_LENGTH); IoBridge.write(mClient, buffer, byteOffset, byteCount); }
实际上就是写入到FileBridge的mClient指向的socket文件。
所以整个过程就是在InstallInstalling的AsyncTask的后台任务中不断地把apk源文件写入到socket文件的mClient端,同时在FileBridge中其mServer端不断地循环读取写入的数据,并写入到mTarget中,也就是/data/app/vmdl[sessionId].tmp/PackageInstaller
这个文件。
看来接下来,就该对这个生成的文件做处理了。 -
提交
通过前文分析我们知道,在onPostExecute中调用了session.commit方法,内部也就是调用了PackageInstallerSession的commit方法:
@Override public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) { ... dispatchSessionSealed(); } private void dispatchSessionSealed() { mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget(); }
可见,这里会通过Handler从AsyncTask回到主线程:
private final Handler.Callback mHandlerCallback = new Handler.Callback() { @Override public boolean handleMessage(Message msg) { switch (msg.what) { case MSG_ON_SESSION_SEALED: handleSessionSealed(); break; case MSG_STREAM_VALIDATE_AND_COMMIT: handleStreamValidateAndCommit(); break; case MSG_INSTALL: handleInstall(); break; case MSG_ON_PACKAGE_INSTALLED: ... sendOnPackageInstalled(mContext, statusReceiver, sessionId, isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, packageName, returnCode, message, extras); break; case MSG_SESSION_VALIDATION_FAILURE: ... break; } return true; } };
如果正常的话,handleSessionSealed中会通过Handler发送通知来相继触发handleStreamValidateAndCommit和handleInstall方法,handleInstall方法中会调用verify方法验证,验证没问题会回调onVerificationComplete方法,其内部会调用install方法:
private CompletableFuture<Void> install() { //installNonStaged方法是安装方法 List<CompletableFuture<InstallResult>> futures = installNonStaged(); CompletableFuture<InstallResult>[] arr = new CompletableFuture[futures.size()]; return CompletableFuture.allOf(futures.toArray(arr)).whenComplete((r, t) -> { //t是Throwable if (t == null) { setSessionApplied(); for (CompletableFuture<InstallResult> f : futures) { InstallResult result = f.join(); //安装成功回调 result.session.dispatchSessionFinished( INSTALL_SUCCEEDED, "Session installed", result.extras); } } else { //出错处理 PackageManagerException e = (PackageManagerException) t.getCause(); setSessionFailed(e.error, PackageManager.installStatusToString(e.error, e.getMessage())); dispatchSessionFinished(e.error, e.getMessage(), null); maybeFinishChildSessions(e.error, e.getMessage()); } }); }
我们先来看installNonStaged方法:
private List<CompletableFuture<InstallResult>> installNonStaged() { try { List<CompletableFuture<InstallResult>> futures = new ArrayList<>(); CompletableFuture<InstallResult> future = new CompletableFuture<>(); futures.add(future); //构造InstallParams并且设置监听回调 final InstallParams installingSession = makeInstallParams(future); if (isMultiPackage()) { ... } else if (installingSession != null) { //安装 installingSession.installStage(); } return futures; } catch (PackageManagerException e) { List<CompletableFuture<InstallResult>> futures = new ArrayList<>(); futures.add(CompletableFuture.failedFuture(e)); return futures; } }
看一下makeInstallParams方法:
private InstallParams makeInstallParams(CompletableFuture<InstallResult> future) throws PackageManagerException { ... final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() { @Override public void onUserActionRequired(Intent intent) { throw new IllegalStateException(); } @Override public void onPackageInstalled(String basePackageName, int returnCode, String msg, Bundle extras) { if (returnCode == INSTALL_SUCCEEDED) { future.complete(new InstallResult(PackageInstallerSession.this, extras)); } else { future.completeExceptionally(new PackageManagerException(returnCode, msg)); } } }; ... synchronized (mLock) { return new InstallParams(stageDir, localObserver, params, mInstallSource, user, mSigningDetails, mInstallerUid, mPackageLite, mPm); } }
可以看到,这里设置了一个IPackageInstallObserver2回调监听器,在InstallParams中被赋值给mObserver。
再来看InstallParams的installStage方法:public void installStage() { final Message msg = mPm.mHandler.obtainMessage(INIT_COPY); setTraceMethod("installStage").setTraceCookie(System.identityHashCode(this)); msg.obj = this; ... mPm.mHandler.sendMessage(msg); }
mPm也就是PackageManagerService,这里使用它的mHandler发送了一个消息,mHandler在构造方法中通过injector.getHandler()获取,injector是在PackageManagerService的main方法中构造的,是一个PackageManagerServiceInjector对象,最终找到它的getHandler方法返回的是:
(i, pm) -> { HandlerThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/); thread.start(); return new PackageHandler(thread.getLooper(), pm); },
在PackageHandler的handleMessage方法中调用了doHandleMessage方法:
void doHandleMessage(Message msg) { switch (msg.what) { case INIT_COPY: { HandlerParams params = (HandlerParams) msg.obj; if (params != null) { ... params.startCopy(); ... } break; } ... } }
对应installStage方法前面发送的what码是INIT_COPY,这里的msg.obj获取的也就是InstallParams,可见调用了InstallParams的startCopy方法,它是父类HandlerParams中的方法:
final void startCopy() { handleStartCopy(); handleReturnCode(); }
可见,InstallParams实现了这俩方法,先看handleStartCopy方法:
public void handleStartCopy() { ... PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext, mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride); ... mRet = overrideInstallLocation(pkgLite.packageName, pkgLite.recommendedInstallLocation, pkgLite.installLocation); }
overrideInstallLocation方法中会进行安装位置策略的决策,比如是放在内部还是外部存储,最后没问题的话会返回PackageManager.INSTALL_SUCCEEDED码。
我们再来看handleReturnCode方法,它调用了processPendingInstall方法:private void processPendingInstall() { InstallArgs args = createInstallArgs(this); if (mRet == PackageManager.INSTALL_SUCCEEDED) { mRet = args.copyApk(); } if (mRet == PackageManager.INSTALL_SUCCEEDED) { F2fsUtils.releaseCompressedBlocks( mPm.mContext.getContentResolver(), new File(args.getCodePath())); } if (mParentInstallParams != null) { mParentInstallParams.tryProcessInstallRequest(args, mRet); } else { PackageInstalledInfo res = new PackageInstalledInfo(mRet); processInstallRequestsAsync( res.mReturnCode == PackageManager.INSTALL_SUCCEEDED, Collections.singletonList(new InstallRequest(args, res))); } }
copyApk方法中:
private int doCopyApk() { //这里会直接return,因为之前复制过了 if (mOriginInfo.mStaged) { if (DEBUG_INSTALL) Slog.d(TAG, mOriginInfo.mFile + " already staged; skipping copy"); mCodeFile = mOriginInfo.mFile; return PackageManager.INSTALL_SUCCEEDED; } ...//复制apk }
之前在构造InstallParams的时候调用的是InstallParams(File stagedDir, ...)构造方法,它内部的mOriginInfo是通过OriginInfo.fromStagedFile(stagedDir)生成的,返回的是OriginInfo(file, true, false):
private OriginInfo(File file, boolean staged, boolean existing) { mFile = file; mStaged = staged; mExisting = existing; ... }
因此doCopyApk方法中mOriginInfo.mStaged为true,所以会直接跳过复制操作(其实doCopyApk中的复制操作主要是用于系统应用的安装方式,我们这里分析的是常规第三方安装方式,在之前doWriteInternal中通过socket方式已经复制过了),stagedDir在PackageInstallerService的buildSessionDir方法中创建的,实际上它的值通过buildTmpSessionDir方法获取,值是
/data/app/vmdl[sessionId].tmp/
([]括起来的表示变量)。
接下来F2fsUtils.releaseCompressedBlocks方法中会解压stagedDir中的所有压缩文件(包括内嵌的子目录下的)。
最后调用processInstallRequestsAsync方法(这里只看常规方式,忽略mParentInstallParams方式):private void processInstallRequestsAsync(boolean success, List<InstallRequest> installRequests) { mPm.mHandler.post(() -> { mInstallPackageHelper.processInstallRequests(success, installRequests); }); }
mInstallPackageHelper是InstallPackageHelper:
public void processInstallRequests(boolean success, List<InstallRequest> installRequests) { ... if (success) { for (InstallRequest request : apkInstallRequests) { request.mArgs.doPreInstall(request.mInstallResult.mReturnCode); } synchronized (mPm.mInstallLock) { //安装 installPackagesTracedLI(apkInstallRequests); } for (InstallRequest request : apkInstallRequests) { request.mArgs.doPostInstall( request.mInstallResult.mReturnCode, request.mInstallResult.mUid); } } for (InstallRequest request : apkInstallRequests) { restoreAndPostInstall(request.mArgs.mUser.getIdentifier(), request.mInstallResult, new PostInstallData(request.mArgs, request.mInstallResult, null)); } }
installPackagesTracedLI方法中调用的是installPackagesLI方法,经过Prepare、Scan、Reconcile、Commit(内部有通过PackageDexOptimizer的performDexOpt进行opt优化)四步进行安装,最底层都是是通过IInstalld这个aidl生成的类来操作的。
-
prepare阶段
prepare阶段通过preparePackageLI方法来完成,里面有一段代码:
PackageParser2的parsePackage方法中又会调用ParsingPackageUtils的parsePackage方法,它又会调用ApkLiteParseUtils的parseClusterPackageLite方法来解析apk:try (PackageParser2 pp = mPm.mInjector.getPreparingPackageParser()) { parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false); AndroidPackageUtils.validatePackageDexMetadata(parsedPackage); }...
isApkFile方法会判断文件是否是.apk后缀,看到这里我陷入了疑惑,因为我们知道之前写入的apk文件名字是“PackageInstaller”,那这里就不会通过判断,因此baseApk是null,在composePackageLiteFromApks方法中又会判断baseApk如果是null的话则返回结果中会设置error,回到PackageParser2的parsePackage方法中后会判断结果是否含有error,如果有则抛出异常:public static ParseResult<PackageLite> parseClusterPackageLite(ParseInput input, File packageDirOrApk, List<File> frameworkSplits, int flags) { final File[] files; ... if (...) {...} else { files = packageDirOrApk.listFiles(); ... } ... ApkLite baseApk = null; final ArrayMap<String, ApkLite> apks = new ArrayMap<>(); try { for (File file : files) { if (isApkFile(file)) { final ParseResult<ApkLite> result = parseApkLite(input, file, flags); ... final ApkLite lite = result.getResult(); ... //这里会把apk文件添加,getSplitName()是null作为key,这里会限制只能有一个apk文件 if (apks.put(lite.getSplitName(), lite) != null) { return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST, "Split name " + lite.getSplitName() + " defined more than once; most recent was " + file); } } } ... //这里通过“null”key获取到apk文件 if (!parsingFrameworkSplits) { baseApk = apks.remove(null); } } ... return composePackageLiteFromApks(input, packageDirOrApk, baseApk, apks); }
最终回到installPackagesLI方法后在catch块中会直接return,之后的scan、commit等阶段都不会执行了,很明显我们漏掉了什么,“PackageInstaller”这个apk文件一定在某个地方被添加了.apk的后缀。ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags, frameworkSplits); if (result.isError()) { throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(), result.getException()); }
还记得在handleStreamValidateAndCommit方法中我们发送了通知开启了上面分析的commit流程嘛:
我们忽略了一个方法:streamValidateAndCommit,这个方法里会调用一个validateApkInstallLocked方法(不考虑Apex的情况),这个方法中会遍历stagedDir的所有文件,重命名操作就是在这里发生的(实际上这里会调用ApkLiteParseUtils.parseApkLite方法先进行过解析并验证了):private void handleStreamValidateAndCommit() { try { ... if (allSessionsReady && streamValidateAndCommit()) { mHandler.obtainMessage(MSG_INSTALL).sendToTarget(); } } ... }
可见targetName是base.apk,resolveAndStageFileLocked方法中会调用stageFileLocked->maybeRenameFile方法:final String targetName = ApkLiteParseUtils.splitNameToFileName(apk); final File targetFile = new File(stageDir, targetName); resolveAndStageFileLocked(addedFile, targetFile, apk.getSplitName()); //ApkLiteParseUtils: public static final String APK_FILE_EXTENSION = ".apk"; public static String splitNameToFileName(@NonNull ApkLite apk) { Objects.requireNonNull(apk); final String fileName = apk.getSplitName() == null ? "base" : "split_" + apk.getSplitName(); return fileName + APK_FILE_EXTENSION; }
可见就是在这里会把“PackageInstaller”重命名成“base.apk”。private static void maybeRenameFile(File from, File to) throws PackageManagerException { if (!from.equals(to)) { if (!from.renameTo(to)) { throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Could not rename file " + from + " to " + to); } } }
还有一个关键的操作就是:
这个不起眼的doRename方法很关键:if (!args.doRename(res.mReturnCode, parsedPackage)) { throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename"); }
PackageManagerServiceUtils.getNextCodePath方法如下:@Override boolean doRename(int status, ParsedPackage parsedPackage) { ... //就是/data/app final File targetDir = resolveTargetDir(); //stagedDir(/data/app/vmdl[sessionId].tmp/) final File beforeCodeFile = mCodeFile; //路径是/data/app/~~[base64随机码]/[packageName]-[base64随机码]/,生成逻辑看下面的getNextCodePath方法解析 final File afterCodeFile = PackageManagerServiceUtils.getNextCodePath(targetDir, parsedPackage.getPackageName()); ... try { //创建afterCodeFile文件 makeDirRecursive(afterCodeFile.getParentFile(), 0775); if (onIncremental) { ... } else { //将base.apk的路径改成afterCodeFile的路径 Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath()); } } ... mCodeFile = afterCodeFile; try { parsedPackage.setPath(afterCodeFile.getCanonicalPath()); } ... parsedPackage.setBaseApkPath(FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile, parsedPackage.getBaseApkPath())); ... return true; }
FileUtils.rewriteAfterRename方法如下://PackageManagerService: static final String RANDOM_DIR_PREFIX = "~~"; static final char RANDOM_CODEPATH_PREFIX = '-'; //PackageManagerServiceUtils: public static File getNextCodePath(File targetDir, String packageName) { SecureRandom random = new SecureRandom(); byte[] bytes = new byte[16]; File firstLevelDir; do { random.nextBytes(bytes); //值为"~~[base64随机码]" String firstLevelDirName = RANDOM_DIR_PREFIX + Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP); firstLevelDir = new File(targetDir, firstLevelDirName); } while (firstLevelDir.exists()); random.nextBytes(bytes); //值为"[packageName]-[base64随机码]" String dirName = packageName + RANDOM_CODEPATH_PREFIX + Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP); //结果是/data/app/~~[base64随机码]/[packageName]-[base64随机码]/ final File result = new File(firstLevelDir, dirName); ... return result; }
总结一下,这里其实就是重新设置parsedPackage的baseApkPath为public static String rewriteAfterRename(File beforeDir, File afterDir, String path) { if (path == null) return null; final File result = rewriteAfterRename(beforeDir, afterDir, new File(path)); return (result != null) ? result.getAbsolutePath() : null; } /** * Given a path under the "before" directory, rewrite it to live under the * "after" directory. For example, {@code /before/foo/bar.txt} would become * {@code /after/foo/bar.txt}. */ public static File rewriteAfterRename(File beforeDir, File afterDir, File file) { if (file == null || beforeDir == null || afterDir == null) return null; if (contains(beforeDir, file)) { final String splice = file.getAbsolutePath().substring( beforeDir.getAbsolutePath().length()); return new File(afterDir, splice); } return null; }
/data/app/~~[base64随机码]/[packageName]-[base64随机码]/base.apk
,比如,这里贴一个虚拟机中打印的路径:/data/app/~~_2wI_UpCAckInB8-R9eQWg==/com.mph.bpp-gwds_0yTePnhkIxwACYhmQ==/base.apk
,还记得吗,之前这个值是/data/app/vmdl[sessionId].tmp/base.apk。parsedPackage是ParsedPackage类型,它的实现类是PackageImpl,这里就是设置它的mBaseApkPath属性(在其父类ParsingPackageImpl中定义)。 -
scan阶段
scan阶段通过scanPackageNewLI方法开启,然后调用ScanPackageUtils.scanPackageOnlyLI方法返回一个PackageSetting对象。这个阶段主要是读取系统配置,和当前新安装的包进行信息整合,并针对prepare阶段的scanFlags进行各项验证。 -
reconcile(核对)阶段
使用上一步返回的ScanResult<PackageSetting>构建ReconcileRequest,然后调用ReconcilePackageUtils.reconcilePackages开启reconcile阶段,主要是整合签名信息,最后返回ReconciledPackage。 -
commit阶段
使用上一步的ReconciledPackage构造出CommitRequest,调用commitPackagesLocked->commitReconciledScanResultLocked->commitPackageSettings中会通过mPm.mPackages.put(pkg.getPackageName(), pkg)把AndroidPackage(即实现类PackageImpl)保存到PackageManagerService的mPackages中。并且把构造的PackageSetting保存在PackageManagerService的mSettings(Settings)的mPackages中:mPackages.put(p.getPackageName(), packageSetting)。
其外还有通过updateSettingsLI->updateSettingsInternalLI->mPm.mPermissionManager.onPackageInstalled(mPermissionManager是PermissionManagerService.PermissionManagerServiceInternalImpl)->PermissionManagerServiceImpl的onPackageInstalled方法修改权限信息的逻辑。
然后在executePostCommitSteps方法中执行commit操作:
调用mAppDataHelper.prepareAppDataPostCommitLIF方法,内部会通过prepareAppData->prepareAppDataLeaf调用到Installer的createAppData方法:private void executePostCommitSteps(CommitRequest commitRequest) { ... for (ReconciledPackage reconciledPkg : commitRequest.mReconciledPackages.values()) { ... //从上一个阶段(reconcile)获取的结果 final AndroidPackage pkg = reconciledPkg.mPkgSetting.getPkg(); final String packageName = pkg.getPackageName(); final String codePath = pkg.getPath(); ... //内部通过binder创建一个/data/user/[userId]/[package_name]的目录 mAppDataHelper.prepareAppDataPostCommitLIF(pkg, 0); ... //准备好配置信息(下一步的dexopt会基于此步骤) mArtManagerService.prepareAppProfiles( pkg, mPm.resolveUserIds(reconciledPkg.mInstallArgs.mUser.getIdentifier()), /* updateReferenceProfileContent= */ true); ... //检查是否支持dex优化(为了加快启动) final boolean performDexopt = (!instantApp || android.provider.Settings.Global.getInt( mContext.getContentResolver(), android.provider.Settings.Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) && !pkg.isDebuggable() && (!onIncremental) && dexoptOptions.isCompilationEnabled(); if (performDexopt) { ... mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting, null /* instructionSets */, mPm.getOrCreateCompilerPackageStats(pkg), mDexManager.getPackageUseInfoOrDefault(packageName), dexoptOptions); } ... } ... }
mInstalld是IInstalld类型,IInstalld是一个aidl类,它的实现类是InstalldNativeService.cpp:public @NonNull CreateAppDataResult createAppData(@NonNull CreateAppDataArgs args) throws InstallerException { ... try { return mInstalld.createAppData(args); } catch (Exception e) { throw InstallerException.from(e); } }
create_data_user_ce_package_path方法最终返回一个binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) { … //这里分FLAG_STORAGE_DE和FLAG_STORAGE_CE两种,我们只看其中之一 auto path = create_data_user_ce_package_path(uuid_, userId, pkgname); if (prepare_app_dir(path, targetMode, uid) || prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) || prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) { return error("Failed to prepare " + path); } … if (!prepare_app_profile_dir(packageName, appId, userId)) { return error("Failed to prepare profiles for " + packageName); } return ok(); }
/data/user/[userId]/[package_name]
([]表示变量)路径,然后prepare_app_dir函数中会调用fs_prepare_dir_strict函数,其中会调用mkdir创建此目录,应用运行中产生的数据都放在这里。
然后mArtManagerService.prepareAppProfiles方法会准备关于dex的配置,内部会通过InstalldNativeService.cpp(IIstalld的实现类)创建配置文件/data/misc/profiles/cur/[userid]/[package_name]/primary.prof
,然后使用GetProcAddress(一个计算机函数,功能是检索指定的动态链接库(DLL)中的输出库函数地址)拿到底层操作函数(位于/system/bin/profman程序)来执行的。
之后在PackageDexOptimizer的performDexOpt方法中进行art的dex优化加载操作(如果虚拟机支持的话),调用链最终也是调用InstalldNativeService.cpp的dexopt函数,底层的操作函数位于/system/bin/dex2oat程序,输出路径是通过getPackageOatDirIfSupported方法获取的:/data/app/~~[base64随机码]/[packageName]-[base64随机码]/oat/
。
回到processInstallRequests方法主流程,调用完installPackagesTracedLI方法后,最后会调用restoreAndPostInstall方法,其内部会调用:
Message msg = mPm.mHandler.obtainMessage(POST_INSTALL, token, 0); mPm.mHandler.sendMessage(msg);
PackageHandler中:
case POST_INSTALL: { ... mInstallPackageHelper.handlePackagePostInstall(data.res, data.args, didRestore); ... } break;
InstallPackageHelper的handlePackagePostInstall方法如下:
void handlePackagePostInstall(PackageInstalledInfo res, InstallArgs installArgs, boolean launchedForRestore) { ... final IPackageInstallObserver2 installObserver = installArgs.mObserver; ... mPm.notifyInstallObserver(res, installObserver); ... }
PackageManagerService的notifyInstallObserver方法中:
void notifyInstallObserver(PackageInstalledInfo info, IPackageInstallObserver2 installObserver) { if (installObserver != null) { try { Bundle extras = extrasForInstallResult(info); installObserver.onPackageInstalled(info.mName, info.mReturnCode, info.mReturnMsg, extras); } catch (RemoteException e) { Slog.i(TAG, "Observer no longer exists."); } } }
可见,这里触发了之前设置的IPackageInstallObserver2的onPackageInstalled方法,之后就会调用PackageInstallerSession的dispatchSessionFinished方法,最终会调用PackageManagerService的sendSessionCommitBroadcast方法发送安装成功通知。
public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId, int launcherUid, @Nullable ComponentName launcherComponent, @Nullable String appPredictionServicePackage) { if (launcherComponent != null) { Intent launcherIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED) .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo) .putExtra(Intent.EXTRA_USER, UserHandle.of(userId)) .setPackage(launcherComponent.getPackageName()); mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUid)); } ... }
launcherComponent就是表示桌面Launcher组件的,猜测Launcher会收到这个通知。
-
-
Launcher接收通知
我们在源码的/packages/apps/Launcher3/AndroidManifest-common.xml中找到了:
<receiver android:name="com.android.launcher3.SessionCommitReceiver" > <intent-filter> <action android:name="android.content.pm.action.SESSION_COMMITTED" /> </intent-filter> </receiver>
这里的action的name属性指定的字符串正是launcherIntent构造时指定的action的值,因此这个receiver就是接收安装成功通知的地方。onReceive方法就不看了,就是创建图标、建立关联等Launcher的工作了。
网友评论