PMS启动 APK 安装流程详解

mac2024-06-16  38

文章目录

概述相关类说明 PMS 服务启动应用程序(APK)安装有界面安装无界面安装APK 安装原理

概述

PackageManagerService(以下简称 PMS)是一个常用的系统服务,主要负责系统中的 Package 的管理,应用程序的安装、卸载、查询等相关功能。其相关类图如下

相关类说明

IPackageManager 定义了服务端和客户端通信的业务函数,还定义了内部类 Stub ,该类从 Binder 派生并实现了 IPackageManager 接口PackageManagerService 继承自 IPackageManager.Stub 类,由于 Stub 类从 Binder 派生,因此 PackageManagerService 将作为服务端参与 Binder 通信Stub 定义了一个内部类 Proxy ,该类有一个 IBinder 类型(实际类型为 BinderProxy )的成员变量 mRemote ,根据 Binder 详解 中介绍的 Binder 系统的知识,mRemote 用于和服务端 PackageManagerService 通信ApplicationPackageManager 承自 PackageManager 类。它并没有直接参与 Binder 通信,而是通过 mPM 成员变量指向一个 IPackageManager.Stub.Proxy 类型的对象。

注:IPackageManager 在 Android Studio 中可能找不到,因为他是一个 AIDL 处理后的文件,这里附上相关的 IPackageManager.aidl 文件

PMS 服务启动

分析 PMS 之前我们先来看下这玩意是在哪里启动的。

我们都知道系统启动的时候会调用 SystemServer 的 main 函数。

注释已经说明了,这个 main 函数是通过 zygote 调用的,这块的调用链涉及到了进程启动相关,此处先不多说。看到 main 创建了一个自己然后调用 run 方法。

可以看到在 run 方法中系统将服务类型按照级别分为了三大类,引导服务、内核服务、其他服务,而 PMS 的启动就在引导服务中启动。

