Add MATCH_APEX flag to getInstalledPackages.

If set, PackageManager will query apexservice and ask for activated
packages.

Test: wrote a small app to test the new query.
Bug: 117589375
Change-Id: I498bd97896f3eab65c88e9684874a30713be585e
diff --git a/api/current.txt b/api/current.txt
index b192d7f..cb71160 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -11137,6 +11137,7 @@
     field public int[] gids;
     field public int installLocation;
     field public android.content.pm.InstrumentationInfo[] instrumentation;
+    field public boolean isApex;
     field public long lastUpdateTime;
     field public java.lang.String packageName;
     field public android.content.pm.PermissionInfo[] permissions;
@@ -11528,6 +11529,7 @@
     field public static final int INSTALL_REASON_UNKNOWN = 0; // 0x0
     field public static final int INSTALL_REASON_USER = 4; // 0x4
     field public static final int MATCH_ALL = 131072; // 0x20000
+    field public static final int MATCH_APEX = 1073741824; // 0x40000000
     field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
     field public static final int MATCH_DIRECT_BOOT_AUTO = 268435456; // 0x10000000
     field public static final int MATCH_DIRECT_BOOT_AWARE = 524288; // 0x80000
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 9e20503..ecdd810 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -18,6 +18,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
+import android.apex.ApexInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -390,6 +391,11 @@
     @Nullable
     public String compileSdkVersionCodename;
 
+    /**
+     * Whether the package is an APEX package.
+     */
+    public boolean isApex;
+
     public PackageInfo() {
     }
 
@@ -472,6 +478,7 @@
         } else {
             dest.writeInt(0);
         }
+        dest.writeBoolean(isApex);
     }
 
     public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -533,7 +540,7 @@
         if (hasSigningInfo != 0) {
             signingInfo = SigningInfo.CREATOR.createFromParcel(source);
         }
-
+        isApex = source.readBoolean();
         // The component lists were flattened with the redundant ApplicationInfo
         // instances omitted.  Distribute the canonical one here as appropriate.
         if (applicationInfo != null) {
@@ -544,6 +551,15 @@
         }
     }
 
+    /**
+     * @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/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e14b17e..361beba 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -145,6 +145,7 @@
             MATCH_FACTORY_ONLY,
             MATCH_DEBUG_TRIAGED_MISSING,
             MATCH_INSTANT,
+            MATCH_APEX,
             GET_DISABLED_COMPONENTS,
             GET_DISABLED_UNTIL_USED_COMPONENTS,
             GET_UNINSTALLED_PACKAGES,
@@ -540,6 +541,17 @@
     public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS =  0x20000000;
 
     /**
+     * {@link PackageInfo} flag: include APEX packages that are currently
+     * installed. In APEX terminology, this corresponds to packages that are
+     * currently active, i.e. mounted and available to other processes of the OS.
+     * In particular, this flag alone will not match APEX files that are staged
+     * for activation at next reboot.
+     * TODO(b/119767311): include uninstalled/inactive APEX if
+     *                    MATCH_UNINSTALLED_PACKAGES is set.
+     */
+    public static final int MATCH_APEX = 0x40000000;
+
+    /**
      * Flag for {@link #addCrossProfileIntentFilter}: if this flag is set: when
      * resolving an intent that matches the {@code CrossProfileIntentFilter},
      * the current profile will be skipped. Only activities in the target user
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e4e8010..9856a2b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -68,6 +68,7 @@
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
 import static android.content.pm.PackageManager.MATCH_ALL;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.content.pm.PackageManager.MATCH_APEX;
 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
@@ -125,6 +126,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.apex.ApexInfo;
+import android.apex.IApexService;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AppDetailsActivity;
@@ -7727,6 +7730,8 @@
         if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
         flags = updateFlagsForPackage(flags, userId, null);
         final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
+        final boolean listApex = (flags & MATCH_APEX) != 0;
+
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /* requireFullPermission */, false /* checkShell */,
                 "get installed packages");
@@ -7765,7 +7770,22 @@
                     }
                 }
             }
-
+            if (listApex) {
+                final IApexService apex = IApexService.Stub.asInterface(
+                        ServiceManager.getService("apexservice"));
+                if (apex != null) {
+                    try {
+                        final ApexInfo[] activePkgs = apex.getActivePackages();
+                        for (ApexInfo apexInfo : activePkgs) {
+                            list.add(new PackageInfo(apexInfo));
+                        }
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Unable to retrieve packages from apexservice: " + e.toString());
+                    }
+                } else {
+                    Log.e(TAG, "Unable to connect to apexservice for querying packages.");
+                }
+            }
             return new ParceledListSlice<>(list);
         }
     }