Protect usage data with OP_GET_USAGE_STATS.
APIs that return package usage data (such as the new ArtManager)
must ensure that callers hold both the PACKAGE_USAGE_STATS permission
and the OP_GET_USAGE_STATS app-op.
Bug: 77662908
Test: atest vendor/xts/gts-tests/hostsidetests/dexapis/host/
Change-Id: I7a85d959f1682d2bd5cf3684415e368fece88101
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 1084b42..0e44833 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2831,7 +2831,7 @@
synchronized (mLock) {
if (mArtManager == null) {
try {
- mArtManager = new ArtManager(mPM.getArtManager());
+ mArtManager = new ArtManager(mContext, mPM.getArtManager());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/dex/ArtManager.java b/core/java/android/content/pm/dex/ArtManager.java
index 4129398..b0970f4 100644
--- a/core/java/android/content/pm/dex/ArtManager.java
+++ b/core/java/android/content/pm/dex/ArtManager.java
@@ -16,12 +16,16 @@
package android.content.pm.dex;
+import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+import static android.Manifest.permission.READ_RUNTIME_PROFILES;
+
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.content.Context;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -62,13 +66,14 @@
@Retention(RetentionPolicy.SOURCE)
public @interface ProfileType {}
-
- private IArtManager mArtManager;
+ private final Context mContext;
+ private final IArtManager mArtManager;
/**
* @hide
*/
- public ArtManager(@NonNull IArtManager manager) {
+ public ArtManager(@NonNull Context context, @NonNull IArtManager manager) {
+ mContext = context;
mArtManager = manager;
}
@@ -99,7 +104,7 @@
* @param callback the callback which should be used for the result
* @param executor the executor which should be used to post the result
*/
- @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
+ @RequiresPermission(allOf = { READ_RUNTIME_PROFILES, PACKAGE_USAGE_STATS })
public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
@Nullable String codePath, @NonNull @CallbackExecutor Executor executor,
@NonNull SnapshotRuntimeProfileCallback callback) {
@@ -108,9 +113,10 @@
SnapshotRuntimeProfileCallbackDelegate delegate =
new SnapshotRuntimeProfileCallbackDelegate(callback, executor);
try {
- mArtManager.snapshotRuntimeProfile(profileType, packageName, codePath, delegate);
+ mArtManager.snapshotRuntimeProfile(profileType, packageName, codePath, delegate,
+ mContext.getOpPackageName());
} catch (RemoteException e) {
- e.rethrowAsRuntimeException();
+ throw e.rethrowAsRuntimeException();
}
}
@@ -122,14 +128,13 @@
* @param profileType can be either {@link ArtManager#PROFILE_APPS}
* or {@link ArtManager#PROFILE_BOOT_IMAGE}
*/
- @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
+ @RequiresPermission(allOf = { READ_RUNTIME_PROFILES, PACKAGE_USAGE_STATS })
public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) {
try {
- return mArtManager.isRuntimeProfilingEnabled(profileType);
+ return mArtManager.isRuntimeProfilingEnabled(profileType, mContext.getOpPackageName());
} catch (RemoteException e) {
- e.rethrowAsRuntimeException();
+ throw e.rethrowAsRuntimeException();
}
- return false;
}
/**
diff --git a/core/java/android/content/pm/dex/IArtManager.aidl b/core/java/android/content/pm/dex/IArtManager.aidl
index 6abfdba..7f0de7e 100644
--- a/core/java/android/content/pm/dex/IArtManager.aidl
+++ b/core/java/android/content/pm/dex/IArtManager.aidl
@@ -44,8 +44,8 @@
* {@link ArtManager#isRuntimeProfilingEnabled(int)} does not return true for the given
* {@code profileType}.
*/
- oneway void snapshotRuntimeProfile(int profileType, in String packageName,
- in String codePath, in ISnapshotRuntimeProfileCallback callback);
+ void snapshotRuntimeProfile(int profileType, in String packageName,
+ in String codePath, in ISnapshotRuntimeProfileCallback callback, String callingPackage);
/**
* Returns true if runtime profiles are enabled for the given type, false otherwise.
@@ -54,5 +54,5 @@
*
* @param profileType
*/
- boolean isRuntimeProfilingEnabled(int profileType);
+ boolean isRuntimeProfilingEnabled(int profileType, String callingPackage);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 50ac4db..19241a3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2462,7 +2462,7 @@
installer, mInstallLock);
mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock,
dexManagerListener);
- mArtManagerService = new ArtManagerService(this, installer, mInstallLock);
+ mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 9c2ad463..1d5c580 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -16,8 +16,9 @@
package com.android.server.pm.dex;
-import android.Manifest;
import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
@@ -38,7 +39,9 @@
import android.os.UserHandle;
import android.system.Os;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Slog;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
@@ -47,14 +50,17 @@
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.PackageManagerServiceCompilerMapping;
+
import dalvik.system.DexFile;
import dalvik.system.VMRuntime;
-import java.io.File;
-import java.io.FileNotFoundException;
+
import libcore.io.IoUtils;
import libcore.util.NonNull;
import libcore.util.Nullable;
+import java.io.File;
+import java.io.FileNotFoundException;
+
/**
* A system service that provides access to runtime and compiler artifacts.
*
@@ -69,9 +75,7 @@
*/
public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
private static final String TAG = "ArtManagerService";
-
- private static boolean DEBUG = false;
- private static boolean DEBUG_IGNORE_PERMISSIONS = false;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
// Package name used to create the profile directory layout when
// taking a snapshot of the boot image profile.
@@ -79,6 +83,7 @@
// Profile name used for the boot image profile.
private static final String BOOT_IMAGE_PROFILE_NAME = "android.prof";
+ private final Context mContext;
private final IPackageManager mPackageManager;
private final Object mInstallLock;
@GuardedBy("mInstallLock")
@@ -90,7 +95,9 @@
verifyTronLoggingConstants();
}
- public ArtManagerService(IPackageManager pm, Installer installer, Object installLock) {
+ public ArtManagerService(Context context, IPackageManager pm, Installer installer,
+ Object installLock) {
+ mContext = context;
mPackageManager = pm;
mInstaller = installer;
mInstallLock = installLock;
@@ -99,9 +106,37 @@
LocalServices.addService(ArtManagerInternal.class, new ArtManagerInternalImpl());
}
+ private boolean checkPermission(int callingUid, String callingPackage) {
+ // Callers always need this permission
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_RUNTIME_PROFILES, TAG);
+
+ // Callers also need the ability to read usage statistics
+ switch (mContext.getSystemService(AppOpsManager.class)
+ .noteOp(AppOpsManager.OP_GET_USAGE_STATS, callingUid, callingPackage)) {
+ case AppOpsManager.MODE_ALLOWED:
+ return true;
+ case AppOpsManager.MODE_DEFAULT:
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.PACKAGE_USAGE_STATS, TAG);
+ return true;
+ default:
+ return false;
+ }
+ }
+
@Override
public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
- @Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback) {
+ @Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback,
+ String callingPackage) {
+ if (!checkPermission(Binder.getCallingUid(), callingPackage)) {
+ try {
+ callback.onError(ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+ } catch (RemoteException ignored) {
+ }
+ return;
+ }
+
// Sanity checks on the arguments.
Preconditions.checkNotNull(callback);
@@ -111,9 +146,8 @@
Preconditions.checkStringNotEmpty(packageName);
}
- // Verify that the caller has the right permissions and that the runtime profiling is
- // enabled. The call to isRuntimePermissions will checkReadRuntimeProfilePermission.
- if (!isRuntimeProfilingEnabled(profileType)) {
+ // Verify that runtime profiling is enabled.
+ if (!isRuntimeProfilingEnabled(profileType, callingPackage)) {
throw new IllegalStateException("Runtime profiling is not enabled for " + profileType);
}
@@ -231,9 +265,10 @@
}
@Override
- public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) {
- // Verify that the caller has the right permissions.
- checkReadRuntimeProfilePermission();
+ public boolean isRuntimeProfilingEnabled(@ProfileType int profileType, String callingPackage) {
+ if (!checkPermission(Binder.getCallingUid(), callingPackage)) {
+ return false;
+ }
switch (profileType) {
case ArtManager.PROFILE_APPS :
@@ -306,27 +341,6 @@
}
/**
- * Verify that the binder calling uid has {@code android.permission.READ_RUNTIME_PROFILE}.
- * If not, it throws a {@link SecurityException}.
- */
- private void checkReadRuntimeProfilePermission() {
- if (DEBUG_IGNORE_PERMISSIONS) {
- return;
- }
- try {
- int result = mPackageManager.checkUidPermission(
- Manifest.permission.READ_RUNTIME_PROFILES, Binder.getCallingUid());
- if (result != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("You need "
- + Manifest.permission.READ_RUNTIME_PROFILES
- + " permission to snapshot profiles.");
- }
- } catch (RemoteException e) {
- // Should not happen.
- }
- }
-
- /**
* Prepare the application profiles.
* For all code paths:
* - create the current primary profile to save time at app startup time.