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;
}