第一次画流程图画的不好
通过adb install安装应用时对framework来说会首先调用Pm.java的runInstall()方法

private int runInstall(){int installFlags = 0;int userId = UserHandle.USER_ALL;String installerPackageName = null;String opt;String originatingUriString = null;String referrer = null;String abi = null;while ((opt=nextOption()) != null) {if (opt.equals("-l")) {installFlags |= PackageManager.INSTALL_FORWARD_LOCK;} else if (opt.equals("-r")) {installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;} else if (opt.equals("-i")) {installerPackageName = nextOptionData();if (installerPackageName == null) {System.err.println("Error: no value specified for -i");return 1;}} else if (opt.equals("-t")) {installFlags |= PackageManager.INSTALL_ALLOW_TEST;} else if (opt.equals("-s")) {// Override if -s option is specified.installFlags |= PackageManager.INSTALL_EXTERNAL;} else if (opt.equals("-f")) {// Override if -s option is specified.installFlags |= PackageManager.INSTALL_INTERNAL;} else if (opt.equals("-d")) {installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;} else if (opt.equals("--originating-uri")) {originatingUriString = nextOptionData();if (originatingUriString == null) {System.err.println("Error: must supply argument for --originating-uri");return 1;}} else if (opt.equals("--referrer")) {referrer = nextOptionData();if (referrer == null) {System.err.println("Error: must supply argument for --referrer");return 1;}} else if (opt.equals("--abi")) {abi = checkAbiArgument(nextOptionData());} else if (opt.equals("--user")) {userId = Integer.parseInt(nextOptionData());} else {System.err.println("Error: Unknown option: " + opt);return 1;}}if (userId == UserHandle.USER_ALL) {userId = UserHandle.USER_OWNER;installFlags |= PackageManager.INSTALL_ALL_USERS;}final Uri verificationURI;final Uri originatingURI;final Uri referrerURI;if (originatingUriString != null) {originatingURI = Uri.parse(originatingUriString);} else {originatingURI = null;}if (referrer != null) {referrerURI = Uri.parse(referrer);} else {referrerURI = null;}// Populate apkURI, must be presentfinal String apkFilePath = nextArg();System.err.println("\tpkg: " + apkFilePath);if (apkFilePath == null) {System.err.println("Error: no package specified");return 1;}// Populate verificationURI, optionally presentfinal String verificationFilePath = nextArg();if (verificationFilePath != null) {System.err.println("\tver: " + verificationFilePath);verificationURI = Uri.fromFile(new File(verificationFilePath));} else {verificationURI = null;}LocalPackageInstallObserver obs = new LocalPackageInstallObserver();try {VerificationParams verificationParams = new VerificationParams(verificationURI,originatingURI, referrerURI, VerificationParams.NO_UID, null);mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,installerPackageName, verificationParams, abi, userId);synchronized (obs) {while (!obs.finished) {try {obs.wait();} catch (InterruptedException e) {}}if (obs.result == PackageManager.INSTALL_SUCCEEDED) {System.out.println("Success");return 0;} else {System.err.println("Failure ["+ installFailureToString(obs)+ "]");return 1;}}} catch (RemoteException e) {System.err.println(e.toString());System.err.println(PM_NOT_RUNNING_ERR);return 1;}}

通过代码可以看出,系统会根据adb传入的参数做不同的处理并且会检查文件是否是以.apk结尾,会通过mPm.installPackageAsUser进行apk的安装,先看下mPm,通过asInterface转换得到PackageManagerService

IPackageManager mPm;
mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));

关于ServiceManager.getService("package")的参数packageSystemServer调用PackageManagerServicemain()方法时加入的

    public static final PackageManagerService main(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {PackageManagerService m = new PackageManagerService(context, installer,factoryTest, onlyCore);ServiceManager.addService("package", m);return m;}

然后看下PackageManagerServiceinstallPackageAsUser()方法

    @Overridepublic void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,int installFlags, String installerPackageName, VerificationParams verificationParams,String packageAbiOverride, int userId) {mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);final int callingUid = Binder.getCallingUid();enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser");if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {try {if (observer != null) {observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);}} catch (RemoteException re) {}return;}if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {installFlags |= PackageManager.INSTALL_FROM_ADB;} else {// Caller holds INSTALL_PACKAGES permission, so we're less strict// about installerPackageName.installFlags &= ~PackageManager.INSTALL_FROM_ADB;installFlags &= ~PackageManager.INSTALL_ALL_USERS;}UserHandle user;if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {user = UserHandle.ALL;} else {user = new UserHandle(userId);}verificationParams.setInstallerUid(callingUid);mFilePath = originPath;final File originFile = new File(originPath);final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);final Message msg = mHandler.obtainMessage(INIT_COPY);msg.obj = new InstallParams(origin, observer, installFlags,installerPackageName, verificationParams, user, packageAbiOverride);mHandler.sendMessage(msg);}

这里面会做一些UID和权限的检查,只有有权限才可以安装apk,在最后通过mHandler传出INIT_COPY消息,然后看下消息接收的地方doHandleMessage

 case INIT_COPY: {HandlerParams params = (HandlerParams) msg.obj;int idx = mPendingInstalls.size();if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);// If a bind was already initiated we dont really// need to do anything. The pending install// will be processed later on.if (!mBound) {// If this is the only one pending we might// have to bind to the service again.if (!connectToService()) {Slog.e(TAG, "Failed to bind to media container service");params.serviceError();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;}

在这个方法中mBound主要时用来判断DefaultContainerService服务是否connect,连接则为true否则为false,系统通过mPendingInstalls.add(idx, params);将apk的信息添加在mPendingInstalls中,当我们的apk排在第一位时会触发mHandler.sendEmptyMessage(MCS_BOUND);真正的去安装我们的apk,看下MCS_BOUND消息的处理

case MCS_BOUND:{if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");if (msg.obj != null) {mContainerService = (IMediaContainerService) msg.obj;}if (mContainerService == null) {// Something seriously wrong. Bail outSlog.e(TAG, "Cannot bind to media container service");for (HandlerParams params : mPendingInstalls) {// Indicate service bind errorparams.serviceError();}mPendingInstalls.clear();} else if (mPendingInstalls.size() > 0) {HandlerParams params = mPendingInstalls.get(0);if (params != null) {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");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);}}}} else {// Should never happen ideally.Slog.w(TAG, "Empty queue");}break;}

MediaContainerService服务正常且apk的信息存在时,系统就会调用params.startCopy()去复制我们的apk,在复制完成之后就会从mPendingInstalls中将这个apk移除队列并解绑服务,我们继续看下startCopy()方法

final boolean startCopy() {boolean res;try {if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);if (++mRetries > MAX_RETRIES) {Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");mHandler.sendEmptyMessage(MCS_GIVE_UP);handleServiceError();return false;} else {handleStartCopy();Slog.i(TAG, "Apk copy done");res = true;}} catch (RemoteException e) {if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");mHandler.sendEmptyMessage(MCS_RECONNECT);res = false;}handleReturnCode();return res;}

系统通过handleStartCopy()去copy我们的apk,其中涉及一些存储空间检查,权限检查等等安全校验
handleReturnCode()则是我们apk安装的下一步

