Merge "Grant access to ephemeral metadata"
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 2590a6b..62f3848 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -223,6 +223,27 @@
             int userId);
 
     /**
+     * Grants access to the package metadata for an ephemeral application.
+     * <p>
+     * When an ephemeral application explicitly tries to interact with a full
+     * install application [via an activity, service or provider that has been
+     * exposed using the {@code visibleToInstantApp} attribute], the normal
+     * application must be able to see metadata about the connecting ephemeral
+     * app. If the ephemeral application uses an implicit intent [ie action VIEW,
+     * category BROWSABLE], it remains hidden from the launched activity.
+     * <p>
+     * If the {@code sourceUid} is not for an ephemeral app or {@code targetUid}
+     * is not for a fully installed app, this method will be a no-op.
+     *
+     * @param userId the user
+     * @param intent the intent that triggered the grant
+     * @param targetAppId The app ID of the fully installed application
+     * @param ephemeralAppId The app ID of the ephemeral application
+     */
+    public abstract void grantEphemeralAccess(int userId, Intent intent,
+            int targetAppId, int ephemeralAppId);
+
+    /**
      * @return The SetupWizard package name.
      */
     public abstract String getSetupWizardPackageName();
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7661127..bfed37d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1907,6 +1907,7 @@
                     mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
                             si.getUriPermissionsLocked());
                 }
+                // TODO b/34123112; Insert ephemeral grant here
                 bumpServiceExecutingLocked(r, execInFg, "start");
                 if (!oomAdjusted) {
                     oomAdjusted = true;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2a324eb..0243391 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8079,6 +8079,12 @@
         return pi;
     }
 
+    void grantEphemeralAccessLocked(int userId, Intent intent,
+            int targetAppId, int ephemeralAppId) {
+        getPackageManagerInternalLocked().
+                grantEphemeralAccess(userId, intent, targetAppId, ephemeralAppId);
+    }
+
     private UriPermission findUriPermissionLocked(int targetUid, GrantUri grantUri) {
         final ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
         if (targetUris != null) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 3f71d12..46e00479 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1131,7 +1131,8 @@
 
         mService.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName,
                 mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
-
+        mService.grantEphemeralAccessLocked(mStartActivity.userId, mIntent,
+                mStartActivity.appInfo.uid, UserHandle.getAppId(mCallingUid));
         if (mSourceRecord != null && mSourceRecord.isRecentsActivity()) {
             mStartActivity.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
         }
diff --git a/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java b/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
index 1e3e0ca..e8be629 100644
--- a/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
+++ b/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
@@ -17,6 +17,7 @@
 package com.android.server.pm;
 
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.EphemeralApplicationInfo;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
@@ -27,10 +28,13 @@
 import android.graphics.drawable.Drawable;
 import android.os.Binder;
 import android.os.Environment;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
 import android.util.Xml;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
@@ -51,6 +55,7 @@
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -62,7 +67,7 @@
 class EphemeralApplicationRegistry {
     private static final boolean DEBUG = false;
 
-    private static final boolean ENABLED = false;
+    private static final boolean ENABLED = true;
 
     private static final String LOG_TAG = "EphemeralAppRegistry";
 
@@ -90,6 +95,16 @@
     @GuardedBy("mService.mPackages")
     private SparseArray<List<UninstalledEphemeralAppState>> mUninstalledEphemeralApps;
 
+    /**
+     * Automatic grants for access to instant app metadata.
+     * The key is the target application UID.
+     * The value is a set of instant app UIDs.
+     * UserID -> TargetAppId -> InstantAppId
+     */
+    private SparseArray<SparseArray<SparseBooleanArray>> mEphemeralGrants;
+    /** The set of all installed instant apps. UserID -> AppID */
+    private SparseArray<SparseBooleanArray> mInstalledEphemeralAppUids;
+
     public EphemeralApplicationRegistry(PackageManagerService service) {
         mService = service;
     }
@@ -189,6 +204,9 @@
 
             // Propagate permissions before removing any state
             propagateEphemeralAppPermissionsIfNeeded(pkg, userId);
+            if (pkg.applicationInfo.isEphemeralApp()) {
+                addEphemeralAppLPw(userId, ps.appId);
+            }
 
             // Remove the in-memory state
             if (mUninstalledEphemeralApps != null) {
@@ -248,9 +266,11 @@
             if (pkg.applicationInfo.isEphemeralApp()) {
                 // Add a record for an uninstalled ephemeral app
                 addUninstalledEphemeralAppLPw(pkg, userId);
+                removeEphemeralAppLPw(userId, ps.appId);
             } else {
                 // Deleting an app prunes all ephemeral state such as cookie
                 deleteDir(getEphemeralApplicationDir(pkg.packageName, userId));
+                removeAppLPw(userId, ps.appId);
             }
         }
     }
@@ -262,9 +282,114 @@
         if (mUninstalledEphemeralApps != null) {
             mUninstalledEphemeralApps.remove(userId);
         }
+        if (mInstalledEphemeralAppUids != null) {
+            mInstalledEphemeralAppUids.remove(userId);
+        }
+        if (mEphemeralGrants != null) {
+            mEphemeralGrants.remove(userId);
+        }
         deleteDir(getEphemeralApplicationsDir(userId));
     }
 