/** * The main entry point from zygote. */ public static void main(String[] args) { new SystemServer().run(); } private void run() { // Start services. try { //系统引导服务 startBootstrapServices(); //内核服务 startCoreServices(); //其他服务 startOtherServices(); } catch (Throwable ex) { Slog.e("System", "******************************************"); Slog.e("System", "************ Failure starting system services", ex); throw ex; } finally { traceEnd(); } } private void startBootstrapServices() { mPackageManagerService = PackageManagerService.main(mSystemContext, installer, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore); mFirstBoot = mPackageManagerService.isFirstBoot(); //标注① mPackageManager = mSystemContext.getPackageManager(); }

我们进入 PackageManagerService 的 main 方法继续追踪。

可以看到在 main 方法中也是 new 了一把自己。然后将服务添加的 ServiceManager 中进行管理。到这,PMS 服务就启动完成了。

public class PackageManagerService extends IPackageManager.Stub implements PackageSender { public static PackageManagerService main(Context context, Installer installer, boolean factoryTest, boolean onlyCore) { // Self-check for initial settings. PackageManagerServiceCompilerMapping.checkProperties(); PackageManagerService m = new PackageManagerService(context, installer, factoryTest, onlyCore); m.enableSystemUserPackages(); ServiceManager.addService("package", m); final PackageManagerNative pmn = m.new PackageManagerNative(); ServiceManager.addService("package_native", pmn); return m; } }

这里在额外看一下 PackageManager 的创建。上面 标注① 出获取了 PackageManager ,我们就以此为入口看下。mSystemContext 为 Content 的一个实现类,可以直接搜 ContextImpl。

可以看到和上面的类图完美的对应上了,这里拿到系统提供的 PM,然后创建一个 ApplicationPackageManager 持有这个 pm 去和系统服务进行交互。

class ContextImpl extends Context { @Override public PackageManager getPackageManager() { if (mPackageManager != null) { return mPackageManager; } IPackageManager pm = ActivityThread.getPackageManager(); if (pm != null) { // Doesn't matter if we make more than one instance. return (mPackageManager = new ApplicationPackageManager(this, pm)); } return null; } }

注:Android 系统启动慢的原因就是在启动 PMS 的时候,需要执行扫描文件夹、处理权限、安装系统应用(文件的解压与copy)等比较耗时的操作。

应用程序(APK)安装

有界面安装

我们下载的安装包都是一个 .apk 文件,当我们点击该文件的时候,会启动系统的一个安装 apk 的应用,该应用是系统应用,在系统启动时就已经安装成功了。这里附上 PackageInstaller.apk 源码

首先这个 Intent 会启动 InstallStart ,在这里做一些校验后就跳转到 PackageInstallerActivity 进行确认安装后,进入 InstallInstalling 界面进行安装。

可以看到在 AndroidManifest.xml 中对外暴露了 InstallStart。

<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> <intent-filter android:priority="1"> <action android:name="android.intent.action.INSTALL_PACKAGE" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="file" /> <data android:scheme="package" /> <data android:scheme="content" /> </intent-filter> <intent-filter android:priority="1"> <action android:name="android.content.pm.action.CONFIRM_PERMISSIONS" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>

安装界面调用流程

public class InstallStart extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ...... Intent nextActivity = new Intent(intent); ...... nextActivity.setClass(this, PackageInstallerActivity.class); ...... if (nextActivity != null) { startActivity(nextActivity); } finish(); } } public class PackageInstallerActivity extends OverlayTouchActivity implements OnClickListener { 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(); } } private void startInstall() { // Start subactivity to actually install the application Intent newIntent = new Intent(); ...... newIntent.setClass(this, InstallInstalling.class); ...... startActivity(newIntent); finish(); } } public class InstallInstalling extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); params.installFlags = PackageManager.INSTALL_FULL_APP; ...... try { //标注② mSessionId = getPackageManager().getPackageInstaller().createSession(params); } catch (IOException e) { launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } } }

上面标注②中获取了 PackageInstaller 执行真正的安装

public class ApplicationPackageManager extends PackageManager { @Override public PackageInstaller getPackageInstaller() { synchronized (mLock) { if (mInstaller == null) { try { mInstaller = new PackageInstaller(mPM.getPackageInstaller(), mContext.getPackageName(), mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return mInstaller; } } } public class PackageInstaller { //标注③ private final IPackageInstaller mInstaller; public int createSession(@NonNull SessionParams params) throws IOException { try { final String installerPackage; if (params.installerPackageName == null) { installerPackage = mInstallerPackageName; } else { installerPackage = params.installerPackageName; } return mInstaller.createSession(params, installerPackage, mUserId); } catch (RuntimeException e) { ExceptionUtils.maybeUnwrapIOException(e); throw e; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } }

上面标注③ 中的 IPackageInstaller 为 PackageInstallerService 的 Binder 对象,看到这里是不发现,系统服务将 Binder 通信用的是淋漓尽致啊。

这里安装完成后通过 Binder 和 Handler 进行回调。

public class PackageInstallerService extends IPackageInstaller.Stub { @Override public int createSession(SessionParams params, String installerPackageName, int userId) { try { return createSessionInternal(params, installerPackageName, userId); } catch (IOException e) { throw ExceptionUtils.wrap(e); } } private int createSessionInternal(SessionParams params, String installerPackageName, int userId) throws IOException { final int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission( callingUid, userId, true, true, "createSession"); if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) { throw new SecurityException("User restriction prevents installing"); } if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) { params.installFlags |= PackageManager.INSTALL_FROM_ADB; } else { // Only apps with INSTALL_PACKAGES are allowed to set an installer that is not the // caller. if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) != PackageManager.PERMISSION_GRANTED) { mAppOps.checkPackage(callingUid, installerPackageName); } params.installFlags &= ~PackageManager.INSTALL_FROM_ADB; params.installFlags &= ~PackageManager.INSTALL_ALL_USERS; params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; if ((params.installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0 && !mPm.isCallerVerifier(callingUid)) { params.installFlags &= ~PackageManager.INSTALL_VIRTUAL_PRELOAD; } } // Only system components can circumvent runtime permissions when installing. if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0 && mContext.checkCallingOrSelfPermission(Manifest.permission .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) { throw new SecurityException("You need the " + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission " + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag"); } if ((params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0 || (params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) { throw new IllegalArgumentException( "New installs into ASEC containers no longer supported"); } // Defensively resize giant app icons if (params.appIcon != null) { final ActivityManager am = (ActivityManager) mContext.getSystemService( Context.ACTIVITY_SERVICE); final int iconSize = am.getLauncherLargeIconSize(); if ((params.appIcon.getWidth() > iconSize * 2) || (params.appIcon.getHeight() > iconSize * 2)) { params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize, true); } } switch (params.mode) { case SessionParams.MODE_FULL_INSTALL: case SessionParams.MODE_INHERIT_EXISTING: break; default: throw new IllegalArgumentException("Invalid install mode: " + params.mode); } // If caller requested explicit location, sanity check it, otherwise // resolve the best internal or adopted location. if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) { if (!PackageHelper.fitsOnInternal(mContext, params)) { throw new IOException("No suitable internal storage available"); } } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) { if (!PackageHelper.fitsOnExternal(mContext, params)) { throw new IOException("No suitable external storage available"); } } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) { // For now, installs to adopted media are treated as internal from // an install flag point-of-view. params.setInstallFlagsInternal(); } else { // For now, installs to adopted media are treated as internal from // an install flag point-of-view. params.setInstallFlagsInternal(); // Resolve best location for install, based on combination of // requested install flags, delta size, and manifest settings. final long ident = Binder.clearCallingIdentity(); try { params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, params); } finally { Binder.restoreCallingIdentity(ident); } } final int sessionId; final PackageInstallerSession session; synchronized (mSessions) { // Sanity check that installer isn't going crazy final int activeCount = getSessionCount(mSessions, callingUid); if (activeCount >= MAX_ACTIVE_SESSIONS) { throw new IllegalStateException( "Too many active sessions for UID " + callingUid); } final int historicalCount = mHistoricalSessionsByInstaller.get(callingUid); if (historicalCount >= MAX_HISTORICAL_SESSIONS) { throw new IllegalStateException( "Too many historical sessions for UID " + callingUid); } sessionId = allocateSessionIdLocked(); } final long createdMillis = System.currentTimeMillis(); // We're staging to exactly one location File stageDir = null; String stageCid = null; if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) { final boolean isInstant = (params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0; stageDir = buildStageDir(params.volumeUuid, sessionId, isInstant); } else { stageCid = buildExternalStageCid(sessionId); } session = new PackageInstallerSession(mInternalCallback, mContext, mPm, mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid, params, createdMillis, stageDir, stageCid, false, false); synchronized (mSessions) { mSessions.put(sessionId, session); } mCallbacks.notifySessionCreated(session.sessionId, session.userId); writeSessionsAsync(); return sessionId; } }

无界面安装

上面说的有界面安装是正常的安装方式,可以让用户感知的。然而还有一种比较流氓的方式,就是静默安装。用户无感知,这个应用就被安装成功了。

这种方式就是通过 adb 命令进行安装 adb install

这个命令我们可以在 commandline.cpp 中找到。可以看到通过命令匹配 执行 install_app 函数。与之相应的还有 adb uninstall 命令

int adb_commandline(int argc, const char** argv) { ...... else if (!strcmp(argv[0], "install")) { if (argc < 2) return syntax_error("install requires an argument"); if (_use_legacy_install()) { return install_app_legacy(argc, argv); } return install_app(argc, argv); } ...... else if (!strcmp(argv[0], "uninstall")) { if (argc < 2) return syntax_error("uninstall requires an argument"); if (_use_legacy_install()) { return uninstall_app_legacy(argc, argv); } return uninstall_app(argc, argv); } ...... } static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) { ...... result = pm_command(transport, serial, argc, argv); ...... return result; } static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) { std::string cmd = "pm"; while (argc-- > 0) { cmd += " " + escape_arg(*argv++); } return send_shell_command(transport, serial, cmd, false); }

顺着调用栈走下去发现最终执行了 pm 命令。而这个命令会执行一个 pm.jar 文件,该 jar 包是通过 Pm.java 打包而成。

命令执行后会调用 Pm 的 main 函数。顺着调用往下走,最终发现还是调用了 PackageInstallerService 的 createSession 函数。殊途同归,和有界面安装走入相同的流程了。

public final class Pm { public static void main(String[] args) { int exitCode = 1; try { exitCode = new Pm().run(args); } catch (Exception e) { Log.e(TAG, "Error", e); System.err.println("Error: " + e); if (e instanceof RemoteException) { System.err.println(PM_NOT_RUNNING_ERR); } } System.exit(exitCode); } public int run(String[] args) throws RemoteException { boolean validCommand = false; if (args.length < 1) { return showUsage(); } mAm = IAccountManager.Stub.asInterface(ServiceManager.getService(Context.ACCOUNT_SERVICE)); mUm = IUserManager.Stub.asInterface(ServiceManager.getService(Context.USER_SERVICE)); mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); if (mPm == null) { System.err.println(PM_NOT_RUNNING_ERR); return 1; } mInstaller = mPm.getPackageInstaller(); ...... if ("install".equals(op)) { return runInstall(); } ...... if ("uninstall".equals(op)) { return runUninstall(); } ...... } private int runInstall() throws RemoteException { ...... final int sessionId = doCreateSession(params.sessionParams, params.installerPackageName, params.userId); ...... } private int doCreateSession(SessionParams params, String installerPackageName, int userId) throws RemoteException { userId = translateUserId(userId, "runInstallCreate"); if (userId == UserHandle.USER_ALL) { userId = UserHandle.USER_SYSTEM; params.installFlags |= PackageManager.INSTALL_ALL_USERS; } final int sessionId = mInstaller.createSession(params, installerPackageName, userId); return sessionId; } }

APK 安装原理

至此对于 PMS 的启动,和 APK 文件的两种安装方式我们都分析完了,下面对结合两种安装方式总结一下

对于 Android 系统安装 APK 应用,就是将 APK 文件解压,把相应的文件 copy 到相应的目录中即可。

data/app/package_name 安装时将 apk 文件复制到此目录,可以将文件取出并安装data/data/package_name 开辟存放应用程序的文件数据的文件夹,包括我们的 so 库、缓存文件等data/dalvik-cache 将 apk 解压出的 dex 文件复制到此目录
最新回复(0)