当前位置: 首页 > article >正文

PackageManagerService

首语

PackageManagerService(以下简称PMS)是Android最核心的系统服务之一,它是应用程序包管理服务,管理手机上所有的应用程序,包括应用程序的安装、卸载、更新、应用信息的查询、应用程序的禁用和启用等。

职责
  • 在Android系统启动过程中扫描特定目录下的APK文件解析。
  • 负责三方应用的安装、卸载及各个应用的更新升级。
  • 对外提供接口查询应用包信息或管理应用。

安装目录

系统应用安装目录
  • system/app。这里存放的是系统自带的应用。
  • system/priv-app。同上,但这里的应用会被授予特权权限,属于特权应用。
  • vendor/app。存放的是odm或oem厂商定制的预制应用。
  • vendor/app。同上,存放的也是特权应用。

这些应用不仅存放APK,还存放了APK运行需要的lib文件。这些路径下的应用不可卸载,会被授予系统级别的权限,调用系统级别的接口。

普通应用安装目录
  • data/app。存放普通应用,即常说的三方应用。可以卸载。只能获得normal和dangerous级别的权限,这里的应用可以内置,也可以后续安装,安装时APK会被拷贝到此目录下,包括APK运行需要依赖的lib文件。
可执行文件保存目录
  • data/dalvik-cache。应用程序安装的时候会将APK中的可执行文件拷贝到此目录下(dex文件是dalvik虚拟机的可执行文件,当然ART-Android Runtime的可执行文件格式是oat,启用ART时,系统会执行dex文件转换至oat文件)。
应用数据目录
  • data/data。存放应用程序的相关数据,不论是三方应用还是系统应用。
应用注册表目录
  • data/system。该目录下有两个重要的文件,一个是packages.xml,一个是packages.list,类似Windows的注册表。packages.xml记录了应用的各种信息,如应用包名、版本、安装路径、声明的各项权限等。packages.list记录了应用包名、userId、用户数据所在路径、SEInfo(应用程序安全上下文)、安装该应用的安装者等信息。当记录的应用信息有变化时将更新这些文件。下次开机时直接从里面读取相关信息添加到内存相关列表中,优化开机启动时间。

应用安装步骤

  • PMS先扫描特定目录下的APK文件或拷贝APK文件至/data/app下,再对APK文件进行解析并生成对应的数据结构。
  • PMS再将包名以及应用相关的四大组件等解析到的应用信息注册到PMS的记录文件中。

应用安装目的

PMS安装目的为了获取APK信息,然后进行缓存,PMS共有两级缓存:

  • 一级缓存是在内存,是一个WatchedArrayMap,安装、卸载应用及开机会更新里面的数据。
//frameworks/base/services/core/java/com/android/server/pm/Settings.java
final WatchedArrayMap<String, PackageSetting> mPackages;
  • 二级缓存是作为文件存储在/data/system/中,主要信息存储在packages.xml中。

PMS扫描安装

系统应用和预置应用的安装,是Android开机启动后,PMS通过扫描系统中的特定目录,寻找里面的APK文件,对这些APK文件进行解析,得到应用的相关信息,再将其添加到各种数据结构中并注册到PMS的记录文件中,完成应用安装。

启动

PMS和其它系统服务一样,都是SystemServer进程启动的。SystemServer的startBootstrapServices方法用于启动引导服务,在启动PMS之前还启动了相关的两个服务,Installer和DomainVerificationService(系统服务,用于访问域验证API)。

