staticintinstall_app(TransportType transport, constchar* serial, int argc, constchar** argv){ // The last argument must be the APK file constchar* file = argv[argc - 1];// 利用参数创建出本地文件的名称
// 添加cmd参数 // don't copy the APK name, but, copy the rest of the arguments as-is while (argc-- > 1) { cmd += " " + escape_arg(std::string(*argv++)); }
// add size parameter [required for streaming installs] // do last to override any user specified value cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
// 连接源端,获取APK文件的描述符 int remoteFd = adb_connect(cmd, &error); if (remoteFd < 0) { fprintf(stderr, "Connect error for write: %s\n", error.c_str()); adb_close(localFd); return1; }
# Script to start "pm" on the device, which has a very rudimentary # shell. # base=/system export CLASSPATH=$base/framework/pm.jar exec app_process $base/bin com.android.commands.pm.Pm "$@" # $@ 表示传给脚本的所有参数的列表
intmain(int argc, char* const argv[]) { if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { // Older kernels don't understand PR_SET_NO_NEW_PRIVS and return // EINVAL. Don't die on such kernels. if (errno != EINVAL) { LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno)); return12; } } AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv)); // Process command line arguments // ignore argv[0](argv[0]应该就是指app_process吧) argc--; argv++; // Everything up to '--' or first non '-' arg goes to the vm. // 把'--'开头之前的参数,或者第一个非'-'开头的参数传递给虚拟机 // // The first argument after the VM args is the "parent dir", which // is currently unused. // 虚拟机参数之后的第一个参数是当前未使用的“父目录” // // After the parent dir, we expect one or more the following internal // arguments : // // --zygote : Start in zygote mode // --start-system-server : Start the system server. // --application : Start in application (stand alone, non zygote) mode. // --nice-name : The nice name for this process. // // For non zygote starts, these arguments will be followed by // the main class name. All remaining arguments are passed to // the main method of this class. // 对于非zygote开始的,这些参数后面跟着主类名称,所有剩余的参数传递给这个类的main方法 // // For zygote starts, all remaining arguments are passed to the zygote. // main function. // 对于zygote开始的,所有剩余参数都将传递给zygote // // Note that we must copy argument string values since we will rewrite the // entire argument block when we apply the nice name to argv0. // 请注意,我们必须复制参数字符串值,因为当我们将nice名称应用于argv0(app_process?)时,我们将重写 // 整个参数块。 int i; for (i = 0; i < argc; i++) { if (argv[i][0] != '-') { break;// 不是以'-'开头,跳出循环 } if (argv[i][1] == '-' && argv[i][2] == 0) { ++i; // Skip --. break; } runtime.addOption(strdup(argv[i])); } // Parse runtime arguments. Stop at first unrecognized option. boolzygote=false; boolstartSystemServer=false; boolapplication=false; String8 niceName; String8 className; ++i; // Skip unused "parent dir" argument. while (i < argc) { const char* arg = argv[i++]; if (strcmp(arg, "--zygote") == 0) { zygote = true; niceName = ZYGOTE_NICE_NAME; } elseif (strcmp(arg, "--start-system-server") == 0) { startSystemServer = true; } elseif (strcmp(arg, "--application") == 0) { application = true; } elseif (strncmp(arg, "--nice-name=", 12) == 0) { niceName.setTo(arg + 12); } elseif (strncmp(arg, "--", 2) != 0) { // 如果arg的前2个字符不为"--",进入该条件分支设置className className.setTo(arg); break; } else { --i; break; } } ... ... } ... ... if (zygote) { runtime.start("com.android.internal.os.ZygoteInit", args, zygote); } elseif (className) { // 不是启动zygote,而是启动className对应的类RuntimeInit runtime.start("com.android.internal.os.RuntimeInit", args, zygote); } else { fprintf(stderr, "Error: no class name or --zygote supplied.\n"); app_usage(); LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); return10; } }
voidAndroidRuntime::start(constchar* className, const Vector<String8>& options, bool zygote) { ... ... /* * Start VM. This thread becomes the main thread of the VM, and will * not return until the VM exits. */ char* slashClassName = toSlashClassName(className); jclass startClass = env->FindClass(slashClassName); if (startClass == NULL) { ALOGE("JavaVM unable to locate class '%s'\n", slashClassName); /* keep going */ } else { jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V"); if (startMeth == NULL) { ALOGE("JavaVM unable to find main() in '%s'\n", className); /* keep going */ } else { // 反射调用main函数,从native层进入java世界 env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0 if (env->ExceptionCheck()) threadExitUncaughtException(env); #endif } } free(slashClassName);
ALOGD("Shutting down VM\n"); if (mJavaVM->DetachCurrentThread() != JNI_OK) ALOGW("Warning: unable to detach main thread\n"); if (mJavaVM->DestroyJavaVM() != 0) ALOGW("Warning: VM did not shut down cleanly\n"); }
/* * Code written in the Java Programming Language calls here from main(). * 从用java写的RuntimeInit的main函数调用此处 */ staticvoidcom_android_internal_os_RuntimeInit_nativeFinishInit(JNIEnv* env, jobject clazz) { gCurRuntime->onStarted(); }
methodId = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V"); if (methodId == NULL) { ALOGE("ERROR: could not find method %s.main(String[])\n", className.string()); return UNKNOWN_ERROR; }
/* * We want to call main() with a String array with our arguments in it. * Create an array and populate it. */ jclass stringClass; jobjectArray strArray;
/* * Keep this around to support existing users of the "pm install" command that may not be * able to be updated [or, at least informed the API has changed] such as ddmlib. * * Moving the implementation of "pm install" to "cmd package install" changes the executing * context. Instead of being a stand alone process, "cmd package install" runs in the * system_server process. Due to SELinux rules, system_server cannot access many directories; * one of which being the package install staging directory [/data/local/tmp]. * * The use of "adb install" or "cmd package install" over "pm install" is highly encouraged. */ privateintrunInstall()throws RemoteException { // 根据install后面的参数创建InstallParams,也包含了SessionParams,标志为MODE_FULL_INSTALL // Mode for an install session whose staged APKs should fully replace any existing APKs for the target app. finalInstallParamsparams= makeInstallParams(); // InstallParams之后的参数,就是所要安装的APK文件,即inPath finalStringinPath= nextArg(); // 是否安装到外置存储 booleaninstallExternal= (params.sessionParams.installFlags & PackageManager.INSTALL_EXTERNAL) != 0; if (params.sessionParams.sizeBytes < 0 && inPath != null) { Filefile=newFile(inPath); if (file.isFile()) { if (installExternal) { try { ApkLitebaseApk= PackageParser.parseApkLite(file, 0); PackageLitepkgLite=newPackageLite(null, baseApk, null, null, null); params.sessionParams.setSize( PackageHelper.calculateInstalledSize(pkgLite, false, params.sessionParams.abiOverride)); } catch (PackageParserException | IOException e) { System.err.println("Error: Failed to parse APK file : " + e); return1; } } else { params.sessionParams.setSize(file.length()); } } }
// Only system components can circumvent runtime permissions when installing. if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0 && mContext.checkCallingOrSelfPermission(Manifest.permission .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) { thrownewSecurityException("You need the " + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission " + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag"); }
} elseif ((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 { // 通过adb安装会进入到这个分支,为SessionParams设置InstallInternal Flag // 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. finallongident= Binder.clearCallingIdentity(); try { params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, params.appPackageName, params.installLocation, params.sizeBytes); } finally { Binder.restoreCallingIdentity(ident); } }
finalint sessionId; final PackageInstallerSession session; synchronized (mSessions) { // Sanity check that installer isn't going crazy // 确保同一个uid没有提交过多的活动Session,MAX_ACTIVE_SESSIONS=1024 finalintactiveCount= getSessionCount(mSessions, callingUid); if (activeCount >= MAX_ACTIVE_SESSIONS) { thrownewIllegalStateException( "Too many active sessions for UID " + callingUid); } // 确保同一个uid没有提交过多的历史Session,MAX_HISTORICAL_SESSIONS=1048576 finalinthistoricalCount= getSessionCount(mHistoricalSessions, callingUid); if (historicalCount >= MAX_HISTORICAL_SESSIONS) { thrownewIllegalStateException( "Too many historical sessions for UID " + callingUid); }
在Write Session中通过session = new PackageInstaller.Session(mInstaller.openSession(sessionId));获取了PackageInstallerSession的调用接口,PackageInstaller.Session的构造函数如下:
// TODO: deliver more granular progress for ASEC allocation mInternalProgress = 0.25f; computeProgressLocked(true); } else { thrownewIllegalArgumentException( "Exactly one of stageDir or stageCid stage must be set"); }
@Override public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) { try { return openWriteInternal(name, offsetBytes, lengthBytes); } catch (IOException e) { throw ExceptionUtils.wrap(e); } }
private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes) throws IOException { // Quick sanity check of state, and allocate a pipe for ourselves. We // then do heavy disk allocation outside the lock, but this open pipe // will block any attempted install transitions. // FileBridge建立客户端和服务端的管道 final FileBridge bridge; synchronized (mLock) { assertPreparedAndNotSealed("openWrite");
bridge = newFileBridge(); mBridges.add(bridge); }
try { // Use installer provided name for now; we always rename later if (!FileUtils.isValidExtFilename(name)) { thrownewIllegalArgumentException("Invalid name: " + name); } final File target; finallongidentity= Binder.clearCallingIdentity(); try { target = newFile(resolveStageDir(), name); } finally { Binder.restoreCallingIdentity(identity); }
// TODO: this should delegate to DCS so the system process avoids // holding open FDs into containers. finalFileDescriptortargetFd= Libcore.os.open(target.getAbsolutePath(), O_CREAT | O_WRONLY, 0644); Os.chmod(target.getAbsolutePath(), 0644);
// If caller specified a total length, allocate it for them. Free up // cache space to grow, if needed. if (lengthBytes > 0) { finalStructStatstat= Libcore.os.fstat(targetFd); finallongdeltaBytes= lengthBytes - stat.st_size; // Only need to free up space when writing to internal stage if (stageDir != null && deltaBytes > 0) { mPm.freeStorage(params.volumeUuid, deltaBytes); } Libcore.os.posix_fallocate(targetFd, 0, lengthBytes); }
if (offsetBytes > 0) { Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET); }
/** * Simple bridge that allows file access across process boundaries without * returning the underlying {@link FileDescriptor}. This is useful when the * server side needs to strongly assert that a client side is completely * hands-off. * * @hide */ publicclassFileBridgeextendsThread { privatestaticfinalStringTAG="FileBridge";
// TODO: consider extending to support bidirectional IO
/** * Attempt to commit everything staged in this session. This may require * user intervention, and so it may not happen immediately. The final * result of the commit will be reported through the given callback. * <p> * Once this method is called, no additional mutations may be performed * on the session. If the device reboots before the session has been * finalized, you may commit the session again. * * @throws SecurityException if streams opened through * {@link #openWrite(String, long, long)} are still open. */ publicvoidcommit(@NonNull IntentSender statusReceiver) { try { // 通过Binder通信调用PackageInstallerSession中的commit函数 mSession.commit(statusReceiver); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
finalboolean wasSealed; // boolean默认值为false synchronized (mLock) { wasSealed = mSealed; if (!mSealed) { // Verify that all writers are hands-off // 在FileBridge.java中run()的finally代码块中(也即doWriteSession传输数据的结尾)会关闭bridge for (FileBridge bridge : mBridges) { if (!bridge.isClosed()) { thrownewSecurityException("Files still open"); } } mSealed = true; }
// Client staging is fully done at this point mClientProgress = 1f; computeProgressLocked(true); }
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); }
// This ongoing commit should keep session active, even though client // will probably close their end. mActiveCount.incrementAndGet();
// Verify that stage looks sane with respect to existing application. // This currently only ensures packageName, versionCode, and certificate // consistency. // 检查apk文件是否满足要求,验证包名,版本号,证书的一致性 validateInstallLocked(pkgInfo, appInfo);
// 检查权限 if (!mPermissionsAccepted) { // User needs to accept permissions; give installer an intent they // can use to involve user. finalIntentintent=newIntent(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. close(); 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. finallongfinalSize= 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) { // 如果新的APK文件继承某些已安装的Package(不懂。。。),此处将copy需要的native库文件等 ... ... }
// TODO: surface more granular state from dexopt mInternalProgress = 0.5f; computeProgressLocked(true);
// Container is ready to go, let's seal it up! // 封装容器,会针对安装在sdcard的操作做一些处理 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. finalIPackageInstallObserver2localObserver=newIPackageInstallObserver2.Stub() { @Override publicvoidonUserActionRequired(Intent intent) { thrownewIllegalStateException(); }
final UserHandle user; if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) { user = UserHandle.ALL; } else { user = newUserHandle(userId); }
publicvoidhandleMessage(Message msg) { try { doHandleMessage(msg); } finally { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } } voiddoHandleMessage(Message msg) { switch (msg.what) { case INIT_COPY: { // 在installStage中msg.obj已经被赋值安装参数 HandlerParamsparams= (HandlerParams) msg.obj; // idx为当前等待处理的安装请求个数 intidx= mPendingInstalls.size(); if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params); // If a bind was already initiated we dont really // need to do anything. The pending install // will be processed later on. // 如果已经有一个绑定被初始化,那就不做任何事情,待安装的操作稍后会进行,初始时mBound的值为false 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. // 绑定实际的安装service if (!connectToService()) { Slog.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等待处理 mPendingInstalls.add(idx, params); } } else { // 如果已经绑定过service,同样将新的请求加入到mPendingInstalls等待处理 mPendingInstalls.add(idx, params); // Already bound to the service. Just make // sure we trigger off processing the first request. if (idx == 0) { // idx=0代表第一个请求,直接发送MCS_BOUND事件,触发处理流程 mHandler.sendEmptyMessage(MCS_BOUND); } } break; } ... ... } } }
voiddoHandleMessage(Message msg) { switch (msg.what) { ... ... case MCS_BOUND: { if (DEBUG_INSTALL) Slog.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) { ... ... } elseif (mPendingInstalls.size() > 0) { // 安装请求的个数大于0 // 获取第一个安装请求 HandlerParamsparams= 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 install if (mPendingInstalls.size() > 0) { mPendingInstalls.remove(0); } if (mPendingInstalls.size() == 0) { // 如果没有安装请求了则10秒钟后解绑service if (mBound) { if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting delayed MCS_UNBIND"); removeMessages(MCS_UNBIND); Messageubmsg= obtainMessage(MCS_UNBIND); // Unbind after a little delay, to avoid // continual thrashing. sendMessageDelayed(ubmsg, 10000); } } else { // 否则继续发送MCS_BOUND消息 // 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. Slog.w(TAG, "Empty queue"); } break; }
classInstallParamsextendsHandlerParams { ... ... /* * Invoke remote method to get package information and install * location values. Override install location based on default * policy if needed and then create install arguments based * on the install location. */ publicvoidhandleStartCopy()throws RemoteException { intret= PackageManager.INSTALL_SUCCEEDED;
// If we're already staged, we've firmly committed to an install location if (origin.staged) { if (origin.file != null) { installFlags |= PackageManager.INSTALL_INTERNAL; installFlags &= ~PackageManager.INSTALL_EXTERNAL; } elseif (origin.cid != null) { installFlags |= PackageManager.INSTALL_EXTERNAL; installFlags &= ~PackageManager.INSTALL_INTERNAL; } else { thrownewIllegalStateException("Invalid stage location"); } }
// 检查APK的安装位置是否正确 if (onInt && onSd) { // Check if both bits are set. // APK不能同时安装在内部存储和SD卡上 Slog.w(TAG, "Conflicting flags specified for installing on both internal and external"); ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } elseif (onSd && ephemeral) { // APK不能短暂的安装在SD卡中 Slog.w(TAG, "Conflicting flags specified for installing ephemeral on external"); ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else { // getMini...用来解析安装包,返回PackageInfoLite对象,判断能否安装,具体见5.1 pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride);
if (DEBUG_EPHEMERAL && ephemeral) { Slog.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 device finalStorageManagerstorage= StorageManager.from(mContext); // 获取设备内部存储空间允许的最小存储空间大小 finallonglowThreshold= storage.getStorageLowBytes( Environment.getDataDirectory());
/* * 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. */ // 如果经过释放cache后还是无法安装,则把安装失败flag保存到recom... if (pkgLite.recommendedInstallLocation == PackageHelper.RECOMMEND_FAILED_INVALID_URI) { pkgLite.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; } } }
if (ret == PackageManager.INSTALL_SUCCEEDED) { // recommendedInstallLocation保存安装路径信息,即内部还是SD卡中,也记录安装失败的信息 intloc= pkgLite.recommendedInstallLocation; if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) { ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } elseif (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) { ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS; } elseif (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } elseif (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) { ret = PackageManager.INSTALL_FAILED_INVALID_APK; } elseif (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) { ret = PackageManager.INSTALL_FAILED_INVALID_URI; } elseif (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) { ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE; } else { // Override with defaults if needed. // 如果安装路径有足够的空间,loc就不会等于上述判断条件 // 代码将会走到这里,installLocationPolicy用来判断APK是否已经安装过,具体见5.2 loc = installLocationPolicy(pkgLite); ... ... } }
if (ret == PackageManager.INSTALL_SUCCEEDED) { // TODO: http://b/22976637 // Apps installed for "all" users use the device owner to verify the app UserHandleverifierUser= 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. */ finalintrequiredUid= mRequiredVerifierPackage == null ? -1 : getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING, verifierUser.getIdentifier()); if (!origin.existing && requiredUid != -1 && isVerificationEnabled(verifierUser.getIdentifier(), installFlags)) { // 存在安装包检查者,并且满足启动检查条件,就利用安装包检查者检查 finalIntentverification=newIntent( Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); verification.setDataAndType(Uri.fromFile(newFile(origin.resolvedPath)), PACKAGE_MIME_TYPE); verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// 检查安装包的操作 ... ... } else { /* * No package verification is enabled, so immediately start * the remote call to initiate copy using temporary file. */ // 没有安装包检查,则直接执行copyApk函数,具体见5.4 ret = args.copyApk(mContainerService, true); } }
/** * Parse given package and return minimal details. * * @param packagePath absolute path to the package to be copied. Can be * a single monolithic APK file or a cluster directory * containing one or more APKs. */ @Override public PackageInfoLite getMinimalPackageInfo(String packagePath, int flags, String abiOverride) { finalContextcontext= DefaultContainerService.this; finalbooleanisForwardLocked= (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
/** * Given a requested {@link PackageInfo#installLocation} and calculated * install size, pick the actual location to install the app. */ publicstaticintresolveInstallLocation(Context context, String packageName, int installLocation, long sizeBytes, int installFlags) { ApplicationInfoexistingInfo=null; try { // 就根据包名获取已经存在的ApplicationInfo信息,意如其名existingInfo existingInfo = context.getPackageManager().getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES); } catch (NameNotFoundException ignored) { }
// 根据prefer和上面得出的fits...再次判断返回的安装目录 // 怎么这么多重复判断呢,感觉代码写的有点冗余,明明可以合在上面代码中一并处理 if (prefer == RECOMMEND_INSTALL_INTERNAL) { // The ephemeral case will either fit and return EPHEMERAL, or will not fit // and will fall through to return INSUFFICIENT_STORAGE if (fitsOnInternal) { return (ephemeral) ? PackageHelper.RECOMMEND_INSTALL_EPHEMERAL : PackageHelper.RECOMMEND_INSTALL_INTERNAL; } } elseif (prefer == RECOMMEND_INSTALL_EXTERNAL) { if (fitsOnExternal) { return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; } }
// 正常情况下以上部分代码已经返回了安装路径 if (checkBoth) { if (fitsOnInternal) { return PackageHelper.RECOMMEND_INSTALL_INTERNAL; } elseif (fitsOnExternal) { return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; } }
/** * Copy package to the target location. * copy安装包到target * * @param packagePath absolute path to the package to be copied. Can be * a single monolithic APK file or a cluster directory * containing one or more APKs. * @return returns status code according to those in * {@link PackageManager} */ @Override publicintcopyPackage(String packagePath, IParcelFileDescriptorFactory target) { if (packagePath == null || target == null) { return PackageManager.INSTALL_FAILED_INVALID_URI; }
classInstallParamsextendsHandlerParams { ... ... @Override voidhandleReturnCode() { if (mArgs != null) { processPendingInstall(mArgs, mRet); } } ... ... privatevoidprocessPendingInstall(final InstallArgs args, finalint currentStatus) { // Queue up an async operation since the package installation may take a little while. // 安装需要一些时间,新建一个线程 mHandler.post(newRunnable() { publicvoidrun() { mHandler.removeCallbacks(this); // Result object to be returned PackageInstalledInfores=newPackageInstalledInfo(); res.setReturnCode(currentStatus); res.uid = -1; res.pkg = null; res.removedInfo = null; if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { // 见6.1 args.doPreInstall(res.returnCode); synchronized (mInstallLock) { // 进行安装,见6.2 installPackageTracedLI(args, res); } // 见6.3 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. // 如果(a)安装成功(b)不是更新操作(c)新的package没有选择退出备份,则进行备份 finalbooleanupdate= res.removedInfo != null && res.removedInfo.removedPackage != null; finalintflags= (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags; booleandoRestore= !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; if (mNextInstallToken < 0) mNextInstallToken = 1; token = mNextInstallToken++;
// doRestore为true的话则进行恢复工作 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. IBackupManagerbm= 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/22388012 if (bm.isBackupServiceActive(UserHandle.USER_SYSTEM)) { bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token); } else { doRestore = false; } } catch (RemoteException e) { // can't happen; the backup manager is local } catch (Exception e) { Slog.e(TAG, "Exception trying to enqueue restore", e); doRestore = false; } } else { Slog.e(TAG, "Backup Manager not found!"); doRestore = false; } }
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);
// If we are installing a clustered package add results for the children // 安装clustered pacakge时的一些操作 if (pkg.childPackages != null) { synchronized (mPackages) { ... ... } } ... ...
try { // either use what we've been given or parse directly from the APK // 权限信息的一些处理 if (args.certificates != null) { try { // 如果args中包含了权限信息,则直接用参数中的权限配置package PackageParser.populateCertificates(pkg, args.certificates); } catch (PackageParserException e) { // there was something wrong with the certificates we were given; // try to pull them from the APK PackageParser.collectCertificates(pkg, parseFlags); } } else { // 否则直接从Manifest.xml中解析出权限信息 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; StringoldCodePath=null; booleansystemApp=false;