系統開機過程中掃描并安裝apk的過程是針對已有的apk文件。針對外部apk的安裝,pms提供了另外的接口。我們一般也就通過兩種方式去安裝外部apk,一種是通過adb的install命令安裝外部應用。另外一種是通過系統應用PackageInstaller,通過界面引導的方式安裝外部應用。下面分別來分析這兩種安裝方式的過程和相同的地方。
adb install命令實際上調用的是/system/bin目錄下的pm可執行程序來實現安裝應用的功能。
下面來分析下pm的實現源碼:
[/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java]
public static void main(String[] args) { int exitCode = 1; try { exitCode = new Pm().run(args); } catch (Exception e) { } System.exit(exitCode);} Pm程序的入口main函數中,創建一個Pm的實例并執行run方法,傳入args參數(含apk路徑等信息)。繼續分析run方法:
public int run(String[] args) throws IOException, RemoteException { boolean validCommand = false; if (args.length < 1) { return showUsage(); } mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user")); mPm = ipackageManager.Stub.asInterface(ServiceManager.getService("package")); ... String op = args[0]; if ("install".equals(op)) { return runInstall(); } ... }run方法中首先讀取第一個參數args[0],第一個參數是op(Operation),表示命令的操作是什么,這里我們來看op=”install”的情況,直接調用runInstall方法,下面來看看runInstall方法的實現:
PRivate int runInstall() { int installFlags = 0; int userId = UserHandle.USER_ALL; String installerPackageName = null; ... try { VerificationParams verificationParams = new VerificationParams(verificationURI, originatingURI, referrerURI, VerificationParams.NO_UID, null); mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags, installerPackageName, verificationParams, abi, userId); ... } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); return 1; }可以看到runInstall方法最終通過binder和pms通信,執行pms的installPackageAsUser方法,分別傳入apk的文件路徑,abi信息等。pms的installPackageAsUser方法在接下來會分析。
通過PackageInstaller應用安裝外部應用
查看PackageInstaller安裝界面代碼:
[/package/app/PackageInstaller/src/com/android/packageinstaller /InstallAppProgress.java]
public void initView() { setContentView(R.layout.op_progress); int installFlags = 0; PackageManager pm = getPackageManager(); ... pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags, installerPackageName, verificationParams, null); ... }在PackageInstaller安裝界面初始化的時候就直接調用PackageManager提供的installPackageWithVerificationAndEncryption方法執行安裝操作。下面來分析下installPackageWithVerificationAndEncryption的具體實現:
[/frameworks/base/core/java/android/app/applicationPackageManager.java]
public void installPackageWithVerificationAndEncryption(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName, VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { installCommon(packageURI, observer, flags, installerPackageName, verificationParams,encryptionParams); }繼續分析installCommon方法:
private void installCommon(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName, VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { if (!"file".equals(packageURI.getScheme())) { throw new UnsupportedOperationException("Only file:// URIs are supported"); } final String originPath = packageURI.getPath(); try { mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName, verificationParams, null); } catch (RemoteException ignored) { } }installCommon方法首先判斷傳入的Uri是否屬于文件類型,如果不是,直接異常返回。如果是,則通過Uri獲得apk文件路徑,最后調用pms的installPackage方法(實際上同樣是調用installPackageAsUser)執行具體的安裝操作。
installPackageAsUser方法分析
上面兩種主要的外部安裝路徑最后都指向了pms的installPakcageAsUser方法。這個方法提供外部安裝功能,我們從應用安裝的第一個階段(復制文件)開始:
[/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer, int installFlags, String installerPackageName, VerificationParams verificationParams, String packageAbiOverride, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);第一步先檢查調用端是否具有INSTALL_PACKAGES權限,如果沒有該權限則拋出權限異常并終止運行。只有系統平臺簽名或者系統應用才會授予這個權限,這也是第三方應用無法實現靜默安裝的原因。
final int callingUid = Binder.getCallingUid(); 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; }第二步檢查callingUid是否等于SHELL或者ROOT,如果是表示通過adb shell命令發起的安裝動作,給installFlags添加對應INSTALL_FROM_ADB標志,否則去掉。這個標志后面會用到。
verificationParams.setInstallerUid(callingUid); 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);}最后創建一個what是INIT_COPY的Message,把調用的參數保存在InstallParams對象中并發送到mHandler的消息隊列中。
下面分析下INIT_COPY類型消息的處理:
class PackageHandler extends Handler { private boolean mBound = false; final ArrayList<HandlerParams> mPendingInstalls = new ArrayList<HandlerParams>(); void doHandleMessage(Message msg) { switch (msg.what) { case INIT_COPY: { HandlerParams params = (HandlerParams) msg.obj; int idx = mPendingInstalls.size(); if (!mBound) { if (!connectToService()) { params.serviceError(); return; } else { 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; }在INIT_COPY消息的處理中將綁定DefaultContainerService,因為這是一個異步的過程,要等待綁定的結果通過onServiceConnected()返回,所以,這里將安裝的參數信息放到mPendingInstalls列表中。如果這個service以前就綁定好了,現在不在需要再綁定,安裝信息也會先放到mPendingInstalls中。如果有多個安裝請求同時到達,通過mPendingInstalls列表可以對它們進行排隊。如果列表中只有一項,說明沒有更多的安裝請求,此時會立即發送MCS_BOUND消息,進入下一步的處理。而onServiceConnected()方法的處理同樣是發送MCS_BOUND消息,onServiceConnected方法如下:
class DefaultContainerConnection implements ServiceConnection { public void onServiceConnected(ComponentName name, IBinder service) { IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service); mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs)); } public void onServiceDisconnected(ComponentName name) { } };一旦DefaultContainerService(該服務用于外部存儲卡應用安裝拷貝用)服務綁定成功后,通過mHandler發MCS_BOUND消息,下面來分析下這個消息的處理過程:
case MCS_BOUND: { if (msg.obj != null) { mContainerService = (IMediaContainerService) msg.obj; } if (mPendingInstalls.size() > 0) { HandlerParams params = mPendingInstalls.get(0); if (params != null) { if (params.startCopy()) { // Delete pending install if (mPendingInstalls.size() > 0) { mPendingInstalls.remove(0); } if (mPendingInstalls.size() == 0) { if (mBound) { removeMessages(MCS_UNBIND); Message ubmsg = obtainMessage(MCS_UNBIND); sendMessageDelayed(ubmsg, 10000); } } else { mHandler.sendEmptyMessage(MCS_BOUND); } } } } }MCS_BOUND消息的處理過程就是調用InstallParams類的startCopy()方法來執行安裝操作,一次安裝完成后在待安裝列表中刪除。只要mPendingInstalls中還有安裝信息,就會重復發送MCS_BOUND消息,直到所有的應用都安裝完畢,最后發送一個延時10秒的MCS_UNBIND消息。MCS_UNBIND消息不細分析。
安裝的開始在InstallParams類的startCopy方法,該方法定義在InstallParams的父類HandlerParams中:
private abstract class HandlerParams { private static final int MAX_RETRIES = 4; … final boolean startCopy() { boolean res; try { handleStartCopy(); res = true; } handleReturnCode(); return res; }父類的startCopy方法最后調用handleStartCopy方法,其中抽象的父類只聲明了handleStartCopy抽象方法,具體實現在子類InstallParams中:
class InstallParams extends HandlerParams { final OriginInfo origin; final IPackageInstallObserver2 observer; int installFlags; final String installerPackageName; final VerificationParams verificationParams; private InstallArgs mArgs; private int mRet; final String packageAbiOverride; public void handleStartCopy() throws RemoteException { int ret = PackageManager.INSTALL_SUCCEEDED; … final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0; final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0; PackageInfoLite pkgLite = null; … final InstallArgs args = createInstallArgs(this); if (ret == PackageManager.INSTALL_SUCCEEDED) { … /* * No package verification is enabled, so immediately start * the remote call to initiate copy using temporary file. */ ret = args.copyApk(mContainerService, true); } mRet = ret; }如果忽略handleStartCopy中一堆繁雜的檢驗判斷,最終調用InstallArgs的copyApk方法,傳入ContainerService的binder實例。另外真正的copyApk方法在其子類FileInstallArgs的copyApk方法中實現:
class FileInstallArgs extends InstallArgs { private File codeFile; private File resourceFile; private File legacyNativeLibraryPath; int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { … int ret = PackageManager.INSTALL_SUCCEEDED; ret = imcs.copyPackage(origin.file.getAbsolutePath(), target); 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 { 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; }發現copyApk方法同樣是調用DefaultContainerService的copyPackage方法將應用的文件復制到/data/app下。如果應用中還有native的動態庫,也會把包在apk文件中的動態庫的文件提取出來。執行完copyApk后,安裝的第一階段工作就完成了,應用安裝到了/data/app目錄下。
第一階段(復制apk文件)完成后,繼續完成第二階段的掃描安裝工作:
在前面的startCopy()方法中已經看到,最后它會調用handleReturnCode()方法,其實現在InstallParams類中:
void handleReturnCode() { if (mArgs != null) { processPendingInstall(mArgs, mRet); } }接下來分析processPendingInstall方法的實現:
private void processPendingInstall(final InstallArgs args, final int currentStatus) { mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); // Result object to be returned PackageInstalledInfo 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); } }); }processPendingInstall方法中post了一個消息,這樣安裝過程將以異步的方式繼續執行。在post消息的處理中,首先調用installPackageLI來裝載應用,接下來的一大段代碼都是執行備份操作,備份是通過BackupManagerService來完成的。備份完成后,通過發送POST_INSTALL消息來繼續處理。現在來分析下apk的裝載過程:
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 returned res.returnCode = PackageManager.INSTALL_SUCCEEDED; PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setDisplayMetrics(mMetrics); final PackageParser.Package pkg; pkg = pp.parsePackage(tmpPackageFile, parseFlags); // Mark that we have an install time CPU ABI override. pkg.cpuAbiOverride = args.abiOverride; String pkgName = res.name = pkg.packageName; pp.collectCertificates(pkg, parseFlags); pp.collectManifestDigest(pkg); pp = null; String oldCodePath = null; boolean systemApp = false; synchronized (mPackages) { // Check if installing already existing package if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { String oldName = mSettings.mRenamedPackages.get(pkgName); if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName) && mPackages.containsKey(oldName)) { pkg.setPackageName(oldName); pkgName = pkg.packageName; replace = true; } else if (mPackages.containsKey(pkgName)) { // This package, under its official name, already exists // on the device; we should replace it. replace = true; } } PackageSetting ps = mSettings.mPackages.get(pkgName); 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)) { return; } } int 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) { 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 (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); } synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps != null) { res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); } } }installPackageLI方法首先解析了安裝的應用文件,得到解析的結果后,主要是判斷新安裝的應用是否和系統中已安裝的應用構成升級關系,如果是,調用replacePackageLI方法繼續處理,否則調用installNewPackageLI方法處理。
新聞熱點
疑難解答