Add cache strategy for getUsageSource().

Cache usage source into SharedPreferences when phone is booting to avoid
calling it too frequently.
It should be safe because the usage source can only change on reboot.

Bug: 293366011
Test: make RunSettingsRoboTests
Change-Id: I35c07539d294737c5764b03b746cfb39f4ce008d
This commit is contained in:
Kuan Wang
2023-07-28 13:31:31 +08:00
parent 98df5bcde2
commit 64177774e2
10 changed files with 190 additions and 85 deletions

View File

@@ -22,8 +22,10 @@ import static org.robolectric.Shadows.shadowOf;
import android.app.AlarmManager;
import android.app.Application;
import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import androidx.test.core.app.ApplicationProvider;
@@ -40,7 +42,6 @@ import org.robolectric.Shadows;
import org.robolectric.shadows.ShadowAlarmManager;
import java.time.Clock;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -65,10 +66,12 @@ public final class BootBroadcastReceiverTest {
BatteryTestUtils.insertDataToBatteryStateTable(
mContext, Clock.systemUTC().millis(), "com.android.systemui");
mDao = database.batteryStateDao();
clearSharedPreferences();
}
@After
public void tearDown() {
clearSharedPreferences();
mPeriodicJobManager.reset();
}
@@ -82,8 +85,21 @@ public final class BootBroadcastReceiverTest {
@Test
public void onReceive_withBootCompletedIntent_refreshesJob() {
final SharedPreferences sharedPreferences = DatabaseUtils.getSharedPreferences(mContext);
sharedPreferences
.edit()
.putInt(DatabaseUtils.KEY_LAST_USAGE_SOURCE,
UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY)
.apply();
mReceiver.onReceive(mContext, new Intent(Intent.ACTION_BOOT_COMPLETED));
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
assertThat(
DatabaseUtils
.getSharedPreferences(mContext)
.contains(DatabaseUtils.KEY_LAST_USAGE_SOURCE))
.isFalse();
}
@Test
@@ -133,15 +149,7 @@ public final class BootBroadcastReceiverTest {
BootBroadcastReceiver.ACTION_PERIODIC_JOB_RECHECK);
}
private void insertExpiredData(int shiftDay) {
final long expiredTimeInMs =
Clock.systemUTC().millis() - Duration.ofDays(shiftDay).toMillis();
BatteryTestUtils.insertDataToBatteryStateTable(
mContext, expiredTimeInMs - 1, "com.android.systemui");
BatteryTestUtils.insertDataToBatteryStateTable(
mContext, expiredTimeInMs, "com.android.systemui");
// Ensures the testing environment is correct.
assertThat(mDao.getAllAfter(0)).hasSize(3);
private void clearSharedPreferences() {
DatabaseUtils.getSharedPreferences(mContext).edit().clear().apply();
}
}

View File

