Remove resource usage stats on user delete and cleanup on system startup
Test: atest CarWatchdogServiceUnitTest WatchdogStorageUnitTest CarWatchdogServiceTest
Bug: 183413754
Change-Id: I550fbd7cd6c94960b3c3ee7d05f2dad7267238ca
Merged-In: I550fbd7cd6c94960b3c3ee7d05f2dad7267238ca
diff --git a/service/src/com/android/car/watchdog/CarWatchdogService.java b/service/src/com/android/car/watchdog/CarWatchdogService.java
index 93243d4..318d132 100644
--- a/service/src/com/android/car/watchdog/CarWatchdogService.java
+++ b/service/src/com/android/car/watchdog/CarWatchdogService.java
@@ -77,6 +77,7 @@
"com.android.server.jobscheduler.GARAGE_MODE_ON";
static final String ACTION_GARAGE_MODE_OFF =
"com.android.server.jobscheduler.GARAGE_MODE_OFF";
+ static final int MISSING_ARG_VALUE = -1;
static final TimeSourceInterface SYSTEM_INSTANCE = new TimeSourceInterface() {
@Override
public Instant now() {
@@ -106,25 +107,15 @@
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- boolean isGarageMode = false;
- if (action.equals(ACTION_GARAGE_MODE_ON)) {
- isGarageMode = true;
- } else if (!action.equals(ACTION_GARAGE_MODE_OFF)) {
- return;
- }
- if (isGarageMode) {
- mWatchdogStorage.shrinkDatabase();
- }
- try {
- mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.GARAGE_MODE,
- isGarageMode ? GarageMode.GARAGE_MODE_ON : GarageMode.GARAGE_MODE_OFF,
- /* arg2= */ -1);
- if (DEBUG) {
- Slogf.d(TAG, "Notified car watchdog daemon of garage mode(%s)",
- isGarageMode ? "ON" : "OFF");
- }
- } catch (RemoteException | RuntimeException e) {
- Slogf.w(TAG, "Notifying garage mode state change failed: %s", e);
+ switch (action) {
+ case ACTION_GARAGE_MODE_ON:
+ case ACTION_GARAGE_MODE_OFF:
+ handleGarageModeIntent(action.equals(ACTION_GARAGE_MODE_ON));
+ break;
+ case Intent.ACTION_USER_REMOVED:
+ UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
+ mWatchdogPerfHandler.deleteUser(user.getIdentifier());
+ break;
}
}
};
@@ -361,6 +352,23 @@
mWatchdogPerfHandler.setTimeSource(timeSource);
}
+ private void handleGarageModeIntent(boolean isOn) {
+ if (isOn) {
+ mWatchdogStorage.shrinkDatabase();
+ }
+ try {
+ mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.GARAGE_MODE,
+ isOn ? GarageMode.GARAGE_MODE_ON : GarageMode.GARAGE_MODE_OFF,
+ /* arg2= */ MISSING_ARG_VALUE);
+ if (DEBUG) {
+ Slogf.d(TAG, "Notified car watchdog daemon of garage mode(%s)",
+ isOn ? "ON" : "OFF");
+ }
+ } catch (RemoteException | RuntimeException e) {
+ Slogf.w(TAG, e, "Notifying garage mode state change failed");
+ }
+ }
+
private void postRegisterToDaemonMessage() {
CarServiceUtils.runOnMain(() -> {
synchronized (mLock) {
@@ -447,7 +455,7 @@
}
try {
mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.POWER_CYCLE,
- powerCycle, /* arg2= */ -1);
+ powerCycle, /* arg2= */ MISSING_ARG_VALUE);
if (DEBUG) {
Slogf.d(TAG, "Notified car watchdog daemon of power cycle(%d)", powerCycle);
}
@@ -499,6 +507,7 @@
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_GARAGE_MODE_ON);
filter.addAction(ACTION_GARAGE_MODE_OFF);
+ filter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiverForAllUsers(mBroadcastReceiver, filter, null, null);
}
diff --git a/service/src/com/android/car/watchdog/WatchdogPerfHandler.java b/service/src/com/android/car/watchdog/WatchdogPerfHandler.java
index 416795e..495c439 100644
--- a/service/src/com/android/car/watchdog/WatchdogPerfHandler.java
+++ b/service/src/com/android/car/watchdog/WatchdogPerfHandler.java
@@ -68,7 +68,6 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -401,12 +400,11 @@
}
private void setPackageKillableStateForAllUsers(String packageName, boolean isKillable) {
- UserManager userManager = UserManager.get(mContext);
- List<UserInfo> userInfos = userManager.getAliveUsers();
+ int[] userIds = getAliveUserIds();
String genericPackageName = null;
synchronized (mLock) {
- for (int i = 0; i < userInfos.size(); ++i) {
- int userId = userInfos.get(i).id;
+ for (int i = 0; i < userIds.length; ++i) {
+ int userId = userIds[i];
String name = mPackageInfoHandler.getNameForUserPackage(packageName, userId);
if (name == null) {
continue;
@@ -449,11 +447,10 @@
return getPackageKillableStatesForUserId(userHandle.getIdentifier(), pm);
}
List<PackageKillableState> packageKillableStates = new ArrayList<>();
- UserManager userManager = UserManager.get(mContext);
- List<UserInfo> userInfos = userManager.getAliveUsers();
- for (int i = 0; i < userInfos.size(); ++i) {
+ int[] userIds = getAliveUserIds();
+ for (int i = 0; i < userIds.length; ++i) {
packageKillableStates.addAll(
- getPackageKillableStatesForUserId(userInfos.get(i).id, pm));
+ getPackageKillableStatesForUserId(userIds[i], pm));
}
if (DEBUG) {
Slogf.d(TAG, "Returning all package killable states for all users");
@@ -749,6 +746,21 @@
}
}
+ /** Deletes all data for specific user. */
+ public void deleteUser(@UserIdInt int userId) {
+ synchronized (mLock) {
+ for (int i = mUsageByUserPackage.size() - 1; i >= 0; --i) {
+ if (userId == mUsageByUserPackage.valueAt(i).userId) {
+ mUsageByUserPackage.removeAt(i);
+ }
+ }
+ mWatchdogStorage.syncUsers(getAliveUserIds());
+ }
+ if (DEBUG) {
+ Slogf.d(TAG, "Resource usage for user id: %d was deleted.", userId);
+ }
+ }
+
/** Sets the time source. */
public void setTimeSource(TimeSourceInterface timeSource) {
synchronized (mLock) {
@@ -801,6 +813,7 @@
}
private void readFromDatabase() {
+ mWatchdogStorage.syncUsers(getAliveUserIds());
List<WatchdogStorage.UserPackageSettingsEntry> settingsEntries =
mWatchdogStorage.getUserPackageSettings();
Slogf.i(TAG, "Read %d user package settings from database", settingsEntries.size());
@@ -1225,6 +1238,17 @@
}
}
+ private int[] getAliveUserIds() {
+ UserManager userManager = UserManager.get(mContext);
+ List<UserHandle> aliveUsers = userManager.getUserHandles(/* excludeDying= */ true);
+ int userSize = aliveUsers.size();
+ int[] userIds = new int[userSize];
+ for (int i = 0; i < userSize; ++i) {
+ userIds[i] = aliveUsers.get(i).getIdentifier();
+ }
+ return userIds;
+ }
+
private static String getUserPackageUniqueId(int userId, String genericPackageName) {
return String.valueOf(userId) + ":" + genericPackageName;
}
diff --git a/service/src/com/android/car/watchdog/WatchdogStorage.java b/service/src/com/android/car/watchdog/WatchdogStorage.java
index 78b6927..42f6313 100644
--- a/service/src/com/android/car/watchdog/WatchdogStorage.java
+++ b/service/src/com/android/car/watchdog/WatchdogStorage.java
@@ -32,6 +32,7 @@
import android.os.Process;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IntArray;
import android.util.Slog;
import com.android.car.CarLog;
@@ -196,6 +197,25 @@
}
}
+ /**
+ * Deletes all user package settings and resource stats for all non-alive users.
+ *
+ * @param aliveUserIds Array of alive user ids.
+ */
+ public void syncUsers(int[] aliveUserIds) {
+ IntArray aliveUsers = IntArray.wrap(aliveUserIds);
+ for (int i = mUserPackagesByKey.size() - 1; i >= 0; --i) {
+ UserPackage userPackage = mUserPackagesByKey.valueAt(i);
+ if (aliveUsers.indexOf(userPackage.getUserId()) == -1) {
+ mUserPackagesByKey.removeAt(i);
+ mUserPackagesById.remove(userPackage.getUniqueId());
+ }
+ }
+ try (SQLiteDatabase db = mDbHelper.getWritableDatabase()) {
+ UserPackageSettingsTable.syncUserPackagesWithAliveUsers(db, aliveUsers);
+ }
+ }
+
@VisibleForTesting
boolean saveIoUsageStats(List<IoUsageStatsEntry> entries, boolean shouldCheckRetention) {
ZonedDateTime currentDate =
@@ -371,6 +391,24 @@
Slogf.i(TAG, "Deleted %d user package settings db rows for user %d and package %s",
deletedRows, userId, packageName);
}
+
+ public static void syncUserPackagesWithAliveUsers(SQLiteDatabase db, IntArray aliveUsers) {
+ StringBuilder queryBuilder = new StringBuilder();
+ for (int i = 0; i < aliveUsers.size(); ++i) {
+ if (i == 0) {
+ queryBuilder.append(COLUMN_USER_ID).append(" NOT IN (");
+ } else {
+ queryBuilder.append(", ");
+ }
+ queryBuilder.append(aliveUsers.get(i));
+ if (i == aliveUsers.size() - 1) {
+ queryBuilder.append(")");
+ }
+ }
+ int deletedRows = db.delete(TABLE_NAME, queryBuilder.toString(), new String[]{});
+ Slogf.i(TAG, "Deleted %d user package settings db rows while syncing with alive users",
+ deletedRows);
+ }
}
/** Defines the I/O usage entry stored in the IoUsageStatsTable. */
diff --git a/tests/carservice_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java b/tests/carservice_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
index ba01d41..9a24a7e 100644
--- a/tests/carservice_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
@@ -77,8 +77,8 @@
private final Executor mExecutor =
InstrumentationRegistry.getInstrumentation().getTargetContext().getMainExecutor();
private final UserInfo[] mUserInfos = new UserInfo[] {
- new UserInfoBuilder(10).setName("user 1").build(),
- new UserInfoBuilder(11).setName("user 2").build()
+ new UserInfoBuilder(100).setName("user 1").build(),
+ new UserInfoBuilder(101).setName("user 2").build()
};
@Mock private Context mMockContext;
@@ -101,8 +101,8 @@
when(mServiceBinder.queryLocalInterface(anyString())).thenReturn(mCarWatchdogService);
when(mMockContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
mockUmGetAllUsers(mUserManager, mUserInfos);
- mockUmIsUserRunning(mUserManager, 10, true);
- mockUmIsUserRunning(mUserManager, 11, false);
+ mockUmIsUserRunning(mUserManager, 100, true);
+ mockUmIsUserRunning(mUserManager, 101, false);
mCarWatchdogService.init();
mWatchdogServiceForSystemImpl = registerCarWatchdogService();
@@ -224,11 +224,11 @@
}
private void expectRunningUser() {
- doReturn(10).when(() -> UserHandle.getUserId(Binder.getCallingUid()));
+ doReturn(100).when(() -> UserHandle.getUserId(Binder.getCallingUid()));
}
private void expectStoppedUser() {
- doReturn(11).when(() -> UserHandle.getUserId(Binder.getCallingUid()));
+ doReturn(101).when(() -> UserHandle.getUserId(Binder.getCallingUid()));
}
private final class TestClient {
diff --git a/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceUnitTest.java b/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceUnitTest.java
index c1e3938..6734322 100644
--- a/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceUnitTest.java
@@ -18,8 +18,8 @@
import static android.automotive.watchdog.internal.ResourceOveruseActionType.KILLED;
import static android.automotive.watchdog.internal.ResourceOveruseActionType.NOT_KILLED;
-import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetAliveUsers;
import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetAllUsers;
+import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetUserHandles;
import static android.car.watchdog.CarWatchdogManager.TIMEOUT_CRITICAL;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
@@ -283,6 +283,15 @@
}
@Test
+ public void testUserRemovedBroadcast() throws Exception {
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101, 102);
+ mBroadcastReceiver.onReceive(mMockContext,
+ new Intent().setAction(Intent.ACTION_USER_REMOVED)
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(100)));
+ verify(mMockWatchdogStorage).syncUsers(new int[] {101, 102});
+ }
+
+ @Test
public void testGetResourceOveruseStats() throws Exception {
int uid = Binder.getCallingUid();
injectPackageInfos(Collections.singletonList(
@@ -1001,7 +1010,7 @@
@Test
public void testSetKillablePackageAsUser() throws Exception {
- mockUmGetAliveUsers(mMockUserManager, 11, 12);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package", 1103456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 1101278, null),
@@ -1029,7 +1038,7 @@
() -> mCarWatchdogService.setKillablePackageAsUser("vendor_package.critical",
userHandle, /* isKillable= */ true));
- mockUmGetAliveUsers(mMockUserManager, 11, 12, 13);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12, 13);
injectPackageInfos(Collections.singletonList(
constructPackageManagerPackageInfo("third_party_package", 1303456, null)));
@@ -1049,7 +1058,7 @@
@Test
public void testSetKillablePackageAsUserWithSharedUids() throws Exception {
- mockUmGetAliveUsers(mMockUserManager, 11, 12);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"third_party_package.A", 1103456, "third_party_shared_package.A"),
@@ -1094,7 +1103,7 @@
@Test
public void testSetKillablePackageAsUserForAllUsers() throws Exception {
- mockUmGetAliveUsers(mMockUserManager, 11, 12);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package", 1103456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 1101278, null),
@@ -1121,7 +1130,7 @@
() -> mCarWatchdogService.setKillablePackageAsUser("vendor_package.critical",
UserHandle.ALL, /* isKillable= */ true));
- mockUmGetAliveUsers(mMockUserManager, 11, 12, 13);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12, 13);
injectPackageInfos(Collections.singletonList(
constructPackageManagerPackageInfo("third_party_package", 1303456, null)));
@@ -1141,7 +1150,7 @@
@Test
public void testSetKillablePackageAsUsersForAllUsersWithSharedUids() throws Exception {
- mockUmGetAliveUsers(mMockUserManager, 11, 12);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"third_party_package.A", 1103456, "third_party_shared_package.A"),
@@ -1174,7 +1183,7 @@
new PackageKillableState("third_party_package.B", 12,
PackageKillableState.KILLABLE_STATE_NO));
- mockUmGetAliveUsers(mMockUserManager, 11, 12, 13);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12, 13);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"third_party_package.A", 1303456, "third_party_shared_package.A"),
@@ -1192,7 +1201,7 @@
@Test
public void testGetPackageKillableStatesAsUser() throws Exception {
- mockUmGetAliveUsers(mMockUserManager, 11, 12);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package", 1103456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 1101278, null),
@@ -1210,7 +1219,7 @@
@Test
public void testGetPackageKillableStatesAsUserWithSafeToKillPackages() throws Exception {
- mockUmGetAliveUsers(mMockUserManager, 11, 12);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("system_package.non_critical.A", 1102459, null),
constructPackageManagerPackageInfo("third_party_package", 1103456, null),
@@ -1242,7 +1251,7 @@
@Test
public void testGetPackageKillableStatesAsUserWithVendorPackagePrefixes() throws Exception {
- mockUmGetAliveUsers(mMockUserManager, 11);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11);
/* Package names which start with "system" are constructed as system packages. */
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("system_package_as_vendor", 1102459, null)));
@@ -1273,7 +1282,7 @@
@Test
public void testGetPackageKillableStatesAsUserWithSharedUids() throws Exception {
- mockUmGetAliveUsers(mMockUserManager, 11, 12);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"system_package.A", 1103456, "vendor_shared_package.A"),
@@ -1304,7 +1313,7 @@
@Test
public void testGetPackageKillableStatesAsUserWithSharedUidsAndSafeToKillPackages()
throws Exception {
- mockUmGetAliveUsers(mMockUserManager, 11);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"vendor_package.non_critical.A", 1103456, "vendor_shared_package.A"),
@@ -1343,7 +1352,7 @@
@Test
public void testGetPackageKillableStatesAsUserWithSharedUidsAndSafeToKillSharedPackage()
throws Exception {
- mockUmGetAliveUsers(mMockUserManager, 11);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"vendor_package.non_critical.A", 1103456, "vendor_shared_package.B"),
@@ -1374,7 +1383,7 @@
@Test
public void testGetPackageKillableStatesAsUserForAllUsers() throws Exception {
- mockUmGetAliveUsers(mMockUserManager, 11, 12);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package", 1103456, null),
constructPackageManagerPackageInfo("vendor_package.critical", 1101278, null),
@@ -1395,7 +1404,7 @@
@Test
public void testGetPackageKillableStatesAsUserForAllUsersWithSharedUids() throws Exception {
- mockUmGetAliveUsers(mMockUserManager, 11, 12);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"system_package.A", 1103456, "vendor_shared_package.A"),
@@ -1897,7 +1906,7 @@
@Test
public void testPersistStatsOnShutdownEnter() throws Exception {
- mockUmGetAliveUsers(mMockUserManager, 10, 11, 12);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 10, 11, 12);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo(
"third_party_package", 1103456, "vendor_shared_package.critical"),
@@ -1961,7 +1970,7 @@
@Test
public void testPersistIoOveruseStatsOnDateChange() throws Exception {
- mockUmGetAliveUsers(mMockUserManager, 10);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 10);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("system_package", 1011200, null),
constructPackageManagerPackageInfo("third_party_package", 1001100, null)));
@@ -2053,7 +2062,7 @@
@Test
public void testResetResourceOveruseStatsResetsUserPackageSettings() throws Exception {
- mockUmGetAliveUsers(mMockUserManager, 100, 101);
+ mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 100, 101);
injectPackageInfos(Arrays.asList(
constructPackageManagerPackageInfo("third_party_package.A", 10001278, null),
constructPackageManagerPackageInfo("third_party_package.A", 10101278, null),
@@ -2388,6 +2397,7 @@
*/
CarServiceUtils.getHandlerThread(CarWatchdogService.class.getSimpleName())
.getThreadHandler().post(() -> {});
+ verify(mMockWatchdogStorage, times(wantedInvocations)).syncUsers(any());
verify(mMockWatchdogStorage, times(wantedInvocations)).getUserPackageSettings();
verify(mMockWatchdogStorage, times(wantedInvocations)).getTodayIoUsageStats();
}
diff --git a/tests/carservice_unit_test/src/com/android/car/watchdog/WatchdogStorageUnitTest.java b/tests/carservice_unit_test/src/com/android/car/watchdog/WatchdogStorageUnitTest.java
index 3f16750..e2c12bb 100644
--- a/tests/carservice_unit_test/src/com/android/car/watchdog/WatchdogStorageUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/watchdog/WatchdogStorageUnitTest.java
@@ -346,6 +346,68 @@
}
@Test
+ public void testSyncUsers() throws Exception {
+ List<WatchdogStorage.UserPackageSettingsEntry> settingsEntries = sampleSettings();
+ List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries = sampleStatsForToday();
+
+ assertThat(mService.saveUserPackageSettings(settingsEntries)).isTrue();
+ assertThat(mService.saveIoUsageStats(ioUsageStatsEntries)).isTrue();
+
+ mService.syncUsers(/* aliveUserIds= */ new int[] {101});
+
+ settingsEntries.removeIf((s) -> s.userId == 100);
+ ioUsageStatsEntries.removeIf((e) -> e.userId == 100);
+
+ UserPackageSettingsEntrySubject.assertThat(mService.getUserPackageSettings())
+ .containsExactlyElementsIn(settingsEntries);
+
+ IoUsageStatsEntrySubject.assertThat(mService.getTodayIoUsageStats())
+ .containsExactlyElementsIn(ioUsageStatsEntries);
+ }
+
+ @Test
+ public void testSyncUsersWithHistoricalIoOveruseStats() throws Exception {
+ List<WatchdogStorage.UserPackageSettingsEntry> settingsEntries = sampleSettings();
+
+ assertThat(mService.saveUserPackageSettings(settingsEntries)).isTrue();
+ assertThat(mService.saveIoUsageStats(sampleStatsBetweenDates(
+ /* includingStartDaysAgo= */ 1, /* excludingEndDaysAgo= */ 6))).isTrue();
+
+ mService.syncUsers(/* aliveUserIds= */ new int[] {101});
+
+ settingsEntries.removeIf((s) -> s.userId == 100);
+
+ UserPackageSettingsEntrySubject.assertThat(mService.getUserPackageSettings())
+ .containsExactlyElementsIn(settingsEntries);
+
+ IoOveruseStats actualSystemPackage = mService.getHistoricalIoOveruseStats(
+ /* userId= */ 100, "system_package.non_critical.A", /* numDaysAgo= */ 7);
+ IoOveruseStats actualVendorPackage = mService.getHistoricalIoOveruseStats(
+ /* userId= */ 100, "vendor_package.critical.C", /* numDaysAgo= */ 7);
+
+ assertWithMessage("System I/O overuse stats for deleted user")
+ .that(actualSystemPackage).isNull();
+ assertWithMessage("Vendor I/O overuse stats for deleted user")
+ .that(actualVendorPackage).isNull();
+ }
+
+ @Test
+ public void testSyncUsersWithNoDataForDeletedUser() throws Exception {
+ List<WatchdogStorage.UserPackageSettingsEntry> settingsEntries = sampleSettings();
+ List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries = sampleStatsForToday();
+
+ assertThat(mService.saveUserPackageSettings(settingsEntries)).isTrue();
+ assertThat(mService.saveIoUsageStats(ioUsageStatsEntries)).isTrue();
+
+ mService.syncUsers(/* aliveUserIds= */ new int[] {100, 101});
+
+ UserPackageSettingsEntrySubject.assertThat(mService.getUserPackageSettings())
+ .containsExactlyElementsIn(settingsEntries);
+ IoUsageStatsEntrySubject.assertThat(mService.getTodayIoUsageStats())
+ .containsExactlyElementsIn(ioUsageStatsEntries);
+ }
+
+ @Test
public void testTruncateStatsOutsideRetentionPeriodOnDateChange() throws Exception {
injectSampleUserPackageSettings();
setDate(/* numDaysAgo= */ 1);
@@ -454,7 +516,7 @@
private static ArrayList<WatchdogStorage.IoUsageStatsEntry> sampleStatsForDate(
long statsDateEpoch, long duration) {
ArrayList<WatchdogStorage.IoUsageStatsEntry> entries = new ArrayList<>();
- for (int i = 100; i < 101; ++i) {
+ for (int i = 100; i <= 101; ++i) {
entries.add(constructIoUsageStatsEntry(
/* userId= */ i, "system_package.non_critical.A", statsDateEpoch, duration,
/* remainingWriteBytes= */