public final class SystemServer implements Dumpable {...private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {Installer installer = mSystemServiceManager.startService(Installer.class);DomainVerificationService domainVerificationService = new DomainVerificationService(mSystemContext, SystemConfig.getInstance(), platformCompat);mSystemServiceManager.startService(domainVerificationService);try {Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain");mPackageManagerService = PackageManagerService.main(mSystemContext, installer, domainVerificationService,mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF);} finally {Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");}mFirstBoot = mPackageManagerService.isFirstBoot();mPackageManager = mSystemContext.getPackageManager();}private void startOtherServices(@NonNull TimingsTraceAndSlog t) {...try {Watchdog.getInstance().pauseWatchingCurrentThread("dexopt");//处理dex优化mPackageManagerService.updatePackagesIfNeeded();} catch (Throwable e) {reportWtf("update packages", e);} finally {Watchdog.getInstance().resumeWatchingCurrentThread("dexopt");}...try {//完成磁盘维护mPackageManagerService.performFstrimIfNeeded();} catch (Throwable e) {reportWtf("performing fstrim", e);}//PMS准备就绪mPackageManagerService.systemReady();...//等待APP数据mPackageManagerService.waitForAppDataPrepared();}
}
Installd服务启动

Installer服务是SystemServiceManager的startService方法启动的,分析其它服务时我们清楚,最终调用的是Service的onStart方法。connect方法去连接installd服务,installd服务是Android Native层的服务进程,由init进程孵化,拥有root权限,主要是对系统中的文件进行读写执行管理。我们知道PMS运行在SystemServer进程,它是以system用户身份运行,system用户并没有访问应用程序目录的权限,而在文件系统中创建和删除文件需要root权限,而installd服务是具有的,因此创建和删除文件、dex优化等操作委托给installd处理。那么installd是如何启动的呢?它和init进程一样,通过installd.rc脚本文件启动,init.rc解析并创建一个名为installd的可执行文件,存储路径位于/system/bin/installd。

//frameworks/base/services/core/java/com/android/server/pm/Installer.java
public class Installer extends SystemService {@Overridepublic void onStart() {if (mIsolated) {mInstalld = null;mInstalldLatch.countDown();} else {connect();}}private void connect() {//installd的binder接口IBinder binder = ServiceManager.getService("installd");if (binder != null) {try {//注册Binder的死亡回调binder.linkToDeath(() -> {Slog.w(TAG, "installd died; reconnecting");mInstalldLatch = new CountDownLatch(1);//重连connect();}, 0);} catch (RemoteException e) {binder = null;}}if (binder != null) {//获取installd的binder对象IInstalld installd = IInstalld.Stub.asInterface(binder);mInstalld = installd;mInstalldLatch.countDown();try {invalidateMounts();executeDeferredActions();} catch (InstallerException ignored) {}} else {//如果installd还没有ready,则不停的等待1s直到Binder不为空Slog.w(TAG, "installd not found; trying again");BackgroundThread.getHandler().postDelayed(this::connect, CONNECT_RETRY_DELAY_MS);}}
}

它从class名为main,同一个class的所有的service必须同时启动或停止,installd的入口函数在installd.cpp中。

installd的入口函数指向了installd_main,它主要完成一些系统目录的初始化工作,比如初始化保存data和system分区的一些目录,像存放用户安装应用的/data/app/,存放系统应用的/system/app/,存放系统特权应用的/system/pri-app等,也会创建系统主用户/data/misc/user/0文件夹,除主用户外(data/user/0)外,其余用户都会在/data/user/目录下创建一个属于自己的文件夹,据此创建/data/misc/user/user_id目录。

最重要的是启动InstalldNativeService,启动InstalldNativeService继承BinderService,start方法中BinderService<InstalldNativeService>::publish()将InstalldNativeService添加到native层的ServiceManager中。InstalldNativeService.h中可以看出其服务名为installd。因此可执行文件installd启动了InstalldNativeService作为Binder的服务端,其它进程就可以通过ServiceManager获取到名为installd的服务,实现跨进程调用。SystemServer启动Installer就是为了连接installd服务.

//frameworks/native/cmds/installd/installd.cpp
namespace android {
namespace installd {
static int installd_main(const int argc ATTRIBUTE_UNUSED, char *argv[]) {int ret;int selinux_enabled = (is_selinux_enabled() > 0);setenv("ANDROID_LOG_TAGS", "*:v", 1);android::base::InitLogging(argv);SLOGI("installd firing up");union selinux_callback cb;cb.func_log = log_callback;selinux_set_callback(SELINUX_CB_LOG, cb);if (!initialize_globals()) {SLOGE("Could not initialize globals; exiting.\n");exit(1);}if (initialize_directories() < 0) {SLOGE("Could not create directories; exiting.\n");exit(1);}if (selinux_enabled && selinux_status_open(true) < 0) {SLOGE("Could not open selinux status; exiting.\n");exit(1);}//启动InstalldNativeServiceif ((ret = InstalldNativeService::start()) != android::OK) {SLOGE("Unable to start InstalldNativeService: %d", ret);exit(1);}IPCThreadState::self()->joinThreadPool();LOG(INFO) << "installd shutting down";return 0;
}
}  // namespace installd
}  // namespace android
int main(const int argc, char *argv[]) {return android::installd::installd_main(argc, argv);
}
//frameworks/native/cmds/installd/InstalldNativeService.cpp
status_t InstalldNativeService::start() {IPCThreadState::self()->disableBackgroundScheduling(true);status_t ret = BinderService<InstalldNativeService>::publish();if (ret != android::OK) {return ret;}sp<ProcessState> ps(ProcessState::self());ps->startThreadPool();ps->giveThreadPoolName();sAppDataIsolationEnabled = android::base::GetBoolProperty(kAppDataIsolationEnabledProperty, true);return android::OK;
}
//frameworks/native/cmds/installd/InstalldNativeService.h
class InstalldNativeService : public BinderService<InstalldNativeService>, public os::BnInstalld {static status_t start();//服务名static char const* getServiceName() { return "installd"; }
PMS启动

Installd服务启动完成后,PMS开始调用main方法启动PMS,首先实例化PackageManagerServiceInjector,其中初始化各种实例化对象,例如PermissionManagerService、Settings、SystemConfig等,也创建了处理应用安装卸载的工作线程。

//frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public class PackageManagerService implements PackageSender, TestUtilityService {public static PackageManagerService main(Context context,Installer installer, @NonNull DomainVerificationService domainVerificationService,boolean factoryTest) {PackageManagerServiceInjector injector = new PackageManagerServiceInjector(context, lock, installer, installLock, new PackageAbiHelperImpl(),backgroundHandler,SYSTEM_PARTITIONS,(i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mUserNeedsBadging),(i, pm) -> PermissionManagerService.create(context,i.getSystemConfig().getAvailableFeatures()),(i, pm) -> new UserManagerService(context, pm,new UserDataPreparer(installer, installLock, context), lock),(i, pm) -> new Settings(Environment.getDataDirectory(),RuntimePermissionsPersistence.createInstance(),i.getPermissionManagerServiceInternal(),domainVerificationService, backgroundHandler,lock),(i, pm) -> AppsFilterImpl.create(i,i.getLocalService(PackageManagerInternal.class)),(i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"),(i, pm) -> SystemConfig.getInstance(),(i, pm) -> new PackageDexOptimizer(i.getInstaller(), i.getInstallLock(),i.getContext(), "*dexopt*"),(i, pm) -> new DexManager(i.getContext(), i.getPackageDexOptimizer(),i.getInstaller(), i.getInstallLock(), i.getDynamicCodeLogger()),(i, pm) -> new DynamicCodeLogger(i.getInstaller()),(i, pm) -> new ArtManagerService(i.getContext(), i.getInstaller(),i.getInstallLock()),(i, pm) -> ApexManager.getInstance(),(i, pm) -> (IncrementalManager)i.getContext().getSystemService(Context.INCREMENTAL_SERVICE),(i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class),() -> LocalServices.getService(UserManagerInternal.class)),(i, pm) -> new DisplayMetrics(),(i, pm) -> new PackageParser2(pm.mSeparateProcesses, i.getDisplayMetrics(),new PackageCacher(pm.mCacheDir, pm.mPackageParserCallback),pm.mPackageParserCallback) /* scanningCachingPackageParserProducer */,(i, pm) -> new PackageParser2(pm.mSeparateProcesses, i.getDisplayMetrics(), null,pm.mPackageParserCallback) /* scanningPackageParserProducer */,(i, pm) -> new PackageParser2(pm.mSeparateProcesses, i.getDisplayMetrics(), null,pm.mPackageParserCallback) /* preparingPackageParserProducer */,(i, pm) -> new PackageInstallerService(i.getContext(), pm, i::getScanningPackageParser),(i, pm, cn) -> new InstantAppResolverConnection(i.getContext(), cn, Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE),(i, pm) -> new ModuleInfoProvider(i.getContext()),(i, pm) -> LegacyPermissionManagerService.create(i.getContext()),(i, pm) -> domainVerificationService,(i, pm) -> {HandlerThread thread = new ServiceThread(TAG,Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);thread.start();return new PackageHandler(thread.getLooper(), pm);},new DefaultSystemWrapper(),LocalServices::getService,context::getSystemService,(i, pm) -> {if (useArtService()) {return null;}try {return new BackgroundDexOptService(i.getContext(), i.getDexManager(), pm);} catch (LegacyDexoptDisabledException e) {throw new RuntimeException(e);}},(i, pm) -> IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE)),(i, pm) -> new SharedLibrariesImpl(pm, i),(i, pm) -> new CrossProfileIntentFilterHelper(i.getSettings(),i.getUserManagerService(), i.getLock(), i.getUserManagerInternal(),context),(i, pm) -> new UpdateOwnershipHelper(),(i, pm) -> new PackageMonitorCallbackHelper());//创建PMSPackageManagerService m = new PackageManagerService(injector, factoryTest,PackagePartitions.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG,Build.VERSION.SDK_INT, Build.VERSION.INCREMENTAL);...m.installAllowlistedSystemPackages();IPackageManagerImpl iPackageManager = m.new IPackageManagerImpl();//package service注册到ServiceManagerServiceManager.addService("package", iPackageManager);final PackageManagerNative pmn = new PackageManagerNative(m);ServiceManager.addService("package_native", pmn);return m;}    
}
Settings

Settings的构造方法中首先创建了data/system 目录及该目录下的package相关文件。