@@ -25,7 +25,6 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageEvents.Event;
import android.content.ContentValues;
@@ -35,7 +34,6 @@ import android.database.MatrixCursor;
import android.os.BatteryManager;
import android.os.BatteryUsageStats;
import android.os.LocaleList;
import android.os.RemoteException;
import android.os.UserHandle;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
@@ -62,14 +60,13 @@ public final class ConvertUtilsTest {
@Mock
private BatteryUsageStats mBatteryUsageStats;
@Mock
private IUsageStatsManager mUsageStatsManager;
@Mock
private BatteryEntry mMockBatteryEntry;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
ConvertUtils.sUsageSource = ConvertUtils.EMPTY_USAGE_SOURCE;
when(mContext.getPackageManager()).thenReturn(mMockPackageManager);
}
@@ -302,7 +299,7 @@ public final class ConvertUtilsTest {
final long userId = 2;
final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(
mContext, mUsageStatsManager, event, userId);
mContext, event, userId);
assertThat(appUsageEvent.getTimestamp()).isEqualTo(101L);
assertThat(appUsageEvent.getType()).isEqualTo(AppUsageEventType.ACTIVITY_RESUMED);
assertThat(appUsageEvent.getPackageName()).isEqualTo("com.android.settings1");
@@ -322,8 +319,8 @@ public final class ConvertUtilsTest {
when(mMockPackageManager.getPackageUidAsUser(any(), anyInt())).thenReturn(1001);
final long userId = 1;
final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(
mContext, mUsageStatsManager, event, userId);
final AppUsageEvent appUsageEvent =
ConvertUtils.convertToAppUsageEvent(mContext, event, userId);
assertThat(appUsageEvent.getTimestamp()).isEqualTo(101L);
assertThat(appUsageEvent.getType()).isEqualTo(AppUsageEventType.DEVICE_SHUTDOWN);
assertThat(appUsageEvent.getPackageName()).isEqualTo("com.android.settings1");
@@ -338,8 +335,8 @@ public final class ConvertUtilsTest {
final Event event = new Event();
event.mPackage = null;
final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(
mContext, mUsageStatsManager, event, /*userId=*/ 0);
final AppUsageEvent appUsageEvent =
ConvertUtils.convertToAppUsageEvent(mContext, event, /*userId=*/ 0);
assertThat(appUsageEvent).isNull();
}
@@ -354,8 +351,8 @@ public final class ConvertUtilsTest {
.thenThrow(new PackageManager.NameNotFoundException());
final long userId = 1;
final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(
mContext, mUsageStatsManager, event, userId);
final AppUsageEvent appUsageEvent =
ConvertUtils.convertToAppUsageEvent(mContext, event, userId);
assertThat(appUsageEvent).isNull();
}
@@ -450,51 +447,47 @@ public final class ConvertUtilsTest {
}
@Test
public void getEffectivePackageName_currentActivity_returnPackageName() throws RemoteException {
when(mUsageStatsManager.getUsageSource()).thenReturn(USAGE_SOURCE_CURRENT_ACTIVITY);
public void getEffectivePackageName_currentActivity_returnPackageName() {
ConvertUtils.sUsageSource = USAGE_SOURCE_CURRENT_ACTIVITY;
final String packageName = "com.android.settings1";
final String taskRootPackageName = "com.android.settings2";
assertThat(ConvertUtils.getEffectivePackageName(
mUsageStatsManager, packageName, taskRootPackageName))
mContext, packageName, taskRootPackageName))
.isEqualTo(packageName);
}
@Test
public void getEffectivePackageName_usageSourceThrowException_returnPackageName()
throws RemoteException {
when(mUsageStatsManager.getUsageSource()).thenThrow(new RemoteException());
public void getEffectivePackageName_emptyUsageSource_returnPackageName() {
final String packageName = "com.android.settings1";
final String taskRootPackageName = "com.android.settings2";
assertThat(ConvertUtils.getEffectivePackageName(
mUsageStatsManager, packageName, taskRootPackageName))
mContext, packageName, taskRootPackageName))
.isEqualTo(packageName);
}
@Test
public void getEffectivePackageName_rootActivity_returnTaskRootPackageName()
throws RemoteException {
when(mUsageStatsManager.getUsageSource()).thenReturn(USAGE_SOURCE_TASK_ROOT_ACTIVITY);
public void getEffectivePackageName_rootActivity_returnTaskRootPackageName() {
ConvertUtils.sUsageSource = USAGE_SOURCE_TASK_ROOT_ACTIVITY;
final String packageName = "com.android.settings1";
final String taskRootPackageName = "com.android.settings2";
assertThat(ConvertUtils.getEffectivePackageName(
mUsageStatsManager, packageName, taskRootPackageName))
mContext, packageName, taskRootPackageName))
.isEqualTo(taskRootPackageName);
}
@Test
public void getEffectivePackageName_nullOrEmptyTaskRoot_returnPackageName()
throws RemoteException {
when(mUsageStatsManager.getUsageSource()).thenReturn(USAGE_SOURCE_TASK_ROOT_ACTIVITY);
public void getEffectivePackageName_nullOrEmptyTaskRoot_returnPackageName() {
ConvertUtils.sUsageSource = USAGE_SOURCE_TASK_ROOT_ACTIVITY;
final String packageName = "com.android.settings1";
assertThat(ConvertUtils.getEffectivePackageName(
mUsageStatsManager, packageName, /*taskRootPackageName=*/ null))
mContext, packageName, /*taskRootPackageName=*/ null))
.isEqualTo(packageName);
assertThat(ConvertUtils.getEffectivePackageName(
mUsageStatsManager, packageName, /*taskRootPackageName=*/ ""))
mContext, packageName, /*taskRootPackageName=*/ ""))
.isEqualTo(packageName);
}
}

