PackageInstaller是system app ,是一个安装应用的应用。因为它本身具有system相关权限,可以直接与pms交互进行apk安装。三方应用因为权限受限,因此需要通过它来间接安装apk。
核心权限:
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
intent隐式启动呼起PackageInstaller入口
匹配:<data android:mimeType="application/vnd.android.package-archive" /> 原生对应的入口是:InstallStart.java
安装的核心逻辑在:PackageInstallerActivity.java
一、初始化逻辑
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
//intent信息校验
bindUi(R.layout.install_confirm, false);//构建安装页UI
checkIfAllowedAndInitiateInstall();//安装校验
}
private void checkIfAllowedAndInitiateInstall() {
if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
initiateInstall();
return;
}
// If the admin prohibits it, just show error and exit.
// 安装应用未知来源
if (isUnknownSourcesDisallowed()) {
...
} else {
handleUnknownSources();
}
}
private void handleUnknownSources() {
if (mOriginatingPackage == null) {
Log.i(TAG, "No source found for package " + mPkgInfo.packageName);
showDialogInner(DLG_ANONYMOUS_SOURCE);
return;
}
//android O 新权限 <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
int appOpMode = mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES,
mOriginatingUid, mOriginatingPackage);
switch (appOpMode) {
...
case AppOpsManager.MODE_ALLOWED:
initiateInstall();//初始化安装
break;
...
}
}
private void initiateInstall() {
...
// 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".
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() {
// We might need to show permissions, load layout with permissions
if (mAppInfo != null) {
bindUi(R.layout.install_confirm_perm_update, true);
} else {
bindUi(R.layout.install_confirm_perm, true);
}
...
//从apk中提取权限,然后把提取出的权限存放在AppSecurityPermissions类的mPermsList变量中
AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);
...
//根据提取出来的权限设置UI内容
}
这部分简单看主要就是页面UI绑定展示和安装相关权限检查。
二、安装逻辑
public void onClick(View v) {
if (v == mOk) { //确定安装
if (mOk.isEnabled()) {
if (mOkCanInstall || mScrollView == null) {
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
finish();
} else {
startInstall();//走安装流程
}
} else {
mScrollView.pageScroll(View.FOCUS_DOWN);
}
}
} else if (v == mCancel) {
// Cancel and finish
setResult(RESULT_CANCELED);
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, false);
}
finish();
}
}
安装跳转到InstallInstalling
protected void onResume() {
super.onResume();
// This is the first onResume in a single life of the activity
if (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 finished
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
}
}
}
protected void onPostExecute(PackageInstaller.Session session) {
if (session != null) {
...
session.commit(pendingIntent.getIntentSender());
}
...
}
public void commit(@NonNull IntentSender statusReceiver) {
try {
//mSession 对应 PackageInstallerSession
mSession.commit(statusReceiver);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
PackageInstallerSession:
public void commit(IntentSender statusReceiver) {
...
final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
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) {
// Cache package manager data without the lock held
final PackageInfo pkgInfo = mPm.getPackageInfo(
params.appPackageName, PackageManager.GET_SIGNATURES
| PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
final ApplicationInfo appInfo = mPm.getApplicationInfo(
params.appPackageName, 0, userId);
synchronized (mLock) {
if (msg.obj != null) {
mRemoteObserver = (IPackageInstallObserver2) msg.obj;
}
try {
commitLocked(pkgInfo, appInfo);
} 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;
}
}
};
private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
throws PackageManagerException {
...
mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
installerPackageName, installerUid, user, mCertificates);
}
PackageInstaller安装流程
网友评论