1 /* 2 * Copyright (C) 2017 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; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.car.Car; 29 import android.car.storagemonitoring.CarStorageMonitoringManager; 30 import android.car.storagemonitoring.IoStatsEntry; 31 import android.car.storagemonitoring.IoStats; 32 import android.car.storagemonitoring.LifetimeWriteInfo; 33 import android.car.storagemonitoring.UidIoRecord; 34 import android.car.storagemonitoring.WearEstimate; 35 import android.car.storagemonitoring.WearEstimateChange; 36 import android.content.Intent; 37 import android.content.pm.PackageManager.NameNotFoundException; 38 import android.content.pm.ResolveInfo; 39 import android.os.SystemClock; 40 import android.support.test.filters.MediumTest; 41 import android.support.test.runner.AndroidJUnit4; 42 import android.util.JsonWriter; 43 import android.util.Log; 44 import android.util.Pair; 45 import android.util.SparseArray; 46 47 import com.android.car.storagemonitoring.LifetimeWriteInfoProvider; 48 import com.android.car.storagemonitoring.UidIoStatsProvider; 49 import com.android.car.storagemonitoring.WearEstimateRecord; 50 import com.android.car.storagemonitoring.WearHistory; 51 import com.android.car.storagemonitoring.WearInformation; 52 import com.android.car.storagemonitoring.WearInformationProvider; 53 import com.android.car.systeminterface.StorageMonitoringInterface; 54 import com.android.car.systeminterface.SystemInterface; 55 import com.android.car.systeminterface.SystemStateInterface; 56 import com.android.car.systeminterface.TimeInterface; 57 58 import com.android.car.test.utils.TemporaryFile; 59 import java.util.ArrayDeque; 60 import java.util.Collection; 61 import java.util.Objects; 62 import java.util.Queue; 63 import org.json.JSONArray; 64 import org.json.JSONObject; 65 import org.junit.Rule; 66 import org.junit.Test; 67 import org.junit.rules.TestName; 68 import org.junit.runner.RunWith; 69 70 import java.io.File; 71 import java.io.FileWriter; 72 import java.io.IOException; 73 import java.time.Duration; 74 import java.time.Instant; 75 import java.util.ArrayList; 76 import java.util.Arrays; 77 import java.util.Comparator; 78 import java.util.HashMap; 79 import java.util.List; 80 import java.util.Map; 81 82 /** Test the public entry points for the CarStorageMonitoringManager */ 83 @RunWith(AndroidJUnit4.class) 84 @MediumTest 85 public class CarStorageMonitoringTest extends MockedCarTestBase { 86 private static final String TAG = CarStorageMonitoringTest.class.getSimpleName(); 87 88 @Rule public TestName mTestName = new TestName(); 89 90 private static final WearInformation DEFAULT_WEAR_INFORMATION = 91 new WearInformation(30, 0, WearInformation.PRE_EOL_INFO_NORMAL); 92 93 final class TestContext extends MockContext { 94 TestContext(MockContext context) { 95 super(context.getBaseContext()); 96 } 97 98 @Override 99 public void sendBroadcast(Intent intent, String receiverPermission) { 100 Log.d(TAG, "test context broadcasting " + intent); 101 if (CarStorageMonitoringManager.INTENT_EXCESSIVE_IO.equals(intent.getAction())) { 102 assertEquals(Car.PERMISSION_STORAGE_MONITORING, receiverPermission); 103 104 List<ResolveInfo> resolveInfoList = mContext.getPackageManager(). 105 queryBroadcastReceivers(intent, 0); 106 107 assertEquals(1, 108 resolveInfoList.stream().map(ri -> ri.activityInfo) 109 .filter(Objects::nonNull) 110 .map(ai -> ai.name) 111 .filter(CarStorageMonitoringBroadcastReceiver.class 112 .getCanonicalName()::equals) 113 .count()); 114 115 CarStorageMonitoringBroadcastReceiver.deliver(intent); 116 } else { 117 super.sendBroadcast(intent, receiverPermission); 118 } 119 } 120 } 121 122 static class ResourceOverrides { 123 private final HashMap<Integer, Integer> mIntegerOverrides = new HashMap<>(); 124 private final HashMap<Integer, String> mStringOverrides = new HashMap<>(); 125 126 void override(int id, int value) { 127 mIntegerOverrides.put(id, value); 128 } 129 void override(int id, String value) { 130 mStringOverrides.put(id, value); 131 } 132 133 void overrideResources(MockResources resources) { 134 mIntegerOverrides.forEach(resources::overrideResource); 135 mStringOverrides.forEach(resources::overrideResource); 136 } 137 } 138 139 private final Map<String, ResourceOverrides> PER_TEST_RESOURCES = 140 new HashMap<String, ResourceOverrides>() { 141 { 142 put("testAggregateIoStats", 143 new ResourceOverrides() {{ 144 override(R.integer.ioStatsNumSamplesToStore, 5); 145 }}); 146 put("testIoStatsDeltas", 147 new ResourceOverrides() {{ 148 override(R.integer.ioStatsNumSamplesToStore, 5); 149 }}); 150 put("testEventDelivery", 151 new ResourceOverrides() {{ 152 override(R.integer.ioStatsNumSamplesToStore, 5); 153 }}); 154 put("testIntentOnExcessiveWrite", 155 new ResourceOverrides() {{ 156 override(R.integer.ioStatsNumSamplesToStore, 5); 157 override(R.integer.maxExcessiveIoSamplesInWindow, 0); 158 override(R.integer.acceptableWrittenKBytesPerSample, 10); 159 override(R.integer.acceptableFsyncCallsPerSample, 1000); 160 override(R.string.intentReceiverForUnacceptableIoMetrics, 161 getFlattenComponent( 162 CarStorageMonitoringBroadcastReceiver.class)); 163 }}); 164 165 put("testIntentOnExcessiveFsync", 166 new ResourceOverrides() {{ 167 override(R.integer.ioStatsNumSamplesToStore, 5); 168 override(R.integer.maxExcessiveIoSamplesInWindow, 0); 169 override(R.integer.acceptableWrittenKBytesPerSample, 1000); 170 override(R.integer.acceptableFsyncCallsPerSample, 2); 171 override(R.string.intentReceiverForUnacceptableIoMetrics, 172 getFlattenComponent( 173 CarStorageMonitoringBroadcastReceiver.class)); 174 }}); 175 176 put("testZeroWindowDisablesCollection", 177 new ResourceOverrides() {{ 178 override(R.integer.ioStatsNumSamplesToStore, 0); 179 }}); 180 181 } 182 }; 183 184 private static final class TestData { 185 static final TestData DEFAULT = new TestData(0, DEFAULT_WEAR_INFORMATION, null, null); 186 187 final long uptime; 188 @NonNull 189 final WearInformation wearInformation; 190 @Nullable 191 final WearHistory wearHistory; 192 @NonNull 193 final UidIoRecord[] ioStats; 194 @NonNull 195 final LifetimeWriteInfo[] previousLifetimeWriteInfo; 196 @NonNull 197 final LifetimeWriteInfo[] currentLifetimeWriteInfo; 198 199 TestData(long uptime, 200 @Nullable WearInformation wearInformation, 201 @Nullable WearHistory wearHistory, 202 @Nullable UidIoRecord[] ioStats) { 203 this(uptime, wearInformation, wearHistory, ioStats, null, null); 204 } 205 206 TestData(long uptime, 207 @Nullable WearInformation wearInformation, 208 @Nullable WearHistory wearHistory, 209 @Nullable UidIoRecord[] ioStats, 210 @Nullable LifetimeWriteInfo[] previousLifetimeWriteInfo, 211 @Nullable LifetimeWriteInfo[] currentLifetimeWriteInfo) { 212 if (wearInformation == null) wearInformation = DEFAULT_WEAR_INFORMATION; 213 if (ioStats == null) ioStats = new UidIoRecord[0]; 214 if (previousLifetimeWriteInfo == null) { 215 previousLifetimeWriteInfo = new LifetimeWriteInfo[0]; 216 } 217 if (currentLifetimeWriteInfo == null) { 218 currentLifetimeWriteInfo = new LifetimeWriteInfo[0]; 219 } 220 this.uptime = uptime; 221 this.wearInformation = wearInformation; 222 this.wearHistory = wearHistory; 223 this.ioStats = ioStats; 224 this.previousLifetimeWriteInfo = previousLifetimeWriteInfo; 225 this.currentLifetimeWriteInfo = currentLifetimeWriteInfo; 226 } 227 } 228 229 private static final Map<String, TestData> PER_TEST_DATA = 230 new HashMap<String, TestData>() { 231 { 232 put("testReadWearHistory", 233 new TestData(6500, DEFAULT_WEAR_INFORMATION, 234 WearHistory.fromRecords( 235 WearEstimateRecord.Builder.newBuilder() 236 .fromWearEstimate(WearEstimate.UNKNOWN_ESTIMATE) 237 .toWearEstimate(new WearEstimate(10, 0)) 238 .atUptime(1000) 239 .atTimestamp(Instant.ofEpochMilli(5000)).build(), 240 WearEstimateRecord.Builder.newBuilder() 241 .fromWearEstimate(new WearEstimate(10, 0)) 242 .toWearEstimate(new WearEstimate(20, 0)) 243 .atUptime(4000) 244 .atTimestamp(Instant.ofEpochMilli(12000)).build(), 245 WearEstimateRecord.Builder.newBuilder() 246 .fromWearEstimate(new WearEstimate(20, 0)) 247 .toWearEstimate(new WearEstimate(30, 0)) 248 .atUptime(6500) 249 .atTimestamp(Instant.ofEpochMilli(17000)).build()), null)); 250 251 put("testNotAcceptableWearEvent", 252 new TestData(2520006499L, 253 new WearInformation(40, 0, WearInformation.PRE_EOL_INFO_NORMAL), 254 WearHistory.fromRecords( 255 WearEstimateRecord.Builder.newBuilder() 256 .fromWearEstimate(WearEstimate.UNKNOWN_ESTIMATE) 257 .toWearEstimate(new WearEstimate(10, 0)) 258 .atUptime(1000) 259 .atTimestamp(Instant.ofEpochMilli(5000)).build(), 260 WearEstimateRecord.Builder.newBuilder() 261 .fromWearEstimate(new WearEstimate(10, 0)) 262 .toWearEstimate(new WearEstimate(20, 0)) 263 .atUptime(4000) 264 .atTimestamp(Instant.ofEpochMilli(12000)).build(), 265 WearEstimateRecord.Builder.newBuilder() 266 .fromWearEstimate(new WearEstimate(20, 0)) 267 .toWearEstimate(new WearEstimate(30, 0)) 268 .atUptime(6500) 269 .atTimestamp(Instant.ofEpochMilli(17000)).build()), null)); 270 271 put("testAcceptableWearEvent", 272 new TestData(2520006501L, 273 new WearInformation(40, 0, WearInformation.PRE_EOL_INFO_NORMAL), 274 WearHistory.fromRecords( 275 WearEstimateRecord.Builder.newBuilder() 276 .fromWearEstimate(WearEstimate.UNKNOWN_ESTIMATE) 277 .toWearEstimate(new WearEstimate(10, 0)) 278 .atUptime(1000) 279 .atTimestamp(Instant.ofEpochMilli(5000)).build(), 280 WearEstimateRecord.Builder.newBuilder() 281 .fromWearEstimate(new WearEstimate(10, 0)) 282 .toWearEstimate(new WearEstimate(20, 0)) 283 .atUptime(4000) 284 .atTimestamp(Instant.ofEpochMilli(12000)).build(), 285 WearEstimateRecord.Builder.newBuilder() 286 .fromWearEstimate(new WearEstimate(20, 0)) 287 .toWearEstimate(new WearEstimate(30, 0)) 288 .atUptime(6500) 289 .atTimestamp(Instant.ofEpochMilli(17000)).build()), null)); 290 291 put("testBootIoStats", 292 new TestData(1000L, 293 new WearInformation(0, 0, WearInformation.PRE_EOL_INFO_NORMAL), 294 null, 295 new UidIoRecord[]{ 296 new UidIoRecord(0, 5000, 6000, 3000, 1000, 1, 297 0, 0, 0, 0, 0), 298 new UidIoRecord(1000, 200, 5000, 0, 4000, 0, 299 1000, 0, 500, 0, 0)})); 300 301 put("testAggregateIoStats", 302 new TestData(1000L, 303 new WearInformation(0, 0, WearInformation.PRE_EOL_INFO_NORMAL), 304 null, 305 new UidIoRecord[]{ 306 new UidIoRecord(0, 5000, 6000, 3000, 1000, 1, 307 0, 0, 0, 0, 0), 308 new UidIoRecord(1000, 200, 5000, 0, 4000, 0, 309 1000, 0, 500, 0, 0)})); 310 311 put("testIoStatsDeltas", 312 new TestData(1000L, 313 new WearInformation(0, 0, WearInformation.PRE_EOL_INFO_NORMAL), 314 null, 315 new UidIoRecord[]{ 316 new UidIoRecord(0, 5000, 6000, 3000, 1000, 1, 317 0, 0, 0, 0, 0)})); 318 319 put("testComputeShutdownCost", 320 new TestData(1000L, 321 new WearInformation(0, 0, WearInformation.PRE_EOL_INFO_NORMAL), 322 null, 323 null, 324 new LifetimeWriteInfo[] { new LifetimeWriteInfo("p1", "ext4", 120), 325 new LifetimeWriteInfo("p2", "ext4", 100), 326 new LifetimeWriteInfo("p3", "f2fs", 100)}, 327 new LifetimeWriteInfo[] { new LifetimeWriteInfo("p1", "ext4", 200), 328 new LifetimeWriteInfo("p2", "ext4", 300), 329 new LifetimeWriteInfo("p3", "f2fs", 100)})); 330 331 put("testNegativeShutdownCost", 332 new TestData(1000L, 333 new WearInformation(0, 0, WearInformation.PRE_EOL_INFO_NORMAL), 334 null, 335 null, 336 new LifetimeWriteInfo[] { new LifetimeWriteInfo("p1", "ext4", 120), 337 new LifetimeWriteInfo("p2", "ext4", 100), 338 new LifetimeWriteInfo("p3", "f2fs", 200)}, 339 new LifetimeWriteInfo[] { new LifetimeWriteInfo("p1", "ext4", 200), 340 new LifetimeWriteInfo("p2", "ext4", 300), 341 new LifetimeWriteInfo("p3", "f2fs", 100)})); 342 }}; 343 344 private final MockSystemStateInterface mMockSystemStateInterface = 345 new MockSystemStateInterface(); 346 private final MockStorageMonitoringInterface mMockStorageMonitoringInterface = 347 new MockStorageMonitoringInterface(); 348 private final MockTimeInterface mMockTimeInterface = 349 new MockTimeInterface(); 350 private TestContext mContext; 351 352 private CarStorageMonitoringManager mCarStorageMonitoringManager; 353 354 @Override 355 protected MockContext getCarServiceContext() throws NameNotFoundException { 356 if (mContext == null) { 357 mContext = new TestContext(super.getCarServiceContext()); 358 } 359 return mContext; 360 } 361 362 @Override 363 protected synchronized SystemInterface.Builder getSystemInterfaceBuilder() { 364 SystemInterface.Builder builder = super.getSystemInterfaceBuilder(); 365 return builder.withSystemStateInterface(mMockSystemStateInterface) 366 .withStorageMonitoringInterface(mMockStorageMonitoringInterface) 367 .withTimeInterface(mMockTimeInterface); 368 } 369 370 @Override 371 protected synchronized void configureFakeSystemInterface() { 372 try { 373 final TestData wearData = PER_TEST_DATA.getOrDefault(getName(), TestData.DEFAULT); 374 final WearHistory wearHistory = wearData.wearHistory; 375 376 mMockStorageMonitoringInterface.setWearInformation(wearData.wearInformation); 377 mMockStorageMonitoringInterface.setWriteInfo(wearData.currentLifetimeWriteInfo); 378 379 if (wearHistory != null) { 380 File wearHistoryFile = new File(getFakeSystemInterface().getFilesDir(), 381 CarStorageMonitoringService.WEAR_INFO_FILENAME); 382 try (JsonWriter jsonWriter = new JsonWriter(new FileWriter(wearHistoryFile))) { 383 wearHistory.writeToJson(jsonWriter); 384 } 385 } 386 387 if (wearData.uptime > 0) { 388 File uptimeFile = new File(getFakeSystemInterface().getFilesDir(), 389 CarStorageMonitoringService.UPTIME_TRACKER_FILENAME); 390 try (JsonWriter jsonWriter = new JsonWriter(new FileWriter(uptimeFile))) { 391 jsonWriter.beginObject(); 392 jsonWriter.name("uptime").value(wearData.uptime); 393 jsonWriter.endObject(); 394 } 395 } 396 397 if (wearData.previousLifetimeWriteInfo.length > 0) { 398 File previousLifetimeFile = new File(getFakeSystemInterface().getFilesDir(), 399 CarStorageMonitoringService.LIFETIME_WRITES_FILENAME); 400 try (JsonWriter jsonWriter = new JsonWriter(new FileWriter(previousLifetimeFile))) { 401 jsonWriter.beginObject(); 402 jsonWriter.name("lifetimeWriteInfo").beginArray(); 403 for (LifetimeWriteInfo writeInfo : wearData.previousLifetimeWriteInfo) { 404 writeInfo.writeToJson(jsonWriter); 405 } 406 jsonWriter.endArray().endObject(); 407 } 408 } 409 410 Arrays.stream(wearData.ioStats).forEach( 411 mMockStorageMonitoringInterface::addIoStatsRecord); 412 413 } catch (IOException e) { 414 Log.e(TAG, "failed to configure fake system interface", e); 415 fail("failed to configure fake system interface instance"); 416 } 417 } 418 419 @Override 420 protected synchronized void configureResourceOverrides(MockResources resources) { 421 final ResourceOverrides overrides = PER_TEST_RESOURCES.getOrDefault(getName(), null); 422 if (overrides != null) { 423 overrides.overrideResources(resources); 424 } 425 } 426 427 @Override 428 public void setUp() throws Exception { 429 super.setUp(); 430 mMockSystemStateInterface.executeBootCompletedActions(); 431 432 mCarStorageMonitoringManager = 433 (CarStorageMonitoringManager) getCar().getCarManager(Car.STORAGE_MONITORING_SERVICE); 434 } 435 436 @Test 437 public void testReadPreEolInformation() throws Exception { 438 assertEquals(DEFAULT_WEAR_INFORMATION.preEolInfo, 439 mCarStorageMonitoringManager.getPreEolIndicatorStatus()); 440 } 441 442 @Test 443 public void testReadWearEstimate() throws Exception { 444 final WearEstimate wearEstimate = mCarStorageMonitoringManager.getWearEstimate(); 445 446 assertNotNull(wearEstimate); 447 assertEquals(DEFAULT_WEAR_INFORMATION.lifetimeEstimateA, wearEstimate.typeA); 448 assertEquals(DEFAULT_WEAR_INFORMATION.lifetimeEstimateB, wearEstimate.typeB); 449 } 450 451 @Test 452 public void testReadWearHistory() throws Exception { 453 final List<WearEstimateChange> wearEstimateChanges = 454 mCarStorageMonitoringManager.getWearEstimateHistory(); 455 456 assertNotNull(wearEstimateChanges); 457 assertFalse(wearEstimateChanges.isEmpty()); 458 459 final WearHistory expectedWearHistory = PER_TEST_DATA.get(getName()).wearHistory; 460 461 assertEquals(expectedWearHistory.size(), wearEstimateChanges.size()); 462 for (int i = 0; i < wearEstimateChanges.size(); ++i) { 463 final WearEstimateRecord expected = expectedWearHistory.get(i); 464 final WearEstimateChange actual = wearEstimateChanges.get(i); 465 466 assertTrue(expected.isSameAs(actual)); 467 } 468 } 469 470 private void checkLastWearEvent(boolean isAcceptable) throws Exception { 471 final List<WearEstimateChange> wearEstimateChanges = 472 mCarStorageMonitoringManager.getWearEstimateHistory(); 473 474 assertNotNull(wearEstimateChanges); 475 assertFalse(wearEstimateChanges.isEmpty()); 476 477 final TestData wearData = PER_TEST_DATA.get(getName()); 478 479 final WearInformation expectedCurrentWear = wearData.wearInformation; 480 final WearEstimate expectedPreviousWear = wearData.wearHistory.getLast().getNewWearEstimate(); 481 482 final WearEstimateChange actualCurrentWear = 483 wearEstimateChanges.get(wearEstimateChanges.size() - 1); 484 485 assertEquals(isAcceptable, actualCurrentWear.isAcceptableDegradation); 486 assertEquals(expectedCurrentWear.toWearEstimate(), actualCurrentWear.newEstimate); 487 assertEquals(expectedPreviousWear, actualCurrentWear.oldEstimate); 488 } 489 490 @Test 491 public void testNotAcceptableWearEvent() throws Exception { 492 checkLastWearEvent(false); 493 } 494 495 @Test 496 public void testAcceptableWearEvent() throws Exception { 497 checkLastWearEvent(true); 498 } 499 500 @Test 501 public void testBootIoStats() throws Exception { 502 final List<IoStatsEntry> bootIoStats = 503 mCarStorageMonitoringManager.getBootIoStats(); 504 505 assertNotNull(bootIoStats); 506 assertFalse(bootIoStats.isEmpty()); 507 508 final UidIoRecord[] bootIoRecords = PER_TEST_DATA.get(getName()).ioStats; 509 510 bootIoStats.forEach(uidIoStats -> assertTrue(Arrays.stream(bootIoRecords).anyMatch( 511 ioRecord -> uidIoStats.representsSameMetrics(ioRecord)))); 512 } 513 514 @Test 515 public void testAggregateIoStats() throws Exception { 516 UidIoRecord oldRecord1000 = mMockStorageMonitoringInterface.getIoStatsRecord(1000); 517 518 UidIoRecord newRecord1000 = new UidIoRecord(1000, 519 oldRecord1000.foreground_rchar, 520 oldRecord1000.foreground_wchar + 50, 521 oldRecord1000.foreground_read_bytes, 522 oldRecord1000.foreground_write_bytes + 100, 523 oldRecord1000.foreground_fsync + 1, 524 oldRecord1000.background_rchar, 525 oldRecord1000.background_wchar, 526 oldRecord1000.background_read_bytes, 527 oldRecord1000.background_write_bytes, 528 oldRecord1000.background_fsync); 529 530 mMockStorageMonitoringInterface.addIoStatsRecord(newRecord1000); 531 532 UidIoRecord record2000 = new UidIoRecord(2000, 533 1024, 534 2048, 535 0, 536 1024, 537 1, 538 0, 539 0, 540 0, 541 0, 542 0); 543 544 mMockStorageMonitoringInterface.addIoStatsRecord(record2000); 545 546 mMockTimeInterface.tick(); 547 548 List<IoStatsEntry> aggregateIoStats = mCarStorageMonitoringManager.getAggregateIoStats(); 549 550 assertNotNull(aggregateIoStats); 551 assertFalse(aggregateIoStats.isEmpty()); 552 553 aggregateIoStats.forEach(serviceIoStat -> { 554 UidIoRecord mockIoStat = mMockStorageMonitoringInterface.getIoStatsRecord( 555 serviceIoStat.uid); 556 557 assertNotNull(mockIoStat); 558 559 assertTrue(serviceIoStat.representsSameMetrics(mockIoStat)); 560 }); 561 } 562 563 @Test 564 public void testIoStatsDeltas() throws Exception { 565 UidIoRecord oldRecord0 = mMockStorageMonitoringInterface.getIoStatsRecord(0); 566 567 UidIoRecord newRecord0 = new UidIoRecord(0, 568 oldRecord0.foreground_rchar, 569 oldRecord0.foreground_wchar + 100, 570 oldRecord0.foreground_read_bytes, 571 oldRecord0.foreground_write_bytes + 50, 572 oldRecord0.foreground_fsync, 573 oldRecord0.background_rchar, 574 oldRecord0.background_wchar, 575 oldRecord0.background_read_bytes + 100, 576 oldRecord0.background_write_bytes, 577 oldRecord0.background_fsync); 578 579 mMockStorageMonitoringInterface.addIoStatsRecord(newRecord0); 580 mMockTimeInterface.setUptime(500).tick(); 581 582 List<IoStats> deltas = mCarStorageMonitoringManager.getIoStatsDeltas(); 583 assertNotNull(deltas); 584 assertEquals(1, deltas.size()); 585 586 IoStats delta0 = deltas.get(0); 587 assertNotNull(delta0); 588 assertEquals(500, delta0.getTimestamp()); 589 590 List<IoStatsEntry> delta0Stats = delta0.getStats(); 591 assertNotNull(delta0Stats); 592 assertEquals(1, delta0Stats.size()); 593 594 IoStatsEntry deltaRecord0 = delta0Stats.get(0); 595 596 assertTrue(deltaRecord0.representsSameMetrics(newRecord0.delta(oldRecord0))); 597 598 UidIoRecord newerRecord0 = new UidIoRecord(0, 599 newRecord0.foreground_rchar + 200, 600 newRecord0.foreground_wchar + 10, 601 newRecord0.foreground_read_bytes, 602 newRecord0.foreground_write_bytes, 603 newRecord0.foreground_fsync, 604 newRecord0.background_rchar, 605 newRecord0.background_wchar + 100, 606 newRecord0.background_read_bytes, 607 newRecord0.background_write_bytes + 30, 608 newRecord0.background_fsync + 2); 609 610 mMockStorageMonitoringInterface.addIoStatsRecord(newerRecord0); 611 mMockTimeInterface.setUptime(1000).tick(); 612 613 deltas = mCarStorageMonitoringManager.getIoStatsDeltas(); 614 assertNotNull(deltas); 615 assertEquals(2, deltas.size()); 616 617 delta0 = deltas.get(0); 618 assertNotNull(delta0); 619 assertEquals(500, delta0.getTimestamp()); 620 621 delta0Stats = delta0.getStats(); 622 assertNotNull(delta0Stats); 623 assertEquals(1, delta0Stats.size()); 624 625 deltaRecord0 = delta0Stats.get(0); 626 627 assertTrue(deltaRecord0.representsSameMetrics(newRecord0.delta(oldRecord0))); 628 629 IoStats delta1 = deltas.get(1); 630 assertNotNull(delta1); 631 assertEquals(1000, delta1.getTimestamp()); 632 633 List<IoStatsEntry> delta1Stats = delta1.getStats(); 634 assertNotNull(delta1Stats); 635 assertEquals(1, delta1Stats.size()); 636 637 deltaRecord0 = delta1Stats.get(0); 638 639 assertTrue(deltaRecord0.representsSameMetrics(newerRecord0.delta(newRecord0))); 640 } 641 642 @Test 643 public void testEventDelivery() throws Exception { 644 final Duration eventDeliveryDeadline = Duration.ofSeconds(5); 645 646 UidIoRecord record = new UidIoRecord(0, 647 0, 648 100, 649 0, 650 75, 651 1, 652 0, 653 0, 654 0, 655 0, 656 0); 657 658 Listener listener1 = new Listener("listener1"); 659 Listener listener2 = new Listener("listener2"); 660 661 mCarStorageMonitoringManager.registerListener(listener1); 662 mCarStorageMonitoringManager.registerListener(listener2); 663 664 mMockStorageMonitoringInterface.addIoStatsRecord(record); 665 mMockTimeInterface.setUptime(500).tick(); 666 667 assertTrue(listener1.waitForEvent(eventDeliveryDeadline)); 668 assertTrue(listener2.waitForEvent(eventDeliveryDeadline)); 669 670 IoStats event1 = listener1.reset(); 671 IoStats event2 = listener2.reset(); 672 673 assertEquals(event1, event2); 674 event1.getStats().forEach(stats -> assertTrue(stats.representsSameMetrics(record))); 675 676 mCarStorageMonitoringManager.unregisterListener(listener1); 677 678 mMockTimeInterface.setUptime(600).tick(); 679 assertFalse(listener1.waitForEvent(eventDeliveryDeadline)); 680 assertTrue(listener2.waitForEvent(eventDeliveryDeadline)); 681 } 682 683 @Test 684 public void testIntentOnExcessiveWrite() throws Exception { 685 assertNull(CarStorageMonitoringBroadcastReceiver.reset()); 686 687 final Duration intentDeliveryDeadline = Duration.ofSeconds(5); 688 689 UidIoRecord record = new UidIoRecord(0, 690 0, 691 5120, 692 0, 693 5000, 694 1, 695 0, 696 7168, 697 0, 698 7000, 699 0); 700 701 mMockStorageMonitoringInterface.addIoStatsRecord(record); 702 mMockTimeInterface.setUptime(500).tick(); 703 704 assertTrue(CarStorageMonitoringBroadcastReceiver.waitForIntent(intentDeliveryDeadline)); 705 Intent deliveredIntent = CarStorageMonitoringBroadcastReceiver.reset(); 706 assertNotNull(deliveredIntent); 707 assertEquals(CarStorageMonitoringManager.INTENT_EXCESSIVE_IO, deliveredIntent.getAction()); 708 } 709 710 @Test 711 public void testIntentOnExcessiveFsync() throws Exception { 712 assertNull(CarStorageMonitoringBroadcastReceiver.reset()); 713 714 final Duration intentDeliveryDeadline = Duration.ofSeconds(5); 715 716 UidIoRecord record = new UidIoRecord(0, 717 0, 718 0, 719 0, 720 0, 721 2, 722 0, 723 0, 724 0, 725 0, 726 3); 727 728 mMockStorageMonitoringInterface.addIoStatsRecord(record); 729 mMockTimeInterface.setUptime(500).tick(); 730 731 assertTrue(CarStorageMonitoringBroadcastReceiver.waitForIntent(intentDeliveryDeadline)); 732 Intent deliveredIntent = CarStorageMonitoringBroadcastReceiver.reset(); 733 assertNotNull(deliveredIntent); 734 assertEquals(CarStorageMonitoringManager.INTENT_EXCESSIVE_IO, deliveredIntent.getAction()); 735 } 736 737 @Test 738 public void testZeroWindowDisablesCollection() throws Exception { 739 final Duration eventDeliveryDeadline = Duration.ofSeconds(5); 740 741 UidIoRecord record = new UidIoRecord(0, 742 0, 743 100, 744 0, 745 75, 746 1, 747 0, 748 0, 749 0, 750 0, 751 0); 752 753 Listener listener = new Listener("listener"); 754 755 mMockStorageMonitoringInterface.addIoStatsRecord(record); 756 mMockTimeInterface.setUptime(500).tick(); 757 758 assertFalse(listener.waitForEvent(eventDeliveryDeadline)); 759 760 assertEquals(0, mCarStorageMonitoringManager.getIoStatsDeltas().size()); 761 } 762 763 @Test 764 public void testComputeShutdownCost() throws Exception { 765 assertEquals(280, mCarStorageMonitoringManager.getShutdownDiskWriteAmount()); 766 } 767 768 @Test 769 public void testNegativeShutdownCost() throws Exception { 770 assertEquals(CarStorageMonitoringManager.SHUTDOWN_COST_INFO_MISSING, 771 mCarStorageMonitoringManager.getShutdownDiskWriteAmount()); 772 } 773 774 private String getName() { 775 return mTestName.getMethodName(); 776 } 777 778 static final class Listener implements CarStorageMonitoringManager.IoStatsListener { 779 private final String mName; 780 private final Object mSync = new Object(); 781 782 private IoStats mLastEvent = null; 783 784 Listener(String name) { 785 mName = name; 786 } 787 788 IoStats reset() { 789 synchronized (mSync) { 790 IoStats lastEvent = mLastEvent; 791 mLastEvent = null; 792 return lastEvent; 793 } 794 } 795 796 boolean waitForEvent(Duration duration) { 797 long start = SystemClock.elapsedRealtime(); 798 long end = start + duration.toMillis(); 799 synchronized (mSync) { 800 while (mLastEvent == null && SystemClock.elapsedRealtime() < end) { 801 try { 802 mSync.wait(10L); 803 } catch (InterruptedException e) { 804 // ignore 805 } 806 } 807 } 808 809 return (mLastEvent != null); 810 } 811 812 @Override 813 public void onSnapshot(IoStats event) { 814 synchronized (mSync) { 815 Log.d(TAG, "listener " + mName + " received event " + event); 816 // We're going to hold a reference to this object 817 mLastEvent = event; 818 mSync.notify(); 819 } 820 } 821 822 } 823 824 static final class MockStorageMonitoringInterface implements StorageMonitoringInterface, 825 WearInformationProvider { 826 private WearInformation mWearInformation = null; 827 private SparseArray<UidIoRecord> mIoStats = new SparseArray<>(); 828 private UidIoStatsProvider mIoStatsProvider = () -> mIoStats; 829 private LifetimeWriteInfo[] mWriteInfo = new LifetimeWriteInfo[0]; 830 831 void setWearInformation(WearInformation wearInformation) { 832 mWearInformation = wearInformation; 833 } 834 835 void setWriteInfo(LifetimeWriteInfo[] writeInfo) { 836 mWriteInfo = writeInfo; 837 } 838 839 void addIoStatsRecord(UidIoRecord record) { 840 mIoStats.append(record.uid, record); 841 } 842 843 UidIoRecord getIoStatsRecord(int uid) { 844 return mIoStats.get(uid); 845 } 846 847 void deleteIoStatsRecord(int uid) { 848 mIoStats.delete(uid); 849 } 850 851 @Override 852 public WearInformation load() { 853 return mWearInformation; 854 } 855 856 @Override 857 public LifetimeWriteInfoProvider getLifetimeWriteInfoProvider() { 858 // cannot make this directly implement because Java does not allow 859 // overloading based on return type and there already is a method named 860 // load() in WearInformationProvider 861 return new LifetimeWriteInfoProvider() { 862 @Override 863 public LifetimeWriteInfo[] load() { 864 return mWriteInfo; 865 } 866 }; 867 } 868 869 @Override 870 public WearInformationProvider[] getFlashWearInformationProviders() { 871 return new WearInformationProvider[] {this}; 872 } 873 874 @Override 875 public UidIoStatsProvider getUidIoStatsProvider() { 876 return mIoStatsProvider; 877 } 878 } 879 880 static final class MockTimeInterface implements TimeInterface { 881 private final List<Pair<Runnable, Long>> mActionsList = new ArrayList<>(); 882 private long mUptime = 0; 883 884 @Override 885 public long getUptime(boolean includeDeepSleepTime) { 886 return mUptime; 887 } 888 889 @Override 890 public void scheduleAction(Runnable r, long delayMs) { 891 mActionsList.add(Pair.create(r, delayMs)); 892 mActionsList.sort(Comparator.comparing(d -> d.second)); 893 } 894 895 @Override 896 public void cancelAllActions() { 897 mActionsList.clear(); 898 } 899 900 void tick() { 901 mActionsList.forEach(pair -> pair.first.run()); 902 } 903 904 MockTimeInterface setUptime(long time) { 905 mUptime = time; 906 return this; 907 } 908 } 909 910 static final class MockSystemStateInterface implements SystemStateInterface { 911 private final List<Pair<Runnable, Duration>> mActionsList = new ArrayList<>(); 912 913 @Override 914 public void shutdown() {} 915 916 @Override 917 public boolean enterDeepSleep(int wakeupTimeSec) { 918 return true; 919 } 920 921 @Override 922 public void scheduleActionForBootCompleted(Runnable action, Duration delay) { 923 mActionsList.add(Pair.create(action, delay)); 924 mActionsList.sort(Comparator.comparing(d -> d.second)); 925 } 926 927 void executeBootCompletedActions() { 928 for (Pair<Runnable, Duration> action : mActionsList) { 929 action.first.run(); 930 } 931 } 932 } 933 } 934