+    public boolean isEphemeralAccessGranted(int userId, int targetAppId, int ephemeralAppId) {
+        if (mEphemeralGrants == null) {
+            return false;
+        }
+        final SparseArray<SparseBooleanArray> targetAppList = mEphemeralGrants.get(userId);
+        if (targetAppList == null) {
+            return false;
+        }
+        final SparseBooleanArray ephemeralGrantList = targetAppList.get(targetAppId);
+        if (ephemeralGrantList == null) {
+            return false;
+        }
+        return ephemeralGrantList.get(ephemeralAppId);
+    }
+
+    public void grantEphemeralAccessLPw(int userId, Intent intent,
+            int targetAppId, int ephemeralAppId) {
+        if (mInstalledEphemeralAppUids == null) {
+            return;     // no ephemeral apps installed; no need to grant
+        }
+        SparseBooleanArray ephemeralAppList = mInstalledEphemeralAppUids.get(userId);
+        if (ephemeralAppList == null || !ephemeralAppList.get(ephemeralAppId)) {
+            return;     // ephemeral app id isn't installed; no need to grant
+        }
+        if (ephemeralAppList.get(targetAppId)) {
+            return;     // target app id is an ephemeral app; no need to grant
+        }
+        if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
+            final Set<String> categories = intent.getCategories();
+            if (categories != null && categories.contains(Intent.CATEGORY_BROWSABLE)) {
+                return;  // launched via VIEW/BROWSABLE intent; no need to grant
+            }
+        }
+        if (mEphemeralGrants == null) {
+            mEphemeralGrants = new SparseArray<>();
+        }
+        SparseArray<SparseBooleanArray> targetAppList = mEphemeralGrants.get(userId);
+        if (targetAppList == null) {
+            targetAppList = new SparseArray<>();
+            mEphemeralGrants.put(userId, targetAppList);
+        }
+        SparseBooleanArray ephemeralGrantList = targetAppList.get(targetAppId);
+        if (ephemeralGrantList == null) {
+            ephemeralGrantList = new SparseBooleanArray();
+            targetAppList.put(targetAppId, ephemeralGrantList);
+        }
+        ephemeralGrantList.put(ephemeralAppId, true /*granted*/);
+    }
+
+    public void addEphemeralAppLPw(int userId, int ephemeralAppId) {
+        if (mInstalledEphemeralAppUids == null) {
+            mInstalledEphemeralAppUids = new SparseArray<>();
+        }
+        SparseBooleanArray ephemeralAppList = mInstalledEphemeralAppUids.get(userId);
+        if (ephemeralAppList == null) {
+            ephemeralAppList = new SparseBooleanArray();
+            mInstalledEphemeralAppUids.put(userId, ephemeralAppList);
+        }
+        ephemeralAppList.put(ephemeralAppId, true /*installed*/);
+    }
+
+    private void removeEphemeralAppLPw(int userId, int ephemeralAppId) {
+        // remove from the installed list
+        if (mInstalledEphemeralAppUids == null) {
+            return; // no ephemeral apps on the system
+        }
+        final SparseBooleanArray ephemeralAppList = mInstalledEphemeralAppUids.get(userId);
+        if (ephemeralAppList == null) {
+            Slog.w(LOG_TAG, "Remove ephemeral not in install list");
+            return;
+        } else {
+            ephemeralAppList.delete(ephemeralAppId);
+        }
+        // remove any grants
+        if (mEphemeralGrants == null) {
+            return; // no grants on the system
+        }
+        final SparseArray<SparseBooleanArray> targetAppList = mEphemeralGrants.get(userId);
+        if (targetAppList == null) {
+            return; // no grants for this user
+        }
+        final int numApps = targetAppList.size();
+        for (int i = targetAppList.size() - 1; i >= 0; --i) {
+            targetAppList.valueAt(i).delete(ephemeralAppId);
+        }
+    }
+
+    private void removeAppLPw(int userId, int targetAppId) {
+        // remove from the installed list
+        if (mEphemeralGrants == null) {
+            return; // no grants on the system
+        }
+        final SparseArray<SparseBooleanArray> targetAppList = mEphemeralGrants.get(userId);
+        if (targetAppList == null) {
+            return; // no grants for this user
+        }
+        targetAppList.delete(targetAppId);
+    }
+
     private void addUninstalledEphemeralAppLPw(PackageParser.Package pkg, int userId) {
         EphemeralApplicationInfo uninstalledApp = createEphemeralAppInfoForPackage(pkg, userId);
         if (uninstalledApp == null) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9d2085a..d28da6a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2225,6 +2225,7 @@
             Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
 
             mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this);