@Overridevoid handleReturnCode() {// If mArgs is null, then MCS couldn't be reached. When it// reconnects, it will try again to install. At that point, this// will succeed.if (mArgs != null) {processPendingInstall(mArgs, mRet);}}

通过调用processPendingInstall来进行安装apk,判断条件mArgs则是在handleStartCopy()中进行赋值,在handleStartCopy这一步没有问题时才会进入processPendingInstall()

private void processPendingInstall(final InstallArgs args, final int currentStatus) {// Queue up an async operation since the package installation may take a little while.mHandler.post(new Runnable() {public void run() {mHandler.removeCallbacks(this);// Result object to be returnedPackageInstalledInfo res = new PackageInstalledInfo();res.returnCode = currentStatus;res.uid = -1;res.pkg = null;res.removedInfo = new PackageRemovedInfo();if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {args.doPreInstall(res.returnCode);synchronized (mInstallLock) {installPackageLI(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.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;if (mNextInstallToken < 0) mNextInstallToken = 1;token = mNextInstallToken++;PostInstallData data = new PostInstallData(args, res);mRunningInstalls.put(token, data);if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {// Pass responsibility to the Backup Manager.  It will perform a// restore if appropriate, then pass responsibility back to the// Package Manager to run the post-install observer callbacks// and broadcasts.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");try {if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) {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);Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);mHandler.sendMessage(msg);}}});}

在这个方法中做的事情还是很多的
1 installPackageLI(args, res);负责下一步安装
2 args.doPostInstall(res.returnCode, res.uid); 判断是否执行clean操作
3 bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);对apk进行备份且调用mPackageManagerBinder.finishPackageInstall(token);完成apk的安装

我们先看下第一步installPackageLI(args, res)

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {final int installFlags = args.installFlags;String installerPackageName = args.installerPackageName;File tmpPackageFile = new File(args.getCodePath());boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);boolean onSd = ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0);boolean replace = false;final int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE;// Result object to be returnedres.returnCode = PackageManager.INSTALL_SUCCEEDED;if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);// Retrieve PackageSettings and parse packagefinal int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)| (onSd ? PackageParser.PARSE_ON_SDCARD : 0);PackageParser pp = new PackageParser();pp.setSeparateProcesses(mSeparateProcesses);pp.setDisplayMetrics(mMetrics);final PackageParser.Package pkg;try {if (DEBUG_INSTALL) Slog.i(TAG, "Start parsing apk: " + installerPackageName);pkg = pp.parsePackage(tmpPackageFile, parseFlags);if (DEBUG_INSTALL) Slog.i(TAG, "Parsing done for apk: " + installerPackageName);} catch (PackageParserException e) {res.setError("Failed parse during installPackageLI", e);return;}// Mark that we have an install time CPU ABI override.pkg.cpuAbiOverride = args.abiOverride;String pkgName = res.name = pkg.packageName;if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {res.setError(INSTALL_FAILED_TEST_ONLY, "installPackageLI");return;}}try {pp.collectCertificates(pkg, parseFlags);pp.collectManifestDigest(pkg);} catch (PackageParserException e) {res.setError("Failed collect during installPackageLI", e);return;}/* If the installer passed in a manifest digest, compare it now. */if (args.manifestDigest != null) {if (DEBUG_INSTALL) {final String parsedManifest = pkg.manifestDigest == null ? "null": pkg.manifestDigest.toString();Slog.d(TAG, "Comparing manifests: " + args.manifestDigest.toString() + " vs. "+ parsedManifest);}if (!args.manifestDigest.equals(pkg.manifestDigest)) {res.setError(INSTALL_FAILED_PACKAGE_CHANGED, "Manifest digest changed");return;}} else if (DEBUG_INSTALL) {final String parsedManifest = pkg.manifestDigest == null? "null" : pkg.manifestDigest.toString();Slog.d(TAG, "manifestDigest was not present, but parser got: " + parsedManifest);}// Get rid of all references to package scan path via parser.pp = null;String oldCodePath = null;boolean systemApp = false;/// M: [Operator] record if the original package is an operator packageboolean vendorApp = false;synchronized (mPackages) {// Check if installing already existing packageif ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {String oldName = mSettings.mRenamedPackages.get(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.pkg.setPackageName(oldName);pkgName = pkg.packageName;replace = true;if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName="+ oldName + " pkgName=" + pkgName);} else if (mPackages.containsKey(pkgName)) {// This package, under its official name, already exists// on the device; we should replace it.replace = true;if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);}}PackageSetting ps = mSettings.mPackages.get(pkgName);if (ps != null) {if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);// 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 (!ps.keySetData.isUsingUpgradeKeySets() || ps.sharedUser != null) {try {verifySignaturesLP(ps, pkg);} catch (PackageManagerException e) {res.setError(e.error, e.getMessage());return;}} else {if (!checkUpgradeKeySetLP(ps, pkg)) {res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "+ pkg.packageName + " upgrade keys do not match the "+ "previously installed version");return;}}oldCodePath = mSettings.mPackages.get(pkgName).codePathString;if (ps.pkg != null && ps.pkg.applicationInfo != null) {systemApp = (ps.pkg.applicationInfo.flags &ApplicationInfo.FLAG_SYSTEM) != 0;vendorApp = isVendorApp(ps.pkg);}res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);}// Check whether the newly-scanned package wants to define an already-defined permint 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);if (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)|| !bp.packageSetting.keySetData.isUsingUpgradeKeySets()|| ((PackageSetting) bp.packageSetting).sharedUser != null) {sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures,pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;} else {sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg);}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 {Slog.w(TAG, "Package " + pkg.packageName+ " attempting to redeclare system permission "+ perm.info.name + "; ignoring new declaration");pkg.permissions.remove(i);}}}}}/// M: [Operator] Updated operator apps can only be installed on data storageif ((systemApp || vendorApp) && onSd) {// Disable updates to system apps on sdcardres.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,"Cannot install updates to system or vendor apps on sdcard");return;}if (!args.doRename(res.returnCode, pkg, oldCodePath)) {res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");return;}if (DEBUG_INSTALL) Slog.i(TAG, "Start installation for package: " + installerPackageName);mIsInstallApkFlag = true;if (replace) {replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,installerPackageName, res);} else {installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,args.user, installerPackageName, res);}if (DEBUG_INSTALL) Slog.i(TAG, "Installation done for package: " + installerPackageName);synchronized (mPackages) {final PackageSetting ps = mSettings.mPackages.get(pkgName);if (ps != null) {res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);}}}

这个方法做的事情很多,通过pp.parsePackage对apk进行解析然后通过pp.collectCertificates(pkg, parseFlags);
pp.collectManifestDigest(pkg);通过这两个方法对apk进行签名证书的解析和保存,关于这部分可参考PMS-应用安装过程签名(Android L)
这篇文章中pp.collectManifestDigest(pkg)部分后续补齐
之后系统就会对apk的签名,证书,权限等进行验证,最后根据apk是否已经存在会有不同的流程,是否覆盖安装

if (replace) {replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,installerPackageName, res);} else {installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,args.user, installerPackageName, res);}

这里面我们主要看下installNewPackageLI非覆盖安装

