diff options
| author | 2024-09-13 06:41:13 +0000 | |
|---|---|---|
| committer | 2024-09-13 06:41:13 +0000 | |
| commit | dcff4e805371fc4f4cb15d9470d4eb48a5156771 (patch) | |
| tree | 5254817d01f3c73edd72ba659c627a906b25c59b | |
| parent | 27ee47bc87d966d10668fbd3096c79c3ce63629c (diff) | |
| parent | a60bd295bfcfbb457e6f8c41eeb9bf47675ee670 (diff) | |
Merge "Log AppOp access events in DiscreteRegistry" into main
5 files changed, 107 insertions, 24 deletions
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig index 991611a1af10..b0791e36e124 100644 --- a/core/java/android/permission/flags.aconfig +++ b/core/java/android/permission/flags.aconfig @@ -257,3 +257,11 @@ flag { description: "This flag is used to enable replacing permission BODY_SENSORS(and BODY_SENSORS_BACKGROUND) with granular health permission READ_HEART_RATE(and READ_HEALTH_DATA_IN_BACKGROUND)" bug: "364638912" } + +flag { + name: "appop_access_tracking_logging_enabled" + is_fixed_read_only: true + namespace: "permissions" + description: "Enables logging of the AppOp access tracking" + bug: "365584286" +} diff --git a/services/core/java/com/android/server/appop/AttributedOp.java b/services/core/java/com/android/server/appop/AttributedOp.java index 430be035737a..314664b0a79d 100644 --- a/services/core/java/com/android/server/appop/AttributedOp.java +++ b/services/core/java/com/android/server/appop/AttributedOp.java @@ -110,7 +110,8 @@ final class AttributedOp { mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName, persistentDeviceId, tag, uidState, flags, accessTime, - AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE); + AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE, + DiscreteRegistry.ACCESS_TYPE_NOTE_OP); } /** @@ -254,7 +255,7 @@ final class AttributedOp { if (isStarted) { mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName, persistentDeviceId, tag, uidState, flags, startTime, - attributionFlags, attributionChainId); + attributionFlags, attributionChainId, DiscreteRegistry.ACCESS_TYPE_START_OP); } } @@ -290,12 +291,17 @@ final class AttributedOp { * stopping in the HistoricalRegistry, but does not delete it. * * @param triggeredByUidStateChange If {@code true}, then this method operates as usual, except - * that {@link AppOpsService#mActiveWatchers} will not be notified. This is currently only - * used in {@link #onUidStateChanged(int)}, for the purpose of restarting (i.e., - * finishing then immediately starting again in the new uid state) the AttributedOp. In this - * case, the caller is responsible for guaranteeing that either the AttributedOp is started - * again or all {@link AppOpsService#mActiveWatchers} are notified that the AttributedOp is - * finished. + * that {@link AppOpsService#mActiveWatchers} will not be + * notified. This is currently only + * used in {@link #onUidStateChanged(int)}, for the purpose of + * restarting (i.e., + * finishing then immediately starting again in the new uid + * state) the AttributedOp. In this + * case, the caller is responsible for guaranteeing that either + * the AttributedOp is started + * again or all {@link AppOpsService#mActiveWatchers} are + * notified that the AttributedOp is + * finished. */ @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService private void finishOrPause(@NonNull IBinder clientId, boolean triggeredByUidStateChange, @@ -335,7 +341,9 @@ final class AttributedOp { mAppOpsService.mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid, parent.packageName, persistentDeviceId, tag, event.getUidState(), event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration(), - event.getAttributionFlags(), event.getAttributionChainId()); + event.getAttributionFlags(), event.getAttributionChainId(), + isPausing ? DiscreteRegistry.ACCESS_TYPE_PAUSE_OP + : DiscreteRegistry.ACCESS_TYPE_FINISH_OP); if (!isPausing) { mAppOpsService.mInProgressStartOpEventPool.release(event); @@ -443,7 +451,7 @@ final class AttributedOp { mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName, persistentDeviceId, tag, event.getUidState(), event.getFlags(), startTime, event.getAttributionFlags(), - event.getAttributionChainId()); + event.getAttributionChainId(), DiscreteRegistry.ACCESS_TYPE_RESUME_OP); if (shouldSendActive) { mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, parent.packageName, tag, event.getVirtualDeviceId(), true, @@ -864,12 +872,12 @@ final class AttributedOp { } InProgressStartOpEvent acquire(long startTime, long elapsedTime, @NonNull IBinder clientId, - @Nullable String attributionTag, int virtualDeviceId, @NonNull Runnable onDeath, + @Nullable String attributionTag, int virtualDeviceId, @NonNull Runnable onDeath, int proxyUid, @Nullable String proxyPackageName, @Nullable String proxyAttributionTag, @Nullable String proxyDeviceId, - @AppOpsManager.UidState int uidState, - @AppOpsManager.OpFlags int flags, @AppOpsManager.AttributionFlags - int attributionFlags, int attributionChainId) throws RemoteException { + @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, + @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) + throws RemoteException { InProgressStartOpEvent recycled = acquire(); diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java index 2ce4623a19b4..7f161f618618 100644 --- a/services/core/java/com/android/server/appop/DiscreteRegistry.java +++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java @@ -32,13 +32,23 @@ 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_FLAG_TRUSTED_PROXY; +import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION; +import static android.app.AppOpsManager.OP_MONITOR_LOCATION; import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA; import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE; +import static android.app.AppOpsManager.OP_PROCESS_OUTGOING_CALLS; +import static android.app.AppOpsManager.OP_READ_ICC_SMS; +import static android.app.AppOpsManager.OP_READ_SMS; import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO; import static android.app.AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.OP_RESERVED_FOR_TESTING; +import static android.app.AppOpsManager.OP_SEND_SMS; +import static android.app.AppOpsManager.OP_SMS_FINANCIAL_TRANSACTIONS; +import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; +import static android.app.AppOpsManager.OP_WRITE_ICC_SMS; +import static android.app.AppOpsManager.OP_WRITE_SMS; import static android.app.AppOpsManager.flagsToString; import static android.app.AppOpsManager.getUidStateName; import static android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT; @@ -46,6 +56,7 @@ import static android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_I import static java.lang.Long.min; import static java.lang.Math.max; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; @@ -62,6 +73,7 @@ import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; @@ -72,6 +84,8 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.text.SimpleDateFormat; import java.time.Duration; import java.time.Instant; @@ -125,7 +139,6 @@ import java.util.Set; * relies on {@link HistoricalRegistry} for controlling that no calls are allowed until then. All * outside calls are going through {@link HistoricalRegistry}, where * {@link HistoricalRegistry#isPersistenceInitializedMLocked()} check is done. - * */ final class DiscreteRegistry { @@ -142,11 +155,40 @@ final class DiscreteRegistry { + OP_PHONE_CALL_MICROPHONE + "," + OP_PHONE_CALL_CAMERA + "," + OP_RECEIVE_AMBIENT_TRIGGER_AUDIO + "," + OP_RECEIVE_SANDBOX_TRIGGER_AUDIO + "," + OP_RESERVED_FOR_TESTING; + private static final int[] sDiscreteOpsToLog = + new int[]{OP_FINE_LOCATION, OP_COARSE_LOCATION, OP_EMERGENCY_LOCATION, OP_CAMERA, + OP_RECORD_AUDIO, OP_PHONE_CALL_MICROPHONE, OP_PHONE_CALL_CAMERA, + OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, OP_RECEIVE_SANDBOX_TRIGGER_AUDIO, OP_READ_SMS, + OP_WRITE_SMS, OP_SEND_SMS, OP_READ_ICC_SMS, OP_WRITE_ICC_SMS, + OP_SMS_FINANCIAL_TRANSACTIONS, OP_SYSTEM_ALERT_WINDOW, OP_MONITOR_LOCATION, + OP_MONITOR_HIGH_POWER_LOCATION, OP_PROCESS_OUTGOING_CALLS, + }; private static final long DEFAULT_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(7).toMillis(); private static final long MAXIMUM_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(30).toMillis(); private static final long DEFAULT_DISCRETE_HISTORY_QUANTIZATION = Duration.ofMinutes(1).toMillis(); + static final int ACCESS_TYPE_NOTE_OP = + FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__NOTE_OP; + static final int ACCESS_TYPE_START_OP = + FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__START_OP; + static final int ACCESS_TYPE_FINISH_OP = + FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__FINISH_OP; + static final int ACCESS_TYPE_PAUSE_OP = + FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__PAUSE_OP; + static final int ACCESS_TYPE_RESUME_OP = + FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__RESUME_OP; + + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"ACCESS_TYPE_"}, value = { + ACCESS_TYPE_NOTE_OP, + ACCESS_TYPE_START_OP, + ACCESS_TYPE_FINISH_OP, + ACCESS_TYPE_PAUSE_OP, + ACCESS_TYPE_RESUME_OP + }) + public @interface AccessType {} + private static long sDiscreteHistoryCutoff; private static long sDiscreteHistoryQuantization; private static int[] sDiscreteOps; @@ -255,7 +297,23 @@ final class DiscreteRegistry { void recordDiscreteAccess(int uid, String packageName, @NonNull String deviceId, int op, @Nullable String attributionTag, @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, long accessTime, long accessDuration, - @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) { + @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId, + @AccessType int accessType) { + if (shouldLogAccess(op)) { + int firstChar = 0; + if (attributionTag != null && attributionTag.startsWith(packageName)) { + firstChar = packageName.length(); + if (firstChar < attributionTag.length() && attributionTag.charAt(firstChar) + == '.') { + firstChar++; + } + } + FrameworkStatsLog.write(FrameworkStatsLog.APP_OP_ACCESS_TRACKED, uid, op, accessType, + uidState, flags, attributionFlags, + attributionTag == null ? null : attributionTag.substring(firstChar), + attributionChainId); + } + if (!isDiscreteOp(op, flags)) { return; } @@ -388,7 +446,7 @@ final class DiscreteRegistry { if (event == null || event.mAttributionChainId == ATTRIBUTION_CHAIN_ID_NONE || (event.mAttributionFlags & ATTRIBUTION_FLAG_TRUSTED) - == 0) { + == 0) { continue; } @@ -1523,6 +1581,11 @@ final class DiscreteRegistry { return true; } + private static boolean shouldLogAccess(int op) { + return Flags.appopAccessTrackingLoggingEnabled() + && ArrayUtils.contains(sDiscreteOpsToLog, op); + } + private static long discretizeTimeStamp(long timeStamp) { return timeStamp / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization; @@ -1530,7 +1593,7 @@ final class DiscreteRegistry { private static long discretizeDuration(long duration) { return duration == -1 ? -1 : (duration + sDiscreteHistoryQuantization - 1) - / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization; + / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization; } void setDebugMode(boolean debugMode) { diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java index fffb1082acb1..6b0253864e2b 100644 --- a/services/core/java/com/android/server/appop/HistoricalRegistry.java +++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java @@ -474,7 +474,8 @@ final class HistoricalRegistry { void incrementOpAccessedCount(int op, int uid, @NonNull String packageName, @NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, long accessTime, - @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) { + @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId, + @DiscreteRegistry.AccessType int accessType) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (!isPersistenceInitializedMLocked()) { @@ -487,7 +488,7 @@ final class HistoricalRegistry { mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, attributionTag, flags, uidState, accessTime, -1, attributionFlags, - attributionChainId); + attributionChainId, accessType); } } } @@ -510,7 +511,8 @@ final class HistoricalRegistry { void increaseOpAccessDuration(int op, int uid, @NonNull String packageName, @NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, long eventStartTime, long increment, - @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) { + @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId, + @DiscreteRegistry.AccessType int accessType) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (!isPersistenceInitializedMLocked()) { @@ -522,7 +524,7 @@ final class HistoricalRegistry { attributionTag, uidState, flags, increment); mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, attributionTag, flags, uidState, eventStartTime, increment, - attributionFlags, attributionChainId); + attributionFlags, attributionChainId, accessType); } } } diff --git a/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpPersistenceTest.java b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpPersistenceTest.java index bc3a5ca6f7e6..2ff0c6288ece 100644 --- a/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpPersistenceTest.java +++ b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpPersistenceTest.java @@ -86,7 +86,8 @@ public class DiscreteAppOpPersistenceTest { int attributionChainId = AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE; mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, null, opFlags, - uidState, accessTime, duration, attributionFlags, attributionChainId); + uidState, accessTime, duration, attributionFlags, attributionChainId, + DiscreteRegistry.ACCESS_TYPE_FINISH_OP); // Verify in-memory object is correct fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime, @@ -117,7 +118,8 @@ public class DiscreteAppOpPersistenceTest { int attributionChainId = 10; mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, null, opFlags, - uidState, accessTime, duration, attributionFlags, attributionChainId); + uidState, accessTime, duration, attributionFlags, attributionChainId, + DiscreteRegistry.ACCESS_TYPE_START_OP); fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime, duration, uidState, opFlags, attributionFlags, attributionChainId); |