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