Loading services/core/java/com/android/server/appop/AppOpHistoryHelper.java +201 −10 Original line number Diff line number Diff line Loading @@ -22,9 +22,14 @@ import static android.app.AppOpsManager.ATTRIBUTION_FLAG_RECEIVER; import static android.app.AppOpsManager.ATTRIBUTION_FLAG_TRUSTED; import static android.app.AppOpsManager.HISTORY_FLAG_AGGREGATE; import static android.app.AppOpsManager.HISTORY_FLAG_DISCRETE; import static android.app.AppOpsManager.MAX_PRIORITY_UID_STATE; import static android.app.AppOpsManager.OP_FLAG_SELF; import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED; import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED; import static android.app.AppOpsManager.flagsToString; import static android.app.AppOpsManager.getUidStateName; import static com.android.internal.util.FrameworkStatsLog.AGGREGATED_APP_OP_ACCESS_EVENT_REPORTED; import static com.android.internal.util.FrameworkStatsLog.SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_CACHE_FULL; import static com.android.internal.util.FrameworkStatsLog.SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_MIGRATION; import static com.android.internal.util.FrameworkStatsLog.SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_PERIODIC; Loading @@ -43,8 +48,11 @@ import android.os.Process; import android.util.ArrayMap; import android.util.IntArray; import android.util.LongSparseArray; import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; import com.android.server.ServiceThread; import java.io.File; Loading Loading @@ -93,6 +101,7 @@ public class AppOpHistoryHelper { private final Context mContext; private final AppOpHistoryDbHelper mDbHelper; private final SqliteWriteHandler mSqliteWriteHandler; private final MetricHandler mMetricHandler; private final AppOpHistoryCache mCache = new AppOpHistoryCache(1024); AppOpHistoryHelper(@NonNull Context context, File databaseFile, Loading @@ -105,6 +114,7 @@ public class AppOpHistoryHelper { new ServiceThread(TAG, Process.THREAD_PRIORITY_DEFAULT, true); thread.start(); mSqliteWriteHandler = new SqliteWriteHandler(thread.getLooper()); mMetricHandler = new MetricHandler(thread.getLooper()); } // Set parameters before using this class. Loading Loading @@ -223,6 +233,12 @@ public class AppOpHistoryHelper { opEvent.totalDurationMillis()); } private void insertAppOpHistory(@NonNull List<AggregatedAppOpAccessEvent> appOpEvents, int writeSource) { mDbHelper.insertAppOpHistory(appOpEvents, writeSource); sendToMetricsHandler(appOpEvents); } private List<AggregatedAppOpAccessEvent> getAppOpHistory(AppOpsManager.HistoricalOps result, long beginTimeMillis, long endTimeMillis, int filter, int uidFilter, @Nullable String packageNameFilter, Loading @@ -231,10 +247,10 @@ public class AppOpHistoryHelper { IntArray opCodes = AppOpHistoryQueryHelper.getAppOpCodes(filter, opNamesFilter); // flush the cache into database before read. if (opCodes != null) { mDbHelper.insertAppOpHistory(mCache.evict(opCodes), insertAppOpHistory(mCache.evict(opCodes), SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_READ); } else { mDbHelper.insertAppOpHistory(mCache.evictAll(), insertAppOpHistory(mCache.evictAll(), SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_READ); } // Adjust begin & end time to time window's boundary. Loading @@ -259,8 +275,11 @@ public class AppOpHistoryHelper { void shutdown() { mSqliteWriteHandler.removeAllPendingMessages(); mDbHelper.insertAppOpHistory(mCache.evictAll(), insertAppOpHistory(mCache.evictAll(), SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_SHUTDOWN); // Remove pending delayed message. mMetricHandler.removeMessages(MetricHandler.SEND_EVENTS); mMetricHandler.sendEmptyMessage(MetricHandler.SEND_EVENTS); mDbHelper.close(); } Loading Loading @@ -308,10 +327,10 @@ public class AppOpHistoryHelper { IntArray opCodes = AppOpHistoryQueryHelper.getAppOpCodes(filter, opNamesFilter); // flush the cache into database before read. if (opCodes != null) { mDbHelper.insertAppOpHistory(mCache.evict(opCodes), insertAppOpHistory(mCache.evict(opCodes), SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_READ); } else { mDbHelper.insertAppOpHistory(mCache.evictAll(), insertAppOpHistory(mCache.evictAll(), SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_READ); } // Adjust begin & end time to time window's boundary. Loading Loading @@ -349,7 +368,7 @@ public class AppOpHistoryHelper { @AppOpsManager.HistoricalOpsRequestFilter int filter, @NonNull SimpleDateFormat sdf, @NonNull Date date, int limit) { // flush caches to the database mDbHelper.insertAppOpHistory(mCache.evictAll(), insertAppOpHistory(mCache.evictAll(), SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_READ); long currentTime = System.currentTimeMillis(); long beginTimeMillis = discretizeTimestamp(currentTime - mHistoryRetentionMillis); Loading Loading @@ -408,7 +427,7 @@ public class AppOpHistoryHelper { switch (msg.what) { case WRITE_DATABASE_PERIODIC -> { try { mDbHelper.insertAppOpHistory(mCache.evict(), insertAppOpHistory(mCache.evict(), SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_PERIODIC); } finally { ensurePeriodicJobsAreScheduled(); Loading @@ -425,7 +444,7 @@ public class AppOpHistoryHelper { evictedEvents.addAll(mCache.evictAll()); } } mDbHelper.insertAppOpHistory(evictedEvents, insertAppOpHistory(evictedEvents, SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_CACHE_FULL); } finally { ensurePeriodicJobsAreScheduled(); Loading @@ -451,6 +470,178 @@ public class AppOpHistoryHelper { } } void sendToMetricsHandler(@NonNull List<AggregatedAppOpAccessEvent> accessEvents) { Message msg = mMetricHandler.obtainMessage(MetricHandler.RECEIVE_EVENTS, accessEvents); mMetricHandler.sendMessage(msg); } private record AppOpMetricKey( @NonNull String packageName, int opCode, @Nullable String attributionTag ) { } private static class AppOpMetricValue { private long mForegroundDurationMillis; private int mForegroundAccessCount; private int mForegroundRejectCount; private long mBackgroundDurationMillis; private int mBackgroundAccessCount; private int mBackgroundRejectCount; AppOpMetricValue( long foregroundDurationMillis, int foregroundAccessCount, int foregroundRejectCount, long backgroundDurationMillis, int backgroundAccessCount, int backgroundRejectCount ) { this.mForegroundDurationMillis = foregroundDurationMillis; this.mForegroundAccessCount = foregroundAccessCount; this.mForegroundRejectCount = foregroundRejectCount; this.mBackgroundDurationMillis = backgroundDurationMillis; this.mBackgroundAccessCount = backgroundAccessCount; this.mBackgroundRejectCount = backgroundRejectCount; } void addForegroundDurationMillis(long foregroundDurationMillis) { this.mForegroundDurationMillis += foregroundDurationMillis; } void addForegroundAccessCount(int foregroundAccessCount) { this.mForegroundAccessCount += foregroundAccessCount; } void addForegroundRejectCount(int foregroundRejectCount) { this.mForegroundRejectCount += foregroundRejectCount; } void addBackgroundDurationMillis(long backgroundDurationMillis) { this.mBackgroundDurationMillis += backgroundDurationMillis; } void addBackgroundAccessCount(int backgroundAccessCount) { this.mBackgroundAccessCount += backgroundAccessCount; } void addBackgroundRejectCount(int backgroundRejectCount) { this.mBackgroundRejectCount += backgroundRejectCount; } } /** * This handler reports aggregated app op access events to StatsD. */ private static class MetricHandler extends Handler { // Process events after 2 minutes once received by metrics handler. private static final long EVENT_PROCESSING_DELAY_MILLIS = Duration.ofMinutes(2).toMillis(); // Process events immediately when reaching capacity limits. private static final int CACHE_CAPACITY = 128; private static final int OP_FLAGS_REPORTED = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED; static final int RECEIVE_EVENTS = 1; static final int SEND_EVENTS = 2; private final ArrayMap<AppOpMetricKey, AppOpMetricValue> mCache = new ArrayMap<>(); MetricHandler(Looper looper) { super(looper); } @Override public void handleMessage(@NonNull Message msg) { switch (msg.what) { case RECEIVE_EVENTS -> { List<AggregatedAppOpAccessEvent> events = (List<AggregatedAppOpAccessEvent>) msg.obj; if (events.isEmpty()) { return; } synchronized (mCache) { for (AggregatedAppOpAccessEvent accessEvent : events) { if ((accessEvent.opFlags() & OP_FLAGS_REPORTED) == 0) { continue; } AppOpMetricKey metricKey = new AppOpMetricKey(accessEvent.packageName(), accessEvent.opCode(), accessEvent.attributionTag()); boolean isUidStateInForeground = accessEvent.uidState() >= MAX_PRIORITY_UID_STATE && accessEvent.uidState() <= UID_STATE_MAX_LAST_NON_RESTRICTED; long foregroundDurationMillis = 0; int foregroundAccessCount = 0; int foregroundRejectCount = 0; long backgroundDurationMillis = 0; int backgroundAccessCount = 0; int backgroundRejectCount = 0; if (isUidStateInForeground) { foregroundDurationMillis = accessEvent.totalDurationMillis(); foregroundAccessCount = accessEvent.totalAccessCount(); foregroundRejectCount = accessEvent.totalRejectCount(); } else { backgroundDurationMillis = accessEvent.totalDurationMillis(); backgroundAccessCount = accessEvent.totalAccessCount(); backgroundRejectCount = accessEvent.totalRejectCount(); } AppOpMetricValue metricValue = mCache.get(metricKey); if (metricValue != null) { metricValue.addForegroundDurationMillis(foregroundDurationMillis); metricValue.addForegroundAccessCount(foregroundAccessCount); metricValue.addForegroundRejectCount(foregroundRejectCount); metricValue.addBackgroundDurationMillis(backgroundDurationMillis); metricValue.addBackgroundAccessCount(backgroundAccessCount); metricValue.addBackgroundRejectCount(backgroundRejectCount); } else { mCache.put(metricKey, new AppOpMetricValue( foregroundDurationMillis, foregroundAccessCount, foregroundRejectCount, backgroundDurationMillis, backgroundAccessCount, backgroundRejectCount )); } } if (mCache.size() >= CACHE_CAPACITY) { removeMessages(SEND_EVENTS); // Remove any pending delayed messages sendEmptyMessage(SEND_EVENTS); } else if (!hasMessages(SEND_EVENTS)) { sendEmptyMessageDelayed(SEND_EVENTS, EVENT_PROCESSING_DELAY_MILLIS); } } } case SEND_EVENTS -> { List<Pair<AppOpMetricKey, AppOpMetricValue>> events; synchronized (mCache) { if (mCache.isEmpty()) { return; } events = new ArrayList<>(mCache.size()); for (Map.Entry<AppOpMetricKey, AppOpMetricValue> event: mCache.entrySet()) { events.add(new Pair<>(event.getKey(), event.getValue())); } mCache.clear(); } Slog.d(TAG, "MetricHandler reporting " + events.size() + " records."); for (Pair<AppOpMetricKey, AppOpMetricValue> event : events) { AppOpMetricKey metricKey = event.first; AppOpMetricValue value = event.second; FrameworkStatsLog.write(AGGREGATED_APP_OP_ACCESS_EVENT_REPORTED, metricKey.packageName, metricKey.attributionTag, metricKey.opCode, value.mForegroundAccessCount, value.mBackgroundAccessCount, value.mForegroundRejectCount, value.mBackgroundRejectCount, value.mForegroundDurationMillis, value.mBackgroundDurationMillis); } } } } } /** * A cache for aggregating app op accesses in a time window. Individual app op events * aren't stored on the disk, instead an aggregated events are persisted on the disk. Loading Loading
services/core/java/com/android/server/appop/AppOpHistoryHelper.java +201 −10 Original line number Diff line number Diff line Loading @@ -22,9 +22,14 @@ import static android.app.AppOpsManager.ATTRIBUTION_FLAG_RECEIVER; import static android.app.AppOpsManager.ATTRIBUTION_FLAG_TRUSTED; import static android.app.AppOpsManager.HISTORY_FLAG_AGGREGATE; import static android.app.AppOpsManager.HISTORY_FLAG_DISCRETE; import static android.app.AppOpsManager.MAX_PRIORITY_UID_STATE; import static android.app.AppOpsManager.OP_FLAG_SELF; import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED; import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED; import static android.app.AppOpsManager.flagsToString; import static android.app.AppOpsManager.getUidStateName; import static com.android.internal.util.FrameworkStatsLog.AGGREGATED_APP_OP_ACCESS_EVENT_REPORTED; import static com.android.internal.util.FrameworkStatsLog.SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_CACHE_FULL; import static com.android.internal.util.FrameworkStatsLog.SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_MIGRATION; import static com.android.internal.util.FrameworkStatsLog.SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_PERIODIC; Loading @@ -43,8 +48,11 @@ import android.os.Process; import android.util.ArrayMap; import android.util.IntArray; import android.util.LongSparseArray; import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; import com.android.server.ServiceThread; import java.io.File; Loading Loading @@ -93,6 +101,7 @@ public class AppOpHistoryHelper { private final Context mContext; private final AppOpHistoryDbHelper mDbHelper; private final SqliteWriteHandler mSqliteWriteHandler; private final MetricHandler mMetricHandler; private final AppOpHistoryCache mCache = new AppOpHistoryCache(1024); AppOpHistoryHelper(@NonNull Context context, File databaseFile, Loading @@ -105,6 +114,7 @@ public class AppOpHistoryHelper { new ServiceThread(TAG, Process.THREAD_PRIORITY_DEFAULT, true); thread.start(); mSqliteWriteHandler = new SqliteWriteHandler(thread.getLooper()); mMetricHandler = new MetricHandler(thread.getLooper()); } // Set parameters before using this class. Loading Loading @@ -223,6 +233,12 @@ public class AppOpHistoryHelper { opEvent.totalDurationMillis()); } private void insertAppOpHistory(@NonNull List<AggregatedAppOpAccessEvent> appOpEvents, int writeSource) { mDbHelper.insertAppOpHistory(appOpEvents, writeSource); sendToMetricsHandler(appOpEvents); } private List<AggregatedAppOpAccessEvent> getAppOpHistory(AppOpsManager.HistoricalOps result, long beginTimeMillis, long endTimeMillis, int filter, int uidFilter, @Nullable String packageNameFilter, Loading @@ -231,10 +247,10 @@ public class AppOpHistoryHelper { IntArray opCodes = AppOpHistoryQueryHelper.getAppOpCodes(filter, opNamesFilter); // flush the cache into database before read. if (opCodes != null) { mDbHelper.insertAppOpHistory(mCache.evict(opCodes), insertAppOpHistory(mCache.evict(opCodes), SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_READ); } else { mDbHelper.insertAppOpHistory(mCache.evictAll(), insertAppOpHistory(mCache.evictAll(), SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_READ); } // Adjust begin & end time to time window's boundary. Loading @@ -259,8 +275,11 @@ public class AppOpHistoryHelper { void shutdown() { mSqliteWriteHandler.removeAllPendingMessages(); mDbHelper.insertAppOpHistory(mCache.evictAll(), insertAppOpHistory(mCache.evictAll(), SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_SHUTDOWN); // Remove pending delayed message. mMetricHandler.removeMessages(MetricHandler.SEND_EVENTS); mMetricHandler.sendEmptyMessage(MetricHandler.SEND_EVENTS); mDbHelper.close(); } Loading Loading @@ -308,10 +327,10 @@ public class AppOpHistoryHelper { IntArray opCodes = AppOpHistoryQueryHelper.getAppOpCodes(filter, opNamesFilter); // flush the cache into database before read. if (opCodes != null) { mDbHelper.insertAppOpHistory(mCache.evict(opCodes), insertAppOpHistory(mCache.evict(opCodes), SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_READ); } else { mDbHelper.insertAppOpHistory(mCache.evictAll(), insertAppOpHistory(mCache.evictAll(), SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_READ); } // Adjust begin & end time to time window's boundary. Loading Loading @@ -349,7 +368,7 @@ public class AppOpHistoryHelper { @AppOpsManager.HistoricalOpsRequestFilter int filter, @NonNull SimpleDateFormat sdf, @NonNull Date date, int limit) { // flush caches to the database mDbHelper.insertAppOpHistory(mCache.evictAll(), insertAppOpHistory(mCache.evictAll(), SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_READ); long currentTime = System.currentTimeMillis(); long beginTimeMillis = discretizeTimestamp(currentTime - mHistoryRetentionMillis); Loading Loading @@ -408,7 +427,7 @@ public class AppOpHistoryHelper { switch (msg.what) { case WRITE_DATABASE_PERIODIC -> { try { mDbHelper.insertAppOpHistory(mCache.evict(), insertAppOpHistory(mCache.evict(), SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_PERIODIC); } finally { ensurePeriodicJobsAreScheduled(); Loading @@ -425,7 +444,7 @@ public class AppOpHistoryHelper { evictedEvents.addAll(mCache.evictAll()); } } mDbHelper.insertAppOpHistory(evictedEvents, insertAppOpHistory(evictedEvents, SQLITE_APP_OP_EVENT_REPORTED__WRITE_TYPE__WRITE_CACHE_FULL); } finally { ensurePeriodicJobsAreScheduled(); Loading @@ -451,6 +470,178 @@ public class AppOpHistoryHelper { } } void sendToMetricsHandler(@NonNull List<AggregatedAppOpAccessEvent> accessEvents) { Message msg = mMetricHandler.obtainMessage(MetricHandler.RECEIVE_EVENTS, accessEvents); mMetricHandler.sendMessage(msg); } private record AppOpMetricKey( @NonNull String packageName, int opCode, @Nullable String attributionTag ) { } private static class AppOpMetricValue { private long mForegroundDurationMillis; private int mForegroundAccessCount; private int mForegroundRejectCount; private long mBackgroundDurationMillis; private int mBackgroundAccessCount; private int mBackgroundRejectCount; AppOpMetricValue( long foregroundDurationMillis, int foregroundAccessCount, int foregroundRejectCount, long backgroundDurationMillis, int backgroundAccessCount, int backgroundRejectCount ) { this.mForegroundDurationMillis = foregroundDurationMillis; this.mForegroundAccessCount = foregroundAccessCount; this.mForegroundRejectCount = foregroundRejectCount; this.mBackgroundDurationMillis = backgroundDurationMillis; this.mBackgroundAccessCount = backgroundAccessCount; this.mBackgroundRejectCount = backgroundRejectCount; } void addForegroundDurationMillis(long foregroundDurationMillis) { this.mForegroundDurationMillis += foregroundDurationMillis; } void addForegroundAccessCount(int foregroundAccessCount) { this.mForegroundAccessCount += foregroundAccessCount; } void addForegroundRejectCount(int foregroundRejectCount) { this.mForegroundRejectCount += foregroundRejectCount; } void addBackgroundDurationMillis(long backgroundDurationMillis) { this.mBackgroundDurationMillis += backgroundDurationMillis; } void addBackgroundAccessCount(int backgroundAccessCount) { this.mBackgroundAccessCount += backgroundAccessCount; } void addBackgroundRejectCount(int backgroundRejectCount) { this.mBackgroundRejectCount += backgroundRejectCount; } } /** * This handler reports aggregated app op access events to StatsD. */ private static class MetricHandler extends Handler { // Process events after 2 minutes once received by metrics handler. private static final long EVENT_PROCESSING_DELAY_MILLIS = Duration.ofMinutes(2).toMillis(); // Process events immediately when reaching capacity limits. private static final int CACHE_CAPACITY = 128; private static final int OP_FLAGS_REPORTED = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED; static final int RECEIVE_EVENTS = 1; static final int SEND_EVENTS = 2; private final ArrayMap<AppOpMetricKey, AppOpMetricValue> mCache = new ArrayMap<>(); MetricHandler(Looper looper) { super(looper); } @Override public void handleMessage(@NonNull Message msg) { switch (msg.what) { case RECEIVE_EVENTS -> { List<AggregatedAppOpAccessEvent> events = (List<AggregatedAppOpAccessEvent>) msg.obj; if (events.isEmpty()) { return; } synchronized (mCache) { for (AggregatedAppOpAccessEvent accessEvent : events) { if ((accessEvent.opFlags() & OP_FLAGS_REPORTED) == 0) { continue; } AppOpMetricKey metricKey = new AppOpMetricKey(accessEvent.packageName(), accessEvent.opCode(), accessEvent.attributionTag()); boolean isUidStateInForeground = accessEvent.uidState() >= MAX_PRIORITY_UID_STATE && accessEvent.uidState() <= UID_STATE_MAX_LAST_NON_RESTRICTED; long foregroundDurationMillis = 0; int foregroundAccessCount = 0; int foregroundRejectCount = 0; long backgroundDurationMillis = 0; int backgroundAccessCount = 0; int backgroundRejectCount = 0; if (isUidStateInForeground) { foregroundDurationMillis = accessEvent.totalDurationMillis(); foregroundAccessCount = accessEvent.totalAccessCount(); foregroundRejectCount = accessEvent.totalRejectCount(); } else { backgroundDurationMillis = accessEvent.totalDurationMillis(); backgroundAccessCount = accessEvent.totalAccessCount(); backgroundRejectCount = accessEvent.totalRejectCount(); } AppOpMetricValue metricValue = mCache.get(metricKey); if (metricValue != null) { metricValue.addForegroundDurationMillis(foregroundDurationMillis); metricValue.addForegroundAccessCount(foregroundAccessCount); metricValue.addForegroundRejectCount(foregroundRejectCount); metricValue.addBackgroundDurationMillis(backgroundDurationMillis); metricValue.addBackgroundAccessCount(backgroundAccessCount); metricValue.addBackgroundRejectCount(backgroundRejectCount); } else { mCache.put(metricKey, new AppOpMetricValue( foregroundDurationMillis, foregroundAccessCount, foregroundRejectCount, backgroundDurationMillis, backgroundAccessCount, backgroundRejectCount )); } } if (mCache.size() >= CACHE_CAPACITY) { removeMessages(SEND_EVENTS); // Remove any pending delayed messages sendEmptyMessage(SEND_EVENTS); } else if (!hasMessages(SEND_EVENTS)) { sendEmptyMessageDelayed(SEND_EVENTS, EVENT_PROCESSING_DELAY_MILLIS); } } } case SEND_EVENTS -> { List<Pair<AppOpMetricKey, AppOpMetricValue>> events; synchronized (mCache) { if (mCache.isEmpty()) { return; } events = new ArrayList<>(mCache.size()); for (Map.Entry<AppOpMetricKey, AppOpMetricValue> event: mCache.entrySet()) { events.add(new Pair<>(event.getKey(), event.getValue())); } mCache.clear(); } Slog.d(TAG, "MetricHandler reporting " + events.size() + " records."); for (Pair<AppOpMetricKey, AppOpMetricValue> event : events) { AppOpMetricKey metricKey = event.first; AppOpMetricValue value = event.second; FrameworkStatsLog.write(AGGREGATED_APP_OP_ACCESS_EVENT_REPORTED, metricKey.packageName, metricKey.attributionTag, metricKey.opCode, value.mForegroundAccessCount, value.mBackgroundAccessCount, value.mForegroundRejectCount, value.mBackgroundRejectCount, value.mForegroundDurationMillis, value.mBackgroundDurationMillis); } } } } } /** * A cache for aggregating app op accesses in a time window. Individual app op events * aren't stored on the disk, instead an aggregated events are persisted on the disk. Loading