blob: 97b54b13e1538a1fe89079b2d2965b8fa48a48c4 [file] [log] [blame]
/*
* Copyright (C) 2017 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.internal.os;
import static android.os.BatteryStats.STATS_SINCE_CHARGED;
import static android.os.BatteryStats.WAKE_TYPE_PARTIAL;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.FIRST_ISOLATED_UID;
import static com.android.internal.os.BatteryStatsImpl.WAKE_LOCK_WEIGHT;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.os.BatteryStats;
import android.os.UserHandle;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.SparseLongArray;
import com.android.internal.util.ArrayUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Arrays;
/**
* To run the tests, use
*
* runtest -c com.android.internal.os.BatteryStatsCpuTimesTest frameworks-core
*
* or
*
* Build: m FrameworksCoreTests
* Install: adb install -r \
* ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
* Run: adb shell am instrument -e class com.android.internal.os.BatteryStatsCpuTimesTest -w \
* com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
*
* or
*
* bit FrameworksCoreTests:com.android.internal.os.BatteryStatsCpuTimesTest
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BatteryStatsCpuTimesTest {
@Mock KernelUidCpuTimeReader mKernelUidCpuTimeReader;
@Mock KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader;
@Mock BatteryStatsImpl.UserInfoProvider mUserInfoProvider;
@Mock PowerProfile mPowerProfile;
private MockClocks mClocks;
private MockBatteryStatsImpl mBatteryStatsImpl;
private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mClocks = new MockClocks();
mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks)
.setKernelUidCpuTimeReader(mKernelUidCpuTimeReader)
.setKernelUidCpuFreqTimeReader(mKernelUidCpuFreqTimeReader)
.setUserInfoProvider(mUserInfoProvider);
}
@Test
public void testUpdateCpuTimeLocked() {
// PRECONDITIONS
mBatteryStatsImpl.setPowerProfile(mPowerProfile);
mBatteryStatsImpl.setOnBatteryInternal(false);
final int numClusters = 3;
initKernelCpuSpeedReaders(numClusters);
final long[] freqs = {1, 12, 123, 12, 1234};
when(mKernelUidCpuFreqTimeReader.readFreqs(mPowerProfile)).thenReturn(freqs);
// RUN
mBatteryStatsImpl.updateCpuTimeLocked();
// VERIFY
assertArrayEquals("Unexpected cpu freqs", freqs, mBatteryStatsImpl.getCpuFreqs());
verify(mKernelUidCpuTimeReader).readDelta(null);
verify(mKernelUidCpuFreqTimeReader).readDelta(null);
for (int i = 0; i < numClusters; ++i) {
verify(mKernelCpuSpeedReaders[i]).readDelta();
}
// Prepare for next test
Mockito.reset(mUserInfoProvider, mKernelUidCpuFreqTimeReader, mKernelUidCpuTimeReader);
for (int i = 0; i < numClusters; ++i) {
Mockito.reset(mKernelCpuSpeedReaders[i]);
}
// PRECONDITIONS
mBatteryStatsImpl.setOnBatteryInternal(true);
// RUN
mBatteryStatsImpl.updateCpuTimeLocked();
// VERIFY
verify(mUserInfoProvider).refreshUserIds();
verify(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
// perClusterTimesAvailable is called twice, once in updateCpuTimeLocked() and the other
// in readKernelUidCpuFreqTimesLocked.
verify(mKernelUidCpuFreqTimeReader, times(2)).perClusterTimesAvailable();
verify(mKernelUidCpuFreqTimeReader).readDelta(
any(KernelUidCpuFreqTimeReader.Callback.class));
verifyNoMoreInteractions(mKernelUidCpuFreqTimeReader);
for (int i = 0; i < numClusters; ++i) {
verify(mKernelCpuSpeedReaders[i]).readDelta();
}
}
@Test
public void testMarkPartialTimersAsEligible() {
// PRECONDITIONS
final ArrayList<BatteryStatsImpl.StopwatchTimer> partialTimers = getPartialTimers(
10032, 10042, 10052);
final ArrayList<BatteryStatsImpl.StopwatchTimer> lastPartialTimers
= new ArrayList<>(partialTimers);
mBatteryStatsImpl.setPartialTimers(partialTimers);
mBatteryStatsImpl.setLastPartialTimers(lastPartialTimers);
final boolean[] inList = {false, true, false};
for (int i = 0; i < partialTimers.size(); ++i) {
partialTimers.get(i).mInList = inList[i];
}
// RUN
mBatteryStatsImpl.markPartialTimersAsEligible();
// VERIFY
assertTrue(ArrayUtils.referenceEquals(partialTimers, lastPartialTimers));
for (int i = 0; i < partialTimers.size(); ++i) {
assertTrue("Timer id=" + i, partialTimers.get(i).mInList);
}
// PRECONDITIONS
partialTimers.addAll(getPartialTimers(10077, 10099));
partialTimers.remove(1 /* index */);
// RUN
mBatteryStatsImpl.markPartialTimersAsEligible();
// VERIFY
assertTrue(ArrayUtils.referenceEquals(partialTimers, lastPartialTimers));
for (int i = 0; i < partialTimers.size(); ++i) {
assertTrue("Timer id=" + i, partialTimers.get(i).mInList);
}
}
@Test
public void testUpdateClusterSpeedTimes() {
// PRECONDITIONS
updateTimeBasesLocked(true, false, 0, 0);
final long[][] clusterSpeedTimesMs = {{20, 30}, {40, 50, 60}};
initKernelCpuSpeedReaders(clusterSpeedTimesMs.length);
for (int i = 0; i < clusterSpeedTimesMs.length; ++i) {
when(mKernelCpuSpeedReaders[i].readDelta()).thenReturn(clusterSpeedTimesMs[i]);
}
when(mPowerProfile.getNumCpuClusters()).thenReturn(clusterSpeedTimesMs.length);
for (int i = 0; i < clusterSpeedTimesMs.length; ++i) {
when(mPowerProfile.getNumSpeedStepsInCpuCluster(i))
.thenReturn(clusterSpeedTimesMs[i].length);
}
final SparseLongArray updatedUids = new SparseLongArray();
final int[] testUids = {10012, 10014, 10016};
final int[] cpuTimeUs = {89, 31, 43};
for (int i = 0; i < testUids.length; ++i) {
updatedUids.put(testUids[i], cpuTimeUs[i]);
}
// RUN
mBatteryStatsImpl.updateClusterSpeedTimes(updatedUids);
// VERIFY
int totalClustersTimeMs = 0;
for (int i = 0; i < clusterSpeedTimesMs.length; ++i) {
for (int j = 0; j < clusterSpeedTimesMs[i].length; ++j) {
totalClustersTimeMs += clusterSpeedTimesMs[i][j];
}
}
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
assertNotNull("No entry for uid=" + testUids[i], u);
for (int cluster = 0; cluster < clusterSpeedTimesMs.length; ++cluster) {
for (int speed = 0; speed < clusterSpeedTimesMs[cluster].length; ++speed) {
assertEquals("Uid=" + testUids[i] + ", cluster=" + cluster + ", speed=" + speed,
cpuTimeUs[i] * clusterSpeedTimesMs[cluster][speed]
/ totalClustersTimeMs,
u.getTimeAtCpuSpeed(cluster, speed, STATS_SINCE_CHARGED));
}
}
}
}
@Test
public void testReadKernelUidCpuTimesLocked() {
//PRECONDITIONS
updateTimeBasesLocked(true, false, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int[] testUids = getUids(testUserId, new int[] {
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
});
final long[][] uidTimesUs = {
{12, 34}, {34897394, 3123983}, {79775429834l, 8430434903489l}
};
doAnswer(invocation -> {
final KernelUidCpuTimeReader.Callback callback =
(KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesUs[i][0], uidTimesUs[i][1]);
}
return null;
}).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
// RUN
final SparseLongArray updatedUids = new SparseLongArray();
mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, updatedUids);
// VERIFY
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
assertNotNull("No entry for uid=" + testUids[i], u);
assertEquals("Unexpected user cpu time for uid=" + testUids[i],
uidTimesUs[i][0], u.getUserCpuTimeUs(STATS_SINCE_CHARGED));
assertEquals("Unexpected system cpu time for uid=" + testUids[i],
uidTimesUs[i][1], u.getSystemCpuTimeUs(STATS_SINCE_CHARGED));
assertEquals("Unexpected entry in updated uids for uid=" + testUids[i],
uidTimesUs[i][0] + uidTimesUs[i][1], updatedUids.get(testUids[i]));
updatedUids.delete(testUids[i]);
}
assertEquals("Updated uids: " + updatedUids, 0, updatedUids.size());
// Repeat the test with a null updatedUids
// PRECONDITIONS
final long[][] deltasUs = {
{9379, 3332409833484l}, {493247, 723234}, {3247819, 123348}
};
doAnswer(invocation -> {
final KernelUidCpuTimeReader.Callback callback =
(KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], deltasUs[i][0], deltasUs[i][1]);
}
return null;
}).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null);
// VERIFY
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
assertNotNull("No entry for uid=" + testUids[i], u);
assertEquals("Unexpected user cpu time for uid=" + testUids[i],
uidTimesUs[i][0] + deltasUs[i][0], u.getUserCpuTimeUs(STATS_SINCE_CHARGED));
assertEquals("Unexpected system cpu time for uid=" + testUids[i],
uidTimesUs[i][1] + deltasUs[i][1], u.getSystemCpuTimeUs(STATS_SINCE_CHARGED));
}
}
@Test
public void testReadKernelUidCpuTimesLocked_isolatedUid() {
//PRECONDITIONS
updateTimeBasesLocked(true, false, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int isolatedAppId = FIRST_ISOLATED_UID + 27;
final int isolatedUid = UserHandle.getUid(testUserId, isolatedAppId);
final int[] testUids = getUids(testUserId, new int[] {
FIRST_APPLICATION_UID + 22,
isolatedAppId,
FIRST_APPLICATION_UID + 33
});
final long[][] uidTimesUs = {
{12, 34}, {34897394, 3123983}, {79775429834l, 8430434903489l}
};
doAnswer(invocation -> {
final KernelUidCpuTimeReader.Callback callback =
(KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesUs[i][0], uidTimesUs[i][1]);
}
return null;
}).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null);
// VERIFY
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
if (UserHandle.isIsolated(testUids[i])) {
assertNull("There shouldn't be an entry for isolated uid=" + testUids[i], u);
continue;
}
assertNotNull("No entry for uid=" + testUids[i], u);
assertEquals("Unexpected user cpu time for uid=" + testUids[i],
uidTimesUs[i][0], u.getUserCpuTimeUs(STATS_SINCE_CHARGED));
assertEquals("Unexpected system cpu time for uid=" + testUids[i],
uidTimesUs[i][1], u.getSystemCpuTimeUs(STATS_SINCE_CHARGED));
}
verify(mKernelUidCpuTimeReader).removeUid(isolatedUid);
// Add an isolated uid mapping and repeat the test.
// PRECONDITIONS
final int ownerUid = UserHandle.getUid(testUserId, FIRST_APPLICATION_UID + 42);
mBatteryStatsImpl.addIsolatedUidLocked(isolatedUid, ownerUid);
final long[][] deltasUs = {
{9379, 3332409833484l}, {493247, 723234}, {3247819, 123348}
};
doAnswer(invocation -> {
final KernelUidCpuTimeReader.Callback callback =
(KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], deltasUs[i][0], deltasUs[i][1]);
}
return null;
}).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null);
// VERIFY
for (int i = 0; i < testUids.length; ++i) {
BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
final long expectedUserTimeUs;
final long expectedSystemTimeUs;
if (UserHandle.isIsolated(testUids[i])) {
assertNull("There shouldn't be an entry for isolated uid=" + testUids[i], u);
// Since we added a mapping, an entry should've been created for owner uid.
u = mBatteryStatsImpl.getUidStats().get(ownerUid);
expectedUserTimeUs = deltasUs[i][0];
expectedSystemTimeUs = deltasUs[i][1];
assertNotNull("No entry for owner uid=" + ownerUid, u);
} else {
assertNotNull("No entry for uid=" + testUids[i], u);
expectedUserTimeUs = uidTimesUs[i][0] + deltasUs[i][0];
expectedSystemTimeUs = uidTimesUs[i][1] + deltasUs[i][1];
}
assertEquals("Unexpected user cpu time for uid=" + testUids[i],
expectedUserTimeUs, u.getUserCpuTimeUs(STATS_SINCE_CHARGED));
assertEquals("Unexpected system cpu time for uid=" + testUids[i],
expectedSystemTimeUs, u.getSystemCpuTimeUs(STATS_SINCE_CHARGED));
}
}
@Test
public void testReadKernelUidCpuTimesLocked_invalidUid() {
//PRECONDITIONS
updateTimeBasesLocked(true, false, 0, 0);
final int testUserId = 11;
final int invalidUserId = 15;
final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99);
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false);
final int[] testUids = getUids(testUserId, new int[] {
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
});
final long[][] uidTimesUs = {
{12, 34}, {34897394, 3123983}, {79775429834l, 8430434903489l}
};
doAnswer(invocation -> {
final KernelUidCpuTimeReader.Callback callback =
(KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesUs[i][0], uidTimesUs[i][1]);
}
// And one for the invalid uid
callback.onUidCpuTime(invalidUid, 3879, 239);
return null;
}).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null);
// VERIFY
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
assertNotNull("No entry for uid=" + testUids[i], u);
assertEquals("Unexpected user cpu time for uid=" + testUids[i],
uidTimesUs[i][0], u.getUserCpuTimeUs(STATS_SINCE_CHARGED));
assertEquals("Unexpected system cpu time for uid=" + testUids[i],
uidTimesUs[i][1], u.getSystemCpuTimeUs(STATS_SINCE_CHARGED));
}
assertNull("There shouldn't be an entry for invalid uid=" + invalidUid,
mBatteryStatsImpl.getUidStats().get(invalidUid));
verify(mKernelUidCpuTimeReader).removeUid(invalidUid);
}
@Test
public void testReadKernelUidCpuTimesLocked_withPartialTimers() {
//PRECONDITIONS
updateTimeBasesLocked(true, false, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int[] testUids = getUids(testUserId, new int[] {
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
});
final int[] partialTimerUids = {
UserHandle.getUid(testUserId, FIRST_APPLICATION_UID + 48),
UserHandle.getUid(testUserId, FIRST_APPLICATION_UID + 10)
};
final ArrayList<BatteryStatsImpl.StopwatchTimer> partialTimers
= getPartialTimers(partialTimerUids);
final long[][] uidTimesUs = {
{12, 34}, {3394, 3123}, {7977, 80434}
};
doAnswer(invocation -> {
final KernelUidCpuTimeReader.Callback callback =
(KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesUs[i][0], uidTimesUs[i][1]);
}
return null;
}).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
// RUN
final SparseLongArray updatedUids = new SparseLongArray();
mBatteryStatsImpl.readKernelUidCpuTimesLocked(partialTimers, updatedUids);
// VERIFY
long totalUserTimeUs = 0;
long totalSystemTimeUs = 0;
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
assertNotNull("No entry for uid=" + testUids[i], u);
final long expectedUserTimeUs = uidTimesUs[i][0] * WAKE_LOCK_WEIGHT / 100;
final long expectedSystemTimeUs = uidTimesUs[i][1] * WAKE_LOCK_WEIGHT / 100;
assertEquals("Unexpected user cpu time for uid=" + testUids[i],
expectedUserTimeUs, u.getUserCpuTimeUs(STATS_SINCE_CHARGED));
assertEquals("Unexpected system cpu time for uid=" + testUids[i],
expectedSystemTimeUs, u.getSystemCpuTimeUs(STATS_SINCE_CHARGED));
assertEquals("Unexpected entry in updated uids for uid=" + testUids[i],
expectedUserTimeUs + expectedSystemTimeUs, updatedUids.get(testUids[i]));
updatedUids.delete(testUids[i]);
totalUserTimeUs += uidTimesUs[i][0];
totalSystemTimeUs += uidTimesUs[i][1];
}
totalUserTimeUs = totalUserTimeUs * (100 - WAKE_LOCK_WEIGHT) / 100;
totalSystemTimeUs = totalSystemTimeUs * (100 - WAKE_LOCK_WEIGHT) / 100;
for (int i = 0; i < partialTimerUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(partialTimerUids[i]);
assertNotNull("No entry for partial timer uid=" + partialTimerUids[i], u);
final long expectedUserTimeUs = totalUserTimeUs / (partialTimerUids.length - i);
final long expectedSystemTimeUs = totalSystemTimeUs / (partialTimerUids.length - i);
assertEquals("Unexpected user cpu time for partial timer uid=" + partialTimerUids[i],
expectedUserTimeUs, u.getUserCpuTimeUs(STATS_SINCE_CHARGED));
assertEquals("Unexpected system cpu time for partial timer uid=" + partialTimerUids[i],
expectedSystemTimeUs, u.getSystemCpuTimeUs(STATS_SINCE_CHARGED));
assertEquals("Unexpected entry in updated uids for partial timer uid="
+ partialTimerUids[i],
expectedUserTimeUs + expectedSystemTimeUs,
updatedUids.get(partialTimerUids[i]));
updatedUids.delete(partialTimerUids[i]);
totalUserTimeUs -= expectedUserTimeUs;
totalSystemTimeUs -= expectedSystemTimeUs;
final BatteryStats.Uid.Proc proc = u.getProcessStats().get("*wakelock*");
assertEquals("Unexpected user cpu time for *wakelock* in uid=" + partialTimerUids[i],
expectedUserTimeUs / 1000, proc.getUserTime(STATS_SINCE_CHARGED));
assertEquals("Unexpected system cpu time for *wakelock* in uid=" + partialTimerUids[i],
expectedSystemTimeUs / 1000, proc.getSystemTime(STATS_SINCE_CHARGED));
}
assertEquals(0, totalUserTimeUs);
assertEquals(0, totalSystemTimeUs);
assertEquals("Updated uids: " + updatedUids, 0, updatedUids.size());
}
@Test
public void testReadKernelUidCpuFreqTimesLocked() {
// PRECONDITIONS
updateTimeBasesLocked(true, false, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int[] testUids = getUids(testUserId, new int[] {
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
});
final long[][] uidTimesMs = {
{4, 10, 5, 9, 4},
{5, 1, 12, 2, 10},
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
final KernelUidCpuFreqTimeReader.Callback callback =
(KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
}
return null;
}).when(mKernelUidCpuFreqTimeReader).readDelta(
any(KernelUidCpuFreqTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null);
// VERIFY
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
assertNotNull("No entry for uid=" + testUids[i], u);
assertArrayEquals("Unexpected cpu times for uid=" + testUids[i],
uidTimesMs[i], u.getCpuFreqTimes(STATS_SINCE_CHARGED));
assertNull("Unexpected screen-off cpu times for uid=" + testUids[i],
u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
}
// Repeat the test when the screen is off.
// PRECONDITIONS
updateTimeBasesLocked(true, true, 0, 0);
final long[][] deltasMs = {
{3, 12, 55, 100, 32},
{3248327490475l, 232349349845043l, 123, 2398, 0},
{43, 3345, 2143, 123, 4554}
};
doAnswer(invocation -> {
final KernelUidCpuFreqTimeReader.Callback callback =
(KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuFreqTime(testUids[i], deltasMs[i]);
}
return null;
}).when(mKernelUidCpuFreqTimeReader).readDelta(
any(KernelUidCpuFreqTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null);
// VERIFY
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
assertNotNull("No entry for uid=" + testUids[i], u);
assertArrayEquals("Unexpected cpu times for uid=" + testUids[i],
sum(uidTimesMs[i], deltasMs[i]), u.getCpuFreqTimes(STATS_SINCE_CHARGED));
assertArrayEquals("Unexpected screen-off cpu times for uid=" + testUids[i],
deltasMs[i], u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
}
}
@Test
public void testReadKernelUidCpuFreqTimesLocked_perClusterTimesAvailable() {
// PRECONDITIONS
updateTimeBasesLocked(true, false, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int[] testUids = getUids(testUserId, new int[] {
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
});
final long[] freqs = {1, 12, 123, 12, 1234};
// Derived from freqs above, 2 clusters with {3, 2} freqs in each of them.
final int[] clusterFreqs = {3, 2};
when(mPowerProfile.getNumCpuClusters()).thenReturn(clusterFreqs.length);
for (int i = 0; i < clusterFreqs.length; ++i) {
when(mPowerProfile.getNumSpeedStepsInCpuCluster(i))
.thenReturn(clusterFreqs[i]);
}
final long[][] uidTimesMs = {
{4, 10, 5, 9, 4},
{5, 1, 12, 2, 10},
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
final KernelUidCpuFreqTimeReader.Callback callback =
(KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
}
return null;
}).when(mKernelUidCpuFreqTimeReader).readDelta(
any(KernelUidCpuFreqTimeReader.Callback.class));
when(mKernelUidCpuFreqTimeReader.perClusterTimesAvailable()).thenReturn(true);
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null);
// VERIFY
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
assertNotNull("No entry for uid=" + testUids[i], u);
assertArrayEquals("Unexpected cpu times for uid=" + testUids[i],
uidTimesMs[i], u.getCpuFreqTimes(STATS_SINCE_CHARGED));
assertNull("Unexpected screen-off cpu times for uid=" + testUids[i],
u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
int idx = 0;
for (int cluster = 0; cluster < clusterFreqs.length; ++cluster) {
for (int speed = 0; speed < clusterFreqs[cluster]; ++speed) {
assertEquals("Unexpected time at cluster=" + cluster + ", speed=" + speed,
uidTimesMs[i][idx] * 1000,
u.getTimeAtCpuSpeed(cluster, speed, STATS_SINCE_CHARGED));
idx++;
}
}
}
// Repeat the test when the screen is off.
// PRECONDITIONS
updateTimeBasesLocked(true, true, 0, 0);
final long[][] deltasMs = {
{3, 12, 55, 100, 32},
{3248327490475l, 232349349845043l, 123, 2398, 0},
{43, 3345, 2143, 123, 4554}
};
doAnswer(invocation -> {
final KernelUidCpuFreqTimeReader.Callback callback =
(KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuFreqTime(testUids[i], deltasMs[i]);
}
return null;
}).when(mKernelUidCpuFreqTimeReader).readDelta(
any(KernelUidCpuFreqTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null);
// VERIFY
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
assertNotNull("No entry for uid=" + testUids[i], u);
assertArrayEquals("Unexpected cpu times for uid=" + testUids[i],
sum(uidTimesMs[i], deltasMs[i]), u.getCpuFreqTimes(STATS_SINCE_CHARGED));
assertArrayEquals("Unexpected screen-off cpu times for uid=" + testUids[i],
deltasMs[i], u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
int idx = 0;
for (int cluster = 0; cluster < clusterFreqs.length; ++cluster) {
for (int speed = 0; speed < clusterFreqs[cluster]; ++speed) {
assertEquals("Unexpected time at cluster=" + cluster + ", speed=" + speed,
(uidTimesMs[i][idx] + deltasMs[i][idx]) * 1000,
u.getTimeAtCpuSpeed(cluster, speed, STATS_SINCE_CHARGED));
idx++;
}
}
}
}
@Test
public void testReadKernelUidCpuFreqTimesLocked_partialTimers() {
// PRECONDITIONS
updateTimeBasesLocked(true, false, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int[] testUids = getUids(testUserId, new int[] {
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
});
final int[] partialTimerUids = {
UserHandle.getUid(testUserId, FIRST_APPLICATION_UID + 48),
UserHandle.getUid(testUserId, FIRST_APPLICATION_UID + 10)
};
final ArrayList<BatteryStatsImpl.StopwatchTimer> partialTimers
= getPartialTimers(partialTimerUids);
final long[] freqs = {1, 12, 123, 12, 1234};
// Derived from freqs above, 2 clusters with {3, 2} freqs in each of them.
final int[] clusterFreqs = {3, 2};
when(mPowerProfile.getNumCpuClusters()).thenReturn(clusterFreqs.length);
for (int i = 0; i < clusterFreqs.length; ++i) {
when(mPowerProfile.getNumSpeedStepsInCpuCluster(i))
.thenReturn(clusterFreqs[i]);
}
final long[][] uidTimesMs = {
{4, 10, 5, 9, 4},
{5, 1, 12, 2, 10},
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
final KernelUidCpuFreqTimeReader.Callback callback =
(KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
}
return null;
}).when(mKernelUidCpuFreqTimeReader).readDelta(
any(KernelUidCpuFreqTimeReader.Callback.class));
when(mKernelUidCpuFreqTimeReader.perClusterTimesAvailable()).thenReturn(true);
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(partialTimers);
// VERIFY
final long[][] expectedWakeLockUidTimesUs = new long[clusterFreqs.length][];
for (int cluster = 0; cluster < clusterFreqs.length; ++cluster) {
expectedWakeLockUidTimesUs[cluster] = new long[clusterFreqs[cluster]];
}
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
assertNotNull("No entry for uid=" + testUids[i], u);
assertArrayEquals("Unexpected cpu times for uid=" + testUids[i],
uidTimesMs[i], u.getCpuFreqTimes(STATS_SINCE_CHARGED));
assertNull("Unexpected screen-off cpu times for uid=" + testUids[i],
u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
int idx = 0;
for (int cluster = 0; cluster < clusterFreqs.length; ++cluster) {
for (int speed = 0; speed < clusterFreqs[cluster]; ++speed) {
final long expectedTimeUs =
(uidTimesMs[i][idx] * 1000 * WAKE_LOCK_WEIGHT) / 100;
expectedWakeLockUidTimesUs[cluster][speed] += expectedTimeUs;
assertEquals("Unexpected time for uid= " + testUids[i]
+ " at cluster=" + cluster + ", speed=" + speed,
expectedTimeUs,
u.getTimeAtCpuSpeed(cluster, speed, STATS_SINCE_CHARGED));
idx++;
}
}
}
for (int i = 0; i < partialTimerUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(partialTimerUids[i]);
assertNotNull("No entry for partial timer uid=" + partialTimerUids[i], u);
assertNull("Unexpected cpu times for partial timer uid=" + partialTimerUids[i],
u.getCpuFreqTimes(STATS_SINCE_CHARGED));
assertNull("Unexpected screen-off cpu times for partial timer uid="
+ partialTimerUids[i],
u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
for (int cluster = 0; cluster < clusterFreqs.length; ++cluster) {
for (int speed = 0; speed < clusterFreqs[cluster]; ++speed) {
final long expectedTimeUs = expectedWakeLockUidTimesUs[cluster][speed]
/ (partialTimerUids.length - i);
assertEquals("Unexpected time for partial timer uid= " + partialTimerUids[i]
+ " at cluster=" + cluster + ", speed=" + speed,
expectedTimeUs,
u.getTimeAtCpuSpeed(cluster, speed, STATS_SINCE_CHARGED));
expectedWakeLockUidTimesUs[cluster][speed] -= expectedTimeUs;
}
}
}
for (int cluster = 0; cluster < clusterFreqs.length; ++cluster) {
for (int speed = 0 ; speed < clusterFreqs[cluster]; ++speed) {
assertEquals("There shouldn't be any left-overs: "
+ Arrays.deepToString(expectedWakeLockUidTimesUs),
0, expectedWakeLockUidTimesUs[cluster][speed]);
}
}
}
@Test
public void testReadKernelUidCpuFreqTimesLocked_freqsChanged() {
// PRECONDITIONS
updateTimeBasesLocked(true, false, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int[] testUids = getUids(testUserId, new int[] {
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
});
final long[][] uidTimesMs = {
{4, 10, 5, 9, 4},
{5, 1, 12, 2, 10},
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
final KernelUidCpuFreqTimeReader.Callback callback =
(KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
}
return null;
}).when(mKernelUidCpuFreqTimeReader).readDelta(
any(KernelUidCpuFreqTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null);
// VERIFY
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
assertNotNull("No entry for uid=" + testUids[i], u);
assertArrayEquals("Unexpected cpu times for uid=" + testUids[i],
uidTimesMs[i], u.getCpuFreqTimes(STATS_SINCE_CHARGED));
assertNull("Unexpected screen-off cpu times for uid=" + testUids[i],
u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
}
// Repeat the test with the freqs from proc file changed.
// PRECONDITIONS
updateTimeBasesLocked(true, true, 0, 0);
final long[][] deltasMs = {
{3, 12, 55, 100, 32, 34984, 27983},
{3248327490475l, 232349349845043l, 123, 2398, 0, 398, 0},
{43, 3345, 2143, 123, 4554, 9374983794839l, 979875}
};
doAnswer(invocation -> {
final KernelUidCpuFreqTimeReader.Callback callback =
(KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuFreqTime(testUids[i], deltasMs[i]);
}
return null;
}).when(mKernelUidCpuFreqTimeReader).readDelta(
any(KernelUidCpuFreqTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null);
// VERIFY
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
assertNotNull("No entry for uid=" + testUids[i], u);
assertArrayEquals("Unexpected cpu times for uid=" + testUids[i],
deltasMs[i], u.getCpuFreqTimes(STATS_SINCE_CHARGED));
assertArrayEquals("Unexpected screen-off cpu times for uid=" + testUids[i],
deltasMs[i], u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
}
}
@Test
public void testReadKernelUidCpuFreqTimesLocked_isolatedUid() {
// PRECONDITIONS
updateTimeBasesLocked(true, false, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int isolatedAppId = FIRST_ISOLATED_UID + 27;
final int isolatedUid = UserHandle.getUid(testUserId, isolatedAppId);
final int[] testUids = getUids(testUserId, new int[] {
FIRST_APPLICATION_UID + 22,
isolatedAppId,
FIRST_APPLICATION_UID + 33
});
final long[][] uidTimesMs = {
{4, 10, 5, 9, 4},
{5, 1, 12, 2, 10},
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
final KernelUidCpuFreqTimeReader.Callback callback =
(KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
}
return null;
}).when(mKernelUidCpuFreqTimeReader).readDelta(
any(KernelUidCpuFreqTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null);
// VERIFY
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
if (UserHandle.isIsolated(testUids[i])) {
assertNull("There shouldn't be an entry for isolated uid=" + testUids[i], u);
continue;
}
assertNotNull("No entry for uid=" + testUids[i], u);
assertArrayEquals("Unexpected cpu times for uid=" + testUids[i],
uidTimesMs[i], u.getCpuFreqTimes(STATS_SINCE_CHARGED));
assertNull("Unexpected screen-off cpu times for uid=" + testUids[i],
u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
}
verify(mKernelUidCpuFreqTimeReader).removeUid(isolatedUid);
// Add an isolated uid mapping and repeat the test.
// PRECONDITIONS
final int ownerUid = UserHandle.getUid(testUserId, FIRST_APPLICATION_UID + 42);
mBatteryStatsImpl.addIsolatedUidLocked(isolatedUid, ownerUid);
final long[][] deltasMs = {
{3, 12, 55, 100, 32},
{32483274, 232349349, 123, 2398, 0},
{43, 3345, 2143, 123, 4554}
};
doAnswer(invocation -> {
final KernelUidCpuFreqTimeReader.Callback callback =
(KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuFreqTime(testUids[i], deltasMs[i]);
}
return null;
}).when(mKernelUidCpuFreqTimeReader).readDelta(
any(KernelUidCpuFreqTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null);
// VERIFY
for (int i = 0; i < testUids.length; ++i) {
BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
final long[] expectedTimes;
if (UserHandle.isIsolated(testUids[i])) {
assertNull("There shouldn't be an entry for isolated uid=" + testUids[i], u);
// Since we added a mapping, an entry should've been created for owner uid.
u = mBatteryStatsImpl.getUidStats().get(ownerUid);
expectedTimes = deltasMs[i];
assertNotNull("No entry for owner uid=" + ownerUid, u);
} else {
assertNotNull("No entry for uid=" + testUids[i], u);
expectedTimes = sum(uidTimesMs[i], deltasMs[i]);
}
assertArrayEquals("Unexpected cpu times for uid=" + testUids[i],
expectedTimes, u.getCpuFreqTimes(STATS_SINCE_CHARGED));
assertNull("Unexpected screen-off cpu times for uid=" + testUids[i],
u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
}
}
@Test
public void testReadKernelUiidCpuFreqTimesLocked_invalidUid() {
// PRECONDITIONS
updateTimeBasesLocked(true, false, 0, 0);
final int testUserId = 11;
final int invalidUserId = 15;
final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99);
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false);
final int[] testUids = getUids(testUserId, new int[] {
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
});
final long[][] uidTimesMs = {
{4, 10, 5, 9, 4},
{5, 1, 12, 2, 10},
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
final KernelUidCpuFreqTimeReader.Callback callback =
(KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
}
// And one for the invalid uid
callback.onUidCpuFreqTime(invalidUid, new long[] {12, 839, 32, 34, 21});
return null;
}).when(mKernelUidCpuFreqTimeReader).readDelta(
any(KernelUidCpuFreqTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null);
// VERIFY
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
assertNotNull("No entry for uid=" + testUids[i], u);
assertArrayEquals("Unexpected cpu times for uid=" + testUids[i],
uidTimesMs[i], u.getCpuFreqTimes(STATS_SINCE_CHARGED));
assertNull("Unexpected screen-off cpu times for uid=" + testUids[i],
u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
}
assertNull("There shouldn't be an entry for invalid uid=" + invalidUid,
mBatteryStatsImpl.getUidStats().get(invalidUid));
verify(mKernelUidCpuFreqTimeReader).removeUid(invalidUid);
}
private void updateTimeBasesLocked(boolean unplugged, boolean screenOff,
long upTime, long realTime) {
// Set PowerProfile=null before calling updateTimeBasesLocked to avoid execution of
// BatteryStatsImpl.updateCpuTimeLocked
mBatteryStatsImpl.setPowerProfile(null);
mBatteryStatsImpl.updateTimeBasesLocked(unplugged, screenOff, upTime, realTime);
mBatteryStatsImpl.setPowerProfile(mPowerProfile);
}
private void initKernelCpuSpeedReaders(int count) {
mKernelCpuSpeedReaders = new KernelCpuSpeedReader[count];
for (int i = 0; i < count; ++i) {
mKernelCpuSpeedReaders[i] = Mockito.mock(KernelCpuSpeedReader.class);
}
mBatteryStatsImpl.setKernelCpuSpeedReaders(mKernelCpuSpeedReaders);
}
private ArrayList<BatteryStatsImpl.StopwatchTimer> getPartialTimers(int... uids) {
final ArrayList<BatteryStatsImpl.StopwatchTimer> partialTimers = new ArrayList<>();
final BatteryStatsImpl.TimeBase timeBase = new BatteryStatsImpl.TimeBase();
for (int uid : uids) {
final BatteryStatsImpl.Uid u = mBatteryStatsImpl.getUidStatsLocked(uid);
final BatteryStatsImpl.StopwatchTimer timer = new BatteryStatsImpl.StopwatchTimer(
mClocks, u, WAKE_TYPE_PARTIAL, null, timeBase);
partialTimers.add(timer);
}
return partialTimers;
}
private long[] sum(long[] a, long[] b) {
assertEquals("Arrays a: " + Arrays.toString(a) + ", b: " + Arrays.toString(b),
a.length, b.length);
final long[] result = new long[a.length];
for (int i = 0; i < a.length; ++i) {
result[i] = a[i] + b[i];
}
return result;
}
private int[] getUids(int userId, int[] appIds) {
final int[] uids = new int[appIds.length];
for (int i = appIds.length - 1; i >= 0; --i) {
uids[i] = UserHandle.getUid(userId, appIds[i]);
}
return uids;
}
}