1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.watchdog; 18 19 import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_NEVER; 20 import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_NO; 21 import static android.car.watchdog.PackageKillableState.KILLABLE_STATE_YES; 22 23 import static com.android.car.watchdog.WatchdogStorage.RETENTION_PERIOD; 24 import static com.android.car.watchdog.WatchdogStorage.STATS_TEMPORAL_UNIT; 25 import static com.android.car.watchdog.WatchdogStorage.WatchdogDbHelper.DATABASE_NAME; 26 27 import static com.google.common.truth.Truth.assertThat; 28 import static com.google.common.truth.Truth.assertWithMessage; 29 30 import android.automotive.watchdog.PerStateBytes; 31 import android.car.watchdog.IoOveruseStats; 32 import android.content.Context; 33 import android.database.Cursor; 34 import android.database.sqlite.SQLiteDatabase; 35 import android.util.Slog; 36 import android.util.SparseArray; 37 38 import androidx.test.InstrumentationRegistry; 39 import androidx.test.filters.SmallTest; 40 import androidx.test.runner.AndroidJUnit4; 41 42 import org.junit.After; 43 import org.junit.Before; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 47 import java.io.File; 48 import java.time.Instant; 49 import java.time.ZonedDateTime; 50 import java.time.temporal.ChronoUnit; 51 import java.util.ArrayList; 52 import java.util.Arrays; 53 import java.util.Collections; 54 import java.util.List; 55 56 /** 57 * <p>This class contains unit tests for the {@link WatchdogStorage}. 58 */ 59 @SmallTest 60 @RunWith(AndroidJUnit4.class) 61 public final class WatchdogStorageUnitTest { 62 private static final String TAG = WatchdogStorageUnitTest.class.getSimpleName(); 63 64 private Context mContext; 65 private WatchdogStorage mService; 66 private File mDatabaseFile; 67 68 private final TestTimeSource mTimeSource = new TestTimeSource(); 69 70 @Before setUp()71 public void setUp() throws Exception { 72 mTimeSource.updateNow(/* numDaysAgo= */ 0); 73 mContext = InstrumentationRegistry.getTargetContext().createDeviceProtectedStorageContext(); 74 mDatabaseFile = mContext.createDeviceProtectedStorageContext() 75 .getDatabasePath(DATABASE_NAME); 76 mService = new WatchdogStorage(mContext, /* useDataSystemCarDir= */ false, mTimeSource); 77 } 78 79 @After tearDown()80 public void tearDown() { 81 mService.release(); 82 if (!mDatabaseFile.delete()) { 83 Slog.e(TAG, "Failed to delete the database file: " + mDatabaseFile.getAbsolutePath()); 84 } 85 } 86 87 @Test testSaveUserPackageSettings()88 public void testSaveUserPackageSettings() throws Exception { 89 List<WatchdogStorage.UserPackageSettingsEntry> expected = sampleSettings(); 90 91 assertThat(mService.saveUserPackageSettings(expected)).isTrue(); 92 93 assertWithMessage("User package settings").that(mService.getUserPackageSettings()) 94 .containsExactlyElementsIn(expected); 95 } 96 97 @Test testOverwriteUserPackageSettings()98 public void testOverwriteUserPackageSettings() throws Exception { 99 List<WatchdogStorage.UserPackageSettingsEntry> expected = Arrays.asList( 100 new WatchdogStorage.UserPackageSettingsEntry( 101 /* userId= */ 100, "system_package.non_critical.A", KILLABLE_STATE_YES), 102 new WatchdogStorage.UserPackageSettingsEntry( 103 /* userId= */ 100, "system_package.non_critical.B", KILLABLE_STATE_NO)); 104 105 assertThat(mService.saveUserPackageSettings(expected)).isTrue(); 106 107 expected = Arrays.asList( 108 new WatchdogStorage.UserPackageSettingsEntry( 109 /* userId= */ 100, "system_package.non_critical.A", KILLABLE_STATE_NEVER), 110 new WatchdogStorage.UserPackageSettingsEntry( 111 /* userId= */ 100, "system_package.non_critical.B", KILLABLE_STATE_NO)); 112 113 assertThat(mService.saveUserPackageSettings(expected)).isTrue(); 114 115 assertWithMessage("User package settings after overwrite") 116 .that(mService.getUserPackageSettings()).containsExactlyElementsIn(expected); 117 } 118 119 @Test testSaveAndGetIoOveruseStats()120 public void testSaveAndGetIoOveruseStats() throws Exception { 121 injectSampleUserPackageSettings(); 122 /* Start time aligned to the beginning of the day. */ 123 long startTime = mTimeSource.getCurrentDate().toEpochSecond(); 124 125 assertWithMessage("Saved I/O usage stats successfully") 126 .that(mService.saveIoUsageStats(sampleStatsForDate(startTime, /* duration= */ 60))) 127 .isTrue(); 128 129 long expectedDuration = mTimeSource.getCurrentDateTime().toEpochSecond() - startTime; 130 List<WatchdogStorage.IoUsageStatsEntry> expected = sampleStatsForDate( 131 startTime, expectedDuration); 132 133 IoUsageStatsEntrySubject.assertThat(mService.getTodayIoUsageStats()) 134 .containsExactlyElementsIn(expected); 135 } 136 137 @Test testSaveAndGetIoOveruseStatsWithOffsettedStartTime()138 public void testSaveAndGetIoOveruseStatsWithOffsettedStartTime() throws Exception { 139 injectSampleUserPackageSettings(); 140 /* Start time in the middle of the day. */ 141 long startTime = mTimeSource.getCurrentDate().plusHours(12).toEpochSecond(); 142 List<WatchdogStorage.IoUsageStatsEntry> entries = sampleStatsForDate( 143 startTime, /* duration= */ 60); 144 145 assertWithMessage("Saved I/O usage stats successfully") 146 .that(mService.saveIoUsageStats(entries)).isTrue(); 147 148 long expectedStartTime = mTimeSource.getCurrentDate().toEpochSecond(); 149 long expectedDuration = 150 mTimeSource.getCurrentDateTime().toEpochSecond() - expectedStartTime; 151 List<WatchdogStorage.IoUsageStatsEntry> expected = sampleStatsForDate( 152 expectedStartTime, expectedDuration); 153 154 IoUsageStatsEntrySubject.assertThat(mService.getTodayIoUsageStats()) 155 .containsExactlyElementsIn(expected); 156 } 157 158 @Test testOverwriteIoOveruseStats()159 public void testOverwriteIoOveruseStats() throws Exception { 160 injectSampleUserPackageSettings(); 161 long startTime = mTimeSource.getCurrentDate().toEpochSecond(); 162 long duration = mTimeSource.getCurrentDateTime().toEpochSecond() - startTime; 163 164 List<WatchdogStorage.IoUsageStatsEntry> statsBeforeOverwrite = Collections.singletonList( 165 constructIoUsageStatsEntry( 166 /* userId= */ 100, "system_package.non_critical.A", startTime, duration, 167 /* remainingWriteBytes= */ 168 CarWatchdogServiceUnitTest.constructPerStateBytes(200, 300, 400), 169 /* writtenBytes= */ 170 CarWatchdogServiceUnitTest.constructPerStateBytes(1000, 2000, 3000), 171 /* forgivenWriteBytes= */ 172 CarWatchdogServiceUnitTest.constructPerStateBytes(100, 100, 100), 173 /* totalOveruses= */ 2, /* forgivenOveruses= */ 0, 174 /* totalTimesKilled= */ 1)); 175 176 assertWithMessage("Saved I/O usage stats successfully") 177 .that(mService.saveIoUsageStats(statsBeforeOverwrite)).isTrue(); 178 179 IoUsageStatsEntrySubject.assertWithMessage( 180 mService.getTodayIoUsageStats(), "I/O usage stats fetched from database") 181 .containsExactlyElementsIn(statsBeforeOverwrite); 182 183 List<WatchdogStorage.IoUsageStatsEntry> statsAfterOverwrite = Collections.singletonList( 184 constructIoUsageStatsEntry( 185 /* userId= */ 100, "system_package.non_critical.A", startTime, duration, 186 /* remainingWriteBytes= */ 187 CarWatchdogServiceUnitTest.constructPerStateBytes(400, 600, 800), 188 /* writtenBytes= */ 189 CarWatchdogServiceUnitTest.constructPerStateBytes(2000, 3000, 4000), 190 /* forgivenWriteBytes= */ 191 CarWatchdogServiceUnitTest.constructPerStateBytes(1200, 2300, 3400), 192 /* totalOveruses= */ 4, /* forgivenOveruses= */ 2, 193 /* totalTimesKilled= */ 2)); 194 195 assertWithMessage("Saved I/O usage stats successfully") 196 .that(mService.saveIoUsageStats(statsAfterOverwrite)).isTrue(); 197 198 IoUsageStatsEntrySubject.assertWithMessage( 199 mService.getTodayIoUsageStats(), "Cached in memory I/O usage stats") 200 .containsExactlyElementsIn(statsBeforeOverwrite); 201 202 mService.release(); 203 mTimeSource.updateNow(/* numDaysAgo= */ 0); 204 mService = new WatchdogStorage(mContext, /* useDataSystemCarDir= */ false, mTimeSource); 205 206 assertWithMessage("User packages settings").that(mService.getUserPackageSettings()) 207 .isNotEmpty(); 208 209 IoUsageStatsEntrySubject.assertWithMessage(mService.getTodayIoUsageStats(), 210 "I/O usage stats fetched from database after restart") 211 .containsExactlyElementsIn(statsAfterOverwrite); 212 } 213 214 @Test testSaveIoOveruseStatsOutsideRetentionPeriod()215 public void testSaveIoOveruseStatsOutsideRetentionPeriod() throws Exception { 216 injectSampleUserPackageSettings(); 217 int retentionDaysAgo = RETENTION_PERIOD.getDays(); 218 219 assertWithMessage("Saved I/O usage stats successfully") 220 .that(mService.saveIoUsageStats(sampleStatsBetweenDates( 221 /* includingStartDaysAgo= */ retentionDaysAgo, 222 /* excludingEndDaysAgo= */ retentionDaysAgo + 1))).isTrue(); 223 224 assertWithMessage("Didn't fetch I/O overuse stats outside retention period") 225 .that(mService.getHistoricalIoOveruseStats( 226 /* userId= */ 100, "system_package.non_critical.A", retentionDaysAgo)) 227 .isNull(); 228 } 229 230 @Test testGetHistoricalIoOveruseStats()231 public void testGetHistoricalIoOveruseStats() throws Exception { 232 List<WatchdogStorage.UserPackageSettingsEntry> userPackageSettingEntries = sampleSettings(); 233 234 for (int i = 4; i >= 0; i--) { 235 mTimeSource.updateNow(/* numDaysAgo= */ i); 236 // When writing settings and stats for the previous days, mock the behaviour of 237 // the caller to ensure the settings and stats are retrievable after multiple days. 238 assertWithMessage("Save user package settings for " + mTimeSource) 239 .that(mService.saveUserPackageSettings(userPackageSettingEntries)).isTrue(); 240 assertWithMessage("Save I/O usage stats for " + mTimeSource) 241 .that(mService.saveIoUsageStats(sampleStatsForToday())).isTrue(); 242 mService.release(); 243 mService = new WatchdogStorage(mContext, /* useDataSystemCarDir= */ false, mTimeSource); 244 assertWithMessage("User package settings for " + mTimeSource) 245 .that(mService.getUserPackageSettings()).isNotNull(); 246 } 247 mTimeSource.updateNow(/* numDaysAgo= */ 0); 248 249 IoOveruseStats actual = mService.getHistoricalIoOveruseStats( 250 /* userId= */ 100, "system_package.non_critical.A", /* numDaysAgo= */ 7); 251 252 assertWithMessage("Historical I/O overuse stats for the past week").that(actual) 253 .isNotNull(); 254 255 // Returned stats shouldn't include stats for the current date as WatchdogPerfHandler fills 256 // the current day's stats. 257 ZonedDateTime currentDate = mTimeSource.getCurrentDate(); 258 long startTime = currentDate.minus(4, STATS_TEMPORAL_UNIT).toEpochSecond(); 259 long duration = currentDate.toEpochSecond() - startTime; 260 IoOveruseStats expected = new IoOveruseStats.Builder(startTime, duration) 261 .setTotalOveruses(8).setTotalTimesKilled(4).setTotalBytesWritten(25_200).build(); 262 263 IoOveruseStatsSubject.assertWithMessage( 264 "Fetched stats only for 4 days. Expected stats (%s) equals actual stats (%s)", 265 expected.toString(), actual.toString()).that(actual) 266 .isEqualTo(expected); 267 } 268 269 @Test testGetHistoricalIoOveruseStatsWithNoRecentStats()270 public void testGetHistoricalIoOveruseStatsWithNoRecentStats() throws Exception { 271 List<WatchdogStorage.UserPackageSettingsEntry> userPackageSettingEntries = sampleSettings(); 272 273 for (int i = 4; i >= 3; i--) { 274 mTimeSource.updateNow(/* numDaysAgo= */ i); 275 // When writing settings and stats for the previous days, mock the behaviour of 276 // the caller to ensure the settings and stats are retrievable after multiple days. 277 assertWithMessage("Save user package settings for " + mTimeSource) 278 .that(mService.saveUserPackageSettings(userPackageSettingEntries)).isTrue(); 279 assertWithMessage("Save I/O usage stats for " + mTimeSource) 280 .that(mService.saveIoUsageStats(sampleStatsForToday())).isTrue(); 281 mService.release(); 282 mService = new WatchdogStorage(mContext, /* useDataSystemCarDir= */ false, mTimeSource); 283 assertWithMessage("User package settings for " + mTimeSource) 284 .that(mService.getUserPackageSettings()).isNotNull(); 285 } 286 mTimeSource.updateNow(/* numDaysAgo= */ 0); 287 288 IoOveruseStats actual = mService.getHistoricalIoOveruseStats( 289 /* userId= */ 100, "system_package.non_critical.A", /* numDaysAgo= */ 7); 290 291 assertWithMessage("Historical I/O overuse stats for the past week").that(actual) 292 .isNotNull(); 293 294 // Returned stats shouldn't include stats for the current date as WatchdogPerfHandler fills 295 // the current day's stats. 296 ZonedDateTime currentDate = mTimeSource.getCurrentDate(); 297 long startTime = currentDate.minus(4, STATS_TEMPORAL_UNIT).toEpochSecond(); 298 long duration = currentDate.toEpochSecond() - startTime; 299 IoOveruseStats expected = new IoOveruseStats.Builder(startTime, duration) 300 .setTotalOveruses(4).setTotalTimesKilled(2).setTotalBytesWritten(12_600).build(); 301 302 IoOveruseStatsSubject.assertWithMessage( 303 "Fetched stats only for 2 days. Expected stats (%s) equals actual stats (%s)", 304 expected.toString(), actual.toString()).that(actual) 305 .isEqualTo(expected); 306 } 307 308 @Test testGetDailySystemIoUsageSummaries()309 public void testGetDailySystemIoUsageSummaries() throws Exception { 310 injectSampleUserPackageSettings(); 311 List<WatchdogStorage.IoUsageStatsEntry> entries = new ArrayList<>(); 312 for (int i = 1; i <= 30; ++i) { 313 entries.addAll(sampleStatsBetweenDates(/* includingStartDaysAgo= */ i, 314 /* excludingEndDaysAgo= */ i + 1, /* writtenBytesMultiplier= */ i)); 315 } 316 317 assertWithMessage("Save I/O usage stats").that(mService.saveIoUsageStats(entries)).isTrue(); 318 319 ZonedDateTime currentDate = mTimeSource.getCurrentDate(); 320 321 List<AtomsProto.CarWatchdogDailyIoUsageSummary> actual = 322 mService.getDailySystemIoUsageSummaries( 323 /* includingStartEpochSeconds= */ currentDate.minusDays(15).toEpochSecond(), 324 /* excludingEndEpochSeconds= */ currentDate.minusDays(7).toEpochSecond()); 325 326 List<AtomsProto.CarWatchdogDailyIoUsageSummary> expected = new ArrayList<>(); 327 for (int i = 15; i > 7; --i) { 328 expected.add(CarWatchdogServiceUnitTest 329 .constructCarWatchdogDailyIoUsageSummary(/* fgWrBytes= */ 10402L * i, 330 /* bgWrBytes= */ 14402L * i, /* gmWrBytes= */ 18402L * i, 331 /* overuseCount= */ 6)); 332 } 333 334 assertWithMessage("Daily system I/O usage summary stats").that(actual).isEqualTo(expected); 335 } 336 337 @Test testGetDailySystemIoUsageSummariesWithoutStats()338 public void testGetDailySystemIoUsageSummariesWithoutStats() throws Exception { 339 injectSampleUserPackageSettings(); 340 List<WatchdogStorage.IoUsageStatsEntry> entries = new ArrayList<>(); 341 for (int i = 1; i <= 7; ++i) { 342 entries.addAll(sampleStatsBetweenDates(/* includingStartDaysAgo= */ i, 343 /* excludingEndDaysAgo= */ i + 1, /* writtenBytesMultiplier= */ i)); 344 } 345 346 assertWithMessage("Save I/O usage stats").that(mService.saveIoUsageStats(entries)).isTrue(); 347 348 ZonedDateTime currentDate = mTimeSource.getCurrentDate(); 349 350 List<AtomsProto.CarWatchdogDailyIoUsageSummary> actual = 351 mService.getDailySystemIoUsageSummaries( 352 /* includingStartEpochSeconds= */ currentDate.minusDays(15).toEpochSecond(), 353 /* excludingEndEpochSeconds= */ currentDate.minusDays(7).toEpochSecond()); 354 355 assertWithMessage("Daily system I/O usage summary stats").that(actual).isNull(); 356 } 357 358 @Test testGetTopUsersDailyIoUsageSummaries()359 public void testGetTopUsersDailyIoUsageSummaries() throws Exception { 360 injectSampleUserPackageSettings(); 361 List<WatchdogStorage.IoUsageStatsEntry> entries = new ArrayList<>(); 362 for (int i = 1; i <= 30; ++i) { 363 entries.addAll(sampleStatsBetweenDates(/* includingStartDaysAgo= */ i, 364 /* excludingEndDaysAgo= */ i + 1, /* writtenBytesMultiplier= */ i)); 365 } 366 367 assertWithMessage("Save I/O usage stats").that(mService.saveIoUsageStats(entries)).isTrue(); 368 369 ZonedDateTime currentDate = mTimeSource.getCurrentDate(); 370 371 List<WatchdogStorage.UserPackageDailySummaries> actual = 372 mService.getTopUsersDailyIoUsageSummaries(/* numTopUsers= */ 3, 373 /* minSystemTotalWrittenBytes= */ 600_000, 374 /* includingStartEpochSeconds= */ currentDate.minusDays(15).toEpochSecond(), 375 /* excludingEndEpochSeconds= */ currentDate.minusDays(7).toEpochSecond()); 376 377 List<AtomsProto.CarWatchdogDailyIoUsageSummary> user101VendorPkgSummaries = 378 new ArrayList<>(); 379 List<AtomsProto.CarWatchdogDailyIoUsageSummary> user100VendorPkgSummaries = 380 new ArrayList<>(); 381 List<AtomsProto.CarWatchdogDailyIoUsageSummary> user101SystemPkgSummaries = 382 new ArrayList<>(); 383 for (int i = 15; i > 7; --i) { 384 user101VendorPkgSummaries.add(CarWatchdogServiceUnitTest 385 .constructCarWatchdogDailyIoUsageSummary(/* fgWrBytes= */ 4101L * i, 386 /* bgWrBytes= */ 5101L * i, /* gmWrBytes= */ 6101L * i, 387 /* overuseCount= */ 1)); 388 user100VendorPkgSummaries.add(CarWatchdogServiceUnitTest 389 .constructCarWatchdogDailyIoUsageSummary(/* fgWrBytes= */ 4100L * i, 390 /* bgWrBytes= */ 5100L * i, /* gmWrBytes= */ 6100L * i, 391 /* overuseCount= */ 1)); 392 user101SystemPkgSummaries.add(CarWatchdogServiceUnitTest 393 .constructCarWatchdogDailyIoUsageSummary(/* fgWrBytes= */ 1101L * i, 394 /* bgWrBytes= */ 2101L * i, /* gmWrBytes= */ 3101L * i, 395 /* overuseCount= */ 2)); 396 } 397 List<WatchdogStorage.UserPackageDailySummaries> expected = Arrays.asList( 398 new WatchdogStorage.UserPackageDailySummaries(/* userId= */ 101, 399 /* packageName= */ "vendor_package.critical.C", user101VendorPkgSummaries), 400 new WatchdogStorage.UserPackageDailySummaries(/* userId= */ 100, 401 /* packageName= */ "vendor_package.critical.C", user100VendorPkgSummaries), 402 new WatchdogStorage.UserPackageDailySummaries(/* userId= */ 101, 403 /* packageName= */ "system_package.non_critical.A", 404 user101SystemPkgSummaries)); 405 406 assertWithMessage("Top users daily I/O usage summaries").that(actual).isEqualTo(expected); 407 } 408 409 @Test testGetTopUsersDailyIoUsageSummariesWithLowSystemTotalWrittenBytes()410 public void testGetTopUsersDailyIoUsageSummariesWithLowSystemTotalWrittenBytes() 411 throws Exception { 412 injectSampleUserPackageSettings(); 413 List<WatchdogStorage.IoUsageStatsEntry> entries = new ArrayList<>(); 414 for (int i = 1; i <= 30; ++i) { 415 entries.addAll(sampleStatsBetweenDates(/* includingStartDaysAgo= */ i, 416 /* excludingEndDaysAgo= */ i + 1, /* writtenBytesMultiplier= */ i)); 417 } 418 419 assertWithMessage("Save I/O usage stats").that(mService.saveIoUsageStats(entries)).isTrue(); 420 421 ZonedDateTime currentDate = mTimeSource.getCurrentDate(); 422 423 List<WatchdogStorage.UserPackageDailySummaries> actual = 424 mService.getTopUsersDailyIoUsageSummaries(/* numTopUsers= */ 3, 425 /* minSystemTotalWrittenBytes= */ 4_000_000, 426 /* includingStartEpochSeconds= */ currentDate.minusDays(15).toEpochSecond(), 427 /* excludingEndEpochSeconds= */ currentDate.minusDays(7).toEpochSecond()); 428 429 assertWithMessage("Top users daily I/O usage summaries").that(actual).isNull(); 430 } 431 432 @Test testGetTopUsersDailyIoUsageSummariesWithoutStats()433 public void testGetTopUsersDailyIoUsageSummariesWithoutStats() throws Exception { 434 injectSampleUserPackageSettings(); 435 List<WatchdogStorage.IoUsageStatsEntry> entries = new ArrayList<>(); 436 for (int i = 1; i <= 7; ++i) { 437 entries.addAll(sampleStatsBetweenDates(/* includingStartDaysAgo= */ i, 438 /* excludingEndDaysAgo= */ i + 1, /* writtenBytesMultiplier= */ i)); 439 } 440 441 assertWithMessage("Save I/O usage stats").that(mService.saveIoUsageStats(entries)).isTrue(); 442 443 ZonedDateTime currentDate = mTimeSource.getCurrentDate(); 444 445 List<WatchdogStorage.UserPackageDailySummaries> actual = 446 mService.getTopUsersDailyIoUsageSummaries(/* numTopUsers= */ 3, 447 /* minSystemTotalWrittenBytes= */ 600_000, 448 /* includingStartEpochSeconds= */ currentDate.minusDays(15).toEpochSecond(), 449 /* excludingEndEpochSeconds= */ currentDate.minusDays(7).toEpochSecond()); 450 451 assertWithMessage("Top users daily I/O usage summaries").that(actual).isNull(); 452 } 453 454 @Test testDeleteUserPackage()455 public void testDeleteUserPackage() throws Exception { 456 ArrayList<WatchdogStorage.UserPackageSettingsEntry> settingsEntries = sampleSettings(); 457 List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries = sampleStatsForToday(); 458 459 assertThat(mService.saveUserPackageSettings(settingsEntries)).isTrue(); 460 assertThat(mService.saveIoUsageStats(ioUsageStatsEntries)).isTrue(); 461 462 int deleteUserId = 100; 463 String deletePackageName = "system_package.non_critical.A"; 464 465 mService.deleteUserPackage(deleteUserId, deletePackageName); 466 467 settingsEntries.removeIf( 468 (s) -> s.userId == deleteUserId && s.packageName.equals(deletePackageName)); 469 470 assertWithMessage("User package settings after deleting a user package") 471 .that(mService.getUserPackageSettings()).containsExactlyElementsIn(settingsEntries); 472 473 ioUsageStatsEntries.removeIf( 474 (e) -> e.userId == deleteUserId && e.packageName.equals(deletePackageName)); 475 476 IoUsageStatsEntrySubject.assertThat(mService.getTodayIoUsageStats()) 477 .containsExactlyElementsIn(ioUsageStatsEntries); 478 } 479 480 @Test testDeleteUserPackageWithNonexistentPackage()481 public void testDeleteUserPackageWithNonexistentPackage() throws Exception { 482 injectSampleUserPackageSettings(); 483 List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries = sampleStatsForToday(); 484 485 assertThat(mService.saveIoUsageStats(ioUsageStatsEntries)).isTrue(); 486 487 int deleteUserId = 100; 488 String deletePackageName = "system_package.non_existent.A"; 489 490 mService.deleteUserPackage(deleteUserId, deletePackageName); 491 492 assertWithMessage("User package settings").that(mService.getUserPackageSettings()) 493 .containsExactlyElementsIn(sampleSettings()); 494 495 ioUsageStatsEntries.removeIf( 496 (e) -> e.userId == deleteUserId && e.packageName.equals(deletePackageName)); 497 498 IoUsageStatsEntrySubject.assertThat(mService.getTodayIoUsageStats()) 499 .containsExactlyElementsIn(ioUsageStatsEntries); 500 } 501 502 @Test testDeleteUserPackageWithHistoricalIoOveruseStats()503 public void testDeleteUserPackageWithHistoricalIoOveruseStats() 504 throws Exception { 505 ArrayList<WatchdogStorage.UserPackageSettingsEntry> settingsEntries = sampleSettings(); 506 507 assertThat(mService.saveUserPackageSettings(settingsEntries)).isTrue(); 508 assertThat(mService.saveIoUsageStats(sampleStatsBetweenDates( 509 /* includingStartDaysAgo= */ 1, /* excludingEndDaysAgo= */ 6))).isTrue(); 510 511 int deleteUserId = 100; 512 String deletePackageName = "system_package.non_critical.A"; 513 514 mService.deleteUserPackage(deleteUserId, deletePackageName); 515 516 settingsEntries.removeIf( 517 (s) -> s.userId == deleteUserId && s.packageName.equals(deletePackageName)); 518 519 assertWithMessage("User package settings after deleting user package with historical stats") 520 .that(mService.getUserPackageSettings()).containsExactlyElementsIn(settingsEntries); 521 522 IoOveruseStats actual = mService.getHistoricalIoOveruseStats( 523 /* userId= */ 100, "system_package.non_critical.A", /* numDaysAgo= */ 7); 524 525 assertWithMessage("Fetched historical I/O overuse stats").that(actual).isNull(); 526 } 527 528 @Test testSyncUsers()529 public void testSyncUsers() throws Exception { 530 List<WatchdogStorage.UserPackageSettingsEntry> settingsEntries = sampleSettings(); 531 List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries = sampleStatsForToday(); 532 533 assertThat(mService.saveUserPackageSettings(settingsEntries)).isTrue(); 534 assertThat(mService.saveIoUsageStats(ioUsageStatsEntries)).isTrue(); 535 536 mService.syncUsers(/* aliveUserIds= */ new int[] {101}); 537 538 settingsEntries.removeIf((s) -> s.userId == 100); 539 ioUsageStatsEntries.removeIf((e) -> e.userId == 100); 540 541 assertWithMessage("User package settings after syncing alive users") 542 .that(mService.getUserPackageSettings()).containsExactlyElementsIn(settingsEntries); 543 544 IoUsageStatsEntrySubject.assertThat(mService.getTodayIoUsageStats()) 545 .containsExactlyElementsIn(ioUsageStatsEntries); 546 } 547 548 @Test testSyncUsersWithHistoricalIoOveruseStats()549 public void testSyncUsersWithHistoricalIoOveruseStats() throws Exception { 550 List<WatchdogStorage.UserPackageSettingsEntry> settingsEntries = sampleSettings(); 551 552 assertThat(mService.saveUserPackageSettings(settingsEntries)).isTrue(); 553 assertThat(mService.saveIoUsageStats(sampleStatsBetweenDates( 554 /* includingStartDaysAgo= */ 1, /* excludingEndDaysAgo= */ 6))).isTrue(); 555 556 mService.syncUsers(/* aliveUserIds= */ new int[] {101}); 557 558 settingsEntries.removeIf((s) -> s.userId == 100); 559 560 assertWithMessage("User package settings after syncing alive users with historical stats") 561 .that(mService.getUserPackageSettings()).containsExactlyElementsIn(settingsEntries); 562 563 IoOveruseStats actualSystemPackage = mService.getHistoricalIoOveruseStats( 564 /* userId= */ 100, "system_package.non_critical.A", /* numDaysAgo= */ 7); 565 IoOveruseStats actualVendorPackage = mService.getHistoricalIoOveruseStats( 566 /* userId= */ 100, "vendor_package.critical.C", /* numDaysAgo= */ 7); 567 568 assertWithMessage("System I/O overuse stats for deleted user") 569 .that(actualSystemPackage).isNull(); 570 assertWithMessage("Vendor I/O overuse stats for deleted user") 571 .that(actualVendorPackage).isNull(); 572 } 573 574 @Test testSyncUsersWithNoDataForDeletedUser()575 public void testSyncUsersWithNoDataForDeletedUser() throws Exception { 576 List<WatchdogStorage.UserPackageSettingsEntry> settingsEntries = sampleSettings(); 577 List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries = sampleStatsForToday(); 578 579 assertThat(mService.saveUserPackageSettings(settingsEntries)).isTrue(); 580 assertThat(mService.saveIoUsageStats(ioUsageStatsEntries)).isTrue(); 581 582 mService.syncUsers(/* aliveUserIds= */ new int[] {100, 101}); 583 584 assertWithMessage("User package settings after syncing users") 585 .that(mService.getUserPackageSettings()).containsExactlyElementsIn(settingsEntries); 586 IoUsageStatsEntrySubject.assertThat(mService.getTodayIoUsageStats()) 587 .containsExactlyElementsIn(ioUsageStatsEntries); 588 } 589 590 @Test testTruncateStatsOutsideRetentionPeriodOnDateChange()591 public void testTruncateStatsOutsideRetentionPeriodOnDateChange() throws Exception { 592 injectSampleUserPackageSettings(); 593 mTimeSource.updateNow(/* numDaysAgo= */ 1); 594 595 assertThat(mService.saveIoUsageStats(sampleStatsBetweenDates( 596 /* includingStartDaysAgo= */ 0, /* excludingEndDaysAgo= */ 40), 597 /* shouldCheckRetention= */ false)).isTrue(); 598 599 IoOveruseStats actual = mService.getHistoricalIoOveruseStats( 600 /* userId= */ 100, "system_package.non_critical.A", /* numDaysAgo= */ 40); 601 602 assertWithMessage("Fetched I/O overuse stats").that(actual).isNotNull(); 603 604 ZonedDateTime currentDate = mTimeSource.getCurrentDate(); 605 long startTime = currentDate.minus(39, STATS_TEMPORAL_UNIT).toEpochSecond(); 606 long duration = currentDate.toEpochSecond() - startTime; 607 IoOveruseStats expected = new IoOveruseStats.Builder(startTime, duration) 608 .setTotalOveruses(78).setTotalTimesKilled(39).setTotalBytesWritten(245_700).build(); 609 610 IoOveruseStatsSubject.assertWithMessage( 611 "Fetched stats only for 39 days. Expected stats (%s) equals actual stats (%s)", 612 expected.toString(), actual.toString()).that(actual) 613 .isEqualTo(expected); 614 615 mTimeSource.updateNow(/* numDaysAgo= */ 0); 616 mService.shrinkDatabase(); 617 618 actual = mService.getHistoricalIoOveruseStats( 619 /* userId= */ 100, "system_package.non_critical.A", /* numDaysAgo= */ 40); 620 621 assertWithMessage("Fetched I/O overuse stats").that(actual).isNotNull(); 622 623 currentDate = mTimeSource.getCurrentDate(); 624 startTime = currentDate.minus(RETENTION_PERIOD.minusDays(1)).toEpochSecond(); 625 duration = currentDate.toEpochSecond() - startTime; 626 expected = new IoOveruseStats.Builder(startTime, duration) 627 .setTotalOveruses(58).setTotalTimesKilled(29).setTotalBytesWritten(182_700).build(); 628 629 IoOveruseStatsSubject.assertWithMessage("Fetched stats only within retention period. " 630 + "Expected stats (%s) equals actual stats (%s)", 631 expected.toString(), actual.toString()).that(actual).isEqualTo(expected); 632 } 633 634 @Test testForgiveHistoricalOveruses()635 public void testForgiveHistoricalOveruses() throws Exception { 636 injectSampleUserPackageSettings(); 637 638 assertThat(mService.saveIoUsageStats(sampleStatsBetweenDates(/* includingStartDaysAgo= */ 1, 639 /* excludingEndDaysAgo= */ 3))).isTrue(); 640 641 List<WatchdogStorage.NotForgivenOverusesEntry> expectedOveruses = Arrays.asList( 642 new WatchdogStorage.NotForgivenOverusesEntry(100, "system_package.non_critical.A", 643 2), 644 new WatchdogStorage.NotForgivenOverusesEntry(101, "system_package.non_critical.A", 645 2), 646 new WatchdogStorage.NotForgivenOverusesEntry(100, "vendor_package.critical.C", 2), 647 new WatchdogStorage.NotForgivenOverusesEntry(101, "vendor_package.critical.C", 2)); 648 649 assertWithMessage("Not forgiven historical overuses before forgiving") 650 .that(mService.getNotForgivenHistoricalIoOveruses(/* numDaysAgo= */ 7)) 651 .containsExactlyElementsIn(expectedOveruses); 652 653 SparseArray<List<String>> packagesToForgiveByUserId = new SparseArray<>(); 654 packagesToForgiveByUserId.put(100, 655 Collections.singletonList("system_package.non_critical.A")); 656 packagesToForgiveByUserId.put(101, Collections.singletonList("vendor_package.critical.C")); 657 658 mService.forgiveHistoricalOveruses(packagesToForgiveByUserId, /* numDaysAgo= */ 7); 659 660 expectedOveruses = Arrays.asList( 661 new WatchdogStorage.NotForgivenOverusesEntry(101, "system_package.non_critical.A", 662 2), 663 new WatchdogStorage.NotForgivenOverusesEntry(100, "vendor_package.critical.C", 2)); 664 665 assertWithMessage("Not forgiven historical overuses after forgiving") 666 .that(mService.getNotForgivenHistoricalIoOveruses(/* numDaysAgo= */ 7)) 667 .containsExactlyElementsIn(expectedOveruses); 668 } 669 670 @Test testUserPackageSettingsAfterUpgradeToVersion2()671 public void testUserPackageSettingsAfterUpgradeToVersion2() throws Exception { 672 SQLiteDatabase db = createDatabaseAndUpgradeToVersion2(); 673 674 List<WatchdogStorage.UserPackageSettingsEntry> actual = new ArrayList<>(); 675 try (Cursor cursor = db.rawQuery("SELECT user_id, package_name, killable_state FROM " 676 + WatchdogStorage.UserPackageSettingsTable.TABLE_NAME, null, null)) { 677 while (cursor.moveToNext()) { 678 actual.add(new WatchdogStorage.UserPackageSettingsEntry(cursor.getInt(0), 679 cursor.getString(1), cursor.getInt(2))); 680 } 681 } 682 683 List<WatchdogStorage.UserPackageSettingsEntry> expected = 684 Arrays.asList(new WatchdogStorage.UserPackageSettingsEntry(100, "package_A", 1), 685 new WatchdogStorage.UserPackageSettingsEntry(101, "package_B", 2)); 686 687 assertWithMessage("User package settings").that(actual).containsExactlyElementsIn(expected); 688 } 689 690 @Test testTablesAfterUpgradeToVersion2()691 public void testTablesAfterUpgradeToVersion2() throws Exception { 692 SQLiteDatabase db = createDatabaseAndUpgradeToVersion2(); 693 694 List<String> actual = new ArrayList<>(); 695 try (Cursor cursor = db.query(/* table= */ "sqlite_master", 696 /* columns= */ new String[]{"name"}, 697 /* selection= */ "name != ? and name not like ?", 698 /* selectionArgs= */ new String[]{"android_metadata", "sqlite_%"}, 699 /* groupBy= */ null, /* having= */ null, /* orderBy= */null)) { 700 while (cursor.moveToNext()) { 701 actual.add(cursor.getString(0)); 702 } 703 } 704 705 assertWithMessage("Table names").that(actual).containsExactlyElementsIn( 706 Arrays.asList(WatchdogStorage.UserPackageSettingsTable.TABLE_NAME, 707 WatchdogStorage.IoUsageStatsTable.TABLE_NAME)); 708 } 709 injectSampleUserPackageSettings()710 private void injectSampleUserPackageSettings() throws Exception { 711 List<WatchdogStorage.UserPackageSettingsEntry> userPackageSettingEntries = sampleSettings(); 712 713 assertThat(mService.saveUserPackageSettings(userPackageSettingEntries)).isTrue(); 714 } 715 sampleSettings()716 private static ArrayList<WatchdogStorage.UserPackageSettingsEntry> sampleSettings() { 717 return new ArrayList<>(Arrays.asList( 718 new WatchdogStorage.UserPackageSettingsEntry( 719 /* userId= */ 100, "system_package.non_critical.A", KILLABLE_STATE_YES), 720 new WatchdogStorage.UserPackageSettingsEntry( 721 /* userId= */ 100, "system_package.non_critical.B", KILLABLE_STATE_NO), 722 new WatchdogStorage.UserPackageSettingsEntry( 723 /* userId= */ 100, "vendor_package.critical.C", KILLABLE_STATE_NEVER), 724 new WatchdogStorage.UserPackageSettingsEntry( 725 /* userId= */ 101, "system_package.non_critical.A", KILLABLE_STATE_NO), 726 new WatchdogStorage.UserPackageSettingsEntry( 727 /* userId= */ 101, "system_package.non_critical.B", KILLABLE_STATE_YES), 728 new WatchdogStorage.UserPackageSettingsEntry( 729 /* userId= */ 101, "vendor_package.critical.C", KILLABLE_STATE_NEVER))); 730 } 731 sampleStatsBetweenDates( int includingStartDaysAgo, int excludingEndDaysAgo)732 private ArrayList<WatchdogStorage.IoUsageStatsEntry> sampleStatsBetweenDates( 733 int includingStartDaysAgo, int excludingEndDaysAgo) { 734 return sampleStatsBetweenDates(includingStartDaysAgo, excludingEndDaysAgo, 735 /* writtenBytesMultiplier= */ 1); 736 } 737 sampleStatsBetweenDates( int includingStartDaysAgo, int excludingEndDaysAgo, int writtenBytesMultiplier)738 private ArrayList<WatchdogStorage.IoUsageStatsEntry> sampleStatsBetweenDates( 739 int includingStartDaysAgo, int excludingEndDaysAgo, int writtenBytesMultiplier) { 740 ZonedDateTime currentDate = mTimeSource.getCurrentDate(); 741 ArrayList<WatchdogStorage.IoUsageStatsEntry> entries = new ArrayList<>(); 742 for (int i = includingStartDaysAgo; i < excludingEndDaysAgo; ++i) { 743 entries.addAll( 744 sampleStatsForDate(currentDate.minus(i, STATS_TEMPORAL_UNIT).toEpochSecond(), 745 STATS_TEMPORAL_UNIT.getDuration().toSeconds(), writtenBytesMultiplier)); 746 } 747 return entries; 748 } 749 sampleStatsForToday()750 private ArrayList<WatchdogStorage.IoUsageStatsEntry> sampleStatsForToday() { 751 long statsDateEpoch = mTimeSource.getCurrentDate().toEpochSecond(); 752 long duration = mTimeSource.getCurrentDateTime().toEpochSecond() - statsDateEpoch; 753 return sampleStatsForDate(statsDateEpoch, duration, /* writtenBytesMultiplier= */ 1); 754 } 755 sampleStatsForDate( long statsDateEpoch, long duration)756 private static ArrayList<WatchdogStorage.IoUsageStatsEntry> sampleStatsForDate( 757 long statsDateEpoch, long duration) { 758 return sampleStatsForDate(statsDateEpoch, duration, /* writtenBytesMultiplier= */ 1); 759 } 760 sampleStatsForDate( long statsDateEpoch, long duration, int writtenBytesMultiplier)761 private static ArrayList<WatchdogStorage.IoUsageStatsEntry> sampleStatsForDate( 762 long statsDateEpoch, long duration, int writtenBytesMultiplier) { 763 ArrayList<WatchdogStorage.IoUsageStatsEntry> entries = new ArrayList<>(); 764 for (int i = 100; i <= 101; ++i) { 765 entries.add(constructIoUsageStatsEntry( 766 /* userId= */ i, "system_package.non_critical.A", statsDateEpoch, duration, 767 /* remainingWriteBytes= */ 768 CarWatchdogServiceUnitTest.constructPerStateBytes(200L, 300L, 400L), 769 /* writtenBytes= */ 770 CarWatchdogServiceUnitTest.constructPerStateBytes( 771 (1000L + i) * writtenBytesMultiplier, 772 (2000L + i) * writtenBytesMultiplier, 773 (3000L + i) * writtenBytesMultiplier), 774 /* forgivenWriteBytes= */ 775 CarWatchdogServiceUnitTest.constructPerStateBytes(100L, 100L, 100L), 776 /* totalOveruses= */ 2, /* forgivenOveruses= */ 1, /* totalTimesKilled= */ 1)); 777 entries.add(constructIoUsageStatsEntry( 778 /* userId= */ i, "vendor_package.critical.C", statsDateEpoch, duration, 779 /* remainingWriteBytes= */ 780 CarWatchdogServiceUnitTest.constructPerStateBytes(500L, 600L, 700L), 781 /* writtenBytes= */ 782 CarWatchdogServiceUnitTest.constructPerStateBytes( 783 (4000L + i) * writtenBytesMultiplier, 784 (5000L + i) * writtenBytesMultiplier, 785 (6000L + i) * writtenBytesMultiplier), 786 /* forgivenWriteBytes= */ 787 CarWatchdogServiceUnitTest.constructPerStateBytes(200L, 200L, 200L), 788 /* totalOveruses= */ 1, /* forgivenOveruses= */ 0, /* totalTimesKilled= */ 0)); 789 } 790 return entries; 791 } 792 constructIoUsageStatsEntry( int userId, String packageName, long startTime, long duration, PerStateBytes remainingWriteBytes, PerStateBytes writtenBytes, PerStateBytes forgivenWriteBytes, int totalOveruses, int forgivenOveruses, int totalTimesKilled)793 static WatchdogStorage.IoUsageStatsEntry constructIoUsageStatsEntry( 794 int userId, String packageName, long startTime, long duration, 795 PerStateBytes remainingWriteBytes, PerStateBytes writtenBytes, 796 PerStateBytes forgivenWriteBytes, int totalOveruses, int forgivenOveruses, 797 int totalTimesKilled) { 798 WatchdogPerfHandler.PackageIoUsage ioUsage = new WatchdogPerfHandler.PackageIoUsage( 799 constructInternalIoOveruseStats(startTime, duration, remainingWriteBytes, 800 writtenBytes, totalOveruses), forgivenWriteBytes, forgivenOveruses, 801 totalTimesKilled); 802 return new WatchdogStorage.IoUsageStatsEntry(userId, packageName, ioUsage); 803 } 804 constructInternalIoOveruseStats( long startTime, long duration, PerStateBytes remainingWriteBytes, PerStateBytes writtenBytes, int totalOveruses)805 private static android.automotive.watchdog.IoOveruseStats constructInternalIoOveruseStats( 806 long startTime, long duration, PerStateBytes remainingWriteBytes, 807 PerStateBytes writtenBytes, int totalOveruses) { 808 android.automotive.watchdog.IoOveruseStats stats = 809 new android.automotive.watchdog.IoOveruseStats(); 810 stats.startTime = startTime; 811 stats.durationInSeconds = duration; 812 stats.remainingWriteBytes = remainingWriteBytes; 813 stats.writtenBytes = writtenBytes; 814 stats.totalOveruses = totalOveruses; 815 return stats; 816 } 817 createDatabaseAndUpgradeToVersion2()818 private SQLiteDatabase createDatabaseAndUpgradeToVersion2() { 819 SQLiteDatabase db = SQLiteDatabase.create(null); 820 assertWithMessage("Create database version 1").that(DatabaseVersion1.create(db)).isTrue(); 821 822 WatchdogStorage.WatchdogDbHelper dbHelper = 823 new WatchdogStorage.WatchdogDbHelper(mContext, /* useDataSystemCarDir= */ false, 824 mTimeSource); 825 dbHelper.onUpgrade(db, /*oldVersion=*/ 1, /*newVersion=*/ 2); 826 827 return db; 828 } 829 830 private static final class TestTimeSource extends TimeSource { 831 private static final Instant TEST_DATE_TIME = Instant.parse("2021-11-12T13:14:15.16Z"); 832 private Instant mNow; TestTimeSource()833 TestTimeSource() { 834 mNow = TEST_DATE_TIME; 835 } 836 837 @Override now()838 public Instant now() { 839 /* Return the same time, so the tests are deterministic. */ 840 return mNow; 841 } 842 843 @Override toString()844 public String toString() { 845 return "Mocked date to " + now(); 846 } 847 updateNow(int numDaysAgo)848 void updateNow(int numDaysAgo) { 849 mNow = TEST_DATE_TIME.minus(numDaysAgo, ChronoUnit.DAYS); 850 } 851 }; 852 853 private static final class DatabaseVersion1 { 854 private static final String CREATE_TABLE_SQL = 855 "CREATE TABLE user_package_settings (user_id INTEGER NOT NULL, " 856 + "package_name TEXT NOT NULL, killable_state INTEGER NOT NULL, PRIMARY KEY " 857 + "(package_name, user_id))"; 858 859 private static final String[] INSERT_SQLS = new String[] { 860 "INSERT INTO user_package_settings (user_id, package_name, killable_state) " 861 + "VALUES (100, \"package_A\", 1)", 862 "INSERT INTO user_package_settings (user_id, package_name, killable_state) " 863 + "VALUES (101, \"package_B\", 2)"}; 864 create(SQLiteDatabase db)865 public static boolean create(SQLiteDatabase db) { 866 boolean isSuccessful = false; 867 db.beginTransaction(); 868 try { 869 db.execSQL(CREATE_TABLE_SQL); 870 for (String insertSql : INSERT_SQLS) { 871 db.execSQL(insertSql); 872 } 873 db.setTransactionSuccessful(); 874 isSuccessful = true; 875 } finally { 876 db.endTransaction(); 877 } 878 return isSuccessful; 879 } 880 } 881 } 882