blob: dca67d6da7ad2c0f49d4f80a0290391b09da4897 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.power.stats.wakeups;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
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;
import android.os.Handler;
import android.util.IntArray;
import android.util.SparseArray;
import android.util.SparseIntArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.frameworks.servicestests.R;
import com.android.server.power.stats.wakeups.CpuWakeupStats.Wakeup;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
@RunWith(AndroidJUnit4.class)
public class CpuWakeupStatsTest {
private static final String KERNEL_REASON_ALARM_IRQ = "120 test.alarm.device";
private static final String KERNEL_REASON_WIFI_IRQ = "130 test.wifi.device";
private static final String KERNEL_REASON_SOUND_TRIGGER_IRQ = "129 test.sound_trigger.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_ALARM_ABNORMAL = "-1 test.alarm.device";
private static final String KERNEL_REASON_ABORT = "Abort: due to test.alarm.device";
private static final int TEST_UID_1 = 13239823;
private static final int TEST_UID_2 = 25268423;
private static final int TEST_UID_3 = 92261423;
private static final int TEST_UID_4 = 56926423;
private static final int TEST_UID_5 = 76421423;
private static final int TEST_PROC_STATE_1 = 72331;
private static final int TEST_PROC_STATE_2 = 792351;
private static final int TEST_PROC_STATE_3 = 138831;
private static final int TEST_PROC_STATE_4 = 23231;
private static final int TEST_PROC_STATE_5 = 42;
private static final Context sContext = InstrumentationRegistry.getTargetContext();
private final Handler mHandler = Mockito.mock(Handler.class);
private final ThreadLocalRandom mRandom = ThreadLocalRandom.current();
private void populateDefaultProcStates(CpuWakeupStats obj) {
obj.mUidProcStates.put(TEST_UID_1, TEST_PROC_STATE_1);
obj.mUidProcStates.put(TEST_UID_2, TEST_PROC_STATE_2);
obj.mUidProcStates.put(TEST_UID_3, TEST_PROC_STATE_3);
obj.mUidProcStates.put(TEST_UID_4, TEST_PROC_STATE_4);
obj.mUidProcStates.put(TEST_UID_5, TEST_PROC_STATE_5);
}
@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 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);
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);
}
}
assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size());
obj.noteWakeupTimeAndReason(firstWakeup + retention + 1242, 231,
KERNEL_REASON_UNKNOWN_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);
assertThat(obj.mWakeupEvents.closestIndexOnOrBefore(now - retention)).isLessThan(0);
}
}
@Test
public void alarmIrqAttributionSolo() {
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long wakeupTime = 12423121;
populateDefaultProcStates(obj);
obj.noteWakeupTimeAndReason(wakeupTime, 1, KERNEL_REASON_ALARM_IRQ);
// Outside the window, so should be ignored.
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
// Should be attributed
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3, TEST_UID_5);
final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
assertThat(attribution).isNotNull();
assertThat(attribution.size()).isEqualTo(1);
assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_1)).isLessThan(
0);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_2)).isLessThan(
0);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(
TEST_PROC_STATE_3);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_4)).isLessThan(
0);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(
TEST_PROC_STATE_5);
}
@Test
public void soundTriggerIrqAttributionSolo() {
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long wakeupTime = 1247121;
populateDefaultProcStates(obj);
obj.noteWakeupTimeAndReason(wakeupTime, 1, KERNEL_REASON_SOUND_TRIGGER_IRQ);
// Outside the window, so should be ignored.
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
// Should be attributed
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER, wakeupTime + 5, TEST_UID_3,
TEST_UID_5);
final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
assertThat(attribution).isNotNull();
assertThat(attribution.size()).isEqualTo(1);
assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER)).isTrue();
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER).indexOfKey(
TEST_UID_1)).isLessThan(0);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER).indexOfKey(
TEST_UID_2)).isLessThan(0);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER).get(TEST_UID_3)).isEqualTo(
TEST_PROC_STATE_3);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER).indexOfKey(
TEST_UID_4)).isLessThan(0);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER).get(TEST_UID_5)).isEqualTo(
TEST_PROC_STATE_5);
}
@Test
public void wifiIrqAttributionSolo() {
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long wakeupTime = 12423121;
populateDefaultProcStates(obj);
obj.noteWakeupTimeAndReason(wakeupTime, 1, KERNEL_REASON_WIFI_IRQ);
// Outside the window, so should be ignored.
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI,
wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI,
wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
// Should be attributed
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime + 3, TEST_UID_4, TEST_UID_5);
final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
assertThat(attribution).isNotNull();
assertThat(attribution.size()).isEqualTo(1);
assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_WIFI)).isTrue();
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_1)).isLessThan(0);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_2)).isLessThan(0);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_3)).isLessThan(0);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_4)).isEqualTo(
TEST_PROC_STATE_4);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo(
TEST_PROC_STATE_5);
}
@Test
public void alarmAndWifiIrqAttribution() {
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long wakeupTime = 92123210;
populateDefaultProcStates(obj);
obj.noteWakeupTimeAndReason(wakeupTime, 4,
KERNEL_REASON_WIFI_IRQ + ":" + KERNEL_REASON_ALARM_IRQ);
// 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);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
wakeupTime + WAKEUP_REASON_HALF_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,
TEST_UID_5);
// 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);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI,
wakeupTime + WAKEUP_REASON_HALF_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,
TEST_UID_5);
final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
assertThat(attribution).isNotNull();
assertThat(attribution.size()).isEqualTo(2);
assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_1)).isLessThan(
0);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_2)).isLessThan(
0);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(
TEST_PROC_STATE_3);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_4)).isEqualTo(
TEST_PROC_STATE_4);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(
TEST_PROC_STATE_5);
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).get(TEST_UID_2)).isEqualTo(
TEST_PROC_STATE_2);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_3)).isLessThan(0);
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);
}
@Test
public void unknownIrqAttribution() {
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);
// 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);
final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
assertThat(attribution).isNotNull();
assertThat(attribution.size()).isEqualTo(1);
assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isTrue();
final SparseIntArray uidProcStates = attribution.get(CPU_WAKEUP_SUBSYSTEM_UNKNOWN);
assertThat(uidProcStates == null || uidProcStates.size() == 0).isTrue();
}
@Test
public void unknownWakeupIgnored() {
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long wakeupTime = 72123210;
obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_UNKNOWN);
// Should be ignored as this type of wakeup is not known.
assertThat(obj.mWakeupEvents.size()).isEqualTo(0);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4);
// Any nearby activity should not end up in the attribution map.
assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
}
@Test
public void abnormalAlarmAttribution() {
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
populateDefaultProcStates(obj);
long wakeupTime = 970934;
obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_ALARM_ABNORMAL);
assertThat(obj.mWakeupEvents.size()).isEqualTo(1);
assertThat(obj.mWakeupEvents.valueAt(0).mType).isEqualTo(Wakeup.TYPE_ABNORMAL);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4);
final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
assertThat(attribution.size()).isEqualTo(1);
assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_1)).isLessThan(
0);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_2)).isLessThan(
0);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(
TEST_PROC_STATE_3);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_4)).isEqualTo(
TEST_PROC_STATE_4);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_5)).isLessThan(
0);
}
@Test
public void abortIgnored() {
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
long wakeupTime = 883124;
obj.noteWakeupTimeAndReason(wakeupTime, 3, KERNEL_REASON_ABORT);
// Should be ignored as this type of wakeup is unsupported.
assertThat(obj.mWakeupEvents.size()).isEqualTo(0);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime + 2, TEST_UID_1, TEST_UID_4);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 5, TEST_UID_3);
// Any nearby activity should not end up in the attribution map.
assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
}
@Test
public void uidProcStateBookkeeping() {
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
assertThat(obj.mUidProcStates.size()).isEqualTo(0);
final IntArray uids = new IntArray(87);
for (int i = 0; i < 87; i++) {
final int uid = mRandom.nextInt(1 << 20);
if (uids.indexOf(uid) < 0) {
uids.add(uid);
}
}
for (int i = 0; i < uids.size(); i++) {
final int uid = uids.get(i);
for (int j = 0; j < 43; j++) {
final int procState = mRandom.nextInt(1 << 15);
obj.noteUidProcessState(uid, procState);
assertThat(obj.mUidProcStates.get(uid)).isEqualTo(procState);
}
assertThat(obj.mUidProcStates.size()).isEqualTo(i + 1);
}
for (int i = 0; i < uids.size(); i++) {
obj.onUidRemoved(uids.get(i));
assertThat(obj.mUidProcStates.indexOfKey(uids.get(i))).isLessThan(0);
}
assertThat(obj.mUidProcStates.size()).isEqualTo(0);
obj.onUidRemoved(213);
assertThat(obj.mUidProcStates.size()).isEqualTo(0);
}
}