+            mEphemeralApplicationRegistry = new EphemeralApplicationRegistry(this);
 
             File dataDir = Environment.getDataDirectory();
             mAppInstallDir = new File(dataDir, "app");
@@ -2807,8 +2808,6 @@
                 setUpEphemeralInstallerActivityLP(mEphemeralInstallerComponent);
             }
 
-            mEphemeralApplicationRegistry = new EphemeralApplicationRegistry(this);
-
             // Read and update the usage of dex files.
             // Do this at the end of PM init so that all the packages have their
             // data directory reconciled.
@@ -3258,6 +3257,31 @@
         if (p == null) {
             return null;
         }
+        // Filter out ephemeral app metadata:
+        //   * The system/shell/root can see metadata for any app
+        //   * An installed app can see metadata for 1) other installed apps
+        //     and 2) ephemeral apps that have explicitly interacted with it
+        //   * Ephemeral apps can only see their own metadata
+        final int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
+        if (callingAppId != Process.SYSTEM_UID
+                && callingAppId != Process.SHELL_UID
+                && callingAppId != Process.ROOT_UID) {
+            final String ephemeralPackageName = getEphemeralPackageName(Binder.getCallingUid());
+            if (ephemeralPackageName != null) {
+                // ephemeral apps can only get information on themselves
+                if (!ephemeralPackageName.equals(p.packageName)) {
+                    return null;
+                }
+            } else {
+                if (p.applicationInfo.isEphemeralApp()) {
+                    // only get access to the ephemeral app if we've been granted access
+                    if (!mEphemeralApplicationRegistry.isEphemeralAccessGranted(
+                            userId, callingAppId, ps.appId)) {
+                        return null;
+                    }
+                }
+            }
+        }
 
         final PermissionsState permissionsState = ps.getPermissionsState();
 
@@ -3337,22 +3361,19 @@
 
         // reader
         synchronized (mPackages) {
-            // Normalize package name to hanlde renamed packages
+            // Normalize package name to handle renamed packages
             packageName = normalizePackageNameLPr(packageName);
 
             final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
-            PackageParser.Package p = null;
             if (matchFactoryOnly) {
                 final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
                 if (ps != null) {
                     return generatePackageInfo(ps, flags, userId);
                 }
             }
-            if (p == null) {
-                p = mPackages.get(packageName);
-                if (matchFactoryOnly && p != null && !isSystemApp(p)) {
-                    return null;
-                }
+            PackageParser.Package p = mPackages.get(packageName);
+            if (matchFactoryOnly && p != null && !isSystemApp(p)) {
+                return null;
             }
             if (DEBUG_PACKAGE_INFO)
                 Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
@@ -8840,6 +8861,10 @@
             // Modify state for the given package setting
             commitPackageSettings(pkg, pkgSetting, user, scanFlags,
                     (policyFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);
+            if (isEphemeral(pkg)) {
+                final int userId = user == null ? 0 : user.getIdentifier();
+                mEphemeralApplicationRegistry.addEphemeralAppLPw(userId, pkgSetting.appId);
+            }
         }
         return pkg;
     }
@@ -21933,6 +21958,15 @@
                     responseObj, origIntent, resolvedType, launchIntent, callingPackage, userId);
         }
 
+        @Override
+        public void grantEphemeralAccess(int userId, Intent intent,
+                int targetAppId, int ephemeralAppId) {
+            synchronized (mPackages) {
+                mEphemeralApplicationRegistry.grantEphemeralAccessLPw(userId, intent,
+                        targetAppId, ephemeralAppId);
+            }
+        }
+
         public String getSetupWizardPackageName() {
             return mSetupWizardPackage;
         }