PackageManager

与ActivityManager和AMS的关系类似,PMS也有一个对应的管理类PackageManager,用于向应用程序进程提供一些功能。PackageManager是一个抽象类,它的具体实现类为ApplicationPackageManager,ApplicationPackageManager中的方法会通过IPackageManager与PMS进行进程间通信,因此PackageManager所提供的功能最终是由PMS来实现的,这么设计的主要用意是为了避免系统服务PMS直接被访问。PackageManager提供了一些功能,主要有以下几点:

  1. 获取一个应用程序的所有信息(ApplicationInfo)。
  2. 解析manifest.xml文件,查询permission相关信息。
  3. 获取包的信息。
  4. 安装、卸载APK.

这里分析点击安装包的情况:

跳转到PackageInstaller应用

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 24) {  //7.0新跳转Uri apkUri = FileProvider.getUriForFile(paramContext, "com.xxx.xxx.xxx", file);intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
}

application/vnd.android.package-archive对应文件类型apk

PackageInstaller的AndroidManifest.xml

<activity android:name=".InstallStart"android:exported="true"android:excludeFromRecents="true"><intent-filter android:priority="1"><action android:name="android.intent.action.VIEW" /><action android:name="android.intent.action.INSTALL_PACKAGE" /><category android:name="android.intent.category.DEFAULT" /><data android:scheme="file" /><data android:scheme="content" /><data android:mimeType="application/vnd.android.package-archive" /></intent-filter>...
</activity>

注意 action与mimeType与Intent参数对应,到InstallStart页面

frameworks\base\packages\PackageInstaller\src\com\android\packageinstaller\InstallStart.java

    @Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);mIPackageManager = AppGlobals.getPackageManager();Intent intent = getIntent();String callingPackage = getCallingPackage();final boolean isSessionInstall =PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());// If the activity was started via a PackageInstaller session, we retrieve the calling// package from that sessionfinal int sessionId = (isSessionInstall? intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1): -1);.....//获取应用信息final ApplicationInfo sourceInfo = getSourceInfo(callingPackage);final int originatingUid = getOriginatingUid(sourceInfo);boolean isTrustedSource = false;if (sourceInfo != null&& (sourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);}....Intent nextActivity = new Intent(intent);nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);// The the installation source as the nextActivity thinks this activity is the source, hence// set the originating UID and sourceInfo explicitlynextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);//这里为falseif (isSessionInstall) {nextActivity.setClass(this, PackageInstallerActivity.class);} else {Uri packageUri = intent.getData();// 判断Uri的Scheme协议是否是contentif (packageUri != null && packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {nextActivity.setClass(this, InstallStaging.class);} else if (packageUri != null && packageUri.getScheme().equals(PackageInstallerActivity.SCHEME_PACKAGE)) {nextActivity.setClass(this, PackageInstallerActivity.class);} else {Intent result = new Intent();result.putExtra(Intent.EXTRA_INSTALL_RESULT,PackageManager.INSTALL_FAILED_INVALID_URI);setResult(RESULT_FIRST_USER, result);nextActivity = null;}}if (nextActivity != null) {startActivity(nextActivity);}finish();}

这里是安卓10的源码,需要用到FileProvider,所以会走到

nextActivity.setClass(this, InstallStaging.class);

