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提供了大量方法供我们查询系统配置。如getAvailableFeatures
、getPermissionAllowlist
等方法。
//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类的initSystemApps
、initNonSystemApps
方法进行扫描。
//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最核心的系统服务之一,它是应用程序包管理服务,管理手机上所有的应用程序,包括应用程序的安装、卸载、更新、应用信息的查询、应用程序的禁用和启用等。 职责 在Android系统启动过程中扫…...
基于大模型的智能客服搭建
引言:智能客服的范式转变 在数字经济浪潮中,客户服务正经历从"人力密集型"向"技术驱动型"的深刻转型。据IDC最新报告,全球智能客服市场规模预计将在2028年突破1200亿美元,年复合增长率达28.6%。这种增长背后…...
Vagrant+VMWare 安装Ubuntu24.04
背景介绍 对于众多 Windows 用户来说, 有时候需要用到 Linux 环境做一些开发或者测试. WSL 目前能覆盖到很大一部分使用场景, 但是仍然有一些场景需要用虚拟机才能解决. 开发者的痛点往往是对于虚拟机环境的配置和管理, 因为手动安装需要很长的时间, 并且每次安装完成之后需要…...
数字化转型 - 数据驱动
数字化转型 一、 数据驱动1.1 监控1.2 分析1.3 挖掘1.4 赋能 二、数据驱动案例2.1 能源工业互联网:绿色节能的数字化路径2.2 光伏产业的数字化升级2.3 数据中心的绿色转型2.4云迁移的质效优化2.5 企业数字化运营的实践2.6数字化转型的最佳实践 一、 数据驱动 从数…...
【软考-架构】11.3、设计模式-新
✨资料&文章更新✨ GitHub地址:https://github.com/tyronczt/system_architect 文章目录 项目中的应用设计模式创建型设计模式结构型设计模式行为型设计模式 💯考试真题题外话 项目中的应用 在实际项目中,我应用过多种设计模式来解决不同…...
leetcode0031 下一个排列-medium
1 题目: 下一个排列 官方标定难度:中等 整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。 例如,arr [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。 整数数组的 下一…...
CmBacktrace的cmb_cfg.h
一:宏定义解析 #ifndef _CMB_CFG_H_ #define _CMB_CFG_H_#ifdef CMB_USER_CFG #include "cmb_user_cfg.h" #else /* print line, must config by user */ #define cmb_println(...) /* e.g., printf(__VA_ARGS__);printf("\r\n"…...
Redis监控:从睁眼瞎到千里眼的进化史
各位在Redis迷雾中摸黑的探险家们!今天我们要给Redis装上"天眼系统"——从连自己内存爆了都不知道的睁眼瞎,进化到连每秒哪个键被摸了几次都门儿清的监控狂魔!准备好迎接《Redisの楚门世界》了吗?👁️ 第一幕…...
mac利用“自动操作”扩展添加 Mac 访达右键菜单项
用惯了 Windows 的资源管理器,换到 Mac 的访达,最不习惯的就是不能通过右键菜单创建文件。 虽然 Mac 的 App Store 中有几个可以增加访达右键菜单的工具,但是居然都要收费(就这么一点点活都能卖钱,真是了不起…...
算法013——水果成篮
水果成篮(点击即可跳转) 这道题其实就是找到一个最长的子数组的长度,子数组中不超过两种类型的水果。 定义两个指针 left 与 right ,固定 left , 当 right 走到一个位置时,left 与 right 之间的种类 kind 2 , 当 ri…...
TCP/IP协议栈----通俗易懂(与OSI七层模型区别)
目录 一、概念 二、模型 对比 编辑(1)OSI的七层模型 (2)TCP/IP的四层模型 三、TCP/IP模型分层 (1)应用层 (2)传输层 (3)网络层 (4&…...
python中print函数的flush如何使用
在 Python 中,print 函数的 flush 参数是一个布尔值,默认值为 False。当设置为 True 时,它会强制将输出缓冲区的内容立即刷新到目标设备(通常是控制台),而不是等待缓冲区满或者程序结束时才输出。 要注意fl…...
python中有几种作用域
在 Python 中,作用域决定了变量的可见性和生命周期。Python 主要有以下四种作用域: 1. 局部作用域(Local Scope): - 在函数或方法内部定义的变量属于局部作用域。 - 这些变量只能在函数或方法内部访问。 def my_f…...
DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加列宽调整功能,示例Table14_13可展开行的固定表头表格
前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加列宽调整功能,示例Table14_13可展开行的固…...
【Java 优选算法】分治 - 快速排序
欢迎关注个人主页:逸狼 创造不易,可以点点赞吗~ 如有错误,欢迎指出~ 分治算法就是将一个问题划分为多个相同类型的子问题,解决这些子问题即解决该类问题 颜色分类 题目链接 解法 利用三指针, i, left, right,将数组分为4个区间,如下图 …...
Kafka相关的面试题
以下是150道Kafka相关的面试题及简洁回答: Kafka基础概念 1. 什么是Kafka? Kafka是一个分布式、可扩展、容错的发布-订阅消息系统,最初由LinkedIn开发,现为Apache项目。它适用于高吞吐量的场景,如大数据处理和实时数据…...
Java基础面经
Java 基础 面试官:重写与重载的区别? 重载:发生在同一个类中,若多个方法之间方法名相同、参数列表不同,则它们构成重载的关系。重载与方法的返回值以及访问修饰符无关,即重载的方法不能根据返回类型进行…...
Let’s Build AI- 实用AI导航网站
Let’s Build AI Let’s Build AI是一个在线实用AI导航网站,由社区驱动的平台,致力于为 AI 爱好者和开发人员共享资源、工具和知识等等,通过GitHub编辑内容更新,目前包括数据库、模型、开发者工具、ChatGPT提示、图像生成、模型开…...
Spring Boot集成EasyExcel
1. 初始化Spring Boot项目 首先,使用Spring Initializr(https://start.spring.io/)生成一个基本的Spring Boot项目。选择以下依赖项: Spring WebLombok (用于减少样板代码)SLF4J (用于日志记录) 2. 添加依赖 在你的pom.xml文件…...
2024年12月CCF-GESP编程能力等级认证C++编程六级真题解析
CCF-GESP C++六级真题难度与考察范围深度解析 考试定位与整体难度 CCF-GESP C++六级认证属于高阶编程能力考核,难度显著高于五级,接近信息学竞赛提高组水平,重点考察复杂算法设计、面向对象编程(OOP)深度应用及高级数据结构实现能力。试题要求考生具备将数学建模与算法优化…...
网络VLAN技术详解:原理、类型与实战配置
网络VLAN技术详解:原理、类型与实战配置 1. 什么是VLAN? VLAN(Virtual Local Area Network,虚拟局域网) 是一种通过逻辑划分而非物理连接隔离网络设备的技术。它允许管理员将同一物理网络中的设备划分为多个独立的广播…...
深入探讨RAID 5的性能与容错能力:实验与分析(磁盘阵列)
前言—— 本实验旨在探讨 RAID 5 的性能和容错能力。通过创建 RAID 5 阵列并进行一系列读写性能测试及故障模拟,我们将观察 RAID 5 在数据冗余和故障恢复方面的表现,以验证其在实际应用中的可靠性和效率。 首先说明:最少三块硬盘, 使用 4 块…...
如何让ai问答机器人通人性?
领域专用的问答机器人,数据是灵魂。通用模型的问题在于,它们虽然知识广博,但对特定领域的深度理解不足。解决这个问题的第一步,就是构建一个高质量的领域知识库。 数据要精准且全面 想让机器人真正“懂”一个领域,数…...
最新版Chrome浏览器加载ActiveX控件技术--allWebPlugin中间件一键部署浏览器扩展
allWebPlugin简介 allWebPlugin中间件是一款为用户提供安全、可靠、便捷的浏览器插件服务的中间件产品,致力于将浏览器插件重新应用到所有浏览器。它将现有ActiveX控件直接嵌入浏览器,实现插件加载、界面显示、接口调用、事件回调等。支持Chrome、Firefo…...
重生之我在学Vue--第11天 Vue 3 高级特性
重生之我在学Vue–第11天 Vue 3 高级特性 文章目录 重生之我在学Vue--第11天 Vue 3 高级特性前言一、Teleport:打破组件层级的瞬移术1. 什么是Teleport?2. 核心用法3. 实战技巧 二、Suspense:异步组件的优雅过渡1. 为什么需要Suspense&#x…...
汽车无钥匙启动系统不使用传统机械钥匙启动汽车
汽车无钥匙启动系统 定义 汽车无钥匙启动系统(Keyless Start System),启动车辆时不用掏拧钥匙,只需把钥匙放在包内或口袋里,按下车内按键或拧动导板即可使发动机点火。它无需插入钥匙,通过点按按键或旋转…...
平安养老险深圳分公司积极开展2025年“3·15”金融消费者权益保护教育宣传活动
为深刻把握金融工作的政治性、人民性,帮助社会公众增强维护自身合法权益的意识和能力,平安养老险深圳分公司在2025年3月7日至3月15日期间,以“保障金融权益,助力美好生活”为口号,聚焦“维护权益”主题,全面…...
python 实现 A* 算法
A*算法是一种广泛使用的路径搜索算法,结合了启发式搜索和Dijkstra算法的优点。它通过评估每个节点的代价函数 ( f(n) g(n) h(n) ) 来选择最优路径,其中: ( g(n) ) 是从起点到当前节点的实际代价。( h(n) ) 是从当前节点到目标节点的启发式…...
MyBatis 如何创建 SqlSession 对象的?
MyBatis 创建 SqlSession 对象的过程主要由 SqlSessionFactory 接口及其实现类来完成。以下是详细步骤: 1. SqlSessionFactory 接口: SqlSessionFactory 是 MyBatis 的核心接口之一,它负责创建 SqlSession 对象。 你可以将 SqlSessionFactory 视为 Sql…...
微服务》》四个问题
客户端如何访问 API 网关 如 Core中 Ocelot技术 服务如何治理 服务注册与发现 如 Core中 的 consul技术 服务挂了怎么办 可以利用 重试机制、限流、熔断、降级等 服务之间通信问题 》》同步 1. Http 对外 跨防火墙 【 序列化、反序列化 2 ( 因为http是应用层…...
CockroachDB MCP -cursor适用
CockroachDB MCP 服务器 GitHub仓库置顶 这是一个用于 Cursor 的 CockroachDB MCP 服务器,基于 Model Context Protocol (MCP) 规范实现,可以让你在 Cursor 中直接与 CockroachDB 数据库交互。 功能 连接到 CockroachDB 数据库获取数据库中的所有表获…...
GOC学习
for(int i1;i<5;i){//这里的所有语句都会被执行 5 次 } int main(){pen.a(200,16,1,0).a(200,-16,1,0);pen.rt(16).fd(200).bk(200);pen.lt(32).fd(200).bk(200);///pen.rt(-32).fd(200).bk(200);for(int i1;i<5;i){pen.a(200,16,1,0).a(200,-16,1,0);pen.rt(16).fd(200)…...
【机器学习】基于t-SNE的MNIST数据集可视化探索
一、前言 在机器学习和数据科学领域,高维数据的可视化是一个极具挑战但又至关重要的问题。高维数据难以直观地理解和分析,而有效的可视化方法能够帮助我们发现数据中的潜在结构、模式和关系。本文以经典的MNIST手写数字数据集为例,探讨如何利…...
Vscode工具开发Vue+ts项目时vue文件ts语法报错-红波浪线等
Vscode工具开发Vuets项目时vue文件ts语法报错-红波浪线等 解决方案 问题如题描述,主要原因是开发工具使用的代码检查与项目的中的ts不一致导导致,解决办法,修改 vscode 中, 快捷键:command shift p, 输入ÿ…...
Python在数据处理中的应用:从入门到精通
活动发起人小虚竹 想对你说: 这是一个以写作博客为目的的创作活动,旨在鼓励大学生博主们挖掘自己的创作潜能,展现自己的写作才华。如果你是一位热爱写作的、想要展现自己创作才华的小伙伴,那么,快来参加吧!…...
vue3实现跨页面缓存
避免频繁向后端发送请求,vue3中,可以用缓存机制,为了实现跨页面缓存,可以把缓存放到localsotrage里面 关键代码: const globalCache JSON.parse(localStorage.getItem(globalCache)) || {}; 然后加一个forceRefresh关键字, const fetchData async (forceRefresh false) …...
YOLO优化之多信息融合MIF
设计背景 在目标检测领域,随着深度学习技术的不断进步,研究者们一直在寻求提高模型性能和效率的方法。其中, 多模态数据融合 作为一种有效的策略,近年来受到了广泛关注。多模态融合旨在将来自不同传感器或模态的数据进行整合,以提供更全面、丰富的信息供模型学习和推断。…...
人工智能与人的智能,改变一生的思维模型【8】逆向思维
逆向偏差思维模型:顶尖高手如何「反常识」破局 (斯坦福决策科学中心认证的逆向思考框架) 一、直击本质:什么是逆向偏差思维? 定义: 逆向偏差思维是一种主动对抗本能认知倾向的决策模式,通过系…...
Python 科学计算与机器学习入门:NumPy + Scikit-Learn 实战指南
Langchain系列文章目录 01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…...
深入理解 Qt 系统托盘图标:创建自定义的系统托盘图标类
文章目录 深入理解 Qt 系统托盘图标:创建自定义的系统托盘图标类1. 什么是 QSystemTrayIcon?2. 自定义系统托盘图标类:SysTraylcon3. 代码解析1. **类的定义**2. **构造函数:SysTraylcon::SysTraylcon(QWidget *parent)**3. **ini…...
DeepSeek-R1 面试 -—— GRPO
DeepSeek训练中应用的GRPO算法,它源自于强化学习领域的PPO算法。GRPO与PPO算法之间存在哪些差异?这两种算法各自的优劣何在?为何DeepSeek选择采用GRPO算法而非PPO算法?本文将对这些问题提供解答。 一、PPO算法 PPO(Pr…...
AI作曲DiffRhythm原理及本地部署
1.原理简介 最近AI在音乐生成方面的进展引起了极大的关注,但现有的方法面临着严重的限制。一些当前的生成模型只能合成人声或伴奏轨道。虽然一些模型可以生成组合的人声和伴奏,但它们通常依赖于精心设计的多阶段级联架构和复杂的数据管道,阻…...
农业电商|基于SprinBoot+vue的农业电商服务系统(源码+数据库+文档)
农业电商服务系统 目录 基于SprinBootvue的农业电商服务系统 一、前言 二、系统设计 三、系统功能设计 5.1系统功能实现 5.2后台模块实现 5.2.1管理员模块实现 5.2.2商家模块实现 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码…...
深度学习有哪些算法?
深度学习包含多种算法和模型,广泛应用于图像处理、自然语言处理、语音识别等领域。以下是主要分类及代表性算法: 一、基础神经网络 多层感知机(MLP) 最简单的深度学习模型,由多个全连接层组成,用于分类和回…...
鸿蒙开发-一多开发之媒体查询功能
在HarmonyOS中,使用ArkTS语法实现响应式布局的媒体查询是一个强大的功能,它允许开发者根据不同的设备特征(如屏幕尺寸、屏幕方向等)动态地调整UI布局和样式。以下是一个使用媒体查询实现响应式布局的实例: 1. 导入必要…...
历年华中科技大学计算机考研复试上机真题
历年华中科技大学计算机考研复试上机真题 2022华中科技大学计算机考研复试上机真题 2021华中科技大学计算机考研复试上机真题 2019华中科技大学计算机考研复试上机真题 在线评测:https://pgcode.cn 八进制 题目描述 输入一个整数,将其转换成八进制数…...
卷积神经网络(CNN)的主要架构
卷积神经网络(CNN, Convolutional Neural Networks)是深度学习中最重要的模型之一,广泛应用于计算机视觉、目标检测、语义分割等任务。自 LeNet 诞生以来,CNN 结构经历了多个重要发展阶段,出现了许多经典架构ÿ…...
IDEA:项目结构不见了,项目文件消失解决
IDEA:看不见目录结构了。 1、确认项目是否仍存在:检查项目文件夹是否仍然存在于磁盘上。如果项目文件夹被删除或移动了,您需要将其还原或重新导入到IDEA中。 2、重新导入项目:如果项目文件存在,在 IDEA 中找到项目文…...
基于ssm的宠物医院信息管理系统(全套)
一、系统架构 前端:html | layui | vue | element-ui 后端:spring | springmvc | mybatis 环境:jdk1.8 | mysql | maven | tomcat | idea | nodejs 二、代码及数据库 三、功能介绍 01. web端-首页1 02. web端-首页…...
How to install a package in offline scenario in Ubuntu 24.04
概述 做过信创项目的兄弟们在工作上每天可能面对很多需要解决的问题,不过,有一类问题可能是大家经常遇的,比方说,有时候我们不得不硬着头皮在离线生产环境中安装某些软件包,相信很多兄弟被这种细碎的小事搞得焦头烂额…...