PackageManagerService

news2025/3/17 12:48:15

首语

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 {
    @Override
    public 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);
    }
    //启动InstalldNativeService
    if ((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());
        //创建PMS
        PackageManagerService 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注册到ServiceManager
        ServiceManager.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 migration
        mStoppedPackagesFilename = 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 system
        readPermissions(parser, Environment.buildPath(
                Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);

        // Read configuration from the old permissions dir
        readPermissions(parser, Environment.buildPath(
                Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
        // Vendors are only allowed to customize these
        int 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 compatibility
            vendorPermissionFlag |= (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 {
    @Override
    public 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();
    //添加系统UID
    mSettings.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 maxSdkVersion
                parseFlags |= 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 parallel
        int fileCount = 0;
        for (File file : files) {
            final boolean isPackage = (isApkFile(file) || file.isDirectory())
                    && !PackageInstallerService.isStageName(file.getName());
            if (!isPackage) {
                // Ignore entries which are not packages
                continue;
            }
            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 one
        for (; 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-through
                case "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 mPackages
            mPm.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
@Override
    public 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的抽象方法进行具体实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2316626.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【蓝桥杯每日一题】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;哈哈。 …

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"&…

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 - 给你一个由小写…

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的命…

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等概念。机器学习基础熟悉监督/无监督学习、分类、聚类…

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;路径上…

Python学习第十八天

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

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

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

langchain框架

LangChain的架构分为多个层次&#xff0c;支持Python和JavaScript生态 基础层&#xff08;langchain-core&#xff09;&#xff1a;提供LLM抽象接口、表达式语言&#xff08;LCEL&#xff09;等核心机制&#xff0c;支持超过70种主流模型&#xff08;如GPT-4、Llama&#xff0…