  • package.xml。记录所有应用的安装信息。当系统进行安装、卸载和更新操作时,均会更新此文件。该文件保存了系统中与package相关的一些信息。

将该文件pull到本地进行查看,如果乱码请使用如下命令:

adb shell setprop persist.sys.binary_xml false

文件部分内容如下:这个文件非常庞大,包含许多标签,如permission标签,包含了系统所有定义的权限信息,package标签包含所有安装的应用信息,shared-user标签描述共享用户ID的信息。domain-verifications标签包含域验证信息。

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<packages><version sdkVersion="35" databaseVersion="3" buildFingerprint="" fingerprint="" /><version volumeUuid="primary_physical" sdkVersion="35" databaseVersion="3" buildFingerprint="" fingerprint="" /><permission-trees></permission-trees><permissions><item name="android.permission.INTERNET" package="android" protection="4096" />...</permissions><package name="com.android.settings" codePath="/system_ext/priv-app/Settings" nativeLibraryPath="/system_ext/priv-app/Settings/lib" primaryCpuAbi="arm64-v8a" publicFlags="944488005" privateFlags="-1938812568" ft="" ut="" version="35" targetSdkVersion="35" scannedAsStoppedSystemApp="false" sharedUserId="1000" packageSource="0" isOrphaned="true" loadingProgress="1.0" loadingCompletedTime="0" domainSetId="" appMetadataSource="0"><sigs count="1" schemeVersion="3"><cert index="7" /></sigs><proper-signing-keyset identifier="1" /></package><shared-user name="android.uid.system" userId="1000"><sigs count="1" schemeVersion="3"><cert index="7" /></sigs></shared-user><domain-verifications><active><package-state packageName="com.android.settings" id="bded5a7a-00c4-4dfc-a944-7992e8831784" /></active></domain-verifications>
  • package-backup.xml。package.xml的备份文件。开机完成读取后,会删除该文件。
  • package.list。记录应用数据信息。当应用信息有变动时,PMS就会更新此文件。

将该文件pull到本地进行查看,Settings类的writePackageListLPrInternal方法对各项进行了说明。

第一项是包名,第二个是安装应用的userid,一般是在AndroidManifest.xml中通过android:sharedUserId="android.uid.system",第三项是安装应用是否处于调试模式,也是通过AndroidManifest.xml中通过android:debuggable指定,默认为0,非调试模式,第四项是应用数据存放路径,第五项是Selinux信息,Platform app,第六项是应用所属user group,不属于任何group,则为0。第七项是安装应用是否可以由shell配置,由AndroidManifest.xml中的Profileable指定。第八项是安装应用的版本号,android:versionCode指定,第九项表示安装该应用的包,@System表示赋予FLAG_SYSTEM标志,赋予系统权限,@Product表示赋予PRIVATE_FLAG_PRODUCT标志,有安装源会被打印。

com.android.settings 1000 0 /data/user_de/0/com.android.settings platform:privapp:targetSdkVersion=28:partition=system_ext 1065,3002,3003,3001,3007,2901,1007 0 35 1 @system
//frameworks/base/services/core/java/com/android/server/pm/Settings.java
public final class Settings implements Watchable, Snappable, ResilientAtomicFile.ReadEventLogger {Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence,LegacyPermissionDataProvider permissionDataProvider,@NonNull DomainVerificationManagerInternal domainVerificationManager,@NonNull Handler handler,@NonNull PackageManagerTracedLock lock)  {...mSystemDir = new File(dataDir, "system");mSystemDir.mkdirs();FileUtils.setPermissions(mSystemDir.toString(),FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH|FileUtils.S_IXOTH,-1, -1);mSettingsFilename = new File(mSystemDir, "packages.xml");mSettingsReserveCopyFilename = new File(mSystemDir, "packages.xml.reservecopy");mPreviousSettingsFilename = new File(mSystemDir, "packages-backup.xml");mPackageListFilename = new File(mSystemDir, "packages.list");FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);final File kernelDir = new File("/config/sdcardfs");mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;// Deprecated: Needed for migrationmStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");mDomainVerificationManager = domainVerificationManager;registerObservers();Watchable.verifyWatchedAttributes(this, mObserver);mSnapshot = makeCache();}   private void writePackageListLPrInternal(int creatingUserId) {// we store on each line the following information for now://// pkgName    - package name// userId     - application-specific user id// debugFlag  - 0 or 1 if the package is debuggable.// dataPath   - path to package's data path// seinfo     - seinfo label for the app (assigned at install time)// gids       - supplementary gids this app launches with// profileableFromShellFlag  - 0 or 1 if the package is profileable from shell.// longVersionCode - integer version of the package.// profileable - 0 or 1 if the package is profileable by the platform.// packageInstaller - the package that installed this app, or @system, @product or//                    @null.//// NOTE: We prefer not to expose all ApplicationInfo flags for now.//// DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS// FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES://   system/core/libpackagelistparser...}
}
SystemConfig

SystemConfig的初始化是通过单例形式获取实例,其构造方法中主要通过readAllPermissions方法将/system、/vendor、/odm、/oem、/product、/system_ext的/etc/sysconfig和/etc/permissions的xml定义的各个节点读取出来保存到SystemConfig的成员变量中,其中xml涉及到的标签有feature、library、permission、assign-permission等,这些标签的内容都将被解析出来保存到SystemConfig对应数据结构的全局变量中以方便查询管理,如系统支持的feature。feature标签用来描述设备是否支持的硬件特性,library标签用来指定系统库,应用程序运行时,系统会为进程加载一些必要库,permission标签用于将permission与gid(用户组id)关联,assign-permission标签将system中描述的permission与uid(用户id)关联等等。SystemConfig提供了大量方法供我们查询系统配置。如getAvailableFeaturesgetPermissionAllowlist等方法。

//frameworks/base/services/core/java/com/android/server/SystemConfig.java
public class SystemConfig {public static SystemConfig getInstance() {if (!isSystemProcess()) {Slog.wtf(TAG, "SystemConfig is being accessed by a process other than "+ "system_server.");}synchronized (SystemConfig.class) {if (sInstance == null) {sInstance = new SystemConfig();}return sInstance;}}SystemConfig() {TimingsTraceLog log = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);log.traceBegin("readAllPermissions");try {readAllPermissions();readPublicNativeLibrariesList();} finally {log.traceEnd();}}private void readAllPermissions() {final XmlPullParser parser = Xml.newPullParser();// Read configuration from systemreadPermissions(parser, Environment.buildPath(Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);// Read configuration from the old permissions dirreadPermissions(parser, Environment.buildPath(Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);// Vendors are only allowed to customize theseint vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS| ALLOW_SIGNATURE_PERMISSIONS | ALLOW_ASSOCIATIONS | ALLOW_VENDOR_APEX;if (Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.O_MR1) {// For backward compatibilityvendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS);}readPermissions(parser, Environment.buildPath(Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag);readPermissions(parser, Environment.buildPath(Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag);String vendorSkuProperty = SystemProperties.get(VENDOR_SKU_PROPERTY, "");if (!vendorSkuProperty.isEmpty()) {String vendorSkuDir = "sku_" + vendorSkuProperty;readPermissions(parser, Environment.buildPath(Environment.getVendorDirectory(), "etc", "sysconfig", vendorSkuDir),vendorPermissionFlag);readPermissions(parser, Environment.buildPath(Environment.getVendorDirectory(), "etc", "permissions", vendorSkuDir),vendorPermissionFlag);....}}public PermissionAllowlist getPermissionAllowlist() {return mPermissionAllowlist;}public ArrayMap<String, FeatureInfo> getAvailableFeatures() {return mAvailableFeatures;}
}
HandlerThread

PackageManagerServiceInjector还启动了一个PackageManager的HandlerThread,该线程是PMS的工作线程。PMS的各种操作都将利用返回的PackageHandler分发至该HandlerThread进行处理,以执行系统中所有包的管理及所有组件的查询工作。Message what定义在PMS中。

//frameworks/base/services/core/java/com/android/server/pm/PackageHandler.java
final class PackageHandler extends Handler {@Overridepublic void handleMessage(Message msg) {try {doHandleMessage(msg);} finally {Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);}}void doHandleMessage(Message msg) {switch (msg.what) {case SEND_PENDING_BROADCAST: {mPm.sendPendingBroadcasts();break;}....case WRITE_PACKAGE_LIST: {int userId = msg.arg1;if (!mPm.tryWritePackageList(userId)) {// Failed to write.this.removeMessages(WRITE_PACKAGE_LIST);mPm.scheduleWritePackageList(userId);}} break;}
}
public class PackageManagerService implements PackageSender, TestUtilityService {static final int POST_INSTALL = 9;static final int WRITE_SETTINGS = 13;static final int WRITE_DIRTY_PACKAGE_RESTRICTIONS = 14;static final int PACKAGE_VERIFIED = 15;static final int CHECK_PENDING_VERIFICATION = 16;// public static final int UNUSED = 17;// public static final int UNUSED = 18;static final int WRITE_PACKAGE_LIST = 19;static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20;static final int ENABLE_ROLLBACK_STATUS = 21;static final int ENABLE_ROLLBACK_TIMEOUT = 22;static final int DEFERRED_NO_KILL_POST_DELETE = 23;static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24;static final int INTEGRITY_VERIFICATION_COMPLETE = 25;static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26;static final int DOMAIN_VERIFICATION = 27;static final int PRUNE_UNUSED_STATIC_SHARED_LIBRARIES = 28;static final int DEFERRED_PENDING_KILL_INSTALL_OBSERVER = 29;static final int WRITE_USER_PACKAGE_RESTRICTIONS = 30;
}

初始化

开始阶段

开始阶段首先初始化了一些组件以便交互,接着添加系统UID,共9种系统UID,addSharedUserLPw方法将系统shareUserId的属性值和Process中UID对应起来。

关联的目的是:

