summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
authorTim Murray <timmurray@google.com>2020-02-21 20:22:19 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2020-02-21 20:22:19 +0000
commit8a9c5795e075c5d27f3fb5b8901b76df2c5cbb93 (patch)
tree65e968d34a4fa256049ea8dc7548b6079449a1e7 /core/java
parentb1b13b1b3ffe17f736de8587422a3da47f05e4d4 (diff)
parenta46b7b3f5e9398a245d0730d365d1f847d313938 (diff)
Merge "Cache package and permission information"
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/app/ActivityThread.java15
-rw-r--r--core/java/android/app/ApplicationPackageManager.java45
-rw-r--r--core/java/android/app/ContextImpl.java23
-rw-r--r--core/java/android/app/LoadedApk.java16
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java2
-rw-r--r--core/java/android/content/pm/PackageManager.java191
-rw-r--r--core/java/android/permission/PermissionManager.java192
7 files changed, 413 insertions, 71 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 192156726984..92dd91a877a9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2215,16 +2215,11 @@ public final class ActivityThread extends ClientTransactionHandler {
public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
int flags, int userId) {
final boolean differentUser = (UserHandle.myUserId() != userId);
- ApplicationInfo ai;
- try {
- ai = getPackageManager().getApplicationInfo(packageName,
- PackageManager.GET_SHARED_LIBRARY_FILES
- | PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
- (userId < 0) ? UserHandle.myUserId() : userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
+ ApplicationInfo ai = PackageManager.getApplicationInfoAsUserCached(
+ packageName,
+ PackageManager.GET_SHARED_LIBRARY_FILES
+ | PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+ (userId < 0) ? UserHandle.myUserId() : userId);
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
if (differentUser) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index e781ac606049..db70573e9de9 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -84,6 +84,7 @@ import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.permission.IOnPermissionsChangeListener;
import android.permission.IPermissionManager;
+import android.permission.PermissionManager;
import android.provider.Settings;
import android.system.ErrnoException;
import android.system.Os;
@@ -191,16 +192,15 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
throws NameNotFoundException {
- try {
- PackageInfo pi = mPM.getPackageInfo(packageName,
- updateFlagsForPackage(flags, userId), userId);
- if (pi != null) {
- return pi;
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ PackageInfo pi =
+ getPackageInfoAsUserCached(
+ packageName,
+ updateFlagsForPackage(flags, userId),
+ userId);
+ if (pi == null) {
+ throw new NameNotFoundException(packageName);
}
- throw new NameNotFoundException(packageName);
+ return pi;
}
@Override
@@ -410,20 +410,14 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public ApplicationInfo getApplicationInfoAsUser(String packageName, int flags, int userId)
throws NameNotFoundException {
- try {
- ApplicationInfo ai = mPM.getApplicationInfo(packageName,
- updateFlagsForApplication(flags, userId), userId);
- if (ai != null) {
- // This is a temporary hack. Callers must use
- // createPackageContext(packageName).getApplicationInfo() to
- // get the right paths.
- return maybeAdjustApplicationInfo(ai);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ ApplicationInfo ai = getApplicationInfoAsUserCached(
+ packageName,
+ updateFlagsForApplication(flags, userId),
+ userId);
+ if (ai == null) {
+ throw new NameNotFoundException(packageName);
}
-
- throw new NameNotFoundException(packageName);
+ return maybeAdjustApplicationInfo(ai);
}
private static ApplicationInfo maybeAdjustApplicationInfo(ApplicationInfo info) {
@@ -680,11 +674,8 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public int checkPermission(String permName, String pkgName) {
- try {
- return mPermissionManager.checkPermission(permName, pkgName, getUserId());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return PermissionManager
+ .checkPackageNamePermission(permName, pkgName);
}
@Override
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 480ea8a25f91..96edca19afa7 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -73,6 +73,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.permission.IPermissionManager;
+import android.permission.PermissionManager;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -1941,27 +1942,7 @@ class ContextImpl extends Context {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}
-
- final IActivityManager am = ActivityManager.getService();
- if (am == null) {
- // Well this is super awkward; we somehow don't have an active
- // ActivityManager instance. If we're testing a root or system
- // UID, then they totally have whatever permission this is.
- final int appId = UserHandle.getAppId(uid);
- if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
- Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
- return PERMISSION_GRANTED;
- }
- Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
- + permission);
- return PackageManager.PERMISSION_DENIED;
- }
-
- try {
- return am.checkPermission(permission, pid, uid);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return PermissionManager.checkPermission(permission, pid, uid);
}
/** @hide */
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 44c248637f49..adc545a1c966 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1001,17 +1001,11 @@ public final class LoadedApk {
*/
private void initializeJavaContextClassLoader() {
IPackageManager pm = ActivityThread.getPackageManager();
- android.content.pm.PackageInfo pi;
- try {
- pi = pm.getPackageInfo(mPackageName, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
- UserHandle.myUserId());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- if (pi == null) {
- throw new IllegalStateException("Unable to get package info for "
- + mPackageName + "; is package not installed?");
- }
+ android.content.pm.PackageInfo pi =
+ PackageManager.getPackageInfoAsUserCached(
+ mPackageName,
+ PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+ UserHandle.myUserId());
/*
* Two possible indications that this package could be
* sharing its virtual machine with other packages:
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index cbd2f1a52c0c..ce0d04b9d145 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -445,7 +445,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
SystemProperties.set(name, newValueString);
}
- private Result maybeCheckConsistency(Query query, Result proposedResult) {
+ protected Result maybeCheckConsistency(Query query, Result proposedResult) {
if (VERIFY) {
Result resultToCompare = recompute(query);
boolean nonceChanged = (getCurrentNonce() != mLastSeenNonce);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ab3982a460d8..98d64aa40ab1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -32,9 +32,11 @@ import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.annotation.XmlRes;
import android.app.ActivityManager;
+import android.app.ActivityThread;
import android.app.AppDetailsActivity;
import android.app.PackageDeleteObserver;
import android.app.PackageInstallObserver;
+import android.app.PropertyInvalidatedCache;
import android.app.admin.DevicePolicyManager;
import android.app.usage.StorageStatsManager;
import android.compat.annotation.ChangeId;
@@ -63,6 +65,7 @@ import android.os.UserManager;
import android.os.incremental.IncrementalManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
+import android.permission.PermissionManager;
import android.util.AndroidException;
import android.util.Log;
@@ -76,6 +79,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.Set;
/**
@@ -7863,4 +7867,191 @@ public abstract class PackageManager {
throw new UnsupportedOperationException(
"getMimeGroup not implemented in subclass");
}
+
+ // Some of the flags don't affect the query result, but let's be conservative and cache
+ // each combination of flags separately.
+
+ private static final class ApplicationInfoQuery {
+ final String packageName;
+ final int flags;
+ final int userId;
+
+ ApplicationInfoQuery(@Nullable String packageName, int flags, int userId) {
+ this.packageName = packageName;
+ this.flags = flags;
+ this.userId = userId;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "ApplicationInfoQuery(packageName=\"%s\", flags=%s, userId=%s)",
+ packageName, flags, userId);
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = Objects.hashCode(packageName);
+ hash = hash * 13 + Objects.hashCode(flags);
+ hash = hash * 13 + Objects.hashCode(userId);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object rval) {
+ if (rval == null) {
+ return false;
+ }
+ ApplicationInfoQuery other;
+ try {
+ other = (ApplicationInfoQuery) rval;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+ return Objects.equals(packageName, other.packageName)
+ && flags == other.flags
+ && userId == other.userId;
+ }
+ }
+
+ private static ApplicationInfo getApplicationInfoAsUserUncached(
+ String packageName, int flags, int userId) {
+ try {
+ return ActivityThread.getPackageManager()
+ .getApplicationInfo(packageName, flags, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private static final PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>
+ sApplicationInfoCache =
+ new PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>(
+ 16, PermissionManager.CACHE_KEY_PACKAGE_INFO) {
+ @Override
+ protected ApplicationInfo recompute(ApplicationInfoQuery query) {
+ return getApplicationInfoAsUserUncached(
+ query.packageName, query.flags, query.userId);
+ }
+ @Override
+ protected ApplicationInfo maybeCheckConsistency(
+ ApplicationInfoQuery query, ApplicationInfo proposedResult) {
+ // Implementing this debug check for ApplicationInfo would require a
+ // complicated deep comparison, so just bypass it for now.
+ return proposedResult;
+ }
+ };
+
+ /** @hide */
+ public static ApplicationInfo getApplicationInfoAsUserCached(
+ String packageName, int flags, int userId) {
+ return sApplicationInfoCache.query(
+ new ApplicationInfoQuery(packageName, flags, userId));
+ }
+
+ /**
+ * Make getApplicationInfoAsUser() bypass the cache in this process.
+ *
+ * @hide
+ */
+ public static void disableApplicationInfoCache() {
+ sApplicationInfoCache.disableLocal();
+ }
+
+ /**
+ * Invalidate caches of package and permission information system-wide.
+ *
+ * @hide
+ */
+ public static void invalidatePackageInfoCache() {
+ PropertyInvalidatedCache.invalidateCache(PermissionManager.CACHE_KEY_PACKAGE_INFO);
+ }
+
+ // Some of the flags don't affect the query result, but let's be conservative and cache
+ // each combination of flags separately.
+
+ private static final class PackageInfoQuery {
+ final String packageName;
+ final int flags;
+ final int userId;
+
+ PackageInfoQuery(@Nullable String packageName, int flags, int userId) {
+ this.packageName = packageName;
+ this.flags = flags;
+ this.userId = userId;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "PackageInfoQuery(packageName=\"%s\", flags=%s, userId=%s)",
+ packageName, flags, userId);
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = Objects.hashCode(packageName);
+ hash = hash * 13 + Objects.hashCode(flags);
+ hash = hash * 13 + Objects.hashCode(userId);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object rval) {
+ if (rval == null) {
+ return false;
+ }
+ PackageInfoQuery other;
+ try {
+ other = (PackageInfoQuery) rval;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+ return Objects.equals(packageName, other.packageName)
+ && flags == other.flags
+ && userId == other.userId;
+ }
+ }
+
+ private static PackageInfo getPackageInfoAsUserUncached(
+ String packageName, int flags, int userId) {
+ try {
+ return ActivityThread.getPackageManager().getPackageInfo(packageName, flags, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private static final PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>
+ sPackageInfoCache =
+ new PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>(
+ 16, PermissionManager.CACHE_KEY_PACKAGE_INFO) {
+ @Override
+ protected PackageInfo recompute(PackageInfoQuery query) {
+ return getPackageInfoAsUserUncached(
+ query.packageName, query.flags, query.userId);
+ }
+ @Override
+ protected PackageInfo maybeCheckConsistency(
+ PackageInfoQuery query, PackageInfo proposedResult) {
+ // Implementing this debug check for PackageInfo would require a
+ // complicated deep comparison, so just bypass it for now.
+ return proposedResult;
+ }
+ };
+
+ /** @hide */
+ public static PackageInfo getPackageInfoAsUserCached(
+ String packageName, int flags, int userId) {
+ return sPackageInfoCache.query(new PackageInfoQuery(packageName, flags, userId));
+ }
+
+ /**
+ * Make getPackageInfoAsUser() bypass the cache in this process.
+ * @hide
+ */
+ public static void disablePackageInfoCache() {
+ sPackageInfoCache.disableLocal();
+ }
+
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index d945b65c8522..5d6dc7beb30e 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -27,9 +27,13 @@ import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.ActivityThread;
+import android.app.IActivityManager;
+import android.app.PropertyInvalidatedCache;
import android.content.Context;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
import android.content.pm.permission.SplitPermissionInfoParcelable;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -40,6 +44,7 @@ import com.android.internal.annotations.Immutable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -444,4 +449,189 @@ public final class PermissionManager {
e.rethrowFromSystemServer();
}
}
-} \ No newline at end of file
+
+ /* @hide */
+ private static int checkPermissionUncached(@Nullable String permission, int pid, int uid) {
+ final IActivityManager am = ActivityManager.getService();
+ if (am == null) {
+ // Well this is super awkward; we somehow don't have an active ActivityManager
+ // instance. If we're testing a root or system UID, then they totally have whatever
+ // permission this is.
+ final int appId = UserHandle.getAppId(uid);
+ if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
+ Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
+ + permission);
+ return PackageManager.PERMISSION_DENIED;
+ }
+ try {
+ return am.checkPermission(permission, pid, uid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Identifies a permission query.
+ *
+ * N.B. we include the checking pid for tracking purposes but don't include it in the equality
+ * comparison: we use only uid for the actual security check, so comparing pid would result
+ * in spurious misses.
+ *
+ * @hide
+ */
+ @Immutable
+ private static final class PermissionQuery {
+ final String permission;
+ final int pid;
+ final int uid;
+
+ PermissionQuery(@Nullable String permission, int pid, int uid) {
+ this.permission = permission;
+ this.pid = pid;
+ this.uid = uid;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("PermissionQuery(permission=\"%s\", pid=%s, uid=%s)",
+ permission, pid, uid);
+ }
+
+ @Override
+ public int hashCode() {
+ // N.B. pid doesn't count toward equality and therefore shouldn't count for
+ // hashing either.
+ int hash = Objects.hashCode(permission);
+ hash = hash * 13 + Objects.hashCode(uid);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object rval) {
+ // N.B. pid doesn't count toward equality!
+ if (rval == null) {
+ return false;
+ }
+ PermissionQuery other;
+ try {
+ other = (PermissionQuery) rval;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+ return uid == other.uid
+ && Objects.equals(permission, other.permission);
+ }
+ }
+
+ /** @hide */
+ public static final String CACHE_KEY_PACKAGE_INFO = "cache_key.package_info";
+
+ /** @hide */
+ private static final PropertyInvalidatedCache<PermissionQuery, Integer> sPermissionCache =
+ new PropertyInvalidatedCache<PermissionQuery, Integer>(
+ 16, CACHE_KEY_PACKAGE_INFO) {
+ @Override
+ protected Integer recompute(PermissionQuery query) {
+ return checkPermissionUncached(query.permission, query.pid, query.uid);
+ }
+ };
+
+ /** @hide */
+ public static int checkPermission(@Nullable String permission, int pid, int uid) {
+ return sPermissionCache.query(new PermissionQuery(permission, pid, uid));
+ }
+
+ /**
+ * Make checkPermission() above bypass the permission cache in this process.
+ *
+ * @hide
+ */
+ public static void disablePermissionCache() {
+ sPermissionCache.disableLocal();
+ }
+
+ /**
+ * Like PermissionQuery, but for permission checks based on a package name instead of
+ * a UID.
+ */
+ @Immutable
+ private static final class PackageNamePermissionQuery {
+ final String permName;
+ final String pkgName;
+
+ PackageNamePermissionQuery(@Nullable String permName, @Nullable String pkgName) {
+ this.permName = permName;
+ this.pkgName = pkgName;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("PackageNamePermissionQuery(pkgName=\"%s\", permName=\"%s\")",
+ pkgName, permName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(permName) * 13 + Objects.hashCode(pkgName);
+ }
+
+ @Override
+ public boolean equals(Object rval) {
+ if (rval == null) {
+ return false;
+ }
+ PackageNamePermissionQuery other;
+ try {
+ other = (PackageNamePermissionQuery) rval;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+ return Objects.equals(permName, other.permName)
+ && Objects.equals(pkgName, other.pkgName);
+ }
+ }
+
+ /* @hide */
+ private static int checkPackageNamePermissionUncached(String permName, String pkgName) {
+ try {
+ return ActivityThread.getPermissionManager().checkPermission(
+ permName, pkgName, UserHandle.myUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /* @hide */
+ private static PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>
+ sPackageNamePermissionCache =
+ new PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>(
+ 16, CACHE_KEY_PACKAGE_INFO) {
+ @Override
+ protected Integer recompute(PackageNamePermissionQuery query) {
+ return checkPackageNamePermissionUncached(query.permName, query.pkgName);
+ }
+ };
+
+ /**
+ * Check whether a package has a permission.
+ *
+ * @hide
+ */
+ public static int checkPackageNamePermission(String permName, String pkgName) {
+ return sPackageNamePermissionCache.query(
+ new PackageNamePermissionQuery(permName, pkgName));
+ }
+
+ /**
+ * Make checkPackageNamePermission() bypass the cache in this process.
+ *
+ * @hide
+ */
+ public static void disablePackageNamePermissionCache() {
+ sPackageNamePermissionCache.disableLocal();
+ }
+
+}