View File

@@ -72,7 +72,7 @@ public final class DataProcessManagerTest {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
DataProcessor.sUsageStatsManager = mUsageStatsManager;
DatabaseUtils.sUsageStatsManager = mUsageStatsManager;
doReturn(mContext).when(mContext).getApplicationContext();
doReturn(mUserManager)
.when(mContext)

View File

@@ -93,7 +93,7 @@ public final class DataProcessorTest {
mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider;
DataProcessor.sTestSystemAppsPackageNames = Set.of();
DataProcessor.sUsageStatsManager = mUsageStatsManager;
DatabaseUtils.sUsageStatsManager = mUsageStatsManager;
doReturn(mIntent).when(mContext).registerReceiver(
isA(BroadcastReceiver.class), isA(IntentFilter.class));
doReturn(100).when(mIntent).getIntExtra(eq(BatteryManager.EXTRA_SCALE), anyInt());
@@ -249,7 +249,7 @@ public final class DataProcessorTest {
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>> periodMap =
DataProcessor.generateAppUsagePeriodMap(
14400000L, hourlyBatteryLevelsPerDay, appUsageEventList, new ArrayList<>());
mContext, hourlyBatteryLevelsPerDay, appUsageEventList, new ArrayList<>());
assertThat(periodMap).hasSize(3);
// Day 1
@@ -287,7 +287,8 @@ public final class DataProcessorTest {
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>()));
assertThat(DataProcessor.generateAppUsagePeriodMap(
0L, hourlyBatteryLevelsPerDay, new ArrayList<>(), new ArrayList<>())).isNull();
mContext, hourlyBatteryLevelsPerDay, new ArrayList<>(), new ArrayList<>()))
.isNull();
}
@Test
@@ -1644,7 +1645,7 @@ public final class DataProcessorTest {
final Map<Long, Map<String, List<AppUsagePeriod>>> appUsagePeriodMap =
DataProcessor.buildAppUsagePeriodList(
appUsageEvents, new ArrayList<>(), 0, 5);
mContext, appUsageEvents, new ArrayList<>(), 0, 5);
assertThat(appUsagePeriodMap).hasSize(2);
final Map<String, List<AppUsagePeriod>> userMap1 = appUsagePeriodMap.get(1L);
@@ -1668,7 +1669,7 @@ public final class DataProcessorTest {
@Test
public void buildAppUsagePeriodList_emptyEventList_returnNull() {
assertThat(DataProcessor.buildAppUsagePeriodList(
new ArrayList<>(), new ArrayList<>(), 0, 1)).isNull();
mContext, new ArrayList<>(), new ArrayList<>(), 0, 1)).isNull();
}
@Test
@@ -1680,7 +1681,7 @@ public final class DataProcessorTest {
AppUsageEventType.DEVICE_SHUTDOWN, /*timestamp=*/ 2));
assertThat(DataProcessor.buildAppUsagePeriodList(
appUsageEvents, new ArrayList<>(), 0, 3)).isNull();
mContext, appUsageEvents, new ArrayList<>(), 0, 3)).isNull();
}
@Test

View File