  • 两个或多个进程或进程声明同一种shareUserId的APK可以共享彼此数据,并且可以运行在同一进程。
  • 声明特定的shareUserId,该应用赋予特定的UID,同时被赋予UID对应的权限。

接着读取System Config,获取支持的功能。mSettings的readLPw方法解析/data/system目录下的记录文件,获取已经安装的应用信息,存储在Settings对应的成员变量中,当packages.xml和packages-backup.xml都不存在时返回false,由此判定为系统首次开机,赋值给mFirstBoot,通过此值判定是否为首次开机,同时在Installd中设置首次启动。

//frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public PackageManagerService(PackageManagerServiceInjector injector, boolean factoryTest,final String partitionsFingerprint, final boolean isEngBuild,final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,SystemClock.uptimeMillis());...//初始化组件以便交互mPermissionManager = injector.getPermissionManagerServiceInternal();mSettings = injector.getSettings();//添加系统UIDmSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.log", LOG_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.se", SE_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.uwb", UWB_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);...mPackageDexOptimizer = injector.getPackageDexOptimizer();mDexManager = injector.getDexManager();SystemConfig systemConfig = injector.getSystemConfig();mAvailableFeatures = systemConfig.getAvailableFeatures();FallbackCategoryProvider.loadFallbacks();//解析data/system下记录文件mFirstBoot = !mSettings.readLPw(computer,mInjector.getUserManagerInternal().getUsers(/* excludePartial= */ true,/* excludeDying= */ false,/* excludePreCreated= */ false));if (mFirstBoot) {t.traceBegin("setFirstBoot: ");try {mInstaller.setFirstBoot();} catch (InstallerException e) {Slog.w(TAG, "Could not set First Boot: ", e);}t.traceEnd();}//设置自定义的启动意图组件String customResolverActivityName = Resources.getSystem().getString(R.string.config_customResolverActivity);if (!TextUtils.isEmpty(customResolverActivityName)) {mCustomResolverComponentName = ComponentName.unflattenFromString(customResolverActivityName);}
}
//frameworks/base/core/java/android/os/Process.java
public class Process {public static final int INVALID_UID = -1;public static final int ROOT_UID = 0;public static final int SYSTEM_UID = 1000;public static final int PHONE_UID = 1001;public static final int SHELL_UID = 2000;@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)public static final int LOG_UID = 1007;public static final int WIFI_UID = 1010;@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)public static final int MEDIA_UID = 1013;@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)@TestApi@SystemApi(client = MODULE_LIBRARIES)public static final int NFC_UID = 1027;...
}
扫描阶段

扫描阶段根据系统分区不同分为两部分扫描,系统分区和用户分区。系统分区扫描的路径包含/system,/vendor,/odm,/oem,/product,/system_ext等路径,用户分区扫描的是/data/app路径,即内置三方应用路径。

通过InitAppsHelper类的initSystemAppsinitNonSystemApps方法进行扫描。

//frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private final InitAppsHelper mInitAppsHelper;
public PackageManagerService(PackageManagerServiceInjector injector, boolean factoryTest,final String partitionsFingerprint, final boolean isEngBuild,final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {...final int[] userIds = mUserManager.getUserIds();PackageParser2 packageParser = mInjector.getScanningCachingPackageParser();mOverlayConfig = mInitAppsHelper.initSystemApps(packageParser, packageSettings, userIds,startTime);mInitAppsHelper.initNonSystemApps(packageParser, userIds, startTime);packageParser.close();mRequiredVerifierPackages = getRequiredButNotReallyRequiredVerifiersLPr(computer);mRequiredInstallerPackage = getRequiredInstallerLPr(computer);mRequiredUninstallerPackage = getRequiredUninstallerLPr(computer);....
}

从源码可以发现,两部分扫描都调用了scanDirTracedLI方法,最终通过InitAppsHelper类的installPackagesFromDir方法。扫描逻辑都通过PMS的辅助类去处理。

//frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java
private final InstallPackageHelper mInstallPackageHelper;
public OverlayConfig initSystemApps(PackageParser2 packageParser,WatchedArrayMap<String, PackageSetting> packageSettings,int[] userIds, long startTime) {...scanSystemDirs(packageParser, mExecutorService);...
}
private void scanSystemDirs(PackageParser2 packageParser, ExecutorService executorService) {File frameworkDir = new File(Environment.getRootDirectory(), "framework");for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {final ScanPartition partition = mDirsToScanAsSystem.get(i);if (partition.getOverlayFolder() == null) {continue;}scanDirTracedLI(partition.getOverlayFolder(),mSystemParseFlags, mSystemScanFlags | partition.scanFlag,packageParser, executorService, partition.apexInfo);}scanDirTracedLI(frameworkDir,mSystemParseFlags, mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,packageParser, executorService, null);if (!mPm.mPackages.containsKey("android")) {throw new IllegalStateException("Failed to load frameworks package; check log for warnings");}for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {final ScanPartition partition = mDirsToScanAsSystem.get(i);if (partition.getPrivAppFolder() != null) {scanDirTracedLI(partition.getPrivAppFolder(),mSystemParseFlags,mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag,packageParser, executorService, partition.apexInfo);}scanDirTracedLI(partition.getAppFolder(),mSystemParseFlags, mSystemScanFlags | partition.scanFlag,packageParser, executorService, partition.apexInfo);}
}
public void initNonSystemApps(PackageParser2 packageParser, @NonNull int[] userIds,long startTime) {...scanDirTracedLI(mPm.getAppInstallDir(), 0,mScanFlags | SCAN_REQUIRE_KNOWN, packageParser, mExecutorService, null);...
}
private void scanDirTracedLI(File scanDir, int parseFlags, int scanFlags,PackageParser2 packageParser, ExecutorService executorService,@Nullable ApexManager.ActiveApexInfo apexInfo) {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");try {if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {// when scanning apk in apexes, we want to check the maxSdkVersionparseFlags |= PARSE_APK_IN_APEX;}mInstallPackageHelper.installPackagesFromDir(scanDir, parseFlags,scanFlags, packageParser, executorService, apexInfo);} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}
}

PackageParser2对象和线程池封装为ParallelPackageParser对象,ParallelPackageParser是一个应用解析辅助类,调用它的submit方法解析。解析完成后,通过addForInitLI方法将Package中的内容加入到PMS中。

//frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java
public void installPackagesFromDir(File scanDir, int parseFlags,int scanFlags, PackageParser2 packageParser, ExecutorService executorService,@Nullable ApexManager.ActiveApexInfo apexInfo) {inal File[] files = scanDir.listFiles();if (ArrayUtils.isEmpty(files)) {Log.d(TAG, "No files in app dir " + scanDir);return;}if (DEBUG_PACKAGE_SCANNING) {Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags+ " flags=0x" + Integer.toHexString(parseFlags));}ParallelPackageParser parallelPackageParser =new ParallelPackageParser(packageParser, executorService);// Submit files for parsing in parallelint fileCount = 0;for (File file : files) {final boolean isPackage = (isApkFile(file) || file.isDirectory())&& !PackageInstallerService.isStageName(file.getName());if (!isPackage) {// Ignore entries which are not packagescontinue;}if ((scanFlags & SCAN_DROP_CACHE) != 0) {final PackageCacher cacher = new PackageCacher(mPm.getCacheDir(),mPm.mPackageParserCallback);Log.w(TAG, "Dropping cache of " + file.getAbsolutePath());cacher.cleanCachedResult(file);}//解析包parallelPackageParser.submit(file, parseFlags);fileCount++;}// Process results one by onefor (; fileCount > 0; fileCount--) {//通过mQueue取出的解析结果ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();Throwable throwable = parseResult.throwable;int errorCode = PackageManager.INSTALL_SUCCEEDED;String errorMsg = null;if (throwable == null) {try {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "addForInitLI");//解析的应用内容添加到内部数据结构中addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,new UserHandle(UserHandle.USER_SYSTEM), apexInfo);} catch (PackageManagerException e) {errorCode = e.error;errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();Slog.w(TAG, errorMsg);} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}} ...
}
解析阶段

