Protect usage data with OP_GET_USAGE_STATS.
APIs that return package usage data (such as DropBoxManager) must
ensure that callers hold both the PACKAGE_USAGE_STATS permission
and the OP_GET_USAGE_STATS app-op.
Bug: 78355661
Test: Search output directory for binaries that have READ_LOGS but not
USAGE_STATS and find none.
Change-Id: I85e3bad680bb510439d73c7db5cc50cdcb7bbb42
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 8db7de3..9b6f631 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -7575,7 +7575,7 @@
Lcom/android/internal/os/HandlerCaller;->obtainMessageOOO(ILjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Landroid/os/Message;
Lcom/android/internal/os/HandlerCaller;->sendMessage(Landroid/os/Message;)V
Lcom/android/internal/os/IDropBoxManagerService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/os/IDropBoxManagerService;
-Lcom/android/internal/os/IDropBoxManagerService;->getNextEntry(Ljava/lang/String;J)Landroid/os/DropBoxManager$Entry;
+Lcom/android/internal/os/IDropBoxManagerService;->getNextEntry(Ljava/lang/String;JLjava/lang/String;)Landroid/os/DropBoxManager$Entry;
Lcom/android/internal/os/PowerProfile;-><init>(Landroid/content/Context;)V
Lcom/android/internal/os/PowerProfile;->getAveragePower(Ljava/lang/String;)D
Lcom/android/internal/os/PowerProfile;->getAveragePower(Ljava/lang/String;I)D
diff --git a/core/java/android/os/DropBoxManager.java b/core/java/android/os/DropBoxManager.java
index 97f0e0c..eeae25e 100644
--- a/core/java/android/os/DropBoxManager.java
+++ b/core/java/android/os/DropBoxManager.java
@@ -16,9 +16,14 @@
package android.os;
+import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+import static android.Manifest.permission.READ_LOGS;
+
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
-import android.annotation.SystemService;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemService;
import android.content.Context;
import android.util.Log;
@@ -351,16 +356,23 @@
/**
* Gets the next entry from the drop box <em>after</em> the specified time.
- * Requires <code>android.permission.READ_LOGS</code>. You must always call
- * {@link Entry#close()} on the return value!
+ * You must always call {@link Entry#close()} on the return value!
*
* @param tag of entry to look for, null for all tags
* @param msec time of the last entry seen
* @return the next entry, or null if there are no more entries
*/
- public Entry getNextEntry(String tag, long msec) {
+ @RequiresPermission(allOf = { READ_LOGS, PACKAGE_USAGE_STATS })
+ public @Nullable Entry getNextEntry(String tag, long msec) {
try {
- return mService.getNextEntry(tag, msec);
+ return mService.getNextEntry(tag, msec, mContext.getOpPackageName());
+ } catch (SecurityException e) {
+ if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
+ throw e;
+ } else {
+ Log.w(TAG, e.getMessage());
+ return null;
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/com/android/internal/os/IDropBoxManagerService.aidl b/core/java/com/android/internal/os/IDropBoxManagerService.aidl
index d16579c..70844ee 100644
--- a/core/java/com/android/internal/os/IDropBoxManagerService.aidl
+++ b/core/java/com/android/internal/os/IDropBoxManagerService.aidl
@@ -37,5 +37,5 @@
boolean isTagEnabled(String tag);
/** @see DropBoxManager#getNextEntry */
- DropBoxManager.Entry getNextEntry(String tag, long millis);
+ DropBoxManager.Entry getNextEntry(String tag, long millis, String packageName);
}
diff --git a/libs/services/src/os/DropBoxManager.cpp b/libs/services/src/os/DropBoxManager.cpp
index c2907a6..8282518 100644
--- a/libs/services/src/os/DropBoxManager.cpp
+++ b/libs/services/src/os/DropBoxManager.cpp
@@ -236,7 +236,7 @@
if (service == NULL) {
return Status::fromExceptionCode(Status::EX_NULL_POINTER, "can't find dropbox service");
}
- return service->getNextEntry(tag, msec, entry);
+ return service->getNextEntry(tag, msec, android::String16("android"), entry);
}
}} // namespace android::os
diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java
index 887de74..b393d87 100644
--- a/services/core/java/com/android/server/DropBoxManagerService.java
+++ b/services/core/java/com/android/server/DropBoxManagerService.java
@@ -17,12 +17,12 @@
package com.android.server;
import android.app.ActivityManager;
+import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
@@ -41,13 +41,13 @@
import android.util.ArrayMap;
import android.util.Slog;
-import libcore.io.IoUtils;
-
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IDropBoxManagerService;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.ObjectUtils;
+import libcore.io.IoUtils;
+
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileDescriptor;
@@ -58,7 +58,6 @@
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Objects;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.zip.GZIPOutputStream;
@@ -145,8 +144,8 @@
}
@Override
- public DropBoxManager.Entry getNextEntry(String tag, long millis) {
- return DropBoxManagerService.this.getNextEntry(tag, millis);
+ public DropBoxManager.Entry getNextEntry(String tag, long millis, String callingPackage) {
+ return DropBoxManagerService.this.getNextEntry(tag, millis, callingPackage);
}
@Override
@@ -327,10 +326,29 @@
}
}
- public synchronized DropBoxManager.Entry getNextEntry(String tag, long millis) {
- if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.READ_LOGS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("READ_LOGS permission required");
+ private boolean checkPermission(int callingUid, String callingPackage) {
+ // Callers always need this permission
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_LOGS, TAG);
+
+ // Callers also need the ability to read usage statistics
+ switch (getContext().getSystemService(AppOpsManager.class)
+ .noteOp(AppOpsManager.OP_GET_USAGE_STATS, callingUid, callingPackage)) {
+ case AppOpsManager.MODE_ALLOWED:
+ return true;
+ case AppOpsManager.MODE_DEFAULT:
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.PACKAGE_USAGE_STATS, TAG);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public synchronized DropBoxManager.Entry getNextEntry(String tag, long millis,
+ String callingPackage) {
+ if (!checkPermission(Binder.getCallingUid(), callingPackage)) {
+ return null;
}
try {