diff options
4 files changed, 275 insertions, 106 deletions
diff --git a/core/res/res/xml/irq_device_map.xml b/core/res/res/xml/irq_device_map.xml index 8b3667e9f2a9..2f894b99d603 100644 --- a/core/res/res/xml/irq_device_map.xml +++ b/core/res/res/xml/irq_device_map.xml @@ -28,6 +28,8 @@ - Wifi: Use this to denote network traffic that uses the wifi transport. - Sound_trigger: Use this to denote sound phrase detection, like the ones supported by SoundTriggerManager. + - Sensor: Use this to denote wakeups due to sensor events. + - Cellular_data: Use this to denote network traffic on the cellular transport. The overlay should use tags <device> and <subsystem> to describe this mapping in the following way: diff --git a/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java index 73ab7822ac39..712a6964e82d 100644 --- a/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java +++ b/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java @@ -23,6 +23,7 @@ import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN; import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI; +import android.annotation.SuppressLint; import android.app.ActivityManager; import android.content.Context; import android.os.Handler; @@ -48,6 +49,7 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; +import java.util.function.LongSupplier; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -62,15 +64,14 @@ public class CpuWakeupStats { private static final String SUBSYSTEM_SENSOR_STRING = "Sensor"; private static final String SUBSYSTEM_CELLULAR_DATA_STRING = "Cellular_data"; private static final String TRACE_TRACK_WAKEUP_ATTRIBUTION = "wakeup_attribution"; - @VisibleForTesting - static final long WAKEUP_REASON_HALF_WINDOW_MS = 500; + private static final long WAKEUP_WRITE_DELAY_MS = TimeUnit.SECONDS.toMillis(30); private final Handler mHandler; private final IrqDeviceMap mIrqDeviceMap; @VisibleForTesting final Config mConfig = new Config(); - private final WakingActivityHistory mRecentWakingActivity = new WakingActivityHistory(); + private final WakingActivityHistory mRecentWakingActivity; @VisibleForTesting final TimeSparseArray<Wakeup> mWakeupEvents = new TimeSparseArray<>(); @@ -85,6 +86,8 @@ public class CpuWakeupStats { public CpuWakeupStats(Context context, int mapRes, Handler handler) { mIrqDeviceMap = IrqDeviceMap.getInstance(context, mapRes); + mRecentWakingActivity = new WakingActivityHistory( + () -> mConfig.WAKING_ACTIVITY_RETENTION_MS); mHandler = handler; } @@ -202,15 +205,14 @@ public class CpuWakeupStats { /** Notes a wakeup reason as reported by SuspendControlService to battery stats. */ public synchronized void noteWakeupTimeAndReason(long elapsedRealtime, long uptime, String rawReason) { - final Wakeup parsedWakeup = Wakeup.parseWakeup(rawReason, elapsedRealtime, uptime); + final Wakeup parsedWakeup = Wakeup.parseWakeup(rawReason, elapsedRealtime, uptime, + mIrqDeviceMap); if (parsedWakeup == null) { + // This wakeup is unsupported for attribution. Exit. return; } mWakeupEvents.put(elapsedRealtime, parsedWakeup); attemptAttributionFor(parsedWakeup); - // Assuming that wakeups always arrive in monotonically increasing elapsedRealtime order, - // we can delete all history that will not be useful in attributing future wakeups. - mRecentWakingActivity.clearAllBefore(elapsedRealtime - WAKEUP_REASON_HALF_WINDOW_MS); // Limit history of wakeups and their attribution to the last retentionDuration. Note that // the last wakeup and its attribution (if computed) is always stored, even if that wakeup @@ -244,25 +246,22 @@ public class CpuWakeupStats { } private synchronized void attemptAttributionFor(Wakeup wakeup) { - final SparseBooleanArray subsystems = getResponsibleSubsystemsForWakeup(wakeup); - if (subsystems == null) { - // We don't support attribution for this kind of wakeup yet. - return; - } + final SparseBooleanArray subsystems = wakeup.mResponsibleSubsystems; SparseArray<SparseIntArray> attribution = mWakeupAttribution.get(wakeup.mElapsedMillis); if (attribution == null) { attribution = new SparseArray<>(); mWakeupAttribution.put(wakeup.mElapsedMillis, attribution); } + final long matchingWindowMillis = mConfig.WAKEUP_MATCHING_WINDOW_MS; for (int subsystemIdx = 0; subsystemIdx < subsystems.size(); subsystemIdx++) { final int subsystem = subsystems.keyAt(subsystemIdx); - // Blame all activity that happened WAKEUP_REASON_HALF_WINDOW_MS before or after + // Blame all activity that happened matchingWindowMillis before or after // the wakeup from each responsible subsystem. - final long startTime = wakeup.mElapsedMillis - WAKEUP_REASON_HALF_WINDOW_MS; - final long endTime = wakeup.mElapsedMillis + WAKEUP_REASON_HALF_WINDOW_MS; + final long startTime = wakeup.mElapsedMillis - matchingWindowMillis; + final long endTime = wakeup.mElapsedMillis + matchingWindowMillis; final SparseIntArray uidsToBlame = mRecentWakingActivity.removeBetween(subsystem, startTime, endTime); @@ -272,18 +271,16 @@ public class CpuWakeupStats { private synchronized boolean attemptAttributionWith(int subsystem, long activityElapsed, SparseIntArray uidProcStates) { + final long matchingWindowMillis = mConfig.WAKEUP_MATCHING_WINDOW_MS; + final int startIdx = mWakeupEvents.closestIndexOnOrAfter( - activityElapsed - WAKEUP_REASON_HALF_WINDOW_MS); + activityElapsed - matchingWindowMillis); final int endIdx = mWakeupEvents.closestIndexOnOrBefore( - activityElapsed + WAKEUP_REASON_HALF_WINDOW_MS); + activityElapsed + matchingWindowMillis); for (int wakeupIdx = startIdx; wakeupIdx <= endIdx; wakeupIdx++) { final Wakeup wakeup = mWakeupEvents.valueAt(wakeupIdx); - final SparseBooleanArray subsystems = getResponsibleSubsystemsForWakeup(wakeup); - if (subsystems == null) { - // Unsupported for attribution - continue; - } + final SparseBooleanArray subsystems = wakeup.mResponsibleSubsystems; if (subsystems.get(subsystem)) { // We don't expect more than one wakeup to be found within such a short window, so // just attribute this one and exit @@ -405,11 +402,13 @@ public class CpuWakeupStats { */ @VisibleForTesting static final class WakingActivityHistory { - private static final long WAKING_ACTIVITY_RETENTION_MS = TimeUnit.MINUTES.toMillis(10); - + private LongSupplier mRetentionSupplier; @VisibleForTesting - final SparseArray<TimeSparseArray<SparseIntArray>> mWakingActivity = - new SparseArray<>(); + final SparseArray<TimeSparseArray<SparseIntArray>> mWakingActivity = new SparseArray<>(); + + WakingActivityHistory(LongSupplier retentionSupplier) { + mRetentionSupplier = retentionSupplier; + } void recordActivity(int subsystem, long elapsedRealtime, SparseIntArray uidProcStates) { if (uidProcStates == null) { @@ -433,27 +432,13 @@ public class CpuWakeupStats { } } } - // Limit activity history per subsystem to the last WAKING_ACTIVITY_RETENTION_MS. - // Note that the last activity is always present, even if it occurred before - // WAKING_ACTIVITY_RETENTION_MS. + // Limit activity history per subsystem to the last retention period as supplied by + // mRetentionSupplier. Note that the last activity is always present, even if it + // occurred before the retention period. final int endIdx = wakingActivity.closestIndexOnOrBefore( - elapsedRealtime - WAKING_ACTIVITY_RETENTION_MS); + elapsedRealtime - mRetentionSupplier.getAsLong()); for (int i = endIdx; i >= 0; i--) { - wakingActivity.removeAt(endIdx); - } - } - - void clearAllBefore(long elapsedRealtime) { - for (int subsystemIdx = mWakingActivity.size() - 1; subsystemIdx >= 0; subsystemIdx--) { - final TimeSparseArray<SparseIntArray> activityPerSubsystem = - mWakingActivity.valueAt(subsystemIdx); - final int endIdx = activityPerSubsystem.closestIndexOnOrBefore(elapsedRealtime); - for (int removeIdx = endIdx; removeIdx >= 0; removeIdx--) { - activityPerSubsystem.removeAt(removeIdx); - } - // Generally waking activity is a high frequency occurrence for any subsystem, so we - // don't delete the TimeSparseArray even if it is now empty, to avoid object churn. - // This will leave one TimeSparseArray per subsystem, which are few right now. + wakingActivity.removeAt(i); } } @@ -515,33 +500,6 @@ public class CpuWakeupStats { } } - private SparseBooleanArray getResponsibleSubsystemsForWakeup(Wakeup wakeup) { - if (ArrayUtils.isEmpty(wakeup.mDevices)) { - return null; - } - final SparseBooleanArray result = new SparseBooleanArray(); - for (final Wakeup.IrqDevice device : wakeup.mDevices) { - final List<String> rawSubsystems = mIrqDeviceMap.getSubsystemsForDevice(device.mDevice); - - boolean anyKnownSubsystem = false; - if (rawSubsystems != null) { - for (int i = 0; i < rawSubsystems.size(); i++) { - final int subsystem = stringToKnownSubsystem(rawSubsystems.get(i)); - if (subsystem != CPU_WAKEUP_SUBSYSTEM_UNKNOWN) { - // Just in case the xml had arbitrary subsystem names, we want to make sure - // that we only put the known ones into our attribution map. - result.put(subsystem, true); - anyKnownSubsystem = true; - } - } - } - if (!anyKnownSubsystem) { - result.put(CPU_WAKEUP_SUBSYSTEM_UNKNOWN, true); - } - } - return result; - } - static int stringToKnownSubsystem(String rawSubsystem) { switch (rawSubsystem) { case SUBSYSTEM_ALARM_STRING: @@ -598,15 +556,19 @@ public class CpuWakeupStats { long mElapsedMillis; long mUptimeMillis; IrqDevice[] mDevices; + SparseBooleanArray mResponsibleSubsystems; - private Wakeup(int type, IrqDevice[] devices, long elapsedMillis, long uptimeMillis) { + private Wakeup(int type, IrqDevice[] devices, long elapsedMillis, long uptimeMillis, + SparseBooleanArray responsibleSubsystems) { mType = type; mDevices = devices; mElapsedMillis = elapsedMillis; mUptimeMillis = uptimeMillis; + mResponsibleSubsystems = responsibleSubsystems; } - static Wakeup parseWakeup(String rawReason, long elapsedMillis, long uptimeMillis) { + static Wakeup parseWakeup(String rawReason, long elapsedMillis, long uptimeMillis, + IrqDeviceMap deviceMap) { final String[] components = rawReason.split(":"); if (ArrayUtils.isEmpty(components) || components[0].startsWith(ABORT_REASON_PREFIX)) { // Accounting of aborts is not supported yet. @@ -616,6 +578,7 @@ public class CpuWakeupStats { int type = TYPE_IRQ; int parsedDeviceCount = 0; final IrqDevice[] parsedDevices = new IrqDevice[components.length]; + final SparseBooleanArray responsibleSubsystems = new SparseBooleanArray(); for (String component : components) { final Matcher matcher = sIrqPattern.matcher(component.trim()); @@ -635,13 +598,35 @@ public class CpuWakeupStats { continue; } parsedDevices[parsedDeviceCount++] = new IrqDevice(line, device); + + final List<String> rawSubsystems = deviceMap.getSubsystemsForDevice(device); + boolean anyKnownSubsystem = false; + if (rawSubsystems != null) { + for (int i = 0; i < rawSubsystems.size(); i++) { + final int subsystem = stringToKnownSubsystem(rawSubsystems.get(i)); + if (subsystem != CPU_WAKEUP_SUBSYSTEM_UNKNOWN) { + // Just in case the xml had arbitrary subsystem names, we want to + // make sure that we only put the known ones into our map. + responsibleSubsystems.put(subsystem, true); + anyKnownSubsystem = true; + } + } + } + if (!anyKnownSubsystem) { + responsibleSubsystems.put(CPU_WAKEUP_SUBSYSTEM_UNKNOWN, true); + } } } if (parsedDeviceCount == 0) { return null; } + if (responsibleSubsystems.size() == 1 && responsibleSubsystems.get( + CPU_WAKEUP_SUBSYSTEM_UNKNOWN, false)) { + // There is no attributable subsystem here, so we do not support it. + return null; + } return new Wakeup(type, Arrays.copyOf(parsedDevices, parsedDeviceCount), elapsedMillis, - uptimeMillis); + uptimeMillis, responsibleSubsystems); } @Override @@ -651,6 +636,7 @@ public class CpuWakeupStats { + ", mElapsedMillis=" + mElapsedMillis + ", mUptimeMillis=" + mUptimeMillis + ", mDevices=" + Arrays.toString(mDevices) + + ", mResponsibleSubsystems=" + mResponsibleSubsystems + '}'; } @@ -672,18 +658,28 @@ public class CpuWakeupStats { static final class Config implements DeviceConfig.OnPropertiesChangedListener { static final String KEY_WAKEUP_STATS_RETENTION_MS = "wakeup_stats_retention_ms"; + static final String KEY_WAKEUP_MATCHING_WINDOW_MS = "wakeup_matching_window_ms"; + static final String KEY_WAKING_ACTIVITY_RETENTION_MS = "waking_activity_retention_ms"; private static final String[] PROPERTY_NAMES = { KEY_WAKEUP_STATS_RETENTION_MS, + KEY_WAKEUP_MATCHING_WINDOW_MS, + KEY_WAKING_ACTIVITY_RETENTION_MS, }; static final long DEFAULT_WAKEUP_STATS_RETENTION_MS = TimeUnit.DAYS.toMillis(3); + private static final long DEFAULT_WAKEUP_MATCHING_WINDOW_MS = TimeUnit.SECONDS.toMillis(1); + private static final long DEFAULT_WAKING_ACTIVITY_RETENTION_MS = + TimeUnit.MINUTES.toMillis(5); /** * Wakeup stats are retained only for this duration. */ public volatile long WAKEUP_STATS_RETENTION_MS = DEFAULT_WAKEUP_STATS_RETENTION_MS; + public volatile long WAKEUP_MATCHING_WINDOW_MS = DEFAULT_WAKEUP_MATCHING_WINDOW_MS; + public volatile long WAKING_ACTIVITY_RETENTION_MS = DEFAULT_WAKING_ACTIVITY_RETENTION_MS; + @SuppressLint("MissingPermission") void register(Executor executor) { DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_BATTERY_STATS, executor, this); @@ -702,6 +698,15 @@ public class CpuWakeupStats { WAKEUP_STATS_RETENTION_MS = properties.getLong( KEY_WAKEUP_STATS_RETENTION_MS, DEFAULT_WAKEUP_STATS_RETENTION_MS); break; + case KEY_WAKEUP_MATCHING_WINDOW_MS: + WAKEUP_MATCHING_WINDOW_MS = properties.getLong( + KEY_WAKEUP_MATCHING_WINDOW_MS, DEFAULT_WAKEUP_MATCHING_WINDOW_MS); + break; + case KEY_WAKING_ACTIVITY_RETENTION_MS: + WAKING_ACTIVITY_RETENTION_MS = properties.getLong( + KEY_WAKING_ACTIVITY_RETENTION_MS, + DEFAULT_WAKING_ACTIVITY_RETENTION_MS); + break; } } } @@ -711,7 +716,19 @@ public class CpuWakeupStats { pw.increaseIndent(); - pw.print(KEY_WAKEUP_STATS_RETENTION_MS, WAKEUP_STATS_RETENTION_MS); + pw.print(KEY_WAKEUP_STATS_RETENTION_MS); + pw.print("="); + TimeUtils.formatDuration(WAKEUP_STATS_RETENTION_MS, pw); + pw.println(); + + pw.print(KEY_WAKEUP_MATCHING_WINDOW_MS); + pw.print("="); + TimeUtils.formatDuration(WAKEUP_MATCHING_WINDOW_MS, pw); + pw.println(); + + pw.print(KEY_WAKING_ACTIVITY_RETENTION_MS); + pw.print("="); + TimeUtils.formatDuration(WAKING_ACTIVITY_RETENTION_MS, pw); pw.println(); pw.decreaseIndent(); diff --git a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java index 76b6a820e4a7..d1814199a0b1 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java +++ b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java @@ -23,8 +23,6 @@ import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN; import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI; -import static com.android.server.power.stats.wakeups.CpuWakeupStats.WAKEUP_REASON_HALF_WINDOW_MS; - import static com.google.common.truth.Truth.assertThat; import android.content.Context; @@ -55,7 +53,7 @@ public class CpuWakeupStatsTest { private static final String KERNEL_REASON_SENSOR_IRQ = "15 test.sensor.device"; private static final String KERNEL_REASON_CELLULAR_DATA_IRQ = "18 test.cellular_data.device"; private static final String KERNEL_REASON_UNKNOWN_IRQ = "140 test.unknown.device"; - private static final String KERNEL_REASON_UNKNOWN = "free-form-reason test.alarm.device"; + private static final String KERNEL_REASON_UNKNOWN_FORMAT = "free-form-reason test.alarm.device"; private static final String KERNEL_REASON_ALARM_ABNORMAL = "-1 test.alarm.device"; private static final String KERNEL_REASON_ABORT = "Abort: due to test.alarm.device"; @@ -85,30 +83,29 @@ public class CpuWakeupStatsTest { @Test public void removesOldWakeups() { - // The xml resource doesn't matter for this test. - final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_1, mHandler); + final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler); final long retention = obj.mConfig.WAKEUP_STATS_RETENTION_MS; final Set<Long> timestamps = new HashSet<>(); final long firstWakeup = 453192; - obj.noteWakeupTimeAndReason(firstWakeup, 32, KERNEL_REASON_UNKNOWN_IRQ); + // Reasons do not matter for this test, as long as they map to known subsystems + obj.noteWakeupTimeAndReason(firstWakeup, 32, KERNEL_REASON_ALARM_IRQ); timestamps.add(firstWakeup); for (int i = 1; i < 1000; i++) { final long delta = mRandom.nextLong(retention); if (timestamps.add(firstWakeup + delta)) { - obj.noteWakeupTimeAndReason(firstWakeup + delta, i, KERNEL_REASON_UNKNOWN_IRQ); + obj.noteWakeupTimeAndReason(firstWakeup + delta, i, KERNEL_REASON_SENSOR_IRQ); } } assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size()); - obj.noteWakeupTimeAndReason(firstWakeup + retention + 1242, 231, - KERNEL_REASON_UNKNOWN_IRQ); + obj.noteWakeupTimeAndReason(firstWakeup + retention, 231, KERNEL_REASON_WIFI_IRQ); assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size()); for (int i = 0; i < 100; i++) { final long now = mRandom.nextLong(retention + 1, 100 * retention); - obj.noteWakeupTimeAndReason(now, i, KERNEL_REASON_UNKNOWN_IRQ); + obj.noteWakeupTimeAndReason(now, i, KERNEL_REASON_SOUND_TRIGGER_IRQ); assertThat(obj.mWakeupEvents.closestIndexOnOrBefore(now - retention)).isLessThan(0); } } @@ -201,9 +198,9 @@ public class CpuWakeupStatsTest { // Outside the window, so should be ignored. obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, - wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1); + wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_1); obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, - wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2); + wakeupTime + obj.mConfig.WAKEUP_MATCHING_WINDOW_MS + 1, TEST_UID_2); // Should be attributed obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3, TEST_UID_5); @@ -234,9 +231,9 @@ public class CpuWakeupStatsTest { // Outside the window, so should be ignored. obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER, - wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1); + wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_1); obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER, - wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2); + wakeupTime + obj.mConfig.WAKEUP_MATCHING_WINDOW_MS + 1, TEST_UID_2); // Should be attributed obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER, wakeupTime + 5, TEST_UID_3, TEST_UID_5); @@ -268,9 +265,9 @@ public class CpuWakeupStatsTest { // Outside the window, so should be ignored. obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, - wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1); + wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_1); obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, - wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2); + wakeupTime + obj.mConfig.WAKEUP_MATCHING_WINDOW_MS + 1, TEST_UID_2); // Should be attributed obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime + 3, TEST_UID_4, TEST_UID_5); @@ -300,9 +297,9 @@ public class CpuWakeupStatsTest { // Alarm activity // Outside the window, so should be ignored. obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, - wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1); + wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_1); obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, - wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2); + wakeupTime + obj.mConfig.WAKEUP_MATCHING_WINDOW_MS + 1, TEST_UID_2); // Should be attributed obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3); obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4, @@ -311,9 +308,9 @@ public class CpuWakeupStatsTest { // Wifi activity // Outside the window, so should be ignored. obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, - wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_4); + wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_4); obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, - wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_3); + wakeupTime + obj.mConfig.WAKEUP_MATCHING_WINDOW_MS + 1, TEST_UID_3); // Should be attributed obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime + 2, TEST_UID_1); obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 1, TEST_UID_2, @@ -347,33 +344,67 @@ public class CpuWakeupStatsTest { } @Test - public void unknownIrqAttribution() { + public void unknownIrqSoloIgnored() { final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler); final long wakeupTime = 92123410; obj.noteWakeupTimeAndReason(wakeupTime, 24, KERNEL_REASON_UNKNOWN_IRQ); - assertThat(obj.mWakeupEvents.size()).isEqualTo(1); + assertThat(obj.mWakeupEvents.size()).isEqualTo(0); - // Unrelated subsystems, should not be attributed obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3); obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 3, TEST_UID_4, TEST_UID_5); + // Any nearby activity should not end up in the attribution map. + assertThat(obj.mWakeupAttribution.size()).isEqualTo(0); + } + + @Test + public void unknownAndWifiAttribution() { + final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler); + final long wakeupTime = 92123410; + + populateDefaultProcStates(obj); + + obj.noteWakeupTimeAndReason(wakeupTime, 24, + KERNEL_REASON_UNKNOWN_IRQ + ":" + KERNEL_REASON_WIFI_IRQ); + + // Wifi activity + // Outside the window, so should be ignored. + obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, + wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_4); + // Should be attributed + obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime + 2, TEST_UID_1); + obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 1, TEST_UID_3, + TEST_UID_5); + + // Unrelated, should be ignored. + obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3); + final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime); assertThat(attribution).isNotNull(); - assertThat(attribution.size()).isEqualTo(1); + assertThat(attribution.size()).isEqualTo(2); + assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_WIFI)).isTrue(); + assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_1)).isEqualTo( + TEST_PROC_STATE_1); + assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_2)).isLessThan(0); + assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_3)).isEqualTo( + TEST_PROC_STATE_3); + assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_4)).isLessThan(0); + assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo( + TEST_PROC_STATE_5); assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isTrue(); - final SparseIntArray uidProcStates = attribution.get(CPU_WAKEUP_SUBSYSTEM_UNKNOWN); - assertThat(uidProcStates == null || uidProcStates.size() == 0).isTrue(); + assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isNull(); + assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isFalse(); } @Test - public void unknownWakeupIgnored() { + public void unknownFormatWakeupIgnored() { final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler); final long wakeupTime = 72123210; - obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_UNKNOWN); + obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_UNKNOWN_FORMAT); // Should be ignored as this type of wakeup is not known. assertThat(obj.mWakeupEvents.size()).isEqualTo(0); diff --git a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/WakingActivityHistoryTest.java b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/WakingActivityHistoryTest.java index cba7dbe2d02b..b02618e97b11 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/WakingActivityHistoryTest.java +++ b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/WakingActivityHistoryTest.java @@ -28,8 +28,11 @@ import com.android.server.power.stats.wakeups.CpuWakeupStats.WakingActivityHisto import org.junit.Test; import org.junit.runner.RunWith; +import java.util.concurrent.ThreadLocalRandom; + @RunWith(AndroidJUnit4.class) public class WakingActivityHistoryTest { + private volatile long mTestRetention = 54; private static boolean areSame(SparseIntArray a, SparseIntArray b) { if (a == b) { @@ -55,7 +58,7 @@ public class WakingActivityHistoryTest { @Test public void recordActivityAppendsUids() { - final WakingActivityHistory history = new WakingActivityHistory(); + final WakingActivityHistory history = new WakingActivityHistory(() -> Long.MAX_VALUE); final int subsystem = 42; final long timestamp = 54; @@ -100,7 +103,7 @@ public class WakingActivityHistoryTest { @Test public void recordActivityDoesNotDeleteExistingUids() { - final WakingActivityHistory history = new WakingActivityHistory(); + final WakingActivityHistory history = new WakingActivityHistory(() -> Long.MAX_VALUE); final int subsystem = 42; long timestamp = 101; @@ -151,4 +154,120 @@ public class WakingActivityHistoryTest { assertThat(recordedUids.get(62, -1)).isEqualTo(31); assertThat(recordedUids.get(85, -1)).isEqualTo(39); } + + @Test + public void removeBetween() { + final WakingActivityHistory history = new WakingActivityHistory(() -> Long.MAX_VALUE); + + final int subsystem = 43; + + final SparseIntArray uids = new SparseIntArray(); + uids.put(1, 17); + uids.put(15, 2); + uids.put(62, 31); + history.recordActivity(subsystem, 123, uids); + + uids.put(54, 91); + history.recordActivity(subsystem, 150, uids); + + uids.put(101, 32); + uids.delete(1); + history.recordActivity(subsystem, 191, uids); + + SparseIntArray removedUids = history.removeBetween(subsystem, 100, 122); + assertThat(removedUids).isNull(); + assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(3); + + removedUids = history.removeBetween(subsystem, 124, 149); + assertThat(removedUids).isNull(); + assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(3); + + removedUids = history.removeBetween(subsystem, 151, 190); + assertThat(removedUids).isNull(); + assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(3); + + removedUids = history.removeBetween(subsystem, 192, 240); + assertThat(removedUids).isNull(); + assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(3); + + + // Removing from a different subsystem should do nothing. + removedUids = history.removeBetween(subsystem + 1, 0, 300); + assertThat(removedUids).isNull(); + assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(3); + + removedUids = history.removeBetween(subsystem, 0, 300); + assertThat(removedUids.size()).isEqualTo(5); + assertThat(removedUids.get(1, -1)).isEqualTo(17); + assertThat(removedUids.get(15, -1)).isEqualTo(2); + assertThat(removedUids.get(62, -1)).isEqualTo(31); + assertThat(removedUids.get(54, -1)).isEqualTo(91); + assertThat(removedUids.get(101, -1)).isEqualTo(32); + + assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(0); + + history.recordActivity(subsystem, 23, uids); + uids.put(31, 123); + history.recordActivity(subsystem, 49, uids); + uids.put(177, 432); + history.recordActivity(subsystem, 89, uids); + + removedUids = history.removeBetween(subsystem, 23, 23); + assertThat(removedUids.size()).isEqualTo(4); + assertThat(removedUids.get(15, -1)).isEqualTo(2); + assertThat(removedUids.get(62, -1)).isEqualTo(31); + assertThat(removedUids.get(54, -1)).isEqualTo(91); + assertThat(removedUids.get(101, -1)).isEqualTo(32); + + assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(2); + + removedUids = history.removeBetween(subsystem, 49, 54); + assertThat(removedUids.size()).isEqualTo(5); + assertThat(removedUids.get(15, -1)).isEqualTo(2); + assertThat(removedUids.get(62, -1)).isEqualTo(31); + assertThat(removedUids.get(54, -1)).isEqualTo(91); + assertThat(removedUids.get(101, -1)).isEqualTo(32); + assertThat(removedUids.get(31, -1)).isEqualTo(123); + + assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(1); + + removedUids = history.removeBetween(subsystem, 23, 89); + assertThat(removedUids.size()).isEqualTo(6); + assertThat(removedUids.get(15, -1)).isEqualTo(2); + assertThat(removedUids.get(62, -1)).isEqualTo(31); + assertThat(removedUids.get(54, -1)).isEqualTo(91); + assertThat(removedUids.get(101, -1)).isEqualTo(32); + assertThat(removedUids.get(31, -1)).isEqualTo(123); + assertThat(removedUids.get(177, -1)).isEqualTo(432); + + assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(0); + } + + @Test + public void deletesActivityPastRetention() { + final WakingActivityHistory history = new WakingActivityHistory(() -> mTestRetention); + final int subsystem = 49; + + mTestRetention = 454; + + final long firstTime = 342; + for (int i = 0; i < mTestRetention; i++) { + history.recordActivity(subsystem, firstTime + i, new SparseIntArray()); + } + assertThat(history.mWakingActivity.get(subsystem)).isNotNull(); + assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(mTestRetention); + + history.recordActivity(subsystem, firstTime + mTestRetention + 7, new SparseIntArray()); + assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(mTestRetention - 7); + + final ThreadLocalRandom random = ThreadLocalRandom.current(); + + for (int i = 0; i < 100; i++) { + final long time = random.nextLong(firstTime + mTestRetention + 100, + 456 * mTestRetention); + history.recordActivity(subsystem, time, new SparseIntArray()); + assertThat(history.mWakingActivity.get(subsystem).closestIndexOnOrBefore( + time - mTestRetention)).isLessThan(0); + } + } } |