submit方法完成了解析应用包的任务并将结果返回,该方法通过parsePackage方法解析了应用包,然后将扫描路径的APK文件和结果赋值给ParseResult,再将扫描路径的APK文件和结果赋值给ParseResult,再将ParseResult方法队列的mQueue中,方便后续调用。

parsePackage方法调用了PackageParser2类的parsePackage方法进行应用包的解析,最终调用到ParsingPackageUtils类的parsePackage方法。

//frameworks/base/services/core/java/com/android/server/pm/ParallelPackageParser.java
private final PackageParser2 mPackageParser;
public void submit(File scanFile, int parseFlags) {mExecutorService.submit(() -> {ParseResult pr = new ParseResult();Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");try {pr.scanFile = scanFile;pr.parsedPackage = parsePackage(scanFile, parseFlags);} catch (Throwable e) {pr.throwable = e;} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}try {mQueue.put(pr);} catch (InterruptedException e) {Thread.currentThread().interrupt();mInterruptedInThread = Thread.currentThread().getName();}});}protected ParsedPackage parsePackage(File scanFile, int parseFlags)throws PackageManagerException {try {return mPackageParser.parsePackage(scanFile, parseFlags, true);} catch (PackageParserException e) {throw new PackageManagerException(e.error, e.getMessage(), e);}}

这里需要说下APK的拆分机制,Android 5.0以后,支持APK拆分,即一个APK可以拆分成很多部分,位于相同的目录下,每一个部分都是一个单独的APK文件,所有的APK具有相同的签名,在APK解析过程中,会将拆分的APK重新组合成内存中的一个Package。对于一个完整APK,Android称其为Monolithic,对于拆分后的APK,Android称其为Cluster。Android为支持APK拆分,应用目录多出一级,比如Settings 应用目录是System/app/Settings/Settings.apk,如果拆分,拆出的APK都位于这个目录下即可,这样在包解析时,变成以Cluster方式解析。因此Android 5.0之前,都是使用parseMonolithicPackage方法,之后默认都是使用目录的形式,调用parseClusterPackage方法进行解析。

parseBaseApk方法主要对AndroidManifest.xml进行解析,接着调用重载parseBaseApk方法,通过parseBaseApkTags方法解析XML文件。如果是Application标签,调用parseBaseApplication方法进一步解析,否则解析应用的所有tag。

parseBaseApplication方法会解析子标签的内容,如activity、service、receiver、provider标签等,到这里AndroidManifest.xml解析完成,解析结果传回给ParallelPackageParser类的静态内部类ParseResult的成员变量parsePackage,并将该实例放入mQueue中。

//frameworks/base/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags) {if (packageFile.isDirectory()) {return parseClusterPackage(input, packageFile,  flags);} else {return parseMonolithicPackage(input, packageFile, flags);}}private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,int flags) {...try {//对base.apk进行解析final File baseApk = new File(lite.getBaseApkPath());boolean shouldSkipComponents = lite.isIsSdkLibrary() && disallowSdkLibsToBeApps();final ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,lite.getPath(), assetLoader, flags, shouldSkipComponents);if (result.isError()) {return input.error(result);}ParsingPackage pkg = result.getResult();if (!ArrayUtils.isEmpty(lite.getSplitNames())) {pkg.asSplit(lite.getSplitNames(),lite.getSplitApkPaths(),lite.getSplitRevisionCodes(),splitDependencies);//获取split apk数量final int num = lite.getSplitNames().length;for (int i = 0; i < num; i++) {final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);//对split apk解析final ParseResult<ParsingPackage> split =parseSplitApk(input, pkg, i, splitAssets, flags);if (split.isError()) {return input.error(split);}}}pkg.set32BitAbiPreferred(lite.isUse32bitAbi());return input.success(pkg);...}
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,String codePath, SplitAssetLoader assetLoader, int flags,boolean shouldSkipComponents) {...try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,ANDROID_MANIFEST_FILENAME)) {final Resources res = new Resources(assets, mDisplayMetrics, null);ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,parser, flags, shouldSkipComponents);...
}
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,String codePath, Resources res, XmlResourceParser parser, int flags,boolean shouldSkipComponents)throws XmlPullParserException, IOException {....final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);try {final boolean isCoreApp = parser.getAttributeBooleanValue(null /*namespace*/,"coreApp", false);final ParsingPackage pkg = mCallback.startParsingPackage(pkgName, apkPath, codePath, manifestArray, isCoreApp);final ParseResult<ParsingPackage> result =parseBaseApkTags(input, pkg, manifestArray, res, parser, flags,shouldSkipComponents);if (result.isError()) {return result;}....
}
private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,TypedArray sa, Resources res, XmlResourceParser parser, int flags,boolean shouldSkipComponents) throws XmlPullParserException, IOException {...//application标签if (TAG_APPLICATION.equals(tagName)) {if (foundApp) {if (RIGID_PARSER) {result = input.error("<manifest> has more than one <application>");} else {Slog.w(TAG, "<manifest> has more than one <application>");result = input.success(null);}} else {foundApp = true;result = parseBaseApplication(input, pkg, res, parser, flags,shouldSkipComponents);}} else {result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);}
}
private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,boolean shouldSkipComponents) throws XmlPullParserException, IOException {...//解析子标签,四大组件等。switch (tagName) {case "activity":isActivity = true;// fall-throughcase "receiver":if (shouldSkipComponents) {continue;}ParseResult<ParsedActivity> activityResult =ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,res, parser, flags, sUseRoundIcon, null /*defaultSplitName*/,input);if (activityResult.isSuccess()) {ParsedActivity activity = activityResult.getResult();if (isActivity) {hasActivityOrder |= (activity.getOrder() != 0);pkg.addActivity(activity);} else {hasReceiverOrder |= (activity.getOrder() != 0);pkg.addReceiver(activity);}}result = activityResult;break;case "service":if (shouldSkipComponents) {continue;}ParseResult<ParsedService> serviceResult =ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,flags, sUseRoundIcon, null /*defaultSplitName*/,input);if (serviceResult.isSuccess()) {ParsedService service = serviceResult.getResult();hasServiceOrder |= (service.getOrder() != 0);pkg.addService(service);}result = serviceResult;break;case "provider":if (shouldSkipComponents) {continue;}ParseResult<ParsedProvider> providerResult =ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,flags, sUseRoundIcon, null /*defaultSplitName*/,input);if (providerResult.isSuccess()) {pkg.addProvider(providerResult.getResult());}result = providerResult;break;case "activity-alias":if (shouldSkipComponents) {continue;}activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,parser, sUseRoundIcon, null /*defaultSplitName*/,input);if (activityResult.isSuccess()) {ParsedActivity activity = activityResult.getResult();hasActivityOrder |= (activity.getOrder() != 0);pkg.addActivity(activity);}result = activityResult;break;case "apex-system-service":ParseResult<ParsedApexSystemService> systemServiceResult =ParsedApexSystemServiceUtils.parseApexSystemService(res,parser, input);if (systemServiceResult.isSuccess()) {ParsedApexSystemService systemService =systemServiceResult.getResult();pkg.addApexSystemService(systemService);}result = systemServiceResult;break;default:result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);break;}
}
应用信息保存

