diff options
| author | Tim Murray <timmurray@google.com> | 2020-02-21 20:22:19 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-02-21 20:22:19 +0000 |
| commit | 8a9c5795e075c5d27f3fb5b8901b76df2c5cbb93 (patch) | |
| tree | 65e968d34a4fa256049ea8dc7548b6079449a1e7 /core/java | |
| parent | b1b13b1b3ffe17f736de8587422a3da47f05e4d4 (diff) | |
| parent | a46b7b3f5e9398a245d0730d365d1f847d313938 (diff) | |
Merge "Cache package and permission information"
Diffstat (limited to 'core/java')
| -rw-r--r-- | core/java/android/app/ActivityThread.java | 15 | ||||
| -rw-r--r-- | core/java/android/app/ApplicationPackageManager.java | 45 | ||||
| -rw-r--r-- | core/java/android/app/ContextImpl.java | 23 | ||||
| -rw-r--r-- | core/java/android/app/LoadedApk.java | 16 | ||||
| -rw-r--r-- | core/java/android/app/PropertyInvalidatedCache.java | 2 | ||||
| -rw-r--r-- | core/java/android/content/pm/PackageManager.java | 191 | ||||
| -rw-r--r-- | core/java/android/permission/PermissionManager.java | 192 |
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(); + } + +} |
