APK的安装有很多方式,应用商城下载、文件浏览器安装、adb命令安装,我们以文件浏览器为例。
将一个APK文件放到SD卡目录下,文件浏览器显示apk文件。
当我们点击APK文件时,文件浏览器根据文件的后缀(.apk)解析,执行startActivity,调起packageinstaller.PackageInstallerActivity。
1
| ActivityManager: START u0 {act=android.intent.action.VIEW dat=file:///storage/emulated/0/com.dewmobile.kuaiya.apk typ=application/vnd.android.package-archive flg=0x88000 cmp=com.android.packageinstaller/.PackageInstallerActivity} from uid 10032 on display 0
|
PackageInstallerActivity
PackageInstallerActivity这个类的主要作用是显示安装弹窗,对APK进行解析,判断是否允许未知来源安装,判断应用权限,等待用户安装。
packages/apps/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public void onClick(View v) { if (v == mOk) { if (mOkCanInstall || mScrollView == null) { if (mSessionId != -1) { mInstaller.setPermissionsResult(mSessionId, true); clearCachedApkIfNeededAndFinish(); } else { startInstall(); } } else { mScrollView.pageScroll(View.FOCUS_DOWN); } } else if (v == mCancel) { setResult(RESULT_CANCELED); if (mSessionId != -1) { mInstaller.setPermissionsResult(mSessionId, false); } clearCachedApkIfNeededAndFinish(); } }
|
然后用户点击安装按钮,就会调用startInstall()函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| private void startInstall() { Intent newIntent = new Intent(); 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(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI); startActivity(newIntent); finish(); }
|
startInstall函数用于跳转到InstallAppProgress这个Activity,关闭PackageInstallerActivity。
InstallAppProgress
InstallAppProgress主要用于展示安装界面、向PMS发送包信息, 处理回调。
packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallAppProgress.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @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 );
initView(); }
|
InstallAppProgress中启动了一个HandlerThread,开启安装线程,注册了一个广播,这个广播用来监听系统返回的安装结果。
主要代码在在initView()函数中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 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);
File file = new File(mPackageURI.getPath()); try { 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(TAG, "Cannot parse package " + file + ". Assuming defaults."); Log.e(TAG, "Cannot calculate installed size " + file + ". Try only apk size."); params.setSize(file.length()); } catch (IOException e) { Log.e(TAG, "Cannot calculate installed size " + file + ". Try only apk size."); params.setSize(file.length()); }
mInstallHandler.post(new Runnable() { @Override public void run() { doPackageStage(pm, params); } });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) { final PackageInstaller packageInstaller = pm.getPackageInstaller(); PackageInstaller.Session session = null; try { 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); }
Intent broadcastIntent = new Intent(BROADCAST_ACTION); PendingIntent pendingIntent = PendingIntent.getBroadcast( InstallAppProgress.this , sessionId, broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT); session.commit(pendingIntent.getIntentSender()); } catch (IOException e) { onPackageInstalled(PackageInstaller.STATUS_FAILURE); } finally { IoUtils.closeQuietly(session); } }
|
最终广播接受安装结果
1 2 3 4 5 6 7 8 9 10 11 12
| private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final int statusCode = intent.getIntExtra( PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); if (statusCode == PackageInstaller.STATUS_PENDING_USER_ACTION) { context.startActivity((Intent)intent.getParcelableExtra(Intent.EXTRA_INTENT)); } else { onPackageInstalled(statusCode); } } };
|
应用安装的前期工作,用户可以看到的部分基本就这些,接下来的工作就都由Java框架层处理。
Java框架层
上面最终调用PackageInstallerSession的commit方法,将安装APK的信息发给框架层。
frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
1 2 3 4 5 6 7 8
| @Override public void commit(IntentSender statusReceiver) { ...... mActiveCount.incrementAndGet(); final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext, statusReceiver, sessionId, mIsInstallerDeviceOwner, userId); mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget(); }
|
commit函数中向Handler发送一个类型为MSG_COMMIT的消息,adapter.getBinder()会得到IPackageInstallObserver2观察者,处理消息逻辑如下:
frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| private final Handler.Callback mHandlerCallback = new Handler.Callback() { @Override public boolean handleMessage(Message msg) { final PackageInfo pkgInfo = mPm.getPackageInfo( params.appPackageName, PackageManager.GET_SIGNATURES , 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; } } };
|
1 2 3 4 5 6
| private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo) throws PackageManagerException { ... mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params, installerPackageName, installerUid, user, mCertificates); }
|
在commitLocked方法中,调用PMS的installStage方法,这样逻辑就进入PMS中了。
总结
PackageInstaller安装APK的过程,简单来说就两步:
- 将APK的信息通过IO流的形式写入到PackageInstaller.Session中。
- 调用PackageInstaller.Session的commit方法,将APK的信息交由PMS处理。