Fix: PackageInfo for apex has wrong info

Currently, ApexInfo.packageName is directly fed into PackageInfo. However,
this is wrong because ApexInfo.packageName is from apex_manifest.json,
not AndroidManifest.xml. Name in apex_manifest.json is the internal ID
of an APEX while the name in AndroidManifest.xml is the public
externally-visible name.

Fixing the issue by parsing the AndroidManifest.xml in the apex file
via PackageParser.

This change also fixes the problem that signature is not included in the
PackageInfo for APEX. It is included when GET_SIGNATURES is specified.

Bug: 123029986
Bug: 123028426
Test: adb shell cmd package list packages --apex-only --show-versioncode
Shows com.google.* names

Change-Id: I0021b302f3f4e843f79dd6d40716cefd531c5628
(cherry picked from commit 86f5652341403084a57fb7f1e51d1299c763c4f8)
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index b8d7889..33d8da7 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -18,7 +18,6 @@
 
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
-import android.apex.ApexInfo;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -573,15 +572,6 @@
         }
     }
 
-    /**
-     * @hide
-     */
-    public PackageInfo(ApexInfo apexInfo) {
-        packageName = apexInfo.packageName;
-        setLongVersionCode(apexInfo.versionCode);
-        isApex = true;
-    }
-
     private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) {
         if (components != null) {
             for (ComponentInfo ci : components) {
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index dbf3574..486edf0 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -8484,4 +8484,42 @@
             this.error = error;
         }
     }
+
+    public static PackageInfo generatePackageInfoFromApex(File apexFile, boolean collectCerts)
+            throws PackageParserException {
+        PackageInfo pi = new PackageInfo();
+        // TODO(b/123052859): We should avoid these repeated calls to parseApkLite each time
+        // we want to generate information for APEX modules.
+        PackageParser.ApkLite apk = PackageParser.parseApkLite(apexFile,
+            collectCerts ? PackageParser.PARSE_COLLECT_CERTIFICATES : 0);
+
+        pi.packageName = apk.packageName;
+        pi.setLongVersionCode(apk.getLongVersionCode());
+
+        if (collectCerts) {
+            if (apk.signingDetails.hasPastSigningCertificates()) {
+                // Package has included signing certificate rotation information.  Return
+                // the oldest cert so that programmatic checks keep working even if unaware
+                // of key rotation.
+                pi.signatures = new Signature[1];
+                pi.signatures[0] = apk.signingDetails.pastSigningCertificates[0];
+            } else if (apk.signingDetails.hasSignatures()) {
+                // otherwise keep old behavior
+                int numberOfSigs = apk.signingDetails.signatures.length;
+                pi.signatures = new Signature[numberOfSigs];
+                System.arraycopy(apk.signingDetails.signatures, 0, pi.signatures, 0,
+                    numberOfSigs);
+            }
+
+            if (apk.signingDetails != SigningDetails.UNKNOWN) {
+                // only return a valid SigningInfo if there is signing information to report
+                pi.signingInfo = new SigningInfo(apk.signingDetails);
+            } else {
+                pi.signingInfo = null;
+            }
+        }
+
+        pi.isApex = true;
+        return pi;
+    }
 }
diff --git a/core/tests/coretests/res/raw/com_android_tzdata.apex b/core/tests/coretests/res/raw/com_android_tzdata.apex
new file mode 100644
index 0000000..72294de
--- /dev/null
+++ b/core/tests/coretests/res/raw/com_android_tzdata.apex
Binary files differ
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
index be1b1ce..c5454a6 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
@@ -330,6 +330,28 @@
     }
 
     /**
+     * Copies a specified {@code resourceId} to a file. Returns a non-null file if the copy
+     * succeeded, or {@code null} otherwise.
+     */
+    File copyRawResourceToFile(String baseName, int resourceId) throws Exception {
+        // Copy the resource to a file.
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        InputStream is = context.getResources().openRawResource(resourceId);
+        File outFile = null;
+        try {
+            outFile = new File(context.getFilesDir(), baseName);
+            assertTrue(FileUtils.copyToFile(is, outFile));
+            return outFile;
+        } catch (Exception e) {
+            if (outFile != null) {
+                outFile.delete();
+            }
+
+            return null;
+        }
+    }
+
+    /**
      * Attempts to parse a package.
      *
      * APKs are put into coretests/apks/packageparser_*.
@@ -340,14 +362,14 @@
     Package parsePackage(String apkFileName, int apkResourceId,
             Function<Package, Package> converter) throws Exception {
         // Copy the resource to a file.
-        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
-        File outFile = new File(context.getFilesDir(), apkFileName);
+        File outFile = null;
         try {
-            InputStream is = context.getResources().openRawResource(apkResourceId);
-            assertTrue(FileUtils.copyToFile(is, outFile));
+            outFile = copyRawResourceToFile(apkFileName, apkResourceId);
             return converter.apply(new PackageParser().parsePackage(outFile, 0 /* flags */));
         } finally {
-            outFile.delete();
+            if (outFile != null) {
+                outFile.delete();
+            }
         }
     }
 
@@ -498,4 +520,20 @@
                         "android.permission.READ_CONTACTS"),
                 secondChild.requestedPermissions);
     }
+
+    @Test
+    public void testApexPackageInfoGeneration() throws Exception {
+        File apexFile = copyRawResourceToFile("com.android.tzdata.apex",
+                R.raw.com_android_tzdata);
+        PackageInfo pi = PackageParser.generatePackageInfoFromApex(apexFile, false);
+        assertEquals("com.google.android.tzdata", pi.packageName);
+        assertEquals(1, pi.getLongVersionCode());
+        assertNull(pi.signingInfo);
+
+        pi = PackageParser.generatePackageInfoFromApex(apexFile, true);
+        assertEquals("com.google.android.tzdata", pi.packageName);
+        assertEquals(1, pi.getLongVersionCode());
+        assertNotNull(pi.signingInfo);
+        assertTrue(pi.signingInfo.getApkContentsSigners().length > 0);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 70ead41..e7c8770 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7872,8 +7872,13 @@
                 if (apex != null) {
                     try {
                         final ApexInfo[] activePkgs = apex.getActivePackages();
-                        for (ApexInfo apexInfo : activePkgs) {
-                            list.add(new PackageInfo(apexInfo));
+                        for (ApexInfo ai : activePkgs) {
+                            try {
+                                 list.add(PackageParser.generatePackageInfoFromApex(
+                                         new File(ai.packagePath), true /* collect certs */));
+                            } catch (PackageParserException pe) {
+                                 throw new IllegalStateException("Unable to parse: " + ai, pe);
+                            }
                         }
                     } catch (RemoteException e) {
                         Log.e(TAG, "Unable to retrieve packages from apexservice: " + e.toString());