From f23226136ca221e01d346ac8312d63571a2cbebe Mon Sep 17 00:00:00 2001 From: Joanne Chung <joannechung@google.com> Date: Thu, 23 Nov 2023 12:00:26 +0000 Subject: [PATCH] Add APIs for optional uses-sdk-library We introduce "optional" attr for uses-sdk-library, add two APIs for this new attr. Bug: 295827951 Test: atest PackageManagerShellCommandInstallTest for the flag/enable case Test: atest PackageManagerSettingsTests Change-Id: I607a006f680f4f0b3704871b400b9849434a4b86 --- core/api/current.txt | 1 + core/api/module-lib-current.txt | 1 + .../android/content/pm/ApplicationInfo.java | 37 ++++++ .../android/content/pm/SharedLibraryInfo.java | 82 ++++++++++++- .../com/android/server/pm/ComputerEngine.java | 8 +- .../server/pm/parsing/PackageInfoUtils.java | 30 ++++- .../pm/PackageManagerSettingsTests.java | 110 ++++++++++++++++++ 7 files changed, 258 insertions(+), 11 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 7f261d450b42..6b7fbff94b94 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -13296,6 +13296,7 @@ package android.content.pm { method @NonNull public java.util.List<android.content.pm.VersionedPackage> getDependentPackages(); method @IntRange(from=0xffffffff) public long getLongVersion(); method public String getName(); + method @FlaggedApi("android.content.pm.sdk_lib_independence") @NonNull public java.util.List<android.content.pm.VersionedPackage> getOptionalDependentPackages(); method public int getType(); method @Deprecated @IntRange(from=0xffffffff) public int getVersion(); method public void writeToParcel(android.os.Parcel, int); diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index e7803fbf011d..d395b8cf936e 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -136,6 +136,7 @@ package android.content { package android.content.pm { public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { + method @FlaggedApi("android.content.pm.sdk_lib_independence") @NonNull public java.util.List<android.content.pm.SharedLibraryInfo> getOptionalSharedLibraryInfos(); method @NonNull public java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraryInfos(); field public static final int HIDDEN_API_ENFORCEMENT_DEFAULT = -1; // 0xffffffff field public static final int HIDDEN_API_ENFORCEMENT_DISABLED = 0; // 0x0 diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 16a80e93326a..3713380485ea 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -19,6 +19,7 @@ package android.content.pm; import static android.os.Build.VERSION_CODES.DONUT; import android.Manifest; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1065,10 +1066,24 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving * the structure. * + * NOTE: the list also contains the result of {@link #getOptionalSharedLibraryInfos}. + * * {@hide} */ + @Nullable public List<SharedLibraryInfo> sharedLibraryInfos; + /** + * List of all shared libraries this application is optionally linked against. + * This field is only set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES + * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving + * the structure. + * + * @hide + */ + @Nullable + public List<SharedLibraryInfo> optionalSharedLibraryInfos; + /** * Full path to the default directory assigned to the package for its * persistent data. @@ -1937,6 +1952,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { seInfoUser = orig.seInfoUser; sharedLibraryFiles = orig.sharedLibraryFiles; sharedLibraryInfos = orig.sharedLibraryInfos; + optionalSharedLibraryInfos = orig.optionalSharedLibraryInfos; dataDir = orig.dataDir; deviceProtectedDataDir = orig.deviceProtectedDataDir; credentialProtectedDataDir = orig.credentialProtectedDataDir; @@ -2029,6 +2045,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeString8(seInfoUser); dest.writeString8Array(sharedLibraryFiles); dest.writeTypedList(sharedLibraryInfos); + dest.writeTypedList(optionalSharedLibraryInfos); dest.writeString8(dataDir); dest.writeString8(deviceProtectedDataDir); dest.writeString8(credentialProtectedDataDir); @@ -2129,6 +2146,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { seInfoUser = source.readString8(); sharedLibraryFiles = source.createString8Array(); sharedLibraryInfos = source.createTypedArrayList(SharedLibraryInfo.CREATOR); + optionalSharedLibraryInfos = source.createTypedArrayList(SharedLibraryInfo.CREATOR); dataDir = source.readString8(); deviceProtectedDataDir = source.readString8(); credentialProtectedDataDir = source.readString8(); @@ -2760,6 +2778,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * list will only be set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving the structure. * + * NOTE: the list also contains the result of {@link #getOptionalSharedLibraryInfos}. + * * @hide */ @NonNull @@ -2771,6 +2791,23 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return sharedLibraryInfos; } + /** + * List of all shared libraries this application is optionally linked against. This + * list will only be set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES + * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving the structure. + * + * @hide + */ + @NonNull + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @FlaggedApi(Flags.FLAG_SDK_LIB_INDEPENDENCE) + public List<SharedLibraryInfo> getOptionalSharedLibraryInfos() { + if (optionalSharedLibraryInfos == null) { + return Collections.EMPTY_LIST; + } + return optionalSharedLibraryInfos; + } + /** * Gets the trusted host certificate digests of apps that are allowed to embed activities of * this application. The digests are computed using the SHA-256 digest algorithm. diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java index 25ba72551b04..5acebf54a159 100644 --- a/core/java/android/content/pm/SharedLibraryInfo.java +++ b/core/java/android/content/pm/SharedLibraryInfo.java @@ -16,6 +16,7 @@ package android.content.pm; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -23,6 +24,7 @@ import android.annotation.Nullable; import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; +import android.util.Pair; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -99,8 +101,41 @@ public final class SharedLibraryInfo implements Parcelable { private final boolean mIsNative; private final VersionedPackage mDeclaringPackage; private final List<VersionedPackage> mDependentPackages; + + private final List<VersionedPackage> mOptionalDependentPackages; private List<SharedLibraryInfo> mDependencies; + /** + * Creates a new instance. + * + * @param codePaths For a non {@link #TYPE_BUILTIN builtin} library, the locations of + * jars of + * this shared library. Null for builtin library. + * @param name The lib name. + * @param version The lib version if not builtin. + * @param type The lib type. + * @param declaringPackage The package that declares the library. + * @param dependentPackages The packages that depend on the library. + * @param isNative indicate if this shared lib is a native lib or not (i.e. java) + * @hide + */ + public SharedLibraryInfo(String path, String packageName, List<String> codePaths, + String name, long version, int type, + VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages, + List<SharedLibraryInfo> dependencies, boolean isNative) { + mPath = path; + mPackageName = packageName; + mCodePaths = codePaths; + mName = name; + mVersion = version; + mType = type; + mDeclaringPackage = declaringPackage; + mDependentPackages = dependentPackages; + mDependencies = dependencies; + mIsNative = isNative; + mOptionalDependentPackages = null; + } + /** * Creates a new instance. * @@ -110,15 +145,17 @@ public final class SharedLibraryInfo implements Parcelable { * @param version The lib version if not builtin. * @param type The lib type. * @param declaringPackage The package that declares the library. - * @param dependentPackages The packages that depend on the library. * @param isNative indicate if this shared lib is a native lib or not (i.e. java) + * @param allDependentPackages All packages that depend on the library (including the optional + * sdk libraries). * * @hide */ public SharedLibraryInfo(String path, String packageName, List<String> codePaths, String name, long version, int type, - VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages, - List<SharedLibraryInfo> dependencies, boolean isNative) { + VersionedPackage declaringPackage, + List<SharedLibraryInfo> dependencies, boolean isNative, + Pair<List<VersionedPackage>, List<Boolean>> allDependentPackages) { mPath = path; mPackageName = packageName; mCodePaths = codePaths; @@ -126,9 +163,28 @@ public final class SharedLibraryInfo implements Parcelable { mVersion = version; mType = type; mDeclaringPackage = declaringPackage; - mDependentPackages = dependentPackages; mDependencies = dependencies; mIsNative = isNative; + + var allDependents = allDependentPackages.first; + var usesLibOptional = allDependentPackages.second; + mDependentPackages = allDependents; + List<VersionedPackage> optionalDependents = null; + if (mType == SharedLibraryInfo.TYPE_SDK_PACKAGE + && Flags.sdkLibIndependence() && allDependents != null + && usesLibOptional != null + && allDependents.size() == usesLibOptional.size()) { + for (int k = 0; k < allDependents.size(); k++) { + VersionedPackage versionedPackage = allDependents.get(k); + if (usesLibOptional.get(k)) { + if (optionalDependents == null) { + optionalDependents = new ArrayList<>(); + } + optionalDependents.add(versionedPackage); + } + } + } + mOptionalDependentPackages = optionalDependents; } private SharedLibraryInfo(Parcel parcel) { @@ -148,6 +204,8 @@ public final class SharedLibraryInfo implements Parcelable { parcel.readArrayList(null, android.content.pm.VersionedPackage.class); mDependencies = parcel.createTypedArrayList(SharedLibraryInfo.CREATOR); mIsNative = parcel.readBoolean(); + mOptionalDependentPackages = parcel.readParcelableList(new ArrayList<>(), + VersionedPackage.class.getClassLoader(), VersionedPackage.class); } /** @@ -324,6 +382,8 @@ public final class SharedLibraryInfo implements Parcelable { /** * Gets the packages that depend on the library. * + * NOTE: the list also contains the result of {@link #getOptionalDependentPackages}. + * * @return The dependent packages. */ public @NonNull List<VersionedPackage> getDependentPackages() { @@ -333,6 +393,19 @@ public final class SharedLibraryInfo implements Parcelable { return mDependentPackages; } + /** + * Gets the packages that optionally depend on the library. + * + * @return The dependent packages. + */ + @FlaggedApi(Flags.FLAG_SDK_LIB_INDEPENDENCE) + public @NonNull List<VersionedPackage> getOptionalDependentPackages() { + if (mOptionalDependentPackages == null) { + return Collections.emptyList(); + } + return mOptionalDependentPackages; + } + @Override public int describeContents() { return 0; @@ -362,6 +435,7 @@ public final class SharedLibraryInfo implements Parcelable { parcel.writeList(mDependentPackages); parcel.writeTypedList(mDependencies); parcel.writeBoolean(mIsNative); + parcel.writeParcelableList(mOptionalDependentPackages, flags); } private static String typeToString(int type) { diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index abfd5715810e..744c94661067 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -3864,19 +3864,15 @@ public class ComputerEngine implements Computer { } finally { Binder.restoreCallingIdentity(identity); } - - var usingSharedLibraryPair = - getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId); SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getPath(), libInfo.getPackageName(), libInfo.getAllCodePaths(), libInfo.getName(), libInfo.getLongVersion(), libInfo.getType(), declaringPackage, - usingSharedLibraryPair.first, (libInfo.getDependencies() == null ? null : new ArrayList<>(libInfo.getDependencies())), - libInfo.isNative()); - + libInfo.isNative(), + getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId)); if (result == null) { result = new ArrayList<>(); } diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java index fa54f0ba18cd..d0fe9647618a 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -28,6 +28,7 @@ import android.content.pm.ConfigurationInfo; import android.content.pm.FallbackCategoryProvider; import android.content.pm.FeatureGroupInfo; import android.content.pm.FeatureInfo; +import android.content.pm.Flags; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageItemInfo; @@ -474,7 +475,34 @@ public class PackageInfoUtils { } info.sharedLibraryFiles = usesLibraryFiles.isEmpty() ? null : usesLibraryFiles.toArray(new String[0]); - info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos; + + + if (!Flags.sdkLibIndependence()) { + info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos; + info.optionalSharedLibraryInfos = null; + } else { + // sharedLibraryInfos contains all shared libraries that the app depends on (including + // the optional sdk libraries) + info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos; + String[] libsNames = pkgSetting.getUsesSdkLibraries(); + boolean[] libsOptional = pkgSetting.getUsesSdkLibrariesOptional(); + List<SharedLibraryInfo> optionalSdkLibraries = null; + if (!ArrayUtils.isEmpty(libsOptional) && !ArrayUtils.isEmpty(libsNames) + && libsNames.length == libsOptional.length) { + for (SharedLibraryInfo info1 : usesLibraryInfos) { + if (info1.getType() == SharedLibraryInfo.TYPE_SDK_PACKAGE) { + int index = ArrayUtils.indexOf(libsNames, info1.getName()); + if (index >= 0 && libsOptional[index]) { + if (optionalSdkLibraries == null) { + optionalSdkLibraries = new ArrayList<>(); + } + optionalSdkLibraries.add(info1); + } + } + } + } + info.optionalSharedLibraryInfos = optionalSdkLibraries; + } if (info.category == ApplicationInfo.CATEGORY_UNDEFINED) { info.category = pkgSetting.getCategoryOverride(); } diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java index b396cf498a67..3d0b389aa171 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -45,7 +45,9 @@ import android.annotation.NonNull; import android.app.PropertyInvalidatedCache; import android.content.ComponentName; import android.content.pm.ApplicationInfo; +import android.content.pm.Flags; import android.content.pm.PackageManager; +import android.content.pm.SharedLibraryInfo; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; import android.os.BaseBundle; @@ -54,6 +56,7 @@ import android.os.PersistableBundle; import android.os.Process; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; @@ -67,6 +70,7 @@ import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.permission.persistence.RuntimePermissionsPersistence; import com.android.server.LocalServices; +import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.permission.LegacyPermissionDataProvider; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.ArchiveState; @@ -85,6 +89,7 @@ import com.google.common.truth.Truth; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -109,6 +114,9 @@ import java.util.concurrent.CountDownLatch; @RunWith(AndroidJUnit4.class) @SmallTest public class PackageManagerSettingsTests { + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private static final String PACKAGE_NAME_1 = "com.android.app1"; private static final String PACKAGE_NAME_2 = "com.android.app2"; private static final String PACKAGE_NAME_3 = "com.android.app3"; @@ -140,6 +148,7 @@ public class PackageManagerSettingsTests { public void setup() { // Disable binder caches in this process. PropertyInvalidatedCache.disableForTestMode(); + } @Before @@ -161,6 +170,107 @@ public class PackageManagerSettingsTests { deleteFolder(InstrumentationRegistry.getContext().getFilesDir()); } + @Test + public void testApplicationInfoForUseSdkOptionalEnabled() throws Exception { + mSetFlagsRule.enableFlags(Flags.FLAG_SDK_LIB_INDEPENDENCE); + + // Create basic information for SDK lib + final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); + ps1.setPkg(((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed()) + .setUid(ps1.getAppId()) + .setSystem(true) + .hideAsFinal()); + ps1.setUsesSdkLibraries(new String[] { "com.example.sdk.one" }); + ps1.setUsesSdkLibrariesVersionsMajor(new long[] { 12 }); + ps1.setUsesSdkLibrariesOptional(new boolean[] {true}); + ps1.addUsesLibraryInfo(new SharedLibraryInfo("path1", + "packageName1", + Collections.emptyList(), + "com.example.sdk.one", + 12 /*version*/, + SharedLibraryInfo.TYPE_SDK_PACKAGE, + null /*declaringPackage*/, + null /*dependentPackages*/, + null /*dependencies*/, + false /*isNative*/)); + ps1.addUsesLibraryInfo(new SharedLibraryInfo("path11", + "packageName11", + Collections.emptyList(), + "com.example.sdk.oneone", + 1212 /*version*/, + SharedLibraryInfo.TYPE_STATIC, + null /*declaringPackage*/, + null /*dependentPackages*/, + null /*dependencies*/, + false /*isNative*/)); + ApplicationInfo appInfo1 = PackageInfoUtils.generateApplicationInfo(ps1.getAndroidPackage(), + 0 /*flags*/, ps1.getUserStateOrDefault(0), 0 /*userId*/, + ps1); + assertThat(appInfo1, notNullValue()); + assertThat(appInfo1.sharedLibraryInfos, notNullValue()); + assertThat(appInfo1.optionalSharedLibraryInfos, notNullValue()); + assertEquals(appInfo1.sharedLibraryInfos.get(0).getName(), "com.example.sdk.one"); + assertEquals(appInfo1.optionalSharedLibraryInfos.get(0).getName(), "com.example.sdk.one"); + + final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2); + ps2.setPkg(((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_2).hideAsParsed()) + .setUid(ps2.getAppId()) + .setSystem(true) + .hideAsFinal()); + ps2.setUsesSdkLibraries(new String[] { "com.example.sdk.two" }); + ps2.setUsesSdkLibrariesVersionsMajor(new long[] { 34 }); + ps2.setUsesSdkLibrariesOptional(new boolean[] {false}); + ps2.addUsesLibraryInfo(new SharedLibraryInfo("path2", + "packageName2", + Collections.emptyList(), + "com.example.sdk.two", + 34 /*version*/, + SharedLibraryInfo.TYPE_SDK_PACKAGE, + null /*declaringPackage*/, + null /*dependentPackages*/, + null /*dependencies*/, + false /*isNative*/)); + ApplicationInfo appInfo2 = PackageInfoUtils.generateApplicationInfo(ps2.getAndroidPackage(), + 0 /*flags*/, ps2.getUserStateOrDefault(0), 0 /*userId*/, + ps2); + assertThat(appInfo2, notNullValue()); + assertThat(appInfo2.sharedLibraryInfos, notNullValue()); + assertThat(appInfo2.optionalSharedLibraryInfos, nullValue()); + assertEquals(appInfo2.sharedLibraryInfos.get(0).getName(), "com.example.sdk.two"); + } + + @Test + public void testApplicationInfoForUseSdkOptionalDisabled() throws Exception { + mSetFlagsRule.disableFlags(Flags.FLAG_SDK_LIB_INDEPENDENCE); + + // Create basic information for SDK lib + final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); + ps1.setPkg(((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed()) + .setUid(ps1.getAppId()) + .setSystem(true) + .hideAsFinal()); + ps1.setUsesSdkLibraries(new String[] { "com.example.sdk.one" }); + ps1.setUsesSdkLibrariesVersionsMajor(new long[] { 12 }); + ps1.setUsesSdkLibrariesOptional(new boolean[] {true}); + ps1.addUsesLibraryInfo(new SharedLibraryInfo("path1", + "packageName1", + Collections.emptyList(), + "com.example.sdk.one", + 12 /*version*/, + SharedLibraryInfo.TYPE_SDK_PACKAGE, + null /*declaringPackage*/, + null /*dependentPackages*/, + null /*dependencies*/, + false /*isNative*/)); + ApplicationInfo appInfo1 = PackageInfoUtils.generateApplicationInfo(ps1.getAndroidPackage(), + 0 /*flags*/, ps1.getUserStateOrDefault(0), 0 /*userId*/, + ps1); + assertThat(appInfo1, notNullValue()); + assertThat(appInfo1.sharedLibraryInfos, notNullValue()); + assertThat(appInfo1.optionalSharedLibraryInfos, nullValue()); + assertEquals(appInfo1.sharedLibraryInfos.get(0).getName(), "com.example.sdk.one"); + } + /** make sure our initialized KeySetManagerService metadata matches packages.xml */ @Test public void testReadKeySetSettings() throws Exception { -- GitLab