frameworks\base\packages\PackageInstaller\src\com\android\packageinstaller\InstallStaging.java

    @Overrideprotected void onResume() {super.onResume();// This is the first onResume in a single life of the activityif (mStagingTask == null) {// File does not exist, or became invalidif (mStagedFile == null) {// Create file delayed to be able to show errortry {mStagedFile = TemporaryFileManager.getStagedFile(this);} catch (IOException e) {showError();return;}}mStagingTask = new StagingAsyncTask();mStagingTask.execute(getIntent().getData());}}

frameworks\base\packages\PackageInstaller\src\com\android\packageinstaller\TemporaryFileManager.java

    @NonNullpublic static File getStagedFile(@NonNull Context context) throws IOException {return File.createTempFile("package", ".apk", context.getNoBackupFilesDir());}

mStagedFile是一个叫package.apk的File对象

    private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {@Overrideprotected Boolean doInBackground(Uri... params) {if (params == null || params.length <= 0) {return false;}Uri packageUri = params[0];try (InputStream in = getContentResolver().openInputStream(packageUri)) {// Despite the comments in ContentResolver#openInputStream the returned stream can// be null.if (in == null) {return false;}try (OutputStream out = new FileOutputStream(mStagedFile)) {byte[] buffer = new byte[1024 * 1024];int bytesRead;while ((bytesRead = in.read(buffer)) >= 0) {// Be nice and respond to a cancellationif (isCancelled()) {return false;}out.write(buffer, 0, bytesRead);}}} catch (IOException | SecurityException | IllegalStateException e) {Log.w(LOG_TAG, "Error staging apk from content URI", e);return false;}return true;}@Overrideprotected void onPostExecute(Boolean success) {if (success) {// Now start the installation again from a fileIntent installIntent = new Intent(getIntent());installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class);installIntent.setData(Uri.fromFile(mStagedFile));if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);}installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);startActivity(installIntent);InstallStaging.this.finish();} else {showError();}}}

StagingAsyncTask工作:
1.doInBackground 把uri中需要安装的apk拷贝到临时文件中/data/no_backup/package.apk
2.onPostExecute 当拷贝任务处理完之后,就会把当前的临时文件Uri作为Intent的参数(这个时候会把Uri的类型设置为file),跳转到DeleteStagedFileOnResult中。

frameworks\base\packages\PackageInstaller\src\com\android\packageinstaller\DeleteStagedFileOnResult.java

public class DeleteStagedFileOnResult extends Activity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);if (savedInstanceState == null) {Intent installIntent = new Intent(getIntent());installIntent.setClass(this, PackageInstallerActivity.class);installIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);startActivityForResult(installIntent, 0);}}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {File sourceFile = new File(getIntent().getData().getPath());sourceFile.delete();setResult(resultCode, data);finish();}
}

DeleteStagedFileOnResult是个过度,负责跳转后删除package.apk

APK的安装Activity:PackageInstallerActivity

    @Overrideprotected void onCreate(Bundle icicle) {getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);super.onCreate(null);mPm = getPackageManager();mIpm = AppGlobals.getPackageManager();mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);mInstaller = mPm.getPackageInstaller();mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);....boolean wasSetUp = processPackageUri(packageUri);if (!wasSetUp) {return;}// load dummy layout with OK button disabled until we override this layout in// startInstallConfirmbindUi();checkIfAllowedAndInitiateInstall();}
  • PackageManager 用于向应用程序进程提供一些功能,最终的功能是由PMS来实现的
  • AppOpsManager 用于权限动态检测,在Android4.3中被引入
  • PackageInstaller 提供安装、升级和删除应用程序功能
  • UserManager 用于多用户管理