private void installNewPackageLI(PackageParser.Package pkg,int parseFlags, int scanFlags, UserHandle user,String installerPackageName, PackageInstalledInfo res) {// Remember this for later, in case we need to rollback this installString pkgName = pkg.packageName;if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();synchronized(mPackages) {if (mSettings.mRenamedPackages.containsKey(pkgName)) {// A package with the same name is already installed, though// it has been renamed to an older name.  The package we// are trying to install should be installed as an update to// the existing one, but that has not been requested, so bail.res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName+ " without first uninstalling package running as "+ mSettings.mRenamedPackages.get(pkgName));return;}if (mPackages.containsKey(pkgName)) {// Don't allow installation over an existing package with the same name.res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName+ " without first uninstalling.");return;}}try {PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,System.currentTimeMillis(), user);updateSettingsLI(newPackage, installerPackageName, null, null, res);// delete the partially installed application. the data directory will have to be// restored if it was already existingif (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {// remove package from internal structures.  Note that we want deletePackageX to// delete the package data and cache directories that it created in// scanPackageLocked, unless those directories existed before we even tried to// install.deletePackageLI(pkgName, UserHandle.ALL, false, null, null,dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,res.removedInfo, true);}} catch (PackageManagerException e) {res.setError("Package couldn't be installed in " + pkg.codePath, e);}}

在这一部分中,主要做了三件事,
1 scanPackageLI 继续执行安装流程
2 updateSettingsLI 更新应用相关的packagesetting
3 deletePackageLI 安装失败之后删除apk相关的资源
其他的先不看,我们继续看scanPackageLI方法

{final File scanFile = new File(pkg.codePath);if (pkg.applicationInfo.getCodePath() == null ||pkg.applicationInfo.getResourcePath() == null) {// Bail out. The resource and code paths haven't been set.throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,"Code and resource paths haven't been set correctly");}if(mIsInstallApkFlag) {Log.d("DYNA", "PackageName: " + pkg.packageName);Log.d("DYNA", "App Signature: " + pkg.mSignatures[0].toCharsString());mIsInstallApkFlag = false;Signature[] testkeySigns = null;PackageSetting testkeyps = mSettings.mPackages.get("com.android.webview");if (testkeyps != null) {PackageParser.Package testkeypkg = testkeyps.pkg;if(testkeypkg != null) {testkeySigns = testkeypkg.mSignatures;}}Signature[] platformSigns = null;PackageSetting platformps = mSettings.mPackages.get("com.mediatek.ppl");if (platformps != null) {PackageParser.Package platformpkg = platformps.pkg;if(platformpkg != null) {platformSigns = platformpkg.mSignatures;}}if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;} else {// Only allow system apps to be flagged as core apps.pkg.coreApp = false;}if ((parseFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {pkg.applicationInfo.flags |= ApplicationInfo.FLAG_PRIVILEGED;}/** M: [Operator] Add operator flags @{ */if ((parseFlags & PackageParser.PARSE_IS_OPERATOR) != 0) {pkg.applicationInfo.flagsEx |= ApplicationInfo.FLAG_EX_OPERATOR;}/** @} */if (mCustomResolverComponentName != null &&mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {setUpCustomResolverActivity(pkg);}if (pkg.packageName.equals("android")) {synchronized (mPackages) {if (mAndroidApplication != null) {Slog.w(TAG, "*************************************************");Slog.w(TAG, "Core android package being redefined.  Skipping.");Slog.w(TAG, " file=" + scanFile);Slog.w(TAG, "*************************************************");throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,"Core android package being redefined.  Skipping.");}// Set up information for our fall-back user intent resolution activity.mPlatformPackage = pkg;pkg.mVersionCode = mSdkVersion;mAndroidApplication = pkg.applicationInfo;if (!mResolverReplaced) {mResolveActivity.applicationInfo = mAndroidApplication;mResolveActivity.name = ResolverActivity.class.getName();mResolveActivity.packageName = mAndroidApplication.packageName;mResolveActivity.processName = "system:ui";mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert;mResolveActivity.exported = true;mResolveActivity.enabled = true;mResolveInfo.activityInfo = mResolveActivity;mResolveInfo.priority = 0;mResolveInfo.preferredOrder = 0;mResolveInfo.match = 0;mResolveComponentName = new ComponentName(mAndroidApplication.packageName, mResolveActivity.name);}}}/** M: [CIP] skip duplicated mediatek-res.apk @{ *//// This will replace original resources with CIP resourcesif (pkg.packageName.equals("com.mediatek")) {synchronized (mPackages) {if (mMediatekApplication != null) {Slog.w(TAG, "*************************************************");Slog.w(TAG, "Core mediatek package being redefined.  Skipping.");Slog.w(TAG, " file=" + scanFile);Slog.w(TAG, "*************************************************");throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,"Core android package being redefined.  Skipping.");}mMediatekApplication = pkg.applicationInfo;}}/** @} */if (DEBUG_PACKAGE_SCANNING) {if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)Log.d(TAG, "Scanning package " + pkg.packageName);}if (mPackages.containsKey(pkg.packageName)|| mSharedLibraries.containsKey(pkg.packageName)) {throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,"Application package " + pkg.packageName+ " already installed.  Skipping duplicate.");}// Initialize package source and resource directoriesFile destCodeFile = new File(pkg.applicationInfo.getCodePath());File destResourceFile = new File(pkg.applicationInfo.getResourcePath());SharedUserSetting suid = null;PackageSetting pkgSetting = null;if (!isSystemApp(pkg)) {// Only system apps can use these features.pkg.mOriginalPackages = null;pkg.mRealPackage = null;pkg.mAdoptPermissions = null;}// writersynchronized (mPackages) {if (pkg.mSharedUserId != null) {suid = mSettings.getSharedUserLPw(pkg.mSharedUserId, 0, true);if (suid == null) {throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,"Creating application package " + pkg.packageName+ " for shared user failed");}if (DEBUG_PACKAGE_SCANNING) {if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + suid.userId+ "): packages=" + suid.packages);}}// Check if we are renaming from an original package name.PackageSetting origPackage = null;String realName = null;if (pkg.mOriginalPackages != null) {// This package may need to be renamed to a previously// installed name.  Let's check on that...final String renamed = mSettings.mRenamedPackages.get(pkg.mRealPackage);if (pkg.mOriginalPackages.contains(renamed)) {// This package had originally been installed as the// original name, and we have already taken care of// transitioning to the new one.  Just update the new// one to continue using the old name.realName = pkg.mRealPackage;if (!pkg.packageName.equals(renamed)) {// Callers into this function may have already taken// care of renaming the package; only do it here if// it is not already done.pkg.setPackageName(renamed);}} else {for (int i=pkg.mOriginalPackages.size()-1; i>=0; i--) {if ((origPackage = mSettings.peekPackageLPr(pkg.mOriginalPackages.get(i))) != null) {// We do have the package already installed under its// original name...  should we use it?if (!verifyPackageUpdateLPr(origPackage, pkg)) {// New package is not compatible with original.origPackage = null;continue;} else if (origPackage.sharedUser != null) {// Make sure uid is compatible between packages.if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) {Slog.w(TAG, "Unable to migrate data from " + origPackage.name+ " to " + pkg.packageName + ": old uid "+ origPackage.sharedUser.name+ " differs from " + pkg.mSharedUserId);origPackage = null;continue;}} else {if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "+ pkg.packageName + " to old name " + origPackage.name);}break;}}}}if (mTransferedPackages.contains(pkg.packageName)) {Slog.w(TAG, "Package " + pkg.packageName+ " was transferred to another, but its .apk remains");}// Just create the setting, don't add it yet. For already existing packages// the PkgSetting exists already and doesn't have to be created.pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,pkg.applicationInfo.primaryCpuAbi,pkg.applicationInfo.secondaryCpuAbi,pkg.applicationInfo.flags, pkg.applicationInfo.flagsEx, user, false);if (pkgSetting == null) {throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,"Creating application package " + pkg.packageName + " failed");}if (pkgSetting.origPackage != null) {// If we are first transitioning from an original package,// fix up the new package's name now.  We need to do this after// looking up the package under its new name, so getPackageLP// can take care of fiddling things correctly.pkg.setPackageName(origPackage.name);// File a report about this.String msg = "New package " + pkgSetting.realName+ " renamed to replace old package " + pkgSetting.name;reportSettingsProblem(Log.WARN, msg);// Make a note of it.mTransferedPackages.add(origPackage.name);// No longer need to retain this.pkgSetting.origPackage = null;}if (realName != null) {// Make a note of it.mTransferedPackages.add(pkg.packageName);}if (mSettings.isDisabledSystemPackageLPr(pkg.packageName)) {/** M: [Operator] Operator package should not have FLAG_UPDATED_SYSTEM_APP @{ */if ((parseFlags & PackageParser.PARSE_IS_OPERATOR) == 0) {pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;}/** @} */}if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {// Check all shared libraries and map to their actual file path.// We only do this here for apps not on a system dir, because those// are the only ones that can fail an install due to this.  We// will take care of the system apps by updating all of their// library paths after the scan is done.updateSharedLibrariesLPw(pkg, null);}if (mFoundPolicyFile) {SELinuxMMAC.assignSeinfoValue(pkg);}pkg.applicationInfo.uid = pkgSetting.appId;pkg.mExtras = pkgSetting;if (!pkgSetting.keySetData.isUsingUpgradeKeySets() || pkgSetting.sharedUser != null) {try {verifySignaturesLP(pkgSetting, pkg);// We just determined the app is signed correctly, so bring// over the latest parsed certs.pkgSetting.signatures.mSignatures = pkg.mSignatures;} catch (PackageManagerException e) {if (((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) | (parseFlags & PackageParser.PARSE_IS_OPERATOR)) == 0) {throw e;}// The signature has changed, but this package is in the system// image...  let's recover!pkgSetting.signatures.mSignatures = pkg.mSignatures;// However...  if this package is part of a shared user, but it// doesn't match the signature of the shared user, let's fail.// What this means is that you can't change the signatures// associated with an overall shared user, which doesn't seem all// that unreasonable.if (pkgSetting.sharedUser != null) {if (compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {throw new PackageManagerException(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,"Signature mismatch for shared user : "+ pkgSetting.sharedUser);}}// File a report about this.String msg = "System package " + pkg.packageName+ " signature changed; retaining data.";reportSettingsProblem(Log.WARN, msg);}} else {if (!checkUpgradeKeySetLP(pkgSetting, pkg)) {throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "+ pkg.packageName + " upgrade keys do not match the "+ "previously installed version");} else {// We just determined the app is signed correctly, so bring// over the latest parsed certs.pkgSetting.signatures.mSignatures = pkg.mSignatures;}}// Verify that this new package doesn't have any content providers// that conflict with existing packages.  Only do this if the// package isn't already installed, since we don't want to break// things that are installed.if ((scanFlags & SCAN_NEW_INSTALL) != 0) {final int N = pkg.providers.size();int i;for (i=0; i<N; i++) {PackageParser.Provider p = pkg.providers.get(i);if (p.info.authority != null) {String names[] = p.info.authority.split(";");for (int j = 0; j < names.length; j++) {if (mProvidersByAuthority.containsKey(names[j])) {PackageParser.Provider other = mProvidersByAuthority.get(names[j]);final String otherPackageName =((other != null && other.getComponentName() != null) ?other.getComponentName().getPackageName() : "?");throw new PackageManagerException(INSTALL_FAILED_CONFLICTING_PROVIDER,"Can't install because provider name " + names[j]+ " (in package " + pkg.applicationInfo.packageName+ ") is already used by " + otherPackageName);}}}}}if (pkg.mAdoptPermissions != null) {// This package wants to adopt ownership of permissions from// another package.for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) {final String origName = pkg.mAdoptPermissions.get(i);final PackageSetting orig = mSettings.peekPackageLPr(origName);if (orig != null) {if (verifyPackageUpdateLPr(orig, pkg)) {Slog.i(TAG, "Adopting permissions from " + origName + " to "+ pkg.packageName);mSettings.transferPermissionsLPw(origName, pkg.packageName);}}}}}final String pkgName = pkg.packageName;final long scanFileTime = scanFile.lastModified();final boolean forceDex = (scanFlags & SCAN_FORCE_DEX) != 0;pkg.applicationInfo.processName = fixProcessName(pkg.applicationInfo.packageName,pkg.applicationInfo.processName,pkg.applicationInfo.uid);File dataPath;if (mPlatformPackage == pkg) {// The system package is special.dataPath = new File(Environment.getDataDirectory(), "system");pkg.applicationInfo.dataDir = dataPath.getPath();} else {// This is a normal package, need to make its data directory.dataPath = getDataPathForPackage(pkg.packageName, 0);boolean uidError = false;if (dataPath.exists()) {int currentUid = 0;try {StructStat stat = Os.stat(dataPath.getPath());currentUid = stat.st_uid;} catch (ErrnoException e) {Slog.e(TAG, "Couldn't stat path " + dataPath.getPath(), e);}// If we have mismatched owners for the data path, we have a problem.if (currentUid != pkg.applicationInfo.uid) {boolean recovered = false;if (currentUid == 0) {// The directory somehow became owned by root.  Wow.// This is probably because the system was stopped while// installd was in the middle of messing with its libs// directory.  Ask installd to fix that.int ret = mInstaller.fixUid(pkgName, pkg.applicationInfo.uid,pkg.applicationInfo.uid);if (ret >= 0) {recovered = true;String msg = "Package " + pkg.packageName+ " unexpectedly changed to uid 0; recovered to " ++ pkg.applicationInfo.uid;reportSettingsProblem(Log.WARN, msg);}}if (!recovered && ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0|| (scanFlags&SCAN_BOOTING) != 0)) {// If this is a system app, we can at least delete its// current data so the application will still work.int ret = removeDataDirsLI(pkgName);if (ret >= 0) {// TODO: Kill the processes first// Old data gone!String prefix = (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0? "System package " : "Third party package ";String msg = prefix + pkg.packageName+ " has changed from uid: "+ currentUid + " to "+ pkg.applicationInfo.uid + "; old data erased";reportSettingsProblem(Log.WARN, msg);recovered = true;// And now re-install the app.ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,pkg.applicationInfo.seinfo);if (ret == -1) {// Ack should not happen!msg = prefix + pkg.packageName+ " could not have data directory re-created after delete.";reportSettingsProblem(Log.WARN, msg);throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, msg);}}if (!recovered) {mHasSystemUidErrors = true;}} else if (!recovered) {// If we allow this install to proceed, we will be broken.// Abort, abort!throw new PackageManagerException(INSTALL_FAILED_UID_CHANGED,"scanPackageLI");}if (!recovered) {pkg.applicationInfo.dataDir = "/mismatched_uid/settings_"+ pkg.applicationInfo.uid + "/fs_"+ currentUid;pkg.applicationInfo.nativeLibraryDir = pkg.applicationInfo.dataDir;pkg.applicationInfo.nativeLibraryRootDir = pkg.applicationInfo.dataDir;String msg = "Package " + pkg.packageName+ " has mismatched uid: "+ currentUid + " on disk, "+ pkg.applicationInfo.uid + " in settings";// writersynchronized (mPackages) {mSettings.mReadMessages.append(msg);mSettings.mReadMessages.append('\n');uidError = true;if (!pkgSetting.uidError) {reportSettingsProblem(Log.ERROR, msg);}}}}pkg.applicationInfo.dataDir = dataPath.getPath();if (mShouldRestoreconData) {Slog.i(TAG, "SELinux relabeling of " + pkg.packageName + " issued.");mInstaller.restoreconData(pkg.packageName, pkg.applicationInfo.seinfo,pkg.applicationInfo.uid);}} else {if (DEBUG_PACKAGE_SCANNING) {if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)Log.v(TAG, "Want this data dir: " + dataPath);}//invoke installer to do the actual installationint ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,pkg.applicationInfo.seinfo);if (ret < 0) {// Error from installerthrow new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,"Unable to create data dirs [errorCode=" + ret + "]");}if (dataPath.exists()) {pkg.applicationInfo.dataDir = dataPath.getPath();} else {Slog.w(TAG, "Unable to create data directory: " + dataPath);pkg.applicationInfo.dataDir = null;}}pkgSetting.uidError = uidError;}final String path = scanFile.getPath();final String codePath = pkg.applicationInfo.getCodePath();final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {setBundledAppAbisAndRoots(pkg, pkgSetting);// If we haven't found any native libraries for the app, check if it has// renderscript code. We'll need to force the app to 32 bit if it has// renderscript bitcode.if (pkg.applicationInfo.primaryCpuAbi == null&& pkg.applicationInfo.secondaryCpuAbi == null&& Build.SUPPORTED_64_BIT_ABIS.length >  0) {NativeLibraryHelper.Handle handle = null;try {handle = NativeLibraryHelper.Handle.create(scanFile);if (NativeLibraryHelper.hasRenderscriptBitcode(handle)) {pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];}} catch (IOException ioe) {Slog.w(TAG, "Error scanning system app : " + ioe);} finally {IoUtils.closeQuietly(handle);}}setNativeLibraryPaths(pkg);} else {// TODO: We can probably be smarter about this stuff. For installed apps,// we can calculate this information at install time once and for all. For// system apps, we can probably assume that this information doesn't change// after the first boot scan. As things stand, we do lots of unnecessary work.// Give ourselves some initial paths; we'll come back for another// pass once we've determined ABI below.setNativeLibraryPaths(pkg);final boolean isAsec = isForwardLocked(pkg) || isExternal(pkg);final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir;final boolean useIsaSpecificSubdirs = pkg.applicationInfo.nativeLibraryRootRequiresIsa;NativeLibraryHelper.Handle handle = null;try {handle = NativeLibraryHelper.Handle.create(scanFile);// TODO(multiArch): This can be null for apps that didn't go through the// usual installation process. We can calculate it again, like we// do during install time.//// TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally// unnecessary.final File nativeLibraryRoot = new File(nativeLibraryRootStr);// Null out the abis so that they can be recalculated.pkg.applicationInfo.primaryCpuAbi = null;pkg.applicationInfo.secondaryCpuAbi = null;if (isMultiArch(pkg.applicationInfo)) {// Warn if we've set an abiOverride for multi-lib packages..// By definition, we need to copy both 32 and 64 bit libraries for// such packages.if (pkg.cpuAbiOverride != null&& !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) {Slog.w(TAG, "Ignoring abiOverride for multi arch application.");}int abi32 = PackageManager.NO_NATIVE_LIBRARIES;int abi64 = PackageManager.NO_NATIVE_LIBRARIES;if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {if (isAsec) {abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);} else {abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,useIsaSpecificSubdirs);}}maybeThrowExceptionForMultiArchCopy("Error unpackaging 32 bit native libs for multiarch app.", abi32);if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {if (isAsec) {abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);} else {abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,useIsaSpecificSubdirs);}}maybeThrowExceptionForMultiArchCopy("Error unpackaging 64 bit native libs for multiarch app.", abi64);if (abi64 >= 0) {pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64];}if (abi32 >= 0) {final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];if (abi64 >= 0) {pkg.applicationInfo.secondaryCpuAbi = abi;} else {pkg.applicationInfo.primaryCpuAbi = abi;}}} else {String[] abiList = (cpuAbiOverride != null) ?new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;// Enable gross and lame hacks for apps that are built with old// SDK tools. We must scan their APKs for renderscript bitcode and// not launch them if it's present. Don't bother checking on devices// that don't have 64 bit support.boolean needsRenderScriptOverride = false;if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&NativeLibraryHelper.hasRenderscriptBitcode(handle)) {abiList = Build.SUPPORTED_32_BIT_ABIS;needsRenderScriptOverride = true;}final int copyRet;if (isAsec) {copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);} else {copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,nativeLibraryRoot, abiList, useIsaSpecificSubdirs);}if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,"Error unpackaging native libs for app, errorCode=" + copyRet);}if (copyRet >= 0) {pkg.applicationInfo.primaryCpuAbi = abiList[copyRet];} else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && cpuAbiOverride != null) {pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride;} else if (needsRenderScriptOverride) {pkg.applicationInfo.primaryCpuAbi = abiList[0];} else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && Build.SUPPORTED_64_BIT_ABIS.length > 0) {/// M: Use ArchHelper to verify if the package has other libraries within assets,/// which may cause crash with wrongly setup the primary abi to 64bitString apkPath = pkg.applicationInfo.getBaseCodePath();Slog.d(TAG, "Double check libs within assets, apk file is " + apkPath);ArchHelper archHelper = new ArchHelper(apkPath);int assetArch = archHelper.findSupportedArch();Slog.d(TAG, "libs arch is " + assetArch);if (assetArch == ArchHelper.ARCH_ARM) {Slog.i(TAG, "Force primary CPU ABI to 32 bit");pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];} else if (assetArch == ArchHelper.ARCH_MULTI) {Slog.i(TAG, "Package has both 32 & 64 bit libs, keep with system default");}}}} catch (IOException ioe) {Slog.e(TAG, "Unable to get canonical file " + ioe.toString());} finally {IoUtils.closeQuietly(handle);}// Now that we've calculated the ABIs and determined if it's an internal app,// we will go ahead and populate the nativeLibraryPath.setNativeLibraryPaths(pkg);if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);final int[] userIds = sUserManager.getUserIds();synchronized (mInstallLock) {// Create a native library symlink only if we have native libraries// and if the native libraries are 32 bit libraries. We do not provide// this symlink for 64 bit libraries.if (pkg.applicationInfo.primaryCpuAbi != null &&!VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;for (int userId : userIds) {if (mInstaller.linkNativeLibraryDirectory(pkg.packageName, nativeLibPath, userId) < 0) {throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,"Failed linking native library dir (user=" + userId + ")");}}}}}// This is a special case for the "system" package, where the ABI is// dictated by the zygote configuration (and init.rc). We should keep track// of this ABI so that we can deal with "normal" applications that run under// the same UID correctly.if (mPlatformPackage == pkg) {pkg.applicationInfo.primaryCpuAbi = VMRuntime.getRuntime().is64Bit() ?Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];}pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;pkgSetting.cpuAbiOverrideString = cpuAbiOverride;// Copy the derived override back to the parsed package, so that we can// update the package settings accordingly.pkg.cpuAbiOverride = cpuAbiOverride;if (DEBUG_ABI_SELECTION) {Slog.d(TAG, "Resolved nativeLibraryRoot for " + pkg.applicationInfo.packageName+ " to root=" + pkg.applicationInfo.nativeLibraryRootDir + ", isa="+ pkg.applicationInfo.nativeLibraryRootRequiresIsa);}// Push the derived path down into PackageSettings so we know what to// clean up at uninstall time.pkgSetting.legacyNativeLibraryPathString = pkg.applicationInfo.nativeLibraryRootDir;if (DEBUG_ABI_SELECTION) {Slog.d(TAG, "Abis for package[" + pkg.packageName + "] are" +" primary=" + pkg.applicationInfo.primaryCpuAbi +" secondary=" + pkg.applicationInfo.secondaryCpuAbi);}if ((scanFlags&SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {// We don't do this here during boot because we can do it all// at once after scanning all existing packages.//// We also do this *before* we perform dexopt on this package, so that// we can avoid redundant dexopts, and also to make sure we've got the// code and package path correct.adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages,pkg, forceDex, (scanFlags & SCAN_DEFER_DEX) != 0);}if ((scanFlags & SCAN_NO_DEX) == 0) {Slog.i(TAG, "Perform pre-dex opt for package: " + pkg.packageName);if (performDexOptLI(pkg, null /* instruction sets */, forceDex,(scanFlags & SCAN_DEFER_DEX) != 0, false) == DEX_OPT_FAILED) {throw new PackageManagerException(INSTALL_FAILED_DEXOPT, "scanPackageLI");}}if (mFactoryTest && pkg.requestedPermissions.contains(android.Manifest.permission.FACTORY_TEST)) {pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;}ArrayList<PackageParser.Package> clientLibPkgs = null;// writersynchronized (mPackages) {if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {// Only system apps can add new shared libraries.if (pkg.libraryNames != null) {for (int i=0; i<pkg.libraryNames.size(); i++) {String name = pkg.libraryNames.get(i);boolean allowed = false;if (isUpdatedSystemApp(pkg)) {// New library entries can only be added through the// system image.  This is important to get rid of a lot// of nasty edge cases: for example if we allowed a non-// system update of the app to add a library, then uninstalling// the update would make the library go away, and assumptions// we made such as through app install filtering would now// have allowed apps on the device which aren't compatible// with it.  Better to just have the restriction here, be// conservative, and create many fewer cases that can negatively// impact the user experience.final PackageSetting sysPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName);if (sysPs.pkg != null && sysPs.pkg.libraryNames != null) {for (int j=0; j<sysPs.pkg.libraryNames.size(); j++) {if (name.equals(sysPs.pkg.libraryNames.get(j))) {allowed = true;allowed = true;break;}}}} else {allowed = true;}if (allowed) {if (!mSharedLibraries.containsKey(name)) {mSharedLibraries.put(name, new SharedLibraryEntry(null, pkg.packageName));} else if (!name.equals(pkg.packageName)) {Slog.w(TAG, "Package " + pkg.packageName + " library "+ name + " already exists; skipping");}} else {Slog.w(TAG, "Package " + pkg.packageName + " declares lib "+ name + " that is not declared on system image; skipping");}}if ((scanFlags&SCAN_BOOTING) == 0) {// If we are not booting, we need to update any applications// that are clients of our shared library.  If we are booting,// this will all be done once the scan is complete.clientLibPkgs = updateAllSharedLibrariesLPw(pkg);}}}}// We also need to dexopt any apps that are dependent on this library.  Note that// if these fail, we should abort the install since installing the library will// result in some apps being broken.if (clientLibPkgs != null) {if ((scanFlags & SCAN_NO_DEX) == 0) {for (int i = 0; i < clientLibPkgs.size(); i++) {PackageParser.Package clientPkg = clientLibPkgs.get(i);if (performDexOptLI(clientPkg, null /* instruction sets */, forceDex,(scanFlags & SCAN_DEFER_DEX) != 0, false) == DEX_OPT_FAILED) {throw new PackageManagerException(INSTALL_FAILED_DEXOPT,"scanPackageLI failed to dexopt clientLibPkgs");}}}}// Request the ActivityManager to kill the process(only for existing packages)// so that we do not end up in a confused state while the user is still using the older// version of the application while the new one gets installed.if ((scanFlags & SCAN_REPLACING) != 0) {killApplication(pkg.applicationInfo.packageName,pkg.applicationInfo.uid, "update pkg");}// Also need to kill any apps that are dependent on the library.if (clientLibPkgs != null) {for (int i=0; i<clientLibPkgs.size(); i++) {PackageParser.Package clientPkg = clientLibPkgs.get(i);killApplication(clientPkg.applicationInfo.packageName,clientPkg.applicationInfo.uid, "update lib");}}// writersynchronized (mPackages) {// We don't expect installation to fail beyond this point// Add the new setting to mSettingsmSettings.insertPackageSettingLPw(pkgSetting, pkg);// Add the new setting to mPackagesmPackages.put(pkg.applicationInfo.packageName, pkg);// Make sure we don't accidentally delete its data.final Iterator<PackageCleanItem> iter = mSettings.mPackagesToBeCleaned.iterator();while (iter.hasNext()) {PackageCleanItem item = iter.next();if (pkgName.equals(item.packageName)) {iter.remove();}}// Take care of first install / last update times.if (currentTime != 0) {if (pkgSetting.firstInstallTime == 0) {pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;} else if ((scanFlags&SCAN_UPDATE_TIME) != 0) {pkgSetting.lastUpdateTime = currentTime;}} else if (pkgSetting.firstInstallTime == 0) {// We need *something*.  Take time time stamp of the file.pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;} else if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {if (scanFileTime != pkgSetting.timeStamp) {// A package on the system image has changed; consider this// to be an update.pkgSetting.lastUpdateTime = scanFileTime;}}// Add the package's KeySets to the global KeySetManagerServiceKeySetManagerService ksms = mSettings.mKeySetManagerService;try {// Old KeySetData no longer valid.ksms.removeAppKeySetDataLPw(pkg.packageName);ksms.addSigningKeySetToPackageLPw(pkg.packageName, pkg.mSigningKeys);if (pkg.mKeySetMapping != null) {for (Map.Entry<String, ArraySet<PublicKey>> entry :pkg.mKeySetMapping.entrySet()) {if (entry.getValue() != null) {ksms.addDefinedKeySetToPackageLPw(pkg.packageName,entry.getValue(), entry.getKey());}}if (pkg.mUpgradeKeySets != null) {for (String upgradeAlias : pkg.mUpgradeKeySets) {ksms.addUpgradeKeySetToPackageLPw(pkg.packageName, upgradeAlias);}}}} catch (NullPointerException e) {Slog.e(TAG, "Could not add KeySet to " + pkg.packageName, e);} catch (IllegalArgumentException e) {Slog.e(TAG, "Could not add KeySet to malformed package" + pkg.packageName, e);}int N = pkg.providers.size();StringBuilder r = null;int i;for (i=0; i<N; i++) {PackageParser.Provider p = pkg.providers.get(i);p.info.processName = fixProcessName(pkg.applicationInfo.processName,p.info.processName, pkg.applicationInfo.uid);mProviders.addProvider(p);p.syncable = p.info.isSyncable;if (p.info.authority != null) {String names[] = p.info.authority.split(";");p.info.authority = null;for (int j = 0; j < names.length; j++) {if (j == 1 && p.syncable) {// We only want the first authority for a provider to possibly be// syncable, so if we already added this provider using a different// authority clear the syncable flag. We copy the provider before// changing it because the mProviders object contains a reference// to a provider that we don't want to change.// Only do this for the second authority since the resulting provider// object can be the same for all future authorities for this provider.p = new PackageParser.Provider(p);p.syncable = false;}if (!mProvidersByAuthority.containsKey(names[j])) {mProvidersByAuthority.put(names[j], p);if (p.info.authority == null) {p.info.authority = names[j];} else {p.info.authority = p.info.authority + ";" + names[j];}if (DEBUG_PACKAGE_SCANNING) {if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)Log.d(TAG, "Registered content provider: " + names[j]+ ", className = " + p.info.name + ", isSyncable = "+ p.info.isSyncable);}} else {PackageParser.Provider other = mProvidersByAuthority.get(names[j]);Slog.w(TAG, "Skipping provider name " + names[j] +" (in package " + pkg.applicationInfo.packageName +"): name already used by "+ ((other != null && other.getComponentName() != null)? other.getComponentName().getPackageName() : "?"));}}}if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(p.info.name);}}if (r != null) {if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Providers: " + r);}N = pkg.services.size();r = null;for (i=0; i<N; i++) {PackageParser.Service s = pkg.services.get(i);s.info.processName = fixProcessName(pkg.applicationInfo.processName,s.info.processName, pkg.applicationInfo.uid);mServices.addService(s);if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(s.info.name);}}if (r != null) {if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Services: " + r);}N = pkg.receivers.size();r = null;for (i=0; i<N; i++) {PackageParser.Activity a = pkg.receivers.get(i);a.info.processName = fixProcessName(pkg.applicationInfo.processName,a.info.processName, pkg.applicationInfo.uid);mReceivers.addActivity(a, "receiver");if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(a.info.name);}}if (r != null) {if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Receivers: " + r);}N = pkg.activities.size();r = null;for (i=0; i<N; i++) {PackageParser.Activity a = pkg.activities.get(i);a.info.processName = fixProcessName(pkg.applicationInfo.processName,a.info.processName, pkg.applicationInfo.uid);mActivities.addActivity(a, "activity");if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(a.info.name);}}if (r != null) {if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Activities: " + r);}N = pkg.permissionGroups.size();r = null;for (i=0; i<N; i++) {PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i);PackageParser.PermissionGroup cur = mPermissionGroups.get(pg.info.name);if (cur == null) {mPermissionGroups.put(pg.info.name, pg);if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(pg.info.name);}} else {Slog.w(TAG, "Permission group " + pg.info.name + " from package "+ pg.info.packageName + " ignored: original from "+ cur.info.packageName);if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append("DUP:");r.append(pg.info.name);}}}if (r != null) {if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Permission Groups: " + r);}N = pkg.permissions.size();r = null;for (i=0; i<N; i++) {PackageParser.Permission p = pkg.permissions.get(i);ArrayMap<String, BasePermission> permissionMap =p.tree ? mSettings.mPermissionTrees: mSettings.mPermissions;p.group = mPermissionGroups.get(p.info.group);if (p.info.group == null || p.group != null) {BasePermission bp = permissionMap.get(p.info.name);// Allow system apps to redefine non-system permissionsif (bp != null && !Objects.equals(bp.sourcePackage, p.info.packageName)) {final boolean currentOwnerIsSystem = (bp.perm != null&& isSystemApp(bp.perm.owner));if (isSystemApp(p.owner)) {if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) {// It's a built-in permission and no owner, take ownership nowbp.packageSetting = pkgSetting;bp.perm = p;bp.uid = pkg.applicationInfo.uid;bp.sourcePackage = p.info.packageName;} else if (!currentOwnerIsSystem) {String msg = "New decl " + p.owner + " of permission  "+ p.info.name + " is system; overriding " + bp.sourcePackage;reportSettingsProblem(Log.WARN, msg);bp = null;}}}if (bp == null) {bp = new BasePermission(p.info.name, p.info.packageName,BasePermission.TYPE_NORMAL);permissionMap.put(p.info.name, bp);}if (bp.perm == null) {if (bp.sourcePackage == null|| bp.sourcePackage.equals(p.info.packageName)) {BasePermission tree = findPermissionTreeLP(p.info.name);if (tree == null|| tree.sourcePackage.equals(p.info.packageName)) {bp.packageSetting = pkgSetting;bp.perm = p;bp.uid = pkg.applicationInfo.uid;bp.sourcePackage = p.info.packageName;if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(p.info.name);}} else {Slog.w(TAG, "Permission " + p.info.name + " from package "+ p.info.packageName + " ignored: base tree "+ tree.name + " is from package "+ tree.sourcePackage);}} else {Slog.w(TAG, "Permission " + p.info.name + " from package "+ p.info.packageName + " ignored: original from "+ bp.sourcePackage);}} else if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append("DUP:");r.append(p.info.name);}if (bp.perm == p) {bp.protectionLevel = p.info.protectionLevel;}} else {Slog.w(TAG, "Permission " + p.info.name + " from package "+ p.info.packageName + " ignored: no group "+ p.group);}}if (r != null) {if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Permissions: " + r);}N = pkg.instrumentation.size();r = null;for (i=0; i<N; i++) {PackageParser.Instrumentation a = pkg.instrumentation.get(i);a.info.packageName = pkg.applicationInfo.packageName;a.info.sourceDir = pkg.applicationInfo.sourceDir;a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir;a.info.splitSourceDirs = pkg.applicationInfo.splitSourceDirs;a.info.splitPublicSourceDirs = pkg.applicationInfo.splitPublicSourceDirs;a.info.dataDir = pkg.applicationInfo.dataDir;// TODO: Update instrumentation.nativeLibraryDir as well ? Does it// need other information about the application, like the ABI and what not ?a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;mInstrumentation.put(a.getComponentName(), a);if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(a.info.name);}}if (r != null) {if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Instrumentation: " + r);}if (pkg.protectedBroadcasts != null) {N = pkg.protectedBroadcasts.size();for (i=0; i<N; i++) {mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));}}pkgSetting.setTimeStamp(scanFileTime);// Create idmap files for pairs of (packages, overlay packages).// Note: "android", ie framework-res.apk, is handled by native layers.if (pkg.mOverlayTarget != null) {// This is an overlay package.if (pkg.mOverlayTarget != null && !pkg.mOverlayTarget.equals("android")) {if (!mOverlays.containsKey(pkg.mOverlayTarget)) {mOverlays.put(pkg.mOverlayTarget,new ArrayMap<String, PackageParser.Package>());}ArrayMap<String, PackageParser.Package> map = mOverlays.get(pkg.mOverlayTarget);map.put(pkg.packageName, pkg);PackageParser.Package orig = mPackages.get(pkg.mOverlayTarget);if (orig != null && !createIdmapForPackagePairLI(orig, pkg)) {throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,"scanPackageLI failed to createIdmap");}}} else if (mOverlays.containsKey(pkg.packageName) &&!pkg.packageName.equals("android")) {// This is a regular package, with one or more known overlay packages.createIdmapsForPackageLI(pkg);}}return pkg;}

这一部分的内容非常多,大体上看会对系统apk做一些设置,签名验证,Selinux安全策略的验证,Provider的检查,权限的检查,apk详细信息存储更新设置等,在这个方法之后执行上面说过的updateSettingsLI

private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,int[] allUsers, boolean[] perUserInstalled,PackageInstalledInfo res) {String pkgName = newPackage.packageName;synchronized (mPackages) {//write settings. the installStatus will be incomplete at this stage.//note that the new package setting would have already been//added to mPackages. It hasn't been persisted yet.mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE);mSettings.writeLPr();}if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.codePath);synchronized (mPackages) {updatePermissionsLPw(newPackage.packageName, newPackage,UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0? UPDATE_PERMISSIONS_ALL : 0));// For system-bundled packages, we assume that installing an upgraded version// of the package implies that the user actually wants to run that new code,// so we enable the package.if (isSystemApp(newPackage)) {// NB: implicit assumption that system package upgrades apply to all usersif (DEBUG_INSTALL) {Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);}PackageSetting ps = mSettings.mPackages.get(pkgName);if (ps != null) {if (res.origUsers != null) {for (int userHandle : res.origUsers) {ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,userHandle, installerPackageName);}}// Also convey the prior install/uninstall stateif (allUsers != null && perUserInstalled != null) {for (int i = 0; i < allUsers.length; i++) {if (DEBUG_INSTALL) {Slog.d(TAG, "    user " + allUsers[i]+ " => " + perUserInstalled[i]);}ps.setInstalled(perUserInstalled[i], allUsers[i]);}// these install state changes will be persisted in the// upcoming call to mSettings.writeLPr().}}}res.name = pkgName;res.uid = newPackage.applicationInfo.uid;res.pkg = newPackage;mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);mSettings.setInstallerPackageName(pkgName, installerPackageName);res.returnCode = PackageManager.INSTALL_SUCCEEDED;//to update install statusmSettings.writeLPr();}}

这个方法核心就是updatePermissionsLPw更新权限相关以及写入PKG_INSTALL_COMPLETEINSTALL_SUCCEEDED,最终会进入processPendingInstall()中得备份流程调用mPackageManagerBinder.finishPackageInstall(token)完成整个apk得安装

PMS-adb install安装应用流程(Android L)相关推荐

  1. adb install 安装错误常见列表

    下面列举出几种常见的错误及解决方法. Q1:无效的安装包,安装包已损坏[INSTALL_FAILED_INVALID_APK]  A1:请检查安装包是否完整.如果是xpk包,可以通过 手动安装xpk来 ...

  2. adb install 安装APK Failure INSTALL_FAILED_ALREADY_EXISTS Attempt to re-install without first

      使用adb install安装apk的时候,设备上已经安装过一个版本,再次安装的话,会安装失败,出现如下提示信息:INSTALL_FAILED_ALREADY_EXISTS   这时只要在命令中添 ...

  3. Android 安装apk流程,Android PMS(二)-Apk安装流程

    原创内容,转载请注明出处,多谢配合. 一.APK组成 在APK的安装流程,在此之前先简单了解下APK组成: 目录/文件 描述 assert 存放的原生资源文件,通过AssetManager类访问. l ...

  4. Android 10 解决无法通过adb install 安装persistent app

    安装应用的时候会报如下错误: C:\Users\Administrator>adb install C:\Users\Administrator\Desktop\新建文件夹\test.apk P ...

  5. 我的Android进阶之旅------Android中adb install 安装错误常见列表

    adb的安装过程分为传输与安装两步. 在出错后,adb会报告错误信息,但是信息可能只是一个代号,需要自己定位分析出错的原因. 下面是从网上找到的几种常见的错误及解决方法: 1.INSTALL_FAIL ...

  6. PackageManagerService安装应用流程

    PackageManagerService主要负责Android系统的应用管理,它提供对各种APK文件的安装.卸载.优化和查询服务.PackageManagerService在系统启动时会扫描所有存在 ...

  7. adb install 时 日志输出Performing Streamed Instal 一直卡着不动,处理方法

    问题: 上午用的还好好的,下午 突然就出问题了 adb install 安装apk的时候,日志输出Performing Streamed Instal 一直卡着不动 解决方法: 重启电脑,YYDS 排 ...

  8. 使用拷贝的方式(adb push) 绕过Android系统和adb install直接安装APK

    某些情况下定制的Android系统为了限制用户安装应用,例如电视盒子,车载中控等,通过修改代码屏蔽了正常安装应用的方式 本文探讨如何在 adb shell 具有读写data分区目录的权限前提下,通过a ...

  9. 【Android 教程系列第 31 篇】通过 adb install 命令安装 apk 时提示 signatures do not match previously installed version

    这是[Android 教程系列第 31 篇],如果觉得有用的话,欢迎关注专栏. 遇到的一个小问题,做下记录. 一:问题描述 在使用 adb install 命令安装 apk 时,提示的内容如下 主要提 ...

最新文章

  1. 皮一皮:他为我承受了太多太多...
  2. 城市轨道交通运营票务管理论文_城市轨道交通票务组织管理论文
  3. react不同环境不同配置angular_DevOps 前端项目(angular、vue、react)打包静态资源生成一份Docker镜像支持部署不同环境...
  4. php 接收curl json数据格式,curl发送 JSON格式POST数据的接收,以及在yii2框架中的实现原理【精细剖析】...
  5. html5 json转字符串,web前端-js小记(5)-字符串及json
  6. 阿里《Java开发手册》中的 1 个bug!
  7. SVN禁止提交部分文件
  8. html HTML1300 进行了导航,jquery根据文章H标签自动生成导航目录
  9. php socket 基础知识
  10. Centos 安装配置gerrit
  11. oracle .bdb,oracle 11g RAC crfclust.bdb过大的处理
  12. 防蓝光膜真的能阻挡蓝光,减小辐射吗?
  13. require()与 require_once()、 include与include_once()
  14. python pdf转html代码_python将html转成PDF的实现代码(包含中文)
  15. 【基本办公软件】万彩办公大师教程丨彩色转化PDF为黑白PDF
  16. 优雅的开发Swift和Objective C混编的Framework
  17. 『梦想城镇』终极攻略
  18. 树莓派的Python成功解决TypeError: Image data cannot be converted to float
  19. SSE为兼容ie浏览器使用event-source-polyfill
  20. 伊宅购集团伊家田园勠力同心数字农业项目说明会圆满举行!

热门文章

  1. spf13/viper
  2. SSM框架终极篇——Spring、SpringMVC、MyBatis整合练习(超级详细)
  3. 河北大学生命科学学院期末Biopython编程实践。利用Biopython包读取新冠病毒(id:NC_045512.2)序列(GenBank格式NC_045512.2.gb),将其中FEATURE的类
  4. 不一般的Cover Letter
  5. 数据可视化图表使用场景大全 !
  6. 空洞骑士复活歌女玛丽莎的方法(复活其他灵魂NPC同理)
  7. html bs架构调用客户端打印机用客户端及客户端局域网打印机打印,使用ScriptX.cab控件...
  8. TMS320F280049C 学习笔记31 控制率加速器 CLA 学习随笔
  9. 拼多多面试问了数据库基础知识,今天分享出来
  10. SPA SEO SSR三者有什么区别