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 com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 20 21 import android.car.Car; 22 import android.car.builtin.util.Slogf; 23 import android.car.storagemonitoring.CarStorageMonitoringManager; 24 import android.car.storagemonitoring.ICarStorageMonitoring; 25 import android.car.storagemonitoring.IIoStatsListener; 26 import android.car.storagemonitoring.IoStats; 27 import android.car.storagemonitoring.IoStatsEntry; 28 import android.car.storagemonitoring.IoStatsEntry.Metrics; 29 import android.car.storagemonitoring.LifetimeWriteInfo; 30 import android.car.storagemonitoring.UidIoRecord; 31 import android.car.storagemonitoring.WearEstimate; 32 import android.car.storagemonitoring.WearEstimateChange; 33 import android.content.ActivityNotFoundException; 34 import android.content.ComponentName; 35 import android.content.Context; 36 import android.content.Intent; 37 import android.content.res.Resources; 38 import android.os.RemoteCallbackList; 39 import android.os.RemoteException; 40 import android.util.JsonWriter; 41 import android.util.Log; 42 import android.util.SparseArray; 43 import android.util.proto.ProtoOutputStream; 44 45 import com.android.car.internal.CarPermission; 46 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 47 import com.android.car.internal.util.IndentingPrintWriter; 48 import com.android.car.storagemonitoring.IoStatsTracker; 49 import com.android.car.storagemonitoring.UidIoStatsProvider; 50 import com.android.car.storagemonitoring.WearEstimateRecord; 51 import com.android.car.storagemonitoring.WearHistory; 52 import com.android.car.storagemonitoring.WearInformation; 53 import com.android.car.storagemonitoring.WearInformationProvider; 54 import com.android.car.systeminterface.SystemInterface; 55 import com.android.car.util.SlidingWindow; 56 import com.android.car.util.SparseArrayStream; 57 import com.android.internal.annotations.GuardedBy; 58 59 import org.json.JSONArray; 60 import org.json.JSONException; 61 import org.json.JSONObject; 62 63 import java.io.File; 64 import java.io.FileWriter; 65 import java.io.IOException; 66 import java.nio.file.Files; 67 import java.time.Duration; 68 import java.time.Instant; 69 import java.util.ArrayList; 70 import java.util.Arrays; 71 import java.util.Collections; 72 import java.util.HashMap; 73 import java.util.List; 74 import java.util.Map; 75 import java.util.Objects; 76 import java.util.Optional; 77 import java.util.StringJoiner; 78 import java.util.stream.Collectors; 79 import java.util.stream.IntStream; 80 81 /** 82 * A service to provide storage monitoring data like I/O statistics. In order to receive such data, 83 * users need to implement {@link IIoStatsListener} and register themselves against this service. 84 */ 85 public class CarStorageMonitoringService extends ICarStorageMonitoring.Stub 86 implements CarServiceBase { 87 public static final String INTENT_EXCESSIVE_IO = 88 CarStorageMonitoringManager.INTENT_EXCESSIVE_IO; 89 90 public static final long SHUTDOWN_COST_INFO_MISSING = 91 CarStorageMonitoringManager.SHUTDOWN_COST_INFO_MISSING; 92 93 private static final String TAG = CarLog.tagFor(CarStorageMonitoringService.class); 94 95 private static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG); 96 97 private static final int MIN_WEAR_ESTIMATE_OF_CONCERN = 80; 98 99 static final String UPTIME_TRACKER_FILENAME = "service_uptime"; 100 static final String WEAR_INFO_FILENAME = "wear_info"; 101 static final String LIFETIME_WRITES_FILENAME = "lifetime_write"; 102 103 private final WearInformationProvider[] mWearInformationProviders; 104 private final Context mContext; 105 private final File mUptimeTrackerFile; 106 private final File mWearInfoFile; 107 private final File mLifetimeWriteFile; 108 private final OnShutdownReboot mOnShutdownReboot; 109 private final SystemInterface mSystemInterface; 110 private final UidIoStatsProvider mUidIoStatsProvider; 111 112 private final Object mLock = new Object(); 113 114 @GuardedBy("mLock") 115 private final SlidingWindow<IoStats> mIoStatsSamples; 116 117 private final RemoteCallbackList<IIoStatsListener> mListeners; 118 private final Configuration mConfiguration; 119 private final CarPermission mStorageMonitoringPermission; 120 121 @GuardedBy("mLock") 122 private UptimeTracker mUptimeTracker = null; 123 124 @GuardedBy("mLock") 125 private Optional<WearInformation> mWearInformation = Optional.empty(); 126 127 @GuardedBy("mLock") 128 private List<WearEstimateChange> mWearEstimateChanges; 129 130 @GuardedBy("mLock") 131 private List<IoStatsEntry> mBootIoStats = Collections.emptyList(); 132 133 @GuardedBy("mLock") 134 private IoStatsTracker mIoStatsTracker = null; 135 136 @GuardedBy("mLock") 137 private boolean mInitialized = false; 138 139 @GuardedBy("mLock") 140 private long mShutdownCostInfo = SHUTDOWN_COST_INFO_MISSING; 141 142 @GuardedBy("mLock") 143 private String mShutdownCostMissingReason; 144 CarStorageMonitoringService(Context context, SystemInterface systemInterface)145 public CarStorageMonitoringService(Context context, SystemInterface systemInterface) { 146 mContext = context; 147 Resources resources = mContext.getResources(); 148 mConfiguration = new Configuration(resources); 149 150 if (Slogf.isLoggable(TAG, Log.DEBUG)) { 151 Slogf.d(TAG, "service configuration: " + mConfiguration); 152 } 153 154 mUidIoStatsProvider = systemInterface.getUidIoStatsProvider(); 155 mUptimeTrackerFile = new File(systemInterface.getSystemCarDir(), UPTIME_TRACKER_FILENAME); 156 mWearInfoFile = new File(systemInterface.getSystemCarDir(), WEAR_INFO_FILENAME); 157 mLifetimeWriteFile = new File(systemInterface.getSystemCarDir(), LIFETIME_WRITES_FILENAME); 158 mOnShutdownReboot = new OnShutdownReboot(mContext); 159 mSystemInterface = systemInterface; 160 mWearInformationProviders = systemInterface.getFlashWearInformationProviders( 161 mConfiguration.eMmcLifetimeFilePath, 162 mConfiguration.eMmcEolFilePath); 163 mStorageMonitoringPermission = 164 new CarPermission(mContext, Car.PERMISSION_STORAGE_MONITORING); 165 mWearEstimateChanges = Collections.emptyList(); 166 mIoStatsSamples = new SlidingWindow<>(mConfiguration.ioStatsNumSamplesToStore); 167 mListeners = new RemoteCallbackList<>(); 168 systemInterface.scheduleActionForBootCompleted(() -> { 169 synchronized (mLock) { 170 doInitServiceIfNeededLocked(); 171 }}, Duration.ofSeconds(10)); 172 } 173 loadWearInformation()174 private Optional<WearInformation> loadWearInformation() { 175 for (WearInformationProvider provider : mWearInformationProviders) { 176 WearInformation wearInfo = provider.load(); 177 if (wearInfo != null) { 178 Slogf.d(TAG, "retrieved wear info " + wearInfo + " via provider " + provider); 179 return Optional.of(wearInfo); 180 } 181 } 182 183 Slogf.d(TAG, "no wear info available"); 184 return Optional.empty(); 185 } 186 loadWearHistory()187 private WearHistory loadWearHistory() { 188 if (mWearInfoFile.exists()) { 189 try { 190 WearHistory wearHistory = WearHistory.fromJson(mWearInfoFile); 191 Slogf.d(TAG, "retrieved wear history " + wearHistory); 192 return wearHistory; 193 } catch (IOException | JSONException e) { 194 Slogf.e(TAG, "unable to read wear info file " + mWearInfoFile, e); 195 } 196 } 197 198 Slogf.d(TAG, "no wear history available"); 199 return new WearHistory(); 200 } 201 202 // returns true iff a new event was added (and hence the history needs to be saved) 203 @GuardedBy("mLock") addEventIfNeededLocked(WearHistory wearHistory)204 private boolean addEventIfNeededLocked(WearHistory wearHistory) { 205 if (!mWearInformation.isPresent()) return false; 206 207 WearInformation wearInformation = mWearInformation.get(); 208 WearEstimate lastWearEstimate; 209 WearEstimate currentWearEstimate = wearInformation.toWearEstimate(); 210 211 if (wearHistory.size() == 0) { 212 lastWearEstimate = WearEstimate.UNKNOWN_ESTIMATE; 213 } else { 214 lastWearEstimate = wearHistory.getLast().getNewWearEstimate(); 215 } 216 217 if (currentWearEstimate.equals(lastWearEstimate)) return false; 218 219 WearEstimateRecord newRecord = new WearEstimateRecord(lastWearEstimate, 220 currentWearEstimate, 221 mUptimeTracker.getTotalUptime(), 222 Instant.now()); 223 Slogf.d(TAG, "new wear record generated " + newRecord); 224 wearHistory.add(newRecord); 225 return true; 226 } 227 storeWearHistory(WearHistory wearHistory)228 private void storeWearHistory(WearHistory wearHistory) { 229 try (JsonWriter jsonWriter = new JsonWriter(new FileWriter(mWearInfoFile))) { 230 wearHistory.writeToJson(jsonWriter); 231 } catch (IOException e) { 232 Slogf.e(TAG, "unable to write wear info file" + mWearInfoFile, e); 233 } 234 } 235 236 @Override init()237 public void init() { 238 Slogf.d(TAG, "CarStorageMonitoringService init()"); 239 mOnShutdownReboot.init(); 240 synchronized (mLock) { 241 mUptimeTracker = new UptimeTracker(mUptimeTrackerFile, 242 mConfiguration.uptimeIntervalBetweenUptimeDataWriteMs, 243 mSystemInterface); 244 } 245 } 246 launchWearChangeActivity()247 private void launchWearChangeActivity() { 248 final String activityPath = mConfiguration.activityHandlerForFlashWearChanges; 249 if (activityPath.isEmpty()) return; 250 try { 251 final ComponentName activityComponent = 252 Objects.requireNonNull(ComponentName.unflattenFromString(activityPath)); 253 Intent intent = new Intent(); 254 intent.setComponent(activityComponent); 255 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 256 mContext.startActivity(intent); 257 } catch (ActivityNotFoundException | NullPointerException e) { 258 Slogf.e(TAG, "value of activityHandlerForFlashWearChanges invalid non-empty string " 259 + activityPath, e); 260 } 261 } 262 logOnAdverseWearLevel(WearInformation wearInformation)263 private static void logOnAdverseWearLevel(WearInformation wearInformation) { 264 if (wearInformation.preEolInfo > WearInformation.PRE_EOL_INFO_NORMAL || 265 Math.max(wearInformation.lifetimeEstimateA, 266 wearInformation.lifetimeEstimateB) >= MIN_WEAR_ESTIMATE_OF_CONCERN) { 267 Slogf.w(TAG, "flash storage reached wear a level that requires attention: " 268 + wearInformation); 269 } 270 } 271 loadNewIoStats()272 private SparseArray<UidIoRecord> loadNewIoStats() { 273 SparseArray<UidIoRecord> ioRecords = mUidIoStatsProvider.load(); 274 return (ioRecords == null ? new SparseArray<>() : ioRecords); 275 } 276 collectNewIoMetrics()277 private void collectNewIoMetrics() { 278 SparseArray<IoStatsEntry> currentSample; 279 boolean needsExcessiveIoBroadcast; 280 IoStats ioStats; 281 synchronized (mLock) { 282 mIoStatsTracker.update(loadNewIoStats()); 283 currentSample = mIoStatsTracker.getCurrentSample(); 284 ioStats = new IoStats( 285 SparseArrayStream.valueStream(currentSample).collect(Collectors.toList()), 286 mSystemInterface.getUptime()); 287 mIoStatsSamples.add(ioStats); 288 needsExcessiveIoBroadcast = needsExcessiveIoBroadcastLocked(); 289 } 290 291 dispatchNewIoEvent(ioStats); 292 293 if (DBG) { 294 if (currentSample.size() == 0) { 295 Slogf.d(TAG, "no new I/O stat data"); 296 } else { 297 SparseArrayStream.valueStream(currentSample).forEach( 298 uidIoStats -> Slogf.d(TAG, "updated I/O stat data: " + uidIoStats)); 299 } 300 } 301 302 if (needsExcessiveIoBroadcast) { 303 Slogf.d(TAG, "about to send " + INTENT_EXCESSIVE_IO); 304 sendExcessiveIoBroadcast(); 305 } 306 } 307 sendExcessiveIoBroadcast()308 private void sendExcessiveIoBroadcast() { 309 Slogf.w(TAG, "sending " + INTENT_EXCESSIVE_IO); 310 311 final String receiverPath = mConfiguration.intentReceiverForUnacceptableIoMetrics; 312 if (receiverPath.isEmpty()) return; 313 314 final ComponentName receiverComponent; 315 try { 316 receiverComponent = Objects.requireNonNull( 317 ComponentName.unflattenFromString(receiverPath)); 318 } catch (NullPointerException e) { 319 Slogf.e(TAG, "value of intentReceiverForUnacceptableIoMetrics non-null but invalid:" 320 + receiverPath, e); 321 return; 322 } 323 324 Intent intent = new Intent(INTENT_EXCESSIVE_IO); 325 intent.setComponent(receiverComponent); 326 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 327 mContext.sendBroadcast(intent, mStorageMonitoringPermission.toString()); 328 } 329 330 @GuardedBy("mLock") needsExcessiveIoBroadcastLocked()331 private boolean needsExcessiveIoBroadcastLocked() { 332 return mIoStatsSamples.count((IoStats delta) -> { 333 Metrics total = delta.getTotals(); 334 final boolean tooManyBytesWritten = 335 (total.bytesWrittenToStorage > mConfiguration.acceptableBytesWrittenPerSample); 336 final boolean tooManyFsyncCalls = 337 (total.fsyncCalls > mConfiguration.acceptableFsyncCallsPerSample); 338 return tooManyBytesWritten || tooManyFsyncCalls; 339 }) > mConfiguration.maxExcessiveIoSamplesInWindow; 340 } 341 dispatchNewIoEvent(IoStats delta)342 private void dispatchNewIoEvent(IoStats delta) { 343 final int listenersCount = mListeners.beginBroadcast(); 344 IntStream.range(0, listenersCount).forEach( 345 i -> { 346 try { 347 mListeners.getBroadcastItem(i).onSnapshot(delta); 348 } catch (RemoteException e) { 349 Slogf.w(TAG, "failed to dispatch snapshot", e); 350 } 351 }); 352 mListeners.finishBroadcast(); 353 } 354 355 @GuardedBy("mLock") doInitServiceIfNeededLocked()356 private void doInitServiceIfNeededLocked() { 357 if (mInitialized) return; 358 359 Slogf.d(TAG, "initializing CarStorageMonitoringService"); 360 361 mWearInformation = loadWearInformation(); 362 363 // TODO(egranata): can this be done lazily? 364 final WearHistory wearHistory = loadWearHistory(); 365 final boolean didWearChangeHappen = addEventIfNeededLocked(wearHistory); 366 if (didWearChangeHappen) { 367 storeWearHistory(wearHistory); 368 } 369 Slogf.d(TAG, "wear history being tracked is " + wearHistory); 370 mWearEstimateChanges = wearHistory.toWearEstimateChanges( 371 mConfiguration.acceptableHoursPerOnePercentFlashWear); 372 373 mOnShutdownReboot.addAction((c, i) -> logLifetimeWrites()) 374 .addAction((c, i) -> release()); 375 376 mWearInformation.ifPresent(CarStorageMonitoringService::logOnAdverseWearLevel); 377 378 if (didWearChangeHappen) { 379 launchWearChangeActivity(); 380 } 381 382 long bootUptime = mSystemInterface.getUptime(); 383 mBootIoStats = SparseArrayStream.valueStream(loadNewIoStats()) 384 .map(record -> { 385 // at boot, assume all UIDs have been running for as long as the system has 386 // been up, since we don't really know any better 387 IoStatsEntry stats = new IoStatsEntry(record, bootUptime); 388 if (DBG) { 389 Slogf.d(TAG, "loaded boot I/O stat data: " + stats); 390 } 391 return stats; 392 }).collect(Collectors.toList()); 393 394 mIoStatsTracker = new IoStatsTracker(mBootIoStats, 395 mConfiguration.ioStatsRefreshRateMs, 396 mSystemInterface.getSystemStateInterface()); 397 398 if (mConfiguration.ioStatsNumSamplesToStore > 0) { 399 mSystemInterface.scheduleAction(this::collectNewIoMetrics, 400 mConfiguration.ioStatsRefreshRateMs); 401 } else { 402 Slogf.i(TAG, "service configuration disabled I/O sample window. not collecting " 403 + "samples"); 404 } 405 406 mShutdownCostInfo = computeShutdownCostLocked(); 407 Slogf.d(TAG, "calculated data written in last shutdown was " + mShutdownCostInfo 408 + " bytes"); 409 mLifetimeWriteFile.delete(); 410 411 Slogf.i(TAG, "CarStorageMonitoringService is up"); 412 413 mInitialized = true; 414 } 415 416 @GuardedBy("mLock") computeShutdownCostLocked()417 private long computeShutdownCostLocked() { 418 List<LifetimeWriteInfo> shutdownWrites = loadLifetimeWrites(); 419 if (shutdownWrites.isEmpty()) { 420 Slogf.d(TAG, "lifetime write data from last shutdown missing"); 421 mShutdownCostMissingReason = "no historical writes stored at last shutdown"; 422 return SHUTDOWN_COST_INFO_MISSING; 423 } 424 List<LifetimeWriteInfo> currentWrites = 425 Arrays.asList(mSystemInterface.getLifetimeWriteInfoProvider().load()); 426 if (currentWrites.isEmpty()) { 427 Slogf.d(TAG, "current lifetime write data missing"); 428 mShutdownCostMissingReason = "current write data cannot be obtained"; 429 return SHUTDOWN_COST_INFO_MISSING; 430 } 431 432 long shutdownCost = 0; 433 434 Map<String, Long> shutdownLifetimeWrites = new HashMap<>(); 435 shutdownWrites.forEach(li -> 436 shutdownLifetimeWrites.put(li.partition, li.writtenBytes)); 437 438 // for every partition currently available, look for it in the shutdown data 439 for (int i = 0; i < currentWrites.size(); ++i) { 440 LifetimeWriteInfo li = currentWrites.get(i); 441 // if this partition was not available when we last shutdown the system, then 442 // just pretend we had written the same amount of data then as we have now 443 final long writtenAtShutdown = 444 shutdownLifetimeWrites.getOrDefault(li.partition, li.writtenBytes); 445 final long costDelta = li.writtenBytes - writtenAtShutdown; 446 if (costDelta >= 0) { 447 Slogf.d(TAG, "partition " + li.partition + " had " + costDelta 448 + " bytes written to it during shutdown"); 449 shutdownCost += costDelta; 450 } else { 451 // the counter of written bytes should be monotonic; a decrease might mean 452 // corrupt data, improper shutdown or that the kernel in use does not 453 // have proper monotonic guarantees on the lifetime write data. If any of these 454 // occur, it's probably safer to just bail out and say we don't know 455 mShutdownCostMissingReason = li.partition + " has a negative write amount (" 456 + costDelta + " bytes)"; 457 Slogf.e(TAG, "partition " + li.partition + " reported " + costDelta 458 + " bytes written to it during shutdown. assuming we can't" 459 + " determine proper shutdown information."); 460 return SHUTDOWN_COST_INFO_MISSING; 461 } 462 } 463 464 return shutdownCost; 465 } 466 loadLifetimeWrites()467 private List<LifetimeWriteInfo> loadLifetimeWrites() { 468 if (!mLifetimeWriteFile.exists() || !mLifetimeWriteFile.isFile()) { 469 Slogf.d(TAG, "lifetime write file missing or inaccessible " + mLifetimeWriteFile); 470 return new ArrayList<>(); 471 } 472 try { 473 JSONObject jsonObject = new JSONObject( 474 new String(Files.readAllBytes(mLifetimeWriteFile.toPath()))); 475 476 JSONArray jsonArray = jsonObject.getJSONArray("lifetimeWriteInfo"); 477 478 List<LifetimeWriteInfo> result = new ArrayList<>(); 479 for (int i = 0; i < jsonArray.length(); ++i) { 480 result.add(new LifetimeWriteInfo(jsonArray.getJSONObject(i))); 481 } 482 return result; 483 } catch (JSONException | IOException e) { 484 Slogf.e(TAG, "lifetime write file does not contain valid JSON", e); 485 return new ArrayList<>(); 486 } 487 } 488 logLifetimeWrites()489 private void logLifetimeWrites() { 490 try { 491 LifetimeWriteInfo[] lifetimeWriteInfos = 492 mSystemInterface.getLifetimeWriteInfoProvider().load(); 493 JsonWriter jsonWriter = new JsonWriter(new FileWriter(mLifetimeWriteFile)); 494 jsonWriter.beginObject(); 495 jsonWriter.name("lifetimeWriteInfo").beginArray(); 496 for (LifetimeWriteInfo writeInfo : lifetimeWriteInfos) { 497 Slogf.d(TAG, "storing lifetime write info " + writeInfo); 498 writeInfo.writeToJson(jsonWriter); 499 } 500 jsonWriter.endArray().endObject(); 501 jsonWriter.close(); 502 } catch (IOException e) { 503 Slogf.e(TAG, "unable to save lifetime write info on shutdown", e); 504 } 505 } 506 507 @Override release()508 public void release() { 509 Slogf.i(TAG, "tearing down CarStorageMonitoringService"); 510 synchronized (mLock) { 511 if (mUptimeTracker != null) { 512 mUptimeTracker.onDestroy(); 513 } 514 } 515 mOnShutdownReboot.release(); 516 mListeners.kill(); 517 } 518 519 @Override 520 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)521 public void dump(IndentingPrintWriter writer) { 522 writer.println("*CarStorageMonitoringService*"); 523 synchronized (mLock) { 524 doInitServiceIfNeededLocked(); 525 writer.println("last wear information retrieved: " 526 + mWearInformation.map(WearInformation::toString).orElse("missing")); 527 writer.println(listToString(mWearEstimateChanges, "\n", 528 "wear change history: ")); 529 writer.println(listToString(mBootIoStats, "\n", "boot I/O stats: ")); 530 writer.println("aggregate I/O stats: " 531 + SparseArrayStream.valueStream(mIoStatsTracker.getTotal()) 532 .map(IoStatsEntry::toString) 533 .collect(Collectors.joining("\n"))); 534 writer.println("I/O stats snapshots: "); 535 writer.println( 536 mIoStatsSamples.stream().map( 537 sample -> listToString(sample.getStats(), "\n", "")) 538 .collect(Collectors.joining("\n------\n"))); 539 if (mShutdownCostInfo < 0) { 540 writer.print("last shutdown cost: missing. "); 541 if (mShutdownCostMissingReason != null && !mShutdownCostMissingReason.isEmpty()) { 542 writer.println("reason: " + mShutdownCostMissingReason); 543 } 544 } else { 545 writer.println("last shutdown cost: " + mShutdownCostInfo + " bytes, estimated"); 546 } 547 } 548 } 549 listToString(List<T> list, CharSequence delimiter, String prefix)550 private <T> String listToString(List<T> list, CharSequence delimiter, String prefix) { 551 StringJoiner stringJoiner = new StringJoiner(delimiter, prefix, /* suffix= */ ""); 552 for (int index = 0; index < list.size(); index++) { 553 stringJoiner.add(list.get(index).toString()); 554 } 555 return stringJoiner.toString(); 556 } 557 558 @Override 559 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dumpProto(ProtoOutputStream proto)560 public void dumpProto(ProtoOutputStream proto) {} 561 562 // ICarStorageMonitoring implementation 563 564 @Override getPreEolIndicatorStatus()565 public int getPreEolIndicatorStatus() { 566 mStorageMonitoringPermission.assertGranted(); 567 synchronized (mLock) { 568 doInitServiceIfNeededLocked(); 569 570 return mWearInformation.map(wi -> wi.preEolInfo) 571 .orElse(WearInformation.UNKNOWN_PRE_EOL_INFO); 572 } 573 } 574 575 /** 576 * @deprecated wear estimate data is unreliable 577 */ 578 @Deprecated 579 @Override getWearEstimate()580 public WearEstimate getWearEstimate() { 581 mStorageMonitoringPermission.assertGranted(); 582 synchronized (mLock) { 583 doInitServiceIfNeededLocked(); 584 585 return mWearInformation.map(wi -> 586 new WearEstimate(wi.lifetimeEstimateA, wi.lifetimeEstimateB)).orElse( 587 WearEstimate.UNKNOWN_ESTIMATE); 588 } 589 } 590 591 /** 592 * @deprecated wear estimate data is unreliable 593 */ 594 @Deprecated 595 @Override getWearEstimateHistory()596 public List<WearEstimateChange> getWearEstimateHistory() { 597 mStorageMonitoringPermission.assertGranted(); 598 synchronized (mLock) { 599 doInitServiceIfNeededLocked(); 600 601 return Collections.unmodifiableList(mWearEstimateChanges); 602 } 603 } 604 605 /** 606 * @deprecated use 607 * {@link com.android.car.watchdog.CarWatchdogService#getResourceOveruseStats(int, int)} 608 * instead. 609 * WARNING: The metrics provided are aggregated through time and could include data retrieved 610 * after system boot. Also, the I/O stats are only for the calling package. 611 */ 612 @Deprecated 613 @Override getBootIoStats()614 public List<IoStatsEntry> getBootIoStats() { 615 mStorageMonitoringPermission.assertGranted(); 616 synchronized (mLock) { 617 doInitServiceIfNeededLocked(); 618 619 return Collections.unmodifiableList(mBootIoStats); 620 } 621 } 622 623 /** 624 * @deprecated use 625 * {@link com.android.car.watchdog.CarWatchdogService#getResourceOveruseStats(int, int)} instead. 626 * WARNING: The I/O stats returned are only for the calling package. 627 */ 628 @Deprecated 629 @Override getAggregateIoStats()630 public List<IoStatsEntry> getAggregateIoStats() { 631 mStorageMonitoringPermission.assertGranted(); 632 synchronized (mLock) { 633 doInitServiceIfNeededLocked(); 634 635 return Collections.unmodifiableList(SparseArrayStream.valueStream( 636 mIoStatsTracker.getTotal()).collect(Collectors.toList())); 637 } 638 } 639 640 /** 641 * @deprecated use 642 * {@link com.android.car.watchdog.CarWatchdogService#getResourceOveruseStats(int, int)} 643 * instead. 644 * WARNING: The metrics provided are aggregated through time and could include data not related 645 * to system shutdown. Also, the I/O stats are only for the calling package. 646 */ 647 @Deprecated 648 @Override getShutdownDiskWriteAmount()649 public long getShutdownDiskWriteAmount() { 650 mStorageMonitoringPermission.assertGranted(); 651 synchronized (mLock) { 652 doInitServiceIfNeededLocked(); 653 654 return mShutdownCostInfo; 655 } 656 } 657 658 /** 659 * @deprecated use 660 * {@link com.android.car.watchdog.CarWatchdogService#getResourceOveruseStats(int, int)} instead. 661 * WARNING: The I/O stats returned are only for the calling package. 662 */ 663 @Deprecated 664 @Override getIoStatsDeltas()665 public List<IoStats> getIoStatsDeltas() { 666 mStorageMonitoringPermission.assertGranted(); 667 synchronized (mLock) { 668 doInitServiceIfNeededLocked(); 669 670 return Collections.unmodifiableList( 671 mIoStatsSamples.stream().collect(Collectors.toList())); 672 } 673 } 674 675 /** 676 * @deprecated {@link IIoStatsListener} is deprecated 677 */ 678 @Deprecated 679 @Override registerListener(IIoStatsListener listener)680 public void registerListener(IIoStatsListener listener) { 681 mStorageMonitoringPermission.assertGranted(); 682 synchronized (mLock) { 683 doInitServiceIfNeededLocked(); 684 } 685 mListeners.register(listener); 686 } 687 688 /** 689 * @deprecated {@link IIoStatsListener} is deprecated 690 */ 691 @Deprecated 692 @Override unregisterListener(IIoStatsListener listener)693 public void unregisterListener(IIoStatsListener listener) { 694 mStorageMonitoringPermission.assertGranted(); 695 // no need to initialize service if unregistering 696 697 mListeners.unregister(listener); 698 } 699 700 private static final class Configuration { 701 final long acceptableBytesWrittenPerSample; 702 final int acceptableFsyncCallsPerSample; 703 final int acceptableHoursPerOnePercentFlashWear; 704 final String activityHandlerForFlashWearChanges; 705 final String intentReceiverForUnacceptableIoMetrics; 706 final int ioStatsNumSamplesToStore; 707 final int ioStatsRefreshRateMs; 708 final int maxExcessiveIoSamplesInWindow; 709 final long uptimeIntervalBetweenUptimeDataWriteMs; 710 final String eMmcLifetimeFilePath; 711 final String eMmcEolFilePath; 712 Configuration(Resources resources)713 Configuration(Resources resources) throws Resources.NotFoundException { 714 ioStatsNumSamplesToStore = resources.getInteger(R.integer.ioStatsNumSamplesToStore); 715 acceptableBytesWrittenPerSample = 716 1024L * resources.getInteger(R.integer.acceptableWrittenKBytesPerSample); 717 acceptableFsyncCallsPerSample = 718 resources.getInteger(R.integer.acceptableFsyncCallsPerSample); 719 maxExcessiveIoSamplesInWindow = 720 resources.getInteger(R.integer.maxExcessiveIoSamplesInWindow); 721 uptimeIntervalBetweenUptimeDataWriteMs = 60L * 60 * 1000 722 * resources.getInteger(R.integer.uptimeHoursIntervalBetweenUptimeDataWrite); 723 acceptableHoursPerOnePercentFlashWear = 724 resources.getInteger(R.integer.acceptableHoursPerOnePercentFlashWear); 725 ioStatsRefreshRateMs = 1000 * resources.getInteger(R.integer.ioStatsRefreshRateSeconds); 726 activityHandlerForFlashWearChanges = 727 resources.getString(R.string.activityHandlerForFlashWearChanges); 728 intentReceiverForUnacceptableIoMetrics = 729 resources.getString(R.string.intentReceiverForUnacceptableIoMetrics); 730 eMmcLifetimeFilePath = resources.getString(R.string.eMmcLifetimeFilePath); 731 eMmcEolFilePath = resources.getString(R.string.eMmcEolFilePath); 732 } 733 734 @Override toString()735 public String toString() { 736 return String.format("acceptableBytesWrittenPerSample = %d, " 737 + "acceptableFsyncCallsPerSample = %d, " 738 + "acceptableHoursPerOnePercentFlashWear = %d, " 739 + "activityHandlerForFlashWearChanges = %s, " 740 + "intentReceiverForUnacceptableIoMetrics = %s, " 741 + "ioStatsNumSamplesToStore = %d, " 742 + "ioStatsRefreshRateMs = %d, " 743 + "maxExcessiveIoSamplesInWindow = %d, " 744 + "uptimeIntervalBetweenUptimeDataWriteMs = %d, " 745 + "eMmcLifetimeFilePath = %s, " 746 + "eMmcEolFilePath = %s", 747 acceptableBytesWrittenPerSample, 748 acceptableFsyncCallsPerSample, 749 acceptableHoursPerOnePercentFlashWear, 750 activityHandlerForFlashWearChanges, 751 intentReceiverForUnacceptableIoMetrics, 752 ioStatsNumSamplesToStore, 753 ioStatsRefreshRateMs, 754 maxExcessiveIoSamplesInWindow, 755 uptimeIntervalBetweenUptimeDataWriteMs, 756 eMmcLifetimeFilePath, 757 eMmcEolFilePath); 758 } 759 } 760 } 761