1.processPackageUri(packageUri);

    /*** **解析Uri并设置此包的安装程序。** @param packageUri The URI to parse** @return {@code true} 如果安装程序可以设置*/private boolean processPackageUri(final Uri packageUri) {mPackageURI = packageUri;final String scheme = packageUri.getScheme();switch (scheme) {case SCHEME_PACKAGE: {....} break;case ContentResolver.SCHEME_FILE: {File sourceFile = new File(packageUri.getPath());PackageParser.Package parsed = PackageUtil.getPackageInfo(this, sourceFile);// Check for parse errorsif (parsed == null) {Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");showDialogInner(DLG_PACKAGE_ERROR);setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);return false;}mPkgInfo = PackageParser.generatePackageInfo(parsed, null,PackageManager.GET_PERMISSIONS, 0, 0, null,new PackageUserState());mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);} break;default: {throw new IllegalArgumentException("Unexpected URI scheme " + packageUri);}}return true;}

前面apk的数据已经用

installIntent.setData(Uri.fromFile(mStagedFile));

传到PackageInstallerActivity了,所以这里走的是file。

processPackageUri解析uri获取

  1. mPkgInfo,主要获取Permission相关信息。
  2. mAppSnippet,应用label名,没有定义则用应用包名

2.bindUi()

    private void bindUi() {mAlert.setIcon(mAppSnippet.icon);mAlert.setTitle(mAppSnippet.label);mAlert.setView(R.layout.install_content_view);mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),(ignored, ignored2) -> {if (mOk.isEnabled()) {if (mSessionId != -1) {mInstaller.setPermissionsResult(mSessionId, true);finish();} else {startInstall();}}}, null);mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),(ignored, ignored2) -> {// Cancel and finishsetResult(RESULT_CANCELED);if (mSessionId != -1) {mInstaller.setPermissionsResult(mSessionId, false);}finish();}, null);setupAlert();mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);mOk.setEnabled(false);}

bindUI()就是给弹出来的安装界面设置UI,然后为安装和取消两个按钮设置点击事件。安装按钮的点击事件是startInstall();

3.checkIfAllowedAndInitiateInstall()

    private void checkIfAllowedAndInitiateInstall() {// Check for install apps user restriction first.final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource(UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle());if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER);return;} else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) {startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));finish();return;}if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {initiateInstall();} else {// Check for unknown sources restrictions.final int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle());final int unknownSourcesGlobalRestrictionSource = mUserManager.getUserRestrictionSource(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, Process.myUserHandle());final int systemRestriction = UserManager.RESTRICTION_SOURCE_SYSTEM& (unknownSourcesRestrictionSource | unknownSourcesGlobalRestrictionSource);if (systemRestriction != 0) {showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);} else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);} else if (unknownSourcesGlobalRestrictionSource != UserManager.RESTRICTION_NOT_SET) {startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);} else {handleUnknownSources();}}}

这个方法作用就如名字所示了,检查应用权限,还有就是是否是未知来源安装包。

主要看initiateInstall();

    private void initiateInstall() {String pkgName = mPkgInfo.packageName;// Check if there is already a package on the device with this name// but it has been renamed to something else.String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });if (oldName != null && oldName.length > 0 && oldName[0] != null) {pkgName = oldName[0];mPkgInfo.packageName = pkgName;mPkgInfo.applicationInfo.packageName = pkgName;}// Check if package is already installed. display confirmation dialog if replacing pkgtry {// This is a little convoluted because we want to get all uninstalled// apps, but this may include apps with just data, and if it is just// data we still want to count it as "installed".mAppInfo = mPm.getApplicationInfo(pkgName,PackageManager.MATCH_UNINSTALLED_PACKAGES);if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {mAppInfo = null;}} catch (NameNotFoundException e) {mAppInfo = null;}startInstallConfirm();}private void startInstallConfirm() {View viewToEnable;if (mAppInfo != null) {viewToEnable = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0? requireViewById(R.id.install_confirm_question_update_system): requireViewById(R.id.install_confirm_question_update);} else {// This is a new application with no permissions.viewToEnable = requireViewById(R.id.install_confirm_question);}viewToEnable.setVisibility(View.VISIBLE);mEnableOk = true;mOk.setEnabled(true);}

mOk.setEnabled(true);这里让界面的安装按钮可点击。

onCreate就分析完了,接下来看点击安装按钮的点击事件startInstall()。

    private void startInstall() {// Start subactivity to actually install the applicationIntent newIntent = new Intent();newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,mPkgInfo.applicationInfo);newIntent.setData(mPackageURI);newIntent.setClass(this, InstallInstalling.class);String installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);if (mOriginatingURI != null) {newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);}if (mReferrerURI != null) {newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);}if (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);}if (installerPackageName != null) {newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,installerPackageName);}if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);}newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);startActivity(newIntent);finish();}

startInstall方法用于跳转到InstallInstalling这个Activity,并关闭掉当前的PackageInstallerActivity。

frameworks\base\packages\PackageInstaller\src\com\android\packageinstaller\InstallInstalling.java

    @Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);ApplicationInfo appInfo = getIntent().getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);mPackageURI = getIntent().getData();//这里为falseif ("package".equals(mPackageURI.getScheme())) {.....} else {final File sourceFile = new File(mPackageURI.getPath());PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);mAlert.setIcon(as.icon);mAlert.setTitle(as.label);mAlert.setView(R.layout.install_content_view);mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),(ignored, ignored2) -> {if (mInstallingTask != null) {mInstallingTask.cancel(true);}if (mSessionId > 0) {getPackageManager().getPackageInstaller().abandonSession(mSessionId);mSessionId = 0;}setResult(RESULT_CANCELED);finish();}, null);setupAlert();requireViewById(R.id.installing).setVisibility(View.VISIBLE);if (savedInstanceState != null) {....} else {PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);params.setInstallAsInstantApp(false);params.setReferrerUri(getIntent().getParcelableExtra(Intent.EXTRA_REFERRER));params.setOriginatingUri(getIntent().getParcelableExtra(Intent.EXTRA_ORIGINATING_URI));params.setOriginatingUid(getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,UID_UNKNOWN));params.setInstallerPackageName(getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME));params.setInstallReason(PackageManager.INSTALL_REASON_USER);File file = new File(mPackageURI.getPath());try {PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);params.setAppPackageName(pkg.packageName);params.setInstallLocation(pkg.installLocation);params.setSize(PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));} catch (PackageParser.PackageParserException e) {Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");Log.e(LOG_TAG,"Cannot calculate installed size " + file + ". Try only apk size.");params.setSize(file.length());} catch (IOException e) {Log.e(LOG_TAG,"Cannot calculate installed size " + file + ". Try only apk size.");params.setSize(file.length());}try {mInstallId = InstallEventReceiver.addObserver(this, EventResultPersister.GENERATE_NEW_ID,this::launchFinishBasedOnResult);} catch (EventResultPersister.OutOfIdsException e) {launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);}try {mSessionId = getPackageManager().getPackageInstaller().createSession(params);} catch (IOException e) {launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);}}mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE);mSessionCallback = new InstallSessionCallback();}}

解析pkg获取安装包参数,通过PMS获取mSessionId。

    @Overrideprotected void onResume() {super.onResume();// This is the first onResume in a single life of the activityif (mInstallingTask == null) {PackageInstaller installer = getPackageManager().getPackageInstaller();PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);if (sessionInfo != null && !sessionInfo.isActive()) {mInstallingTask = new InstallingAsyncTask();mInstallingTask.execute();} else {// we will receive a broadcast when the install is finishedmCancelButton.setEnabled(false);setFinishOnTouchOutside(false);}}}

onResume执行了 InstallingAsyncTask

    /*** Send the package to the package installer and then register a event result observer that* will call {@link #launchFinishBasedOnResult(int, int, String)}*/private final class InstallingAsyncTask extends AsyncTask<Void, Void,PackageInstaller.Session> {volatile boolean isDone;@Overrideprotected PackageInstaller.Session doInBackground(Void... params) {PackageInstaller.Session session;try {session = getPackageManager().getPackageInstaller().openSession(mSessionId);} catch (IOException e) {return null;}session.setStagingProgress(0);try {File file = new File(mPackageURI.getPath());try (InputStream in = new FileInputStream(file)) {long sizeBytes = file.length();try (OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes)) {byte[] buffer = new byte[1024 * 1024];while (true) {int numRead = in.read(buffer);if (numRead == -1) {session.fsync(out);break;}if (isCancelled()) {session.close();break;}out.write(buffer, 0, numRead);if (sizeBytes > 0) {float fraction = ((float) numRead / (float) sizeBytes);session.addProgress(fraction);}}}}return session;} catch (IOException | SecurityException e) {Log.e(LOG_TAG, "Could not write package", e);session.close();return null;} finally {synchronized (this) {isDone = true;notifyAll();}}}@Overrideprotected void onPostExecute(PackageInstaller.Session session) {if (session != null) {Intent broadcastIntent = new Intent(BROADCAST_ACTION);broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);broadcastIntent.setPackage(getPackageName());broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);PendingIntent pendingIntent = PendingIntent.getBroadcast(InstallInstalling.this,mInstallId,broadcastIntent,PendingIntent.FLAG_UPDATE_CURRENT);session.commit(pendingIntent.getIntentSender());mCancelButton.setEnabled(false);setFinishOnTouchOutside(false);} else {getPackageManager().getPackageInstaller().abandonSession(mSessionId);if (!isCancelled()) {launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);}}}}