addForInitLI方法将解析后的Package信息保存到内部数据结构中,同时在此方法中,也进行了安装包的校验、签名检查、apk更新等操作。reconcilePackages方法主要是为了校验签名(verifySignatures),最终生成ReconciledPackage并返回。

commitReconciledScanResultLocked方法调用commitPackageSettings方法,将解析出的相关package组件信息添加到PMS中,相当于将package信息从私有转换到共有,完成此方法后,Package可用于查询、解析等。

//frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java
private AndroidPackage addForInitLI(ParsedPackage parsedPackage,@ParsingPackageUtils.ParseFlags int parseFlags,@PackageManagerService.ScanFlags int scanFlags,@Nullable UserHandle user, @Nullable ApexManager.ActiveApexInfo activeApexInfo)throws PackageManagerException {...try {final String pkgName = scanResult.mPkgSetting.getPackageName();//一致化处理final List<ReconciledPackage> reconcileResult =ReconcilePackageUtils.reconcilePackages(Collections.singletonList(installRequest),mPm.mPackages, Collections.singletonMap(pkgName,mPm.getSettingsVersionForPackage(parsedPackage)),mSharedLibraries, mPm.mSettings.getKeySetManagerService(),mPm.mSettings, mPm.mInjector.getSystemConfig());if ((scanFlags & SCAN_AS_APEX) == 0) {appIdCreated = optimisticallyRegisterAppId(installRequest);} else {installRequest.setScannedPackageSettingAppId(Process.INVALID_UID);}//提交扫描和解析结果commitReconciledScanResultLocked(reconcileResult.get(0),mPm.mUserManager.getUserIds());...
}
private AndroidPackage commitReconciledScanResultLocked(@NonNull ReconciledPackage reconciledPkg, int[] allUsers) {...commitPackageSettings(pkg, pkgSetting, oldPkgSetting, reconciledPkg);...
}
private void commitPackageSettings(@NonNull AndroidPackage pkg,@NonNull PackageSetting pkgSetting, @Nullable PackageSetting oldPkgSetting,ReconciledPackage reconciledPkg) {mPm.mSettings.insertPackageSettingLPw(pkgSetting, pkg);// Add the new setting to mPackagesmPm.mPackages.put(pkg.getPackageName(), pkg);mPm.mComponentResolver.addAllComponents(pkg, chatty, mPm.mSetupWizardPackage, snapshot);mPm.mAppsFilter.addPackage(snapshot, pkgSetting, isReplace,(scanFlags & SCAN_DONT_KILL_APP) != 0 /* retainImplicitGrantOnReplace */);mPm.addAllPackageProperties(pkg);...
}
扫描结束

扫描结束后的任务是读取和更新应用权限授予状态,生成package的应用数据,更新package.xml等文件的信息,完成应用安装。

介绍下生成数据目录的过程,首先SystemServerInitThreadPool线程池提交了一个task,这个方法中首先调用fixupAppData方法,创建/data/user/用户id和/data/data目录,这个目录由StoreManagerService进行管理。每一个App的安装实际路径是/data/user/用户 id/包名。而我们常见到的/data/data/包名的目录的其实是它的软链接。主用户的目录就是/data/user/0。

调用prepareAppDataAndMigrate方法,准备应用数据,最终调用到prepareAppData方法。调用createAppData方法,为每一个应用创建一个cache及code_cache的目录,用于缓存编译优化后的结果。调用prepareAppProfiles方法会调用installd的prepareAppProfile方法,并且调用保存在/system/bin/profman这个程序,在程序目录下生成一个.prof文件,这个文件可以加速dex2oat编译优化的速度。调用prepareAppDataContentsLeafLIF方法,核心就是调用installd的linkNativeLibraryDirectory方法,把应用安装so库的目录/data/data/包名/lib和/data/user/用户id/包名/lib链接上。

//frameworks/base/services/core/java/com/android/server/pm/AppDataHelper.java
public Future<?> fixAppsDataOnBoot() {Future<?> prepareAppDataFuture = SystemServerInitThreadPool.submit(() -> {TimingsTraceLog traceLog = new TimingsTraceLog("SystemServerTimingAsync",Trace.TRACE_TAG_PACKAGE_MANAGER);traceLog.traceBegin("AppDataFixup");try {mInstaller.fixupAppData(StorageManager.UUID_PRIVATE_INTERNAL,StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);} catch (Installer.InstallerException e) {Slog.w(TAG, "Trouble fixing GIDs", e);}...for (String pkgName : deferPackages) {final Computer snapshot = mPm.snapshotComputer();final PackageStateInternal packageStateInternal = snapshot.getPackageStateInternal(pkgName);if (packageStateInternal != null&& packageStateInternal.getUserStateOrDefault(UserHandle.USER_SYSTEM).isInstalled()) {AndroidPackage pkg = packageStateInternal.getPkg();prepareAppDataAndMigrate(batch, pkg,UserHandle.USER_SYSTEM, storageFlags, true /* maybeMigrateAppData */);count++;}}
}
private @NonNull CompletableFuture<?> prepareAppData(@NonNull Installer.Batch batch,@NonNull PackageSetting ps, int previousAppId, int userId, int flags) {...try {createAppDataResult = mInstaller.createAppData(args);...if (pkg != null && (mPm.isDeviceUpgrading() || mPm.isFirstBoot()|| (userId != UserHandle.USER_SYSTEM))) {try {mArtManagerService.prepareAppProfiles(pkg, userId,/* updateReferenceProfileContent= */ false);if (pkg != null) {prepareAppDataContentsLeafLIF(pkg, ps, userId, flags);}
}                                                                       
就绪阶段

PMS的最后阶段就是获取PackageInstallerService对象,GC回收内存。

使用场景

获取PMS代理对象

平常使用的getPackageManager方法是如何调用到PMS对象呢?首先getPackageManager方法的实现在ContextImpl中,通过ActivityThread的getPackageManager获取IPackageManager代理,然后创建ApplicationPackageManager对象并返回。

//frameworks/base/core/java/android/app/ContextImpl.java
@Overridepublic PackageManager getPackageManager() {if (mPackageManager != null) {return mPackageManager;}final 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;}
//frameworks/base/core/java/android/app/ActivityThread.java
public static IPackageManager getPackageManager() {if (sPackageManager != null) {return sPackageManager;}final IBinder b = ServiceManager.getService("package");sPackageManager = IPackageManager.Stub.asInterface(b);return sPackageManager;}
系统预制应用配置

在源码目录下找个位置放入要预置的APK文件,同时新建一个Android.mk文件,一般系统应用在package/app目录下。

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Test
LOCAL_MODULE_TAGS := optional
LOCAL_PACKAGE_NAME := Test
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_SRC_FILES := apk/Test.apk
LOCAL_MODULE_CLASS := APPS
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_PROGUARD_ENABLED := disabled
#内置为可卸载应用
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
#内置为核心应用
LOCAL_PRIVILEGED_MODULE := true
LOCAL_MULTILIB := 32
include $(BUILD_PREBUILT)

在MK文件中,用PRODUCT_PACKAGES变量追加项目名,这样这个应用就预置到系统了。

PRODUCT_PACKAGES += Test

从Android 8.0开始,预置应用时需要在/etc/permissions目录下系统配置文件中授予特许权限,从Android 9.0开始,需要明确授予需要的特许权限,否则设备无法启动。预置到pri-app的应用,如需申请signature|privileged的权限,可在frameworks/base/data/etc/privapp-permissions-platform.xml中授予。

调试命令

adb shell pm list features
adb shell pm list package
adb shell pm list permissions
adb shell pm list libraries
adb shell pm list users
adb shell pm path 包名
adb shell pm dump 包名
adb install apk名
adb uninstall 包名
adb shell dumpsys package 包名

架构

PMS和其它系统服务一样,也是采用Binder进行通信。IPackageManager.aidl由工具转换自动生成binder的服务端IPackageManager.Stub和客户端IPackageManager.Stub.Proxy。

Binder服务端:PMS中提供实现。IPackageManagerBase 继承IPackageManager.Stub,PMS内部IPackageManagerImpl继承IPackageManagerBase。

Binder客户端:ApplicationPackageManager(简称APM)并没有直接参与Binder通信,而是通过IPackageManager成员变量mPM,mPM指向的是一个IPackageManager.Stub.Proxy对象,当调用到mPM的方法时,将会调用到IPackageManager的Proxy代理方法,然后通过Binder机制的mRemote与服务端PMS通信,APM继承PackageManager类,PackageManager是一个抽象类,对IpackageManager接口类中的业务方法进行了封装并对外公开。而APM对PackageManager的抽象方法进行具体实现。

相关文章:

PackageManagerService

首语 PackageManagerService(以下简称PMS)是Android最核心的系统服务之一&#xff0c;它是应用程序包管理服务&#xff0c;管理手机上所有的应用程序&#xff0c;包括应用程序的安装、卸载、更新、应用信息的查询、应用程序的禁用和启用等。 职责 在Android系统启动过程中扫…...

【蓝桥杯每日一题】3.16

&#x1f3dd;️专栏&#xff1a; 【蓝桥杯备篇】 &#x1f305;主页&#xff1a; f狐o狸x 目录 3.9 高精度算法 一、高精度加法 题目链接&#xff1a; 题目描述&#xff1a; 解题思路&#xff1a; 解题代码&#xff1a; 二、高精度减法 题目链接&#xff1a; 题目描述&…...

2.7 滑动窗口专题:串联所有单词的子串

LeetCode 30. 串联所有单词的子串算法对比分析 1. 题目链接 LeetCode 30. 串联所有单词的子串 2. 题目描述 给定一个字符串 s 和一个字符串数组 words&#xff0c;words 中所有单词长度相同。要求找到 s 中所有起始索引&#xff0c;使得从该位置开始的连续子串包含 words 中所…...

电脑实用小工具--VMware常用功能简介

一、创建、编辑虚拟机 1.1 创建新的虚拟机 详见文章新创建虚拟机流程 1.2 编辑虚拟机 创建完成后&#xff0c;点击编辑虚拟机设置&#xff0c;可对虚拟机内存、处理器、硬盘等各再次进行编辑设置。 二、虚拟机开关机 2.1 打开虚拟机 虚拟机创建成功后&#xff0c;点击…...

为训练大模型而努力-分享2W多张卡通头像的图片

最近我一直在研究AI大模型相关的内容&#xff0c;想着从现在开始慢慢收集各种各样的图片&#xff0c;万一以后需要训练大模型的时候可以用到&#xff0c;或者自己以后也许会需要。于是决定慢慢收集这些图片&#xff0c;为未来的学习和训练大模型做一些铺垫&#xff0c;哈哈。 …...

从零开始学习机器人---如何高效学习机械原理

如何高效学习机械原理 1. 理解课程的核心概念2. 结合图形和模型学习3. 掌握公式和计算方法4. 理论与实践相结合5. 总结和复习6. 保持好奇心和探索精神 总结 机械原理是一门理论性和实践性都很强的课程&#xff0c;涉及到机械系统的运动、动力传递、机构设计等内容。快速学习机械…...

JVM 垃圾回收器的选择

一&#xff1a;jvm性能指标吞吐量以及用户停顿时间解释。 二&#xff1a;垃圾回收器的选择。 三&#xff1a;垃圾回收器在jvm中的配置。 四&#xff1a;jvm中常用的gc算法。 一&#xff1a;jvm性能指标吞吐量以及用户停顿时间解释。 在 JVM 调优和垃圾回收器选择中&#xff0…...

使用GPTQ量化Llama-3-8B大模型

使用GPTQ量化8B生成式语言模型 服务器配置&#xff1a;4*3090 描述&#xff1a;使用四张3090&#xff0c;分别进行单卡量化&#xff0c;多卡量化。并使用SGLang部署量化后的模型&#xff0c;使用GPTQ量化 原来的模型精度为FP16&#xff0c;量化为4bit 首先下载gptqmodel量化…...

2025-03-16 学习记录--C/C++-PTA 习题4-2 求幂级数展开的部分和

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、题目描述 ⭐️ 习题4-2 求幂级数展开的部分和 已知函数e^x可以展开为幂级数1xx^2/2!x^3/3!⋯x^k/k!⋯。现给定一个实数x&a…...

【C#】Http请求设置接收不安全的证书

在进行HTTP请求时&#xff0c;出现以下报错&#xff0c;可设置接收不安全证书跳过证书验证&#xff0c;建议仅测试环境设置&#xff0c;生产环境可能会造成系统漏洞 /// <summary> /// HttpGet请求方法 /// </summary> /// <param name"requestUrl"&…...

从PDF文件中提取数据

笔记 import pdfplumber # 打开PDF文件 with pdfplumber.open(数学公式.pdf) as pdf:for i in pdf.pages: # 遍历页print(i.extract_text()) # extract_text()方法提取内容print(f---------第{i.page_number}页结束---------)...

【k8s001】K8s架构浅析

Kubernetes 架构浅析 #mermaid-svg-irCZnQUuietSX3Ro {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-irCZnQUuietSX3Ro .error-icon{fill:#552222;}#mermaid-svg-irCZnQUuietSX3Ro .error-text{fill:#552222;stroke…...

NPU、边缘计算与算力都是什么啊?

考虑到灵活性和经济性&#xff0c;公司购置一台边缘计算机&#xff0c;正在尝试将PCGPU的计算机视觉项目转到边缘计算机NPU上。本文简单整理了三个概念&#xff0c;并试图将其做个概要的说明。 一、算力&#xff1a;数字世界的“基础能源” 1.1 算力是什么 **算力&#xff08…...

AP AR

混淆矩阵 真实值正例真实值负例预测值正例TPFP预测值负例FNTN &#xff08;根据阈值预测&#xff09; P精确度计算&#xff1a;TP/(TPFP) R召回率计算&#xff1a;TP/(TPFN) AP 综合考虑P R 根据不同的阈值计算出不同的PR组合&#xff0c; 画出PR曲线&#xff0c;计算曲线…...

Leetcode-1278.Palindrome Partitioning III [C++][Java]

目录 一、题目描述 二、解题思路 【C】 【Java】 Leetcode-1278.Palindrome Partitioning IIIhttps://leetcode.com/problems/palindrome-partitioning-iii/description/1278. 分割回文串 III - 力扣&#xff08;LeetCode&#xff09;1278. 分割回文串 III - 给你一个由小写…...

Java集合 - ArrayList

ArrayList 是 Java 集合框架中最常用的动态数组实现类&#xff0c;位于 java.util 包中。它基于数组实现&#xff0c;支持动态扩容和随机访问。 1. 特点 动态数组&#xff1a;ArrayList 的底层是一个数组&#xff0c;可以根据需要动态扩展容量。 有序&#xff1a;元素按照插入…...

C++特性——智能指针

为什么需要智能指针 对于定义的局部变量&#xff0c;当作用域结束之后&#xff0c;就会自动回收&#xff0c;这没有什么问题。 当时用new delete的时候&#xff0c;就是动态分配对象的时候&#xff0c;如果new了一个变量&#xff0c;但却没有delete&#xff0c;这会造成内存泄…...

ctf web入门知识合集

文章目录 01做题思路02信息泄露及利用robots.txt.git文件泄露dirsearch ctfshow做题记录信息搜集web1web2web3web4web5web6web7web8SVN泄露与 Git泄露的区别web9web10 php的基础概念php的基础语法1. PHP 基本语法结构2. PHP 变量3.输出数据4.数组5.超全局变量6.文件操作 php的命…...

DeepSeek:技术教育领域的AI变革者——从理论到实践的全面解析

一、技术教育为何需要DeepSeek&#xff1f; 在数字化转型的浪潮下&#xff0c;技术教育面临着知识更新快、实践门槛高、个性化需求强三大核心挑战。传统的教学模式难以满足开发者快速掌握前沿技术、构建复杂系统能力的需求。DeepSeek作为国产开源大模型的代表&#xff0c;凭借…...

MySQL-存储过程和自定义函数

存储过程 存储过程&#xff0c;一组预编译的 SQL 语句和流程控制语句&#xff0c;被命名并存储在数据库中。存储过程可以用来封装复杂的数据库操作逻辑&#xff0c;并在需要时进行调用。 使用存储过程 创建存储过程 create procedure 存储过程名() begin存储过程的逻辑代码&…...

图——表示与遍历

图的两种主要表示方法 图有两种常用的表示方法&#xff0c;一种是邻接表法&#xff08;adjacency-list&#xff09;&#xff0c;另一种是邻接矩阵法&#xff08;adjacency-matrix&#xff09;。 邻接表法储存数据更紧凑&#xff0c;适合稀疏的图&#xff08;sparse graphs&am…...

新手村:数据预处理-异常值检测方法

机器学习中异常值检测方法 一、前置条件 知识领域要求编程基础Python基础&#xff08;变量、循环、函数&#xff09;、Jupyter Notebook或PyCharm使用。统计学基础理解均值、中位数、标准差、四分位数、正态分布、Z-score等概念。机器学习基础熟悉监督/无监督学习、分类、聚类…...

电磁兼容性|电子设备(EMC)测试与系统化整改

在现代电子工程领域&#xff0c;5G通信、物联网与人工智能技术深度融合&#xff0c;电磁兼容性&#xff08;EMC&#xff09;已成为衡量设备可靠性的关键指标。据国际电磁兼容协会&#xff08;IEEE EMC Society&#xff09;2024年度报告显示&#xff0c;全球因EMC问题导致的电子…...

联合体定义与应用

引言 讲到了结构体,那同时类似的结构就还有联合体,本文就将详解介绍联合体。 在C语言中,联合体(union)是一种特殊的数据结构,它与结构体(struct)相似,但有一个显著的不同:联合体的所有成员共用同一块内存空间。这意味着在任何时候,联合体中只能有一个成员保存有效数…...

ChatGPT-4

第一章&#xff1a;ChatGPT-4的技术背景与核心架构 1.1 生成式AI的发展脉络 生成式人工智能&#xff08;Generative AI&#xff09;的演进历程可追溯至20世纪50年代的早期自然语言处理研究。从基于规则的ELIZA系统到统计语言模型&#xff0c;再到深度学习的革命性突破&#x…...

C语言_数据结构总结9:树的基础知识介绍

1. 树的基本术语 - 祖先&#xff1a;考虑结点K&#xff0c;从根A到结点K的唯一路径上的所有其它结点&#xff0c;称为结点K的祖先。 - 子孙&#xff1a;结点B是结点K的祖先&#xff0c;结点K是B的子孙。结点B的子孙包括&#xff1a;E,F,K,L。 - 双亲&#xff1a;路径上…...

基于ydoVr算法的车辆智能防盗系统的设计与实现

标题:基于ydoVr算法的车辆智能防盗系统的设计与实现 内容:1.摘要 随着汽车保有量的不断增加&#xff0c;车辆被盗问题日益严重&#xff0c;给车主带来了巨大的经济损失。为解决这一问题&#xff0c;本文旨在设计并实现基于ydoVr算法的车辆智能防盗系统。该系统综合运用传感器技…...

Python学习第十八天

Django模型 定义&#xff1a;模型是 Django 中用于定义数据库结构的 Python 类。每个模型类对应数据库中的一张表&#xff0c;类的属性对应表的字段。 作用&#xff1a;通过模型&#xff0c;Django 可以将 Python 代码与数据库表结构关联起来&#xff0c;开发者无需直接编写 S…...

蓝桥杯备考:图论之Prim算法

嗯。通过我们前面的学习&#xff0c;我们知道了&#xff0c;一个具有n个顶点的连通图&#xff0c;它的生成树包括n-1个边&#xff0c;如果边多一条就会变成图&#xff0c;少一条就不连通了 接下来我们来学一下把图变成生成树的一个算法 Prim算法&#xff0c;我们从任意一个结…...

min_element用法

查找字典中的最小value值 auto max_it std::min_element(my_map.begin(), my_map.end(),[](const auto& a, const auto& b) {return a.second < b.second; // 查找最小值});其中&#xff0c;这是一个查找最小值的自定义函数 [](const auto& a, const auto&am…...