diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 4a12ff6932dee..6614e06aba8c7 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -5276,8 +5276,12 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(" Limit output to data associated with the given package name."); pw.println(" --attributionTag [attributionTag]"); pw.println(" Limit output to data associated with the given attribution tag."); + pw.println(" --include-discrete [n]"); + pw.println(" Include discrete ops limited to n per dimension. Use zero for no limit."); pw.println(" --watchers"); pw.println(" Only output the watcher sections."); + pw.println(" --history"); + pw.println(" Only output history."); } private void dumpStatesLocked(@NonNull PrintWriter pw, @Nullable String filterAttributionTag, @@ -5412,10 +5416,12 @@ public class AppOpsService extends IAppOpsService.Stub { boolean dumpWatchers = false; // TODO ntmyren: Remove the dumpHistory and dumpFilter boolean dumpHistory = false; + boolean includeDiscreteOps = false; + int nDiscreteOps = 10; @HistoricalOpsRequestFilter int dumpFilter = 0; if (args != null) { - for (int i=0; i 0 && arg.charAt(0) == '-'){ + } else if ("--include-discrete".equals(arg)) { + i++; + if (i >= args.length) { + pw.println("No argument for --include-discrete option"); + return; + } + try { + nDiscreteOps = Integer.valueOf(args[i]); + } catch (NumberFormatException e) { + pw.println("Wrong parameter: " + args[i]); + return; + } + includeDiscreteOps = true; + } else if ("--history".equals(arg)) { + dumpHistory = true; + } else if (arg.length() > 0 && arg.charAt(0) == '-') { pw.println("Unknown option: " + arg); return; } else { @@ -5483,6 +5504,8 @@ public class AppOpsService extends IAppOpsService.Stub { } } + final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + final Date date = new Date(); synchronized (this) { pw.println("Current AppOps Service state:"); if (!dumpHistory && !dumpWatchers) { @@ -5492,8 +5515,6 @@ public class AppOpsService extends IAppOpsService.Stub { final long now = System.currentTimeMillis(); final long nowElapsed = SystemClock.elapsedRealtime(); final long nowUptime = SystemClock.uptimeMillis(); - final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - final Date date = new Date(); boolean needSep = false; if (dumpFilter == 0 && dumpMode < 0 && mProfileOwners != null && !dumpWatchers && !dumpHistory) { @@ -5961,6 +5982,11 @@ public class AppOpsService extends IAppOpsService.Stub { mHistoricalRegistry.dump(" ", pw, dumpUid, dumpPackage, dumpAttributionTag, dumpOp, dumpFilter); } + if (includeDiscreteOps) { + pw.println("Discrete accesses: "); + mHistoricalRegistry.dumpDiscreteData(pw, dumpUid, dumpPackage, dumpAttributionTag, + dumpFilter, dumpOp, sdf, date, " ", nDiscreteOps); + } } @Override diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java index a99d90883f876..ed62abc7d7731 100644 --- a/services/core/java/com/android/server/appop/DiscreteRegistry.java +++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java @@ -23,9 +23,13 @@ import static android.app.AppOpsManager.FILTER_BY_UID; import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_COARSE_LOCATION; import static android.app.AppOpsManager.OP_FINE_LOCATION; +import static android.app.AppOpsManager.OP_FLAGS_ALL; import static android.app.AppOpsManager.OP_FLAG_SELF; import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED; +import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.OP_RECORD_AUDIO; +import static android.app.AppOpsManager.flagsToString; +import static android.app.AppOpsManager.getUidStateName; import static java.lang.Math.max; @@ -45,15 +49,20 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ArrayUtils; import com.android.internal.util.XmlUtils; +import libcore.util.EmptyArray; + import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Date; import java.util.List; /** @@ -237,6 +246,23 @@ final class DiscreteRegistry { } } + void dump(@NonNull PrintWriter pw, int uidFilter, @Nullable String packageNameFilter, + @Nullable String attributionTagFilter, + @AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp, + @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix, + int nDiscreteOps) { + DiscreteOps discreteOps = new DiscreteOps(); + synchronized (mOnDiskLock) { + writeAndClearAccessHistory(); + String[] opNamesFilter = dumpOp == OP_NONE ? EmptyArray.STRING + : new String[]{AppOpsManager.opToPublicName(dumpOp)}; + readDiscreteOpsFromDisk(discreteOps, 0, Instant.now().toEpochMilli(), filter, + uidFilter, packageNameFilter, opNamesFilter, attributionTagFilter, + OP_FLAGS_ALL); + } + discreteOps.dump(pw, sdf, date, prefix, nDiscreteOps); + } + public static boolean isDiscreteOp(int op, int uid, @AppOpsManager.OpFlags int flags) { if (!isDiscreteOp(op)) { return false; @@ -306,6 +332,18 @@ final class DiscreteRegistry { stream.close(); } + private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf, + @NonNull Date date, @NonNull String prefix, int nDiscreteOps) { + int nUids = mUids.size(); + for (int i = 0; i < nUids; i++) { + pw.print(prefix); + pw.print("Uid: "); + pw.print(mUids.keyAt(i)); + pw.println(); + mUids.valueAt(i).dump(pw, sdf, date, prefix + " ", nDiscreteOps); + } + } + private DiscreteUidOps getOrCreateDiscreteUidOps(int uid) { DiscreteUidOps result = mUids.get(uid); if (result == null) { @@ -395,6 +433,18 @@ final class DiscreteRegistry { } } + private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf, + @NonNull Date date, @NonNull String prefix, int nDiscreteOps) { + int nPackages = mPackages.size(); + for (int i = 0; i < nPackages; i++) { + pw.print(prefix); + pw.print("Package: "); + pw.print(mPackages.keyAt(i)); + pw.println(); + mPackages.valueAt(i).dump(pw, sdf, date, prefix + " ", nDiscreteOps); + } + } + void deserialize(TypedXmlPullParser parser, long beginTimeMillis, long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, @Nullable String packageNameFilter, @@ -458,6 +508,17 @@ final class DiscreteRegistry { } } + private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf, + @NonNull Date date, @NonNull String prefix, int nDiscreteOps) { + int nOps = mPackageOps.size(); + for (int i = 0; i < nOps; i++) { + pw.print(prefix); + pw.print(AppOpsManager.opToName(mPackageOps.keyAt(i))); + pw.println(); + mPackageOps.valueAt(i).dump(pw, sdf, date, prefix + " ", nDiscreteOps); + } + } + void deserialize(TypedXmlPullParser parser, long beginTimeMillis, long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, @Nullable String[] opNamesFilter, @Nullable String attributionTagFilter, @@ -535,6 +596,24 @@ final class DiscreteRegistry { } } + private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf, + @NonNull Date date, @NonNull String prefix, int nDiscreteOps) { + int nAttributions = mAttributedOps.size(); + for (int i = 0; i < nAttributions; i++) { + pw.print(prefix); + pw.print("Attribution: "); + pw.print(mAttributedOps.keyAt(i)); + pw.println(); + List ops = mAttributedOps.valueAt(i); + int nOps = ops.size(); + int first = nDiscreteOps < 1 ? 0 : max(0, nOps - nDiscreteOps); + for (int j = first; j < nOps; j++) { + ops.get(j).dump(pw, sdf, date, prefix + " "); + + } + } + } + void serialize(TypedXmlSerializer out) throws Exception { int nAttributions = mAttributedOps.size(); for (int i = 0; i < nAttributions; i++) { @@ -609,6 +688,24 @@ final class DiscreteRegistry { mOpFlag = opFlag; } + private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf, + @NonNull Date date, @NonNull String prefix) { + pw.print(prefix); + pw.print("Access ["); + pw.print(getUidStateName(mUidState)); + pw.print("-"); + pw.print(flagsToString(mOpFlag)); + pw.print("] at "); + date.setTime(mNoteTime); + pw.print(sdf.format(date)); + if (mNoteDuration != -1) { + pw.print(" for "); + pw.print(mNoteDuration); + pw.print(" milliseconds "); + } + pw.println(); + } + private void serialize(TypedXmlSerializer out) throws Exception { out.attributeLong(null, ATTR_NOTE_TIME, mNoteTime); if (mNoteDuration != -1) { diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java index 0fcf5ee35e11a..22d628b8e7899 100644 --- a/services/core/java/com/android/server/appop/HistoricalRegistry.java +++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java @@ -349,6 +349,15 @@ final class HistoricalRegistry { } } + void dumpDiscreteData(@NonNull PrintWriter pw, int uidFilter, + @Nullable String packageNameFilter, @Nullable String attributionTagFilter, + @HistoricalOpsRequestFilter int filter, int dumpOp, + @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix, + int nDiscreteOps) { + mDiscreteRegistry.dump(pw, uidFilter, packageNameFilter, attributionTagFilter, filter, + dumpOp, sdf, date, prefix, nDiscreteOps); + } + @HistoricalMode int getMode() { synchronized (mInMemoryLock) { return mMode;