PMS源码探究相关记录 - Xiasm/Java-Android-Learn GitHub Wiki
包管理机制是Android中一个重要的知识点,分析Android的包管理机制可以让我们更加熟悉framework层的工作方式。
包管理机制最重要的就是apk包的安装,所以我们从安装apk开始分析。Android APK的安装场景有很多种,如:
- adb install
- 用户下载的apk通过系统安装器安装
- 通过应用商店安装
我们就从开发最常用的用户下载apk调用系统安装器安装开始分析。在Android7.0之前,我们通过下面代码启动apk的安装程序,可见这是一个隐式调用:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.parse("file://" + path),"application/vnd.android.package-archive");
context.startActivity(intent);
Android7.0之后,再这么做会报FileUriExposedException异常,这是因为StrictMode API 政策禁止应用程序将file:// Uri暴露给另一个应用程序,而我们的系统安装器就是一个内置在系统里的应用程序。为解决这个问题,google提供了FileProvider,这里我们不再陈述,因为它并不影响我们调用系统安装器的核心代码。
在我们的应用程序调用安装apk的代码里,会有这么两句:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("file://" + path),"application/vnd.android.package-archive");
可见我们要调用的activity在manifest文件里一定声明了一个过滤器如下:
-<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<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>
我们这里是基于Android 7.1的源码进行分析,在源码解压后的文件夹里,会有一个packages文件夹,点击进入,打开apps文件夹,可见这里都是系统内置的应用程序。其中有一个文件夹PackageInstaller,就是我们的系统安装器应用程序,打开如下
点击打开AndroidManifest.xml文件,然后搜索“application/vnd.android.package-archive”,会找到一下activity配置:
PackageInstallerActivity就是PackageInstaller的入口Activity,下面我们看一下PackageInstallerActivity onCreate处的代码:
目录:E:\Android_7_1_SourceCode\android-7.1.0_r1\packages\apps\PackageInstaller\src\com\android\packageinstaller
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mPm = getPackageManager();
mInstaller = mPm.getPackageInstaller();
mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
final Intent intent = getIntent();
mOriginatingUid = getOriginatingUid(intent);
final Uri packageUri;
//标注1
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
finish();
return;
}
mSessionId = sessionId;
packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath));
mOriginatingURI = null;
mReferrerURI = null;
} else {
mSessionId = -1;
packageUri = intent.getData();
mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
}
// if there's nothing to do, quietly slip into the ether
if (packageUri == null) {
Log.w(TAG, "Unspecified source");
setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
finish();
return;
}
//...
//set view
setContentView(R.layout.install_start);
mInstallConfirm = findViewById(R.id.install_confirm_panel);
mInstallConfirm.setVisibility(View.INVISIBLE);
mOk = (Button)findViewById(R.id.ok_button);
mCancel = (Button)findViewById(R.id.cancel_button);
mOk.setOnClickListener(this);
mCancel.setOnClickListener(this);
// Block the install attempt on the Unknown Sources setting if necessary.
//标注2
final boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent);
if (!requestFromUnknownSource) {
processPackageUri(packageUri);
return;
}
// If the admin prohibits it, or we're running in a managed profile, just show error
// and exit. Otherwise show an option to take the user to Settings to change the setting.
final boolean isManagedProfile = mUserManager.isManagedProfile();
if (isUnknownSourcesDisallowed()) {
if ((mUserManager.getUserRestrictionSource(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
Process.myUserHandle()) & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
showDialogInner(DLG_UNKNOWN_SOURCES);
} else {
startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
clearCachedApkIfNeededAndFinish();
}
} else if (!isUnknownSourcesEnabled() && isManagedProfile) {
showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES);
} else if (!isUnknownSourcesEnabled()) {
// Ask user to enable setting first
showDialogInner(DLG_UNKNOWN_SOURCES);
} else {
processPackageUri(packageUri);
}
}
在onCreate的开头首先会初始化安装所需要的各种对象,如PackageManager、UserManager、PackageInstaller等。
在标注1处有个判断,intent的action如果是ACTION_CONFIRM_PERMISSIONS,显然我们不是,进入else里面,则packageUri就是我们通过setDataAndType传入的uri。
接着看标注2,会通过isInstallRequestFromUnknownSource(intent)方法检测是否是未知来源,如果不是,执行processPackageUri(packageUri)方法。如果是未知来源,会弹框提示,这里我们不在分析,因为用户同意安装未知来源的apk后,还是会走入processPackageUri方法。这里我把processPackageUri方法贴出来:
private void processPackageUri(final Uri packageUri) {
mPackageURI = packageUri;
final String scheme = packageUri.getScheme();
final PackageUtil.AppSnippet as;
switch (scheme) {
case SCHEME_PACKAGE: {
try {
mPkgInfo = mPm.getPackageInfo(packageUri.getSchemeSpecificPart(),
PackageManager.GET_PERMISSIONS
| PackageManager.GET_UNINSTALLED_PACKAGES);
} catch (NameNotFoundException e) {
}
if (mPkgInfo == null) {
Log.w(TAG, "Requested package " + packageUri.getScheme()
+ " not available. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
return;
}
as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
mPm.getApplicationIcon(mPkgInfo.applicationInfo));
} break;
//标注1
case SCHEME_FILE: {
File sourceFile = new File(packageUri.getPath());
PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
// Check for parse errors
if (parsed == null) {
Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
return;
}
//标注2
mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
PackageManager.GET_PERMISSIONS, 0, 0, null,
new PackageUserState());
as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
} break;
case SCHEME_CONTENT: {
mStagingAsynTask = new StagingAsyncTask();
mStagingAsynTask.execute(packageUri);
return;
}
default: {
Log.w(TAG, "Unsupported scheme " + scheme);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
clearCachedApkIfNeededAndFinish();
return;
}
}
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
//标注3
initiateInstall();
}
直接看标注1处,因为我们的uri无论有没有通过FileProvider进行处理,最后还是file的协议。然后通过PackageUtil.getPackageInfo(sourceFile)解析这个file(这个file实际上就是一个apk文件)得到Package,标注2处会根据uid、用户状态信息和PackageManager的配置等变量对包信息Package做进一步处理得到PackageInfo。
mPkgInfo拿到之后看标注3,会执行initiateInstall()方法,代码如下:
private void initiateInstall() {
//标注1
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 pkg
try {
// 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".
//标注2
mAppInfo = mPm.getApplicationInfo(pkgName,
PackageManager.GET_UNINSTALLED_PACKAGES);
if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
mAppInfo = null;
}
} catch (NameNotFoundException e) {
mAppInfo = null;
}
startInstallConfirm();
}
标注1处通过mPkgInfo拿到pkgName,标注2处通过pkgName拿到应用程序的信息mAppInfo,然后调用startInstallConfirm()方法确认安装。startInstallConfirm()方法如下:
private void startInstallConfirm() {
TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost);
tabHost.setup();
tabHost.setVisibility(View.VISIBLE);
ViewPager viewPager = (ViewPager)findViewById(R.id.pager);
TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager);
// If the app supports runtime permissions the new permissions will
// be requested at runtime, hence we do not show them at install.
boolean supportsRuntimePermissions = mPkgInfo.applicationInfo.targetSdkVersion
>= Build.VERSION_CODES.M;
boolean permVisible = false;
mScrollView = null;
mOkCanInstall = false;
int msg = 0;
//标注1
AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);
final int N = perms.getPermissionCount(AppSecurityPermissions.WHICH_ALL);
if (mAppInfo != null) {
msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
? R.string.install_confirm_question_update_system
: R.string.install_confirm_question_update;
mScrollView = new CaffeinatedScrollView(this);
mScrollView.setFillViewport(true);
boolean newPermissionsFound = false;
if (!supportsRuntimePermissions) {
newPermissionsFound =
(perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);
if (newPermissionsFound) {
permVisible = true;
mScrollView.addView(perms.getPermissionsView(
AppSecurityPermissions.WHICH_NEW));
}
}
if (!supportsRuntimePermissions && !newPermissionsFound) {
LayoutInflater inflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
TextView label = (TextView)inflater.inflate(R.layout.label, null);
label.setText(R.string.no_new_perms);
mScrollView.addView(label);
}
adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator(
getText(R.string.newPerms)), mScrollView);
} else {
findViewById(R.id.tabscontainer).setVisibility(View.GONE);
findViewById(R.id.spacer).setVisibility(View.VISIBLE);
}
if (!supportsRuntimePermissions && N > 0) {
permVisible = true;
LayoutInflater inflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
View root = inflater.inflate(R.layout.permissions_list, null);
if (mScrollView == null) {
mScrollView = (CaffeinatedScrollView)root.findViewById(R.id.scrollview);
}
((ViewGroup)root.findViewById(R.id.permission_list)).addView(
perms.getPermissionsView(AppSecurityPermissions.WHICH_ALL));
adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator(
getText(R.string.allPerms)), root);
}
//...
//标注2
mInstallConfirm.setVisibility(View.VISIBLE);
mOk.setEnabled(true);
if (mScrollView == null) {
// There is nothing to scroll view, so the ok button is immediately
// set to install.
mOk.setText(R.string.install);
mOkCanInstall = true;
} else {
mScrollView.setFullScrollAction(new Runnable() {
@Override
public void run() {
mOk.setText(R.string.install);
mOkCanInstall = true;
}
});
}
}
startInstallConfirm()方法会首先初始化确认安装界面,就是我们平常的安装apk界面,注意不同厂商定制的系统对这一块可能会有更改。标注1处会通过上面拿到的mPkgInfo去得到apk需要的权限信息,并通过CaffeinatedScrollView这个view展示出来,到这一步PackageInstaller安装apk的初始化工作就完成了。
看上面代码的标注2,mOk.setEnabled(true),这个mOk就是确认安装按钮。开始安装apk需要用户点击确认安装按钮触发,那么我们就直接来看onClick(View v)方法的代码:
public void onClick(View v) {
if (v == mOk) {
if (mOkCanInstall || mScrollView == null) {
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
clearCachedApkIfNeededAndFinish();
} else {
//标注1
startInstall();
}
} else {
mScrollView.pageScroll(View.FOCUS_DOWN);
}
} else if (v == mCancel) {
// Cancel and finish
setResult(RESULT_CANCELED);
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, false);
}
clearCachedApkIfNeededAndFinish();
}
}
由上面onCreate处代码分析可知,此时mSessionId为-1,则走入标注1处startInstall()方法。
private void startInstall() {
// Start subactivity to actually install the application
Intent newIntent = new Intent();
//标注1
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallAppProgress.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 != VerificationParams.NO_UID) {
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();
}
看标注1出知,安装apk跳到了InstallAppProgress这个activity,并把获取到的mPkgInfo的applicationInfo传入extras。可以看到这时也把我们通过隐式Intent设置的data重新传给了InstallAppProgress这个activity,接下来我们看下InstallAppProgress这个类
文件位置:E:\Android_7_1_SourceCode\android-7.1.0_r1\packages\apps\PackageInstaller\src\com\android\packageinstaller
看此类介绍可知,这个类主要作用是安装apk并显示进度等等,我们直接看onCreate()处的代码:
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Intent intent = getIntent();
mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mPackageURI = intent.getData();
final String scheme = mPackageURI.getScheme();
if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
throw new IllegalArgumentException("unexpected scheme " + scheme);
}
mInstallThread = new HandlerThread("InstallThread");
mInstallThread.start();
mInstallHandler = new Handler(mInstallThread.getLooper());
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BROADCAST_ACTION);
registerReceiver(
mBroadcastReceiver, intentFilter, BROADCAST_SENDER_PERMISSION, null /*scheduler*/);
initView();
}
看到这里注册了一个广播,新建一个handler用来处理安装过程中的各种状态,然后又走入initView()方法。
void initView() {
setContentView(R.layout.op_progress);
final PackageUtil.AppSnippet as;
final PackageManager pm = getPackageManager();
final int installFlags = getInstallFlags(mAppInfo.packageName);
if((installFlags & PackageManager.INSTALL_REPLACE_EXISTING )!= 0) {
Log.w(TAG, "Replacing package:" + mAppInfo.packageName);
}
if ("package".equals(mPackageURI.getScheme())) {
as = new PackageUtil.AppSnippet(pm.getApplicationLabel(mAppInfo),
pm.getApplicationIcon(mAppInfo));
} else {
final File sourceFile = new File(mPackageURI.getPath());
as = PackageUtil.getAppSnippet(this, mAppInfo, sourceFile);
}
mLabel = as.label;
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
mStatusTextView = (TextView)findViewById(R.id.center_text);
mExplanationTextView = (TextView) findViewById(R.id.explanation);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
mProgressBar.setIndeterminate(true);
// Hide button till progress is being displayed
mOkPanel = findViewById(R.id.buttons_panel);
mDoneButton = (Button)findViewById(R.id.done_button);
mLaunchButton = (Button)findViewById(R.id.launch_button);
mOkPanel.setVisibility(View.INVISIBLE);
if ("package".equals(mPackageURI.getScheme())) {
try {
pm.installExistingPackage(mAppInfo.packageName);
onPackageInstalled(PackageInstaller.STATUS_SUCCESS);
} catch (PackageManager.NameNotFoundException e) {
onPackageInstalled(PackageInstaller.STATUS_FAILURE_INVALID);
}
} else {
//标注1
final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
params.referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);
params.originatingUri = getIntent().getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
params.originatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
UID_UNKNOWN);
//标注2
File file = new File(mPackageURI.getPath());
try {
params.setInstallLocation(PackageParser.parsePackageLite(file, 0).installLocation);
} catch (PackageParser.PackageParserException e) {
Log.e(TAG, "Cannot parse package " + file + ". Assuming defaults.");
}
mInstallHandler.post(new Runnable() {
@Override
public void run() {
doPackageStage(pm, params);
}
});
}
}
因为我们通过隐式意图传入的是file协议,所以直接看标注1处,会创建SessionParams,它是安装程序会话的参数。注意到PackageInstaller这个类,这个类是整个安装的核心类,它在android.content.pm包下面,这个类主要提供在设备上安装,升级和删除应用程序等的功能。标注2处对安装包进行位置等解析,然后执行doPackageStage()方法并把解析的params传过去。接下来我们看下doPackageStage(pm, params)方法:
private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) {
final PackageInstaller packageInstaller = pm.getPackageInstaller();
PackageInstaller.Session session = null;
try {
//标注1
final String packageLocation = mPackageURI.getPath();
final File file = new File(packageLocation);
final int sessionId = packageInstaller.createSession(params);
final byte[] buffer = new byte[65536];
session = packageInstaller.openSession(sessionId);
final InputStream in = new FileInputStream(file);
final long sizeBytes = file.length();
final OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes);
try {
int c;
while ((c = in.read(buffer)) != -1) {
out.write(buffer, 0, c);
if (sizeBytes > 0) {
final float fraction = ((float) c / (float) sizeBytes);
session.addProgress(fraction);
}
}
session.fsync(out);
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
}
// Create a PendingIntent and use it to generate the IntentSender
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstallAppProgress.this /*context*/,
sessionId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
//标注2
session.commit(pendingIntent.getIntentSender());
} catch (IOException e) {
onPackageInstalled(PackageInstaller.STATUS_FAILURE);
} finally {
IoUtils.closeQuietly(session);
}
}
标注1处可看出,拿到文件位置之后,会通过流去读文件,session.fsync(out)会将流数据刷到磁盘.然后看标注2,session会话会进行提交,并传入一个IntentSender,我们来看下commit方法的实现:
文件目录:android.content.pm.PackageInstaller
public void commit(@NonNull IntentSender statusReceiver) {
try {
mSession.commit(statusReceiver);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
mSeesion是Session类持有的一个对象,如下:
public static class Session implements Closeable {
private IPackageInstallerSession mSession;
//...
}
这说明要通过IPackageInstallerSession来进行进程间的通信,最终会调用PackageInstallerSession的commit方法,这样代码逻辑就到了Java框架层的。
文件位置:E:\Android_7_1_SourceCode\android-7.1.0_r1\frameworks\base\services\core\java\com\android\server\pm
@Override
public void commit(IntentSender statusReceiver) {
Preconditions.checkNotNull(statusReceiver);
final boolean wasSealed;
synchronized (mLock) {
wasSealed = mSealed;
if (!mSealed) {
// Verify that all writers are hands-off
for (FileBridge bridge : mBridges) {
if (!bridge.isClosed()) {
throw new SecurityException("Files still open");
}
}
mSealed = true;
}
// Client staging is fully done at this point
mClientProgress = 1f;
computeProgressLocked(true);
}
if (!wasSealed) {
// Persist the fact that we've sealed ourselves to prevent
// mutations of any hard links we create. We do this without holding
// the session lock, since otherwise it's a lock inversion.
mCallback.onSessionSealedBlocking(this);
}
// This ongoing commit should keep session active, even though client
// will probably close their end.
mActiveCount.incrementAndGet();
final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
//标注1
mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
}
commit方法中会将包的信息封装为PackageInstallObserverAdapter ,它在PMS中被定义。在标注1处会向Handler发送一个类型为MSG_COMMIT的消息,其中adapter.getBinder()会得到IPackageInstallObserver2.Stub类型的观察者,从类型就知道这个观察者是可以跨进程进行回调的。处理该消息的代码如下所示。
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
synchronized (mLock) {
if (msg.obj != null) {
mRemoteObserver = (IPackageInstallObserver2) msg.obj;
}
try {
commitLocked();
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
destroyInternal();
dispatchSessionFinished(e.error, completeMsg, null);
}
return true;
}
}
};
我们直接看commitLocked()方法:
private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
throws PackageManagerException {
...
mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
installerPackageName, installerUid, user, mCertificates);
}
commitLocked方法比较长,这里截取最主要的信息,会调用PMS的installStage方法,这样代码逻辑就进入了PMS中。