PackageManagerService分析(Android 10)->original-package机制分析

    科技2024-05-07  95

    <!-- Private tag to declare the original package name that this package is based on. Only used for packages installed in the system image. If given, and different than the actual package name, and the given original package was previously installed on the device but the new one was not, then the data for the old one will be renamed to be for the new package. <p>This appears as a child tag of the root {@link #AndroidManifest manifest} tag. --> <declare-styleable name="AndroidManifestOriginalPackage" parent="AndroidManifest"> <attr name="name" /> </declare-styleable>

    官方的解释很清楚了:之前安装的应用是系统应用,并且包名不同,之前应用的数据就会以新安装应用的名字保留下来。

    也就是新的应用代码,使用旧的数据。(新包名的应用程序代码使用旧包名的数据)。另外还有一个特点,该机制只能通过ota生效。

    所以在分析该问题之前我们提出一个疑问:

    如何指向旧数据新包名?

    针对上述问题我们来分析代码。

    我们采用自顶向下的分析方法:

    在应用启动最开始的部分会调用ActivityThread 的bindApplication方法, 签名如下

    public final void bindApplication(String processName, ApplicationInfo appInfo, List<ProviderInfo> providers, ComponentName instrumentationName, ProfilerInfo profilerInfo, Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, IUiAutomationConnection instrumentationUiConnection, int debugMode, boolean enableBinderTracking, boolean trackAllocation, boolean isRestrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map services, Bundle coreSettings, String buildSerial, AutofillOptions autofillOptions, ContentCaptureOptions contentCaptureOptions)

    这里面重要的参数ApplicationInfo 描述了应用的代码和数据位置。

    public class ApplicationInfo extends PackageItemInfo implements Parcelable { ...... /** * Full path to the base APK for this application. */ public String sourceDir; /** * Full path to the publicly available parts of {@link #sourceDir}, * including resources and manifest. This may be different from * {@link #sourceDir} if an application is forward locked. */ public String publicSourceDir; /** * The names of all installed split APKs, ordered lexicographically. */ public String[] splitNames; /** * Full paths to zero or more split APKs, indexed by the same order as {@link #splitNames}. */ public String[] splitSourceDirs; /** * Full path to the publicly available parts of {@link #splitSourceDirs}, * including resources and manifest. This may be different from * {@link #splitSourceDirs} if an application is forward locked. * * @see #splitSourceDirs */ public String[] splitPublicSourceDirs; /** * Maps the dependencies between split APKs. All splits implicitly depend on the base APK. * * Available since platform version O. * * Only populated if the application opts in to isolated split loading via the * {@link android.R.attr.isolatedSplits} attribute in the &lt;manifest&gt; tag of the app's * AndroidManifest.xml. * * The keys and values are all indices into the {@link #splitNames}, {@link #splitSourceDirs}, * and {@link #splitPublicSourceDirs} arrays. * Each key represents a split and its value is an array of splits. The first element of this * array is the parent split, and the rest are configuration splits. These configuration splits * have no dependencies themselves. * Cycles do not exist because they are illegal and screened for during installation. * * May be null if no splits are installed, or if no dependencies exist between them. * * NOTE: Any change to the way split dependencies are stored must update the logic that * creates the class loader context for dexopt (DexoptUtils#getClassLoaderContexts). * * @hide */ public SparseArray<int[]> splitDependencies; /** * Full paths to the locations of extra resource packages (runtime overlays) * this application uses. This field is only used if there are extra resource * packages, otherwise it is null. * * {@hide} */ @UnsupportedAppUsage public String[] resourceDirs; /** * Full path to the default directory assigned to the package for its * persistent data. */ public String dataDir; /** * Full path to the device-protected directory assigned to the package for * its persistent data. * * @see Context#createDeviceProtectedStorageContext() */ public String deviceProtectedDataDir; /** * Full path to the credential-protected directory assigned to the package * for its persistent data. * * @hide */ @SystemApi public String credentialProtectedDataDir; /** * Full path to the directory where native JNI libraries are stored. */ public String nativeLibraryDir; /** * Full path where unpacked native libraries for {@link #secondaryCpuAbi} * are stored, if present. * * The main reason this exists is for bundled multi-arch apps, where * it's not trivial to calculate the location of libs for the secondary abi * given the location of the primary. * * TODO: Change the layout of bundled installs so that we can use * nativeLibraryRootDir & nativeLibraryRootRequiresIsa there as well. * (e.g {@code [ "/system/app-lib/Foo/arm", "/system/app-lib/Foo/arm64" ]} * instead of {@code [ "/system/lib/Foo", "/system/lib64/Foo" ]}. * * @hide */ @UnsupportedAppUsage public String secondaryNativeLibraryDir; /** * The root path where unpacked native libraries are stored. * <p> * When {@link #nativeLibraryRootRequiresIsa} is set, the libraries are * placed in ISA-specific subdirectories under this path, otherwise the * libraries are placed directly at this path. * * @hide */ @UnsupportedAppUsage public String nativeLibraryRootDir;

    这里面描述数据的变量有deviceProtectedDataDir, credentialProtectedDataDir, dataDir , 分别代表de(fde),ce(fce)数据和当前数据(data指向ce或者de目录)。其他的都为资源和代码路径。

    有了这部分知识我们可以猜想在original-package机制下,代码指向新包名路径,数据指向旧包名路径。

    数据路径的设置在ApplicationInfo类中

    /** {@hide} */ public void initForUser(int userId) { uid = UserHandle.getUid(userId, UserHandle.getAppId(uid)); if ("android".equals(packageName)) {// system_server 进程的数据路径为/data/system dataDir = Environment.getDataSystemDirectory().getAbsolutePath(); return; } deviceProtectedDataDir = Environment .getDataUserDePackageDirectory(volumeUuid, userId, packageName) .getAbsolutePath(); credentialProtectedDataDir = Environment .getDataUserCePackageDirectory(volumeUuid, userId, packageName) .getAbsolutePath(); if ((privateFlags & PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) != 0 && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) { dataDir = deviceProtectedDataDir; } else { dataDir = credentialProtectedDataDir; } } de 目录为/data/user_de/user_de/${userid}/${packageName} 或者 /mnt/expand/${volumeUuid}/user_de/${userid}/${packageName} 。ce目录为/data/user_de/user/${userid}/${packageName} 或者 /mnt/expand/${volumeUuid}/user/${userid}/${packageName} 。dataDir 目录根据标志指向ce或者de。

    ApplicationInfo的packageName成员变量应该指向旧的packageName, 也就是original-package 指向的包名(后面我们通过代码验证)。那会不会加载错误的apk?看下codepath的设置。

    PackageManagerService private PackageParser.Package addForInitLI(PackageParser.Package pkg, @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) pkg.setApplicationVolumeUuid(pkg.volumeUuid); pkg.setApplicationInfoCodePath(pkg.codePath); pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath); pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths); pkg.setApplicationInfoResourcePath(pkg.codePath); pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath); pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths); FileInstallArgs | MoveInstallArgs boolean doRename(int status, PackageParser.Package pkg) pkg.setApplicationVolumeUuid(pkg.volumeUuid); pkg.setApplicationInfoCodePath(pkg.codePath); pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath); pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths); pkg.setApplicationInfoResourcePath(pkg.codePath); pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath); pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);

    在PackageManagerService的 addForInitLI 和 FileInstallArgs 以及 MoveInstallArgs中都有设置, addForInitLI 代表系统启动过程中对已安装应用的扫描过程。 FileInstallArgs 表示正常下载安装应用的过程, MoveInstallArgs则表示应用移动位置的过程。 设置的代码路径都是一直的,都是用过PackageParser.Package来设置的,而PackageParser.Package的代码路径则是创建PackageParser.Package解析的应用文件路径,所以这个路径并不依赖包名获得,所以original-package是不会影响代码和资源的加载。

    这解决了我们提出的问题(如何指向旧数据新包名?),这里我们知道了应用的ApplicationInfo指向了旧的包名。

    另外发现original-package机制在Android 新版本的重构中已经被破坏,具体提交号码为0f877fab392154c77251dbac321b732a2a747911, 新版本Android PackageManagerService变得比较混乱。

    Processed: 0.016, SQL: 8