@@ -16,6 +16,9 @@
package com.android.settings.fuelgauge.batteryusage;
import static android.app.usage.UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY;
import static android.app.usage.UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -23,15 +26,19 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.usage.IUsageStatsManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.MatrixCursor;
import android.os.BatteryManager;
import android.os.BatteryUsageStats;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -67,6 +74,7 @@ public final class DatabaseUtilsTest {
@Mock private BatteryEntry mMockBatteryEntry2;
@Mock private BatteryEntry mMockBatteryEntry3;
@Mock private Context mMockContext;
@Mock private IUsageStatsManager mUsageStatsManager;
@Before
public void setUp() {
@@ -77,6 +85,7 @@ public final class DatabaseUtilsTest {
doReturn(mPackageManager).when(mMockContext).getPackageManager();
doReturn(mPackageManager).when(mContext).getPackageManager();
DatabaseUtils.getSharedPreferences(mContext).edit().clear().apply();
DatabaseUtils.sUsageStatsManager = mUsageStatsManager;
}
@Test
@@ -422,6 +431,71 @@ public final class DatabaseUtilsTest {
assertThat(batteryHistMap).isEmpty();
}
@Test
public void removeUsageSource_hasNoData() {
DatabaseUtils.removeUsageSource(mContext);
assertThat(
DatabaseUtils
.getSharedPreferences(mContext)
.contains(DatabaseUtils.KEY_LAST_USAGE_SOURCE))
.isFalse();
}
@Test
public void removeUsageSource_hasData_deleteUsageSource() {
final SharedPreferences sharedPreferences = DatabaseUtils.getSharedPreferences(mContext);
sharedPreferences
.edit()
.putInt(DatabaseUtils.KEY_LAST_USAGE_SOURCE, USAGE_SOURCE_TASK_ROOT_ACTIVITY)
.apply();
DatabaseUtils.removeUsageSource(mContext);
assertThat(
DatabaseUtils
.getSharedPreferences(mContext)
.contains(DatabaseUtils.KEY_LAST_USAGE_SOURCE))
.isFalse();
}
@Test
public void getUsageSource_hasData() {
final SharedPreferences sharedPreferences = DatabaseUtils.getSharedPreferences(mContext);
sharedPreferences
.edit()
.putInt(DatabaseUtils.KEY_LAST_USAGE_SOURCE, USAGE_SOURCE_TASK_ROOT_ACTIVITY)
.apply();
assertThat(DatabaseUtils.getUsageSource(mContext))
.isEqualTo(USAGE_SOURCE_TASK_ROOT_ACTIVITY);
}
@Test
public void getUsageSource_notHasData_writeLoadedData() throws RemoteException {
when(mUsageStatsManager.getUsageSource()).thenReturn(USAGE_SOURCE_TASK_ROOT_ACTIVITY);
assertThat(DatabaseUtils.getUsageSource(mContext))
.isEqualTo(USAGE_SOURCE_TASK_ROOT_ACTIVITY);
assertThat(
DatabaseUtils
.getSharedPreferences(mContext)
.getInt(DatabaseUtils.KEY_LAST_USAGE_SOURCE, USAGE_SOURCE_CURRENT_ACTIVITY))
.isEqualTo(USAGE_SOURCE_TASK_ROOT_ACTIVITY);
}
@Test
public void getUsageSource_throwException_writeDefaultData() throws RemoteException {
when(mUsageStatsManager.getUsageSource()).thenThrow(new RemoteException());
assertThat(DatabaseUtils.getUsageSource(mContext))
.isEqualTo(USAGE_SOURCE_CURRENT_ACTIVITY);
assertThat(
DatabaseUtils
.getSharedPreferences(mContext)
.getInt(DatabaseUtils.KEY_LAST_USAGE_SOURCE, USAGE_SOURCE_CURRENT_ACTIVITY))
.isEqualTo(USAGE_SOURCE_CURRENT_ACTIVITY);
}
@Test
public void recordDateTime_writeDataIntoSharedPreferences() {
final String preferenceKey = "test_preference_key";