doInBackground将 APK 文件写入 session中。

onPostExecute创建了一个PendingIntent,并将该PendingIntent的IntentSender通过session的commit方法发送出去

通过 IPackageInstallerSession 来进行进程间的通信,最终会调用PackageInstallerSession 的 commit 方法

frameworks\base\services\core\java\com\android\server\pm\PackageInstallerSession.java

    @Overridepublic void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {....mHandler.obtainMessage(MSG_COMMIT).sendToTarget();}private final Handler.Callback mHandlerCallback = new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {switch (msg.what) {case MSG_COMMIT:handleCommit();break;....}return true;}};private void handleCommit() {....try {synchronized (mLock) {commitNonStagedLocked(childSessions);}} catch (PackageManagerException e) {final String completeMsg = ExceptionUtils.getCompleteMessage(e);Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);destroyInternal();dispatchSessionFinished(e.error, completeMsg, null);}}private void commitNonStagedLocked(List<PackageInstallerSession> childSessions)throws PackageManagerException {final PackageManagerService.ActiveInstallSession committingSession =makeSessionActiveLocked();if (committingSession == null) {return;}//多个包if (isMultiPackage()) {.....mPm.installStage(activeChildSessions);} else {mPm.installStage(committingSession);}}

这个mPm是PMS

private final PackageManagerService mPm;

frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java

    void installStage(ActiveInstallSession activeInstallSession) {final Message msg = mHandler.obtainMessage(INIT_COPY);final InstallParams params = new InstallParams(activeInstallSession);params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));msg.obj = params;Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",System.identityHashCode(msg.obj));Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",System.identityHashCode(msg.obj));mHandler.sendMessage(msg);}class PackageHandler extends Handler {PackageHandler(Looper looper) {super(looper);}public void handleMessage(Message msg) {try {doHandleMessage(msg);} finally {Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);}}void doHandleMessage(Message msg) {switch (msg.what) {case INIT_COPY: {HandlerParams params = (HandlerParams) msg.obj;if (params != null) {if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",System.identityHashCode(params));Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");params.startCopy();Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}break;.....}}}}private abstract class HandlerParams {/** User handle for the user requesting the information or installation. */private final UserHandle mUser;String traceMethod;int traceCookie;....final void startCopy() {if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);handleStartCopy();handleReturnCode();}abstract void handleStartCopy();abstract void handleReturnCode();}

PackageHandler 和 HandlerParams都是PMS的内部类,INIT_COPY的msg中,执行了 params.startCopy(),这个params是HandlerParams,而发送到handler之前是通过以下生成的。

InstallParams params = new InstallParams(activeInstallSession);

所以看InstallParams的handleStartCopy()和handleReturnCode()方法

        //调用远程方法获取包信息和安装位置值。如果需要,根据默认策略覆盖安装位置,然后根据安装位置创建安装参数。public void handleStartCopy() {int ret = PackageManager.INSTALL_SUCCEEDED;final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;PackageInfoLite pkgLite = null;pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,origin.resolvedPath, installFlags, packageAbiOverride);/** 如果没有空间,释放缓存*/if (!origin.staged && pkgLite.recommendedInstallLocation== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {// TODO: focus freeing disk space on the target devicefinal StorageManager storage = StorageManager.from(mContext);final long lowThreshold = storage.getStorageLowBytes(Environment.getDataDirectory());final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(origin.resolvedPath, packageAbiOverride);if (sizeBytes >= 0) {try {mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,origin.resolvedPath, installFlags, packageAbiOverride);} catch (InstallerException e) {Slog.w(TAG, "Failed to free cache", e);}}}//决定安装位置if (ret == PackageManager.INSTALL_SUCCEEDED) {int loc = pkgLite.recommendedInstallLocation;if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;} else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;} else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {ret = PackageManager.INSTALL_FAILED_INVALID_APK;} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {ret = PackageManager.INSTALL_FAILED_INVALID_URI;} else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;} else {// Override with defaults if needed.loc = installLocationPolicy(pkgLite);if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;} else if (loc == PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION) {ret = PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;} else if (!onInt) {// Override install location with flagsif (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {// Set the flag to install on external media.installFlags &= ~PackageManager.INSTALL_INTERNAL;} else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {if (DEBUG_INSTANT) {Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");}installFlags |= PackageManager.INSTALL_INSTANT_APP;installFlags &= ~PackageManager.INSTALL_INTERNAL;} else {// Make sure the flag for installing on external// media is unsetinstallFlags |= PackageManager.INSTALL_INTERNAL;}}}}//生成安装参数final InstallArgs args = createInstallArgs(this);mVerificationCompleted = true;mEnableRollbackCompleted = true;mArgs = args;....mRet = ret;}void handleReturnCode() {if (mVerificationCompleted && mEnableRollbackCompleted) {....if (mRet == PackageManager.INSTALL_SUCCEEDED) {mRet = mArgs.copyApk();}processPendingInstall(mArgs, mRet);}}private InstallArgs createInstallArgs(InstallParams params) {if (params.move != null) {//处理现有已安装应用程序移动的逻辑。return new MoveInstallArgs(params);} else {//处理新应用程序安装的逻辑,包括复制和重命名逻辑。return new FileInstallArgs(params);}}

handleReturnCode()有两个重要的方法

  1. mRet = mArgs.copyApk();
  2. processPendingInstall(mArgs, mRet);

他们的作用看名字就能猜到了,1是复制apk,2是安装应用

1.copyApk

FileInstallArgs也是PMS内部类

        int copyApk() {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");try {return doCopyApk();} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}}private int doCopyApk() {try {final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;//创建路径final File tempDir =mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);codeFile = tempDir;resourceFile = tempDir;} catch (IOException e) {Slog.w(TAG, "Failed to create copy file: " + e);return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;}//将包复制到指定路径int ret = PackageManagerServiceUtils.copyPackage(origin.file.getAbsolutePath(), codeFile);if (ret != PackageManager.INSTALL_SUCCEEDED) {Slog.e(TAG, "Failed to copy package");return ret;}final File libraryRoot = new File(codeFile, LIB_DIR_NAME);NativeLibraryHelper.Handle handle = null;try {// 将 apk 中的动态库.so 文件也拷贝到目标路径中。handle = NativeLibraryHelper.Handle.create(codeFile);ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,abiOverride);} catch (IOException e) {Slog.e(TAG, "Copying native libraries failed", e);ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;} finally {IoUtils.closeQuietly(handle);}return ret;}

PackageManagerServiceUtils.copyPackage(
                    origin.file.getAbsolutePath(), codeFile);

copyPackage就是把origin.file.getAbsolutePath()的安装包复制到codeFile,里面的实现就是输入输出流。

至此我们的apk就在 data/app 目录下以 base.apk 的方式保存,至于为啥是在data/app,可以去看下

final File tempDir =
                        mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);

2.processPendingInstall(mArgs, mRet)

    private void processPendingInstall(final InstallArgs args, final int currentStatus) {if (args.mMultiPackageInstallParams != null) {args.mMultiPackageInstallParams.tryProcessInstallRequest(args, currentStatus);} else {PackageInstalledInfo res = createPackageInstalledInfo(currentStatus);processInstallRequestsAsync(res.returnCode == PackageManager.INSTALL_SUCCEEDED,Collections.singletonList(new InstallRequest(args, res)));}}// Queue up an async operation since the package installation may take a little while.private void processInstallRequestsAsync(boolean success,List<InstallRequest> installRequests) {mHandler.post(() -> {if (success) {for (InstallRequest request : installRequests) {//预安装request.args.doPreInstall(request.installResult.returnCode);}synchronized (mInstallLock) {//安装installPackagesTracedLI(installRequests);}for (InstallRequest request : installRequests) {request.args.doPostInstall(request.installResult.returnCode, request.installResult.uid);}}for (InstallRequest request : installRequests) {restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,new PostInstallData(request.args, request.installResult, null));}});}

预安装操作主要是检查安装包的状态,确保安装环境正常,如果安装环境有问题会清理拷贝文件。

installPackagesLI是安装的核心。

Android—PMS: installPackagesLI

简单总结:

InstallStaging会先把apk存储在临时文件夹,然后跳转并把URI传给PackageInstallerActivity,点击PackageInstallerActivity的OK按钮进入InstallInstalling页面,InstallInstalling页面建立异步任务将APK通过IO流的形式写入到PackageInstaller.Session,通过PackageInstaller.Session进行进程间通信,发送一个MSG_COMMIT的Message最终调用到了PackageManagerService.installStage方法。installStage方法发送了一个INIT_COPY的Message,调用copyAPK方法将apk拷贝到data/app/,然后再调用processPendingInstall进行安装。

Android—PMS: PackageInstaller到PMS相关推荐

  1. Android系统重要组件PMS

    Android PMS(Package Manager Service)是Android系统中的一个重要组件,它管理着Android系统中所有应用程序的安装.卸载.更新等工作.下面我们来详细介绍一下A ...

  2. Android签名机制及PMS中校验流程(雷惊风)

    @Android签名机制及PMS中校验流程(雷惊风) 网上看到一篇比较好的关于Android签名的文章,但是文章链接不安全,不知道哪天会不会找不到了,而且需要关注才能查看完整版,所以在这里记录一下,原 ...

  3. Android 7.0修改PMS逻辑添加权限白名单

    今天有任务安排需要实现给任何一个应用在不动态申请权限的情况下,实现权限赋予,大体知道应用权限的赋予逻辑是在PMS中,于是大概研究了下,最终的手段就是在install安装时,就把应用程序AndroidM ...

  4. Android签名机制及PMS中校验签名

    一.签名机制 众所周知,在Android系统中,应用想要安装到设备中,必须要有签名才行,及时是debug的时候,开发工具也会对要运行的应用自动签名,那么我们先来了解一下这个签名究竟是什么. 首先And ...

  5. 透视Android系统AMS、PMS和WMS,了解开发中的重要角色

    原理 在Android系统中,AMS(Activity Manager Service).PMS(PackageManager Service)和WMS(Window Manager Service) ...

  6. 【android系统】根据PMS中的屏幕锁WakeLock,做了一个限制儿童观看视频时长的方案

    背景 随着时代的发展.科技的进步,电子产品的拥有日趋低龄化.电子产品的不当使用可能造成很多问题,过度使用容易上瘾,影响孩子的社交生活.据资料显示看电视绝不仅仅只会影响孩子的视力,更严重的会影响到孩子的 ...

  7. Android pms权限管理,PMS权限管理和鉴权过程

    一.权限的管理基础知识 1.系统的权限机制分为:权限解析.权限分配.鉴权.动态添加权限 2.PermissionInfo :  PackageParser.Permission中包含一个对应的Perm ...

  8. PMS系列1⃣️——PMS的创建

    PMS的构造过程 SystemServer创建PMS过程 SystemServer.startOtherServices() PMS.main() PMS的构造方法的五个阶段 阶段一:START 开始 ...

  9. opera pms 数据库 MySQL_Opera PMS开篇及概览

    总计题数: 7 道    正确数: 7 道    错误数: 0 道    正确率: 100%    折合分: 100分 1.在PMS系统中,各项功能的调用可以使用很多种方法,其中操作最方便的方式是() ...

最新文章

  1. 近期必读的6篇NeurIPS 2019零样本学习论文
  2. 第一批鸿蒙系统手机型号,鸿蒙2.0第一批机型名单正式披露!花粉却感叹:华为不够厚道!...
  3. Xcode 不用签名编译程序
  4. 数学--数论-- HDU6298 Maximum Multiple 打表找规律
  5. jQuery触发a标签的点击事件无效
  6. spring的延迟初始化bean (default-lazy-init 与 lazy-init )
  7. Opencv之斑点(Blob)检测--SimpleBlobDetector_create
  8. Adobe Edge Animate 1.0 概述
  9. bzoj3482: [COCI2013]hiperprostor
  10. Swift3.0:Get/Post同步和异步请求
  11. NekoHtml 乱码出现问号的解决
  12. Arduino使用NRF24L01模块进行无线通信
  13. 7.3万字肝爆Java8新特性,我不信你能看完!(建议收藏)
  14. 安装mysql忘记设置密码后如何重设密码
  15. 小码哥java一期 百度云_小码哥IOS 十一期
  16. 启动Tomcat6.x时manager does not exist or is not a readable directory
  17. 2021年7月国产数据库排行榜:openGauss高歌猛进,GBase丢失第五
  18. 数据仓库建模方法/范式建模法/维度建模法/事实表/维度表/优缺点/建模流程/概念建模/逻辑建模/物理建模
  19. 如何注册表里修改计算机用户名,更改电脑用户名(可更改C:\Users\用户名)
  20. ubuntu下tree命令的使用

热门文章

  1. 【遗传算法】遗传算法介绍
  2. PostgreSQL 源码解读(156)- 后台进程#8(walsender#4)
  3. Eclipse使用小技巧 设置Java视图 展现控制台 设置包视图
  4. nginx配置https访问, 生成自签名证书
  5. 简单几步骤查询大量中通物流,并分析退回延误的单号
  6. 第八章 五虎上将中谁最有心计
  7. php中可以实现多态的是继承,php:对象继承和多态的实例
  8. css将文字置于图片上的方法
  9. 什么是内存对齐?为什么要内存对齐?
  10. 什么是内存泄漏?怎么产生的?如何检测?