Updating CarStorageMonitoringService according to dev guidelines.
Fixes: 143693313
Test: atest CarServiceTest
Change-Id: I2d5093fe5315192e721d8907755fe39072ce87ab
diff --git a/service/src/com/android/car/CarStorageMonitoringService.java b/service/src/com/android/car/CarStorageMonitoringService.java
index fb487b4..2d15a3f 100644
--- a/service/src/com/android/car/CarStorageMonitoringService.java
+++ b/service/src/com/android/car/CarStorageMonitoringService.java
@@ -46,6 +46,7 @@
import com.android.car.storagemonitoring.WearInformation;
import com.android.car.storagemonitoring.WearInformationProvider;
import com.android.car.systeminterface.SystemInterface;
+import com.android.internal.annotations.GuardedBy;
import org.json.JSONArray;
import org.json.JSONException;
@@ -69,6 +70,10 @@
import java.util.stream.Collectors;
import java.util.stream.IntStream;
+/**
+ * A service to provide storage monitoring data like I/O statistics. In order to receive such data,
+ * users need to implement {@link IIoStatsListener} and register themselves against this service.
+ */
public class CarStorageMonitoringService extends ICarStorageMonitoring.Stub
implements CarServiceBase {
public static final String INTENT_EXCESSIVE_IO =
@@ -93,21 +98,38 @@
private final OnShutdownReboot mOnShutdownReboot;
private final SystemInterface mSystemInterface;
private final UidIoStatsProvider mUidIoStatsProvider;
- private final SlidingWindow<IoStats> mIoStatsSamples;
- private final RemoteCallbackList<IIoStatsListener> mListeners;
- private final Object mIoStatsSamplesLock = new Object();
- private final Configuration mConfiguration;
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final SlidingWindow<IoStats> mIoStatsSamples;
+
+ private final RemoteCallbackList<IIoStatsListener> mListeners;
+ private final Configuration mConfiguration;
private final CarPermission mStorageMonitoringPermission;
+ @GuardedBy("mLock")
private UptimeTracker mUptimeTracker = null;
+
+ @GuardedBy("mLock")
private Optional<WearInformation> mWearInformation = Optional.empty();
- private List<WearEstimateChange> mWearEstimateChanges = Collections.emptyList();
+
+ @GuardedBy("mLock")
+ private List<WearEstimateChange> mWearEstimateChanges;
+
+ @GuardedBy("mLock")
private List<IoStatsEntry> mBootIoStats = Collections.emptyList();
+
+ @GuardedBy("mLock")
private IoStatsTracker mIoStatsTracker = null;
+
+ @GuardedBy("mLock")
private boolean mInitialized = false;
+ @GuardedBy("mLock")
private long mShutdownCostInfo = SHUTDOWN_COST_INFO_MISSING;
+
+ @GuardedBy("mLock")
private String mShutdownCostMissingReason;
public CarStorageMonitoringService(Context context, SystemInterface systemInterface) {
@@ -131,8 +153,10 @@
mWearEstimateChanges = Collections.emptyList();
mIoStatsSamples = new SlidingWindow<>(mConfiguration.ioStatsNumSamplesToStore);
mListeners = new RemoteCallbackList<>();
- systemInterface.scheduleActionForBootCompleted(this::doInitServiceIfNeeded,
- Duration.ofSeconds(10));
+ systemInterface.scheduleActionForBootCompleted(() -> {
+ synchronized (mLock) {
+ doInitServiceIfNeededLocked();
+ }}, Duration.ofSeconds(10));
}
private Optional<WearInformation> loadWearInformation() {
@@ -164,7 +188,8 @@
}
// returns true iff a new event was added (and hence the history needs to be saved)
- private boolean addEventIfNeeded(WearHistory wearHistory) {
+ @GuardedBy("mLock")
+ private boolean addEventIfNeededLocked(WearHistory wearHistory) {
if (!mWearInformation.isPresent()) return false;
WearInformation wearInformation = mWearInformation.get();
@@ -180,9 +205,9 @@
if (currentWearEstimate.equals(lastWearEstimate)) return false;
WearEstimateRecord newRecord = new WearEstimateRecord(lastWearEstimate,
- currentWearEstimate,
- mUptimeTracker.getTotalUptime(),
- Instant.now());
+ currentWearEstimate,
+ mUptimeTracker.getTotalUptime(),
+ Instant.now());
Log.d(TAG, "new wear record generated " + newRecord);
wearHistory.add(newRecord);
return true;
@@ -199,10 +224,11 @@
@Override
public void init() {
Log.d(TAG, "CarStorageMonitoringService init()");
-
- mUptimeTracker = new UptimeTracker(mUptimeTrackerFile,
- mConfiguration.uptimeIntervalBetweenUptimeDataWriteMs,
- mSystemInterface);
+ synchronized (mLock) {
+ mUptimeTracker = new UptimeTracker(mUptimeTrackerFile,
+ mConfiguration.uptimeIntervalBetweenUptimeDataWriteMs,
+ mSystemInterface);
+ }
}
private void launchWearChangeActivity() {
@@ -210,22 +236,21 @@
if (activityPath.isEmpty()) return;
try {
final ComponentName activityComponent =
- Objects.requireNonNull(ComponentName.unflattenFromString(activityPath));
+ Objects.requireNonNull(ComponentName.unflattenFromString(activityPath));
Intent intent = new Intent();
intent.setComponent(activityComponent);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
} catch (ActivityNotFoundException | NullPointerException e) {
- Log.e(TAG,
- "value of activityHandlerForFlashWearChanges invalid non-empty string " +
- activityPath, e);
+ Log.e(TAG, "value of activityHandlerForFlashWearChanges invalid non-empty string "
+ + activityPath, e);
}
}
private static void logOnAdverseWearLevel(WearInformation wearInformation) {
if (wearInformation.preEolInfo > WearInformation.PRE_EOL_INFO_NORMAL ||
- Math.max(wearInformation.lifetimeEstimateA,
- wearInformation.lifetimeEstimateB) >= MIN_WEAR_ESTIMATE_OF_CONCERN) {
+ Math.max(wearInformation.lifetimeEstimateA,
+ wearInformation.lifetimeEstimateB) >= MIN_WEAR_ESTIMATE_OF_CONCERN) {
Log.w(TAG, "flash storage reached wear a level that requires attention: "
+ wearInformation);
}
@@ -237,29 +262,31 @@
}
private void collectNewIoMetrics() {
+ SparseArray<IoStatsEntry> currentSample;
+ boolean needsExcessiveIoBroadcast;
IoStats ioStats;
-
- mIoStatsTracker.update(loadNewIoStats());
- synchronized (mIoStatsSamplesLock) {
+ synchronized (mLock) {
+ mIoStatsTracker.update(loadNewIoStats());
+ currentSample = mIoStatsTracker.getCurrentSample();
ioStats = new IoStats(
- SparseArrayStream.valueStream(mIoStatsTracker.getCurrentSample())
- .collect(Collectors.toList()),
- mSystemInterface.getUptime());
+ SparseArrayStream.valueStream(currentSample).collect(Collectors.toList()),
+ mSystemInterface.getUptime());
mIoStatsSamples.add(ioStats);
+ needsExcessiveIoBroadcast = needsExcessiveIoBroadcastLocked();
}
+ dispatchNewIoEvent(ioStats);
+
if (DBG) {
- SparseArray<IoStatsEntry> currentSample = mIoStatsTracker.getCurrentSample();
if (currentSample.size() == 0) {
Log.d(TAG, "no new I/O stat data");
} else {
SparseArrayStream.valueStream(currentSample).forEach(
- uidIoStats -> Log.d(TAG, "updated I/O stat data: " + uidIoStats));
+ uidIoStats -> Log.d(TAG, "updated I/O stat data: " + uidIoStats));
}
}
- dispatchNewIoEvent(ioStats);
- if (needsExcessiveIoBroadcast()) {
+ if (needsExcessiveIoBroadcast) {
Log.d(TAG, "about to send " + INTENT_EXCESSIVE_IO);
sendExcessiveIoBroadcast();
}
@@ -287,33 +314,33 @@
mContext.sendBroadcast(intent, mStorageMonitoringPermission.toString());
}
- private boolean needsExcessiveIoBroadcast() {
- synchronized (mIoStatsSamplesLock) {
- return mIoStatsSamples.count((IoStats delta) -> {
- Metrics total = delta.getTotals();
- final boolean tooManyBytesWritten =
+ @GuardedBy("mLock")
+ private boolean needsExcessiveIoBroadcastLocked() {
+ return mIoStatsSamples.count((IoStats delta) -> {
+ Metrics total = delta.getTotals();
+ final boolean tooManyBytesWritten =
(total.bytesWrittenToStorage > mConfiguration.acceptableBytesWrittenPerSample);
- final boolean tooManyFsyncCalls =
+ final boolean tooManyFsyncCalls =
(total.fsyncCalls > mConfiguration.acceptableFsyncCallsPerSample);
- return tooManyBytesWritten || tooManyFsyncCalls;
- }) > mConfiguration.maxExcessiveIoSamplesInWindow;
- }
+ return tooManyBytesWritten || tooManyFsyncCalls;
+ }) > mConfiguration.maxExcessiveIoSamplesInWindow;
}
private void dispatchNewIoEvent(IoStats delta) {
final int listenersCount = mListeners.beginBroadcast();
IntStream.range(0, listenersCount).forEach(
- i -> {
- try {
- mListeners.getBroadcastItem(i).onSnapshot(delta);
- } catch (RemoteException e) {
- Log.w(TAG, "failed to dispatch snapshot", e);
- }
- });
+ i -> {
+ try {
+ mListeners.getBroadcastItem(i).onSnapshot(delta);
+ } catch (RemoteException e) {
+ Log.w(TAG, "failed to dispatch snapshot", e);
+ }
+ });
mListeners.finishBroadcast();
}
- private synchronized void doInitServiceIfNeeded() {
+ @GuardedBy("mLock")
+ private void doInitServiceIfNeededLocked() {
if (mInitialized) return;
Log.d(TAG, "initializing CarStorageMonitoringService");
@@ -322,7 +349,7 @@
// TODO(egranata): can this be done lazily?
final WearHistory wearHistory = loadWearHistory();
- final boolean didWearChangeHappen = addEventIfNeeded(wearHistory);
+ final boolean didWearChangeHappen = addEventIfNeededLocked(wearHistory);
if (didWearChangeHappen) {
storeWearHistory(wearHistory);
}
@@ -341,15 +368,15 @@
long bootUptime = mSystemInterface.getUptime();
mBootIoStats = SparseArrayStream.valueStream(loadNewIoStats())
- .map(record -> {
- // at boot, assume all UIDs have been running for as long as the system has
- // been up, since we don't really know any better
- IoStatsEntry stats = new IoStatsEntry(record, bootUptime);
- if (DBG) {
- Log.d(TAG, "loaded boot I/O stat data: " + stats);
- }
- return stats;
- }).collect(Collectors.toList());
+ .map(record -> {
+ // at boot, assume all UIDs have been running for as long as the system has
+ // been up, since we don't really know any better
+ IoStatsEntry stats = new IoStatsEntry(record, bootUptime);
+ if (DBG) {
+ Log.d(TAG, "loaded boot I/O stat data: " + stats);
+ }
+ return stats;
+ }).collect(Collectors.toList());
mIoStatsTracker = new IoStatsTracker(mBootIoStats,
mConfiguration.ioStatsRefreshRateMs,
@@ -357,12 +384,12 @@
if (mConfiguration.ioStatsNumSamplesToStore > 0) {
mSystemInterface.scheduleAction(this::collectNewIoMetrics,
- mConfiguration.ioStatsRefreshRateMs);
+ mConfiguration.ioStatsRefreshRateMs);
} else {
Log.i(TAG, "service configuration disabled I/O sample window. not collecting samples");
}
- mShutdownCostInfo = computeShutdownCost();
+ mShutdownCostInfo = computeShutdownCostLocked();
Log.d(TAG, "calculated data written in last shutdown was " +
mShutdownCostInfo + " bytes");
mLifetimeWriteFile.delete();
@@ -372,7 +399,8 @@
mInitialized = true;
}
- private long computeShutdownCost() {
+ @GuardedBy("mLock")
+ private long computeShutdownCostLocked() {
List<LifetimeWriteInfo> shutdownWrites = loadLifetimeWrites();
if (shutdownWrites.isEmpty()) {
Log.d(TAG, "lifetime write data from last shutdown missing");
@@ -391,10 +419,10 @@
Map<String, Long> shutdownLifetimeWrites = new HashMap<>();
shutdownWrites.forEach(li ->
- shutdownLifetimeWrites.put(li.partition, li.writtenBytes));
+ shutdownLifetimeWrites.put(li.partition, li.writtenBytes));
// for every partition currently available, look for it in the shutdown data
- for(int i = 0; i < currentWrites.size(); ++i) {
+ for (int i = 0; i < currentWrites.size(); ++i) {
LifetimeWriteInfo li = currentWrites.get(i);
// if this partition was not available when we last shutdown the system, then
// just pretend we had written the same amount of data then as we have now
@@ -402,19 +430,19 @@
shutdownLifetimeWrites.getOrDefault(li.partition, li.writtenBytes);
final long costDelta = li.writtenBytes - writtenAtShutdown;
if (costDelta >= 0) {
- Log.d(TAG, "partition " + li.partition + " had " + costDelta +
- " bytes written to it during shutdown");
+ Log.d(TAG, "partition " + li.partition + " had " + costDelta
+ + " bytes written to it during shutdown");
shutdownCost += costDelta;
} else {
// the counter of written bytes should be monotonic; a decrease might mean
// corrupt data, improper shutdown or that the kernel in use does not
// have proper monotonic guarantees on the lifetime write data. If any of these
// occur, it's probably safer to just bail out and say we don't know
- mShutdownCostMissingReason = li.partition + " has a negative write amount (" +
- costDelta + " bytes)";
- Log.e(TAG, "partition " + li.partition + " reported " + costDelta +
- " bytes written to it during shutdown. assuming we can't" +
- " determine proper shutdown information.");
+ mShutdownCostMissingReason = li.partition + " has a negative write amount ("
+ + costDelta + " bytes)";
+ Log.e(TAG, "partition " + li.partition + " reported " + costDelta
+ + " bytes written to it during shutdown. assuming we can't"
+ + " determine proper shutdown information.");
return SHUTDOWN_COST_INFO_MISSING;
}
}
@@ -429,7 +457,7 @@
}
try {
JSONObject jsonObject = new JSONObject(
- new String(Files.readAllBytes(mLifetimeWriteFile.toPath())));
+ new String(Files.readAllBytes(mLifetimeWriteFile.toPath())));
JSONArray jsonArray = jsonObject.getJSONArray("lifetimeWriteInfo");
@@ -447,7 +475,7 @@
private void logLifetimeWrites() {
try {
LifetimeWriteInfo[] lifetimeWriteInfos =
- mSystemInterface.getLifetimeWriteInfoProvider().load();
+ mSystemInterface.getLifetimeWriteInfoProvider().load();
JsonWriter jsonWriter = new JsonWriter(new FileWriter(mLifetimeWriteFile));
jsonWriter.beginObject();
jsonWriter.name("lifetimeWriteInfo").beginArray();
@@ -465,8 +493,10 @@
@Override
public void release() {
Log.i(TAG, "tearing down CarStorageMonitoringService");
- if (mUptimeTracker != null) {
- mUptimeTracker.onDestroy();
+ synchronized (mLock) {
+ if (mUptimeTracker != null) {
+ mUptimeTracker.onDestroy();
+ }
}
mOnShutdownReboot.clearActions();
mListeners.kill();
@@ -474,39 +504,38 @@
@Override
public void dump(PrintWriter writer) {
- doInitServiceIfNeeded();
-
writer.println("*CarStorageMonitoringService*");
- writer.println("last wear information retrieved: " +
- mWearInformation.map(WearInformation::toString).orElse("missing"));
- writer.println("wear change history: " +
- mWearEstimateChanges.stream()
- .map(WearEstimateChange::toString)
- .collect(Collectors.joining("\n")));
- writer.println("boot I/O stats: " +
- mBootIoStats.stream()
- .map(IoStatsEntry::toString)
- .collect(Collectors.joining("\n")));
- writer.println("aggregate I/O stats: " +
- SparseArrayStream.valueStream(mIoStatsTracker.getTotal())
- .map(IoStatsEntry::toString)
- .collect(Collectors.joining("\n")));
- writer.println("I/O stats snapshots: ");
- synchronized (mIoStatsSamplesLock) {
+ synchronized (mLock) {
+ doInitServiceIfNeededLocked();
+ writer.println("last wear information retrieved: "
+ + mWearInformation.map(WearInformation::toString).orElse("missing"));
+ writer.println("wear change history: "
+ + mWearEstimateChanges.stream()
+ .map(WearEstimateChange::toString)
+ .collect(Collectors.joining("\n")));
+ writer.println("boot I/O stats: "
+ + mBootIoStats.stream()
+ .map(IoStatsEntry::toString)
+ .collect(Collectors.joining("\n")));
+ writer.println("aggregate I/O stats: "
+ + SparseArrayStream.valueStream(mIoStatsTracker.getTotal())
+ .map(IoStatsEntry::toString)
+ .collect(Collectors.joining("\n")));
+ writer.println("I/O stats snapshots: ");
writer.println(
- mIoStatsSamples.stream().map(
- sample -> sample.getStats().stream()
- .map(IoStatsEntry::toString)
- .collect(Collectors.joining("\n")))
- .collect(Collectors.joining("\n------\n")));
- }
- if (mShutdownCostInfo < 0) {
- writer.print("last shutdown cost: missing. ");
- if (mShutdownCostMissingReason != null && !mShutdownCostMissingReason.isEmpty()) {
- writer.println("reason: " + mShutdownCostMissingReason);
+ mIoStatsSamples.stream().map(
+ sample -> sample.getStats().stream()
+ .map(IoStatsEntry::toString)
+ .collect(Collectors.joining("\n")))
+ .collect(Collectors.joining("\n------\n")));
+ if (mShutdownCostInfo < 0) {
+ writer.print("last shutdown cost: missing. ");
+ if (mShutdownCostMissingReason != null && !mShutdownCostMissingReason.isEmpty()) {
+ writer.println("reason: " + mShutdownCostMissingReason);
+ }
+ } else {
+ writer.println("last shutdown cost: " + mShutdownCostInfo + " bytes, estimated");
}
- } else {
- writer.println("last shutdown cost: " + mShutdownCostInfo + " bytes, estimated");
}
}
@@ -515,70 +544,82 @@
@Override
public int getPreEolIndicatorStatus() {
mStorageMonitoringPermission.assertGranted();
- doInitServiceIfNeeded();
+ synchronized (mLock) {
+ doInitServiceIfNeededLocked();
- return mWearInformation.map(wi -> wi.preEolInfo)
- .orElse(WearInformation.UNKNOWN_PRE_EOL_INFO);
+ return mWearInformation.map(wi -> wi.preEolInfo)
+ .orElse(WearInformation.UNKNOWN_PRE_EOL_INFO);
+ }
}
@Override
public WearEstimate getWearEstimate() {
mStorageMonitoringPermission.assertGranted();
- doInitServiceIfNeeded();
+ synchronized (mLock) {
+ doInitServiceIfNeededLocked();
- return mWearInformation.map(wi ->
- new WearEstimate(wi.lifetimeEstimateA,wi.lifetimeEstimateB)).orElse(
+ return mWearInformation.map(wi ->
+ new WearEstimate(wi.lifetimeEstimateA, wi.lifetimeEstimateB)).orElse(
WearEstimate.UNKNOWN_ESTIMATE);
+ }
}
@Override
public List<WearEstimateChange> getWearEstimateHistory() {
mStorageMonitoringPermission.assertGranted();
- doInitServiceIfNeeded();
+ synchronized (mLock) {
+ doInitServiceIfNeededLocked();
- return mWearEstimateChanges;
+ return Collections.unmodifiableList(mWearEstimateChanges);
+ }
}
@Override
public List<IoStatsEntry> getBootIoStats() {
mStorageMonitoringPermission.assertGranted();
- doInitServiceIfNeeded();
+ doInitServiceIfNeededLocked();
- return mBootIoStats;
+ return Collections.unmodifiableList(mBootIoStats);
}
@Override
public List<IoStatsEntry> getAggregateIoStats() {
mStorageMonitoringPermission.assertGranted();
- doInitServiceIfNeeded();
+ synchronized (mLock) {
+ doInitServiceIfNeededLocked();
- return SparseArrayStream.valueStream(mIoStatsTracker.getTotal())
- .collect(Collectors.toList());
+ return Collections.unmodifiableList(SparseArrayStream.valueStream(
+ mIoStatsTracker.getTotal()).collect(Collectors.toList()));
+ }
}
@Override
public long getShutdownDiskWriteAmount() {
mStorageMonitoringPermission.assertGranted();
- doInitServiceIfNeeded();
+ synchronized (mLock) {
+ doInitServiceIfNeededLocked();
- return mShutdownCostInfo;
+ return mShutdownCostInfo;
+ }
}
@Override
public List<IoStats> getIoStatsDeltas() {
mStorageMonitoringPermission.assertGranted();
- doInitServiceIfNeeded();
+ synchronized (mLock) {
+ doInitServiceIfNeededLocked();
- synchronized (mIoStatsSamplesLock) {
- return mIoStatsSamples.stream().collect(Collectors.toList());
+ return Collections.unmodifiableList(
+ mIoStatsSamples.stream().collect(Collectors.toList()));
}
}
@Override
public void registerListener(IIoStatsListener listener) {
mStorageMonitoringPermission.assertGranted();
- doInitServiceIfNeeded();
-
+ synchronized (mLock) {
+ doInitServiceIfNeededLocked();
+ }
mListeners.register(listener);
}
@@ -609,13 +650,11 @@
resources.getInteger(R.integer.acceptableFsyncCallsPerSample);
maxExcessiveIoSamplesInWindow =
resources.getInteger(R.integer.maxExcessiveIoSamplesInWindow);
- uptimeIntervalBetweenUptimeDataWriteMs =
- 60 * 60 * 1000 *
- resources.getInteger(R.integer.uptimeHoursIntervalBetweenUptimeDataWrite);
+ uptimeIntervalBetweenUptimeDataWriteMs = 60 * 60 * 1000
+ * resources.getInteger(R.integer.uptimeHoursIntervalBetweenUptimeDataWrite);
acceptableHoursPerOnePercentFlashWear =
resources.getInteger(R.integer.acceptableHoursPerOnePercentFlashWear);
- ioStatsRefreshRateMs =
- 1000 * resources.getInteger(R.integer.ioStatsRefreshRateSeconds);
+ ioStatsRefreshRateMs = 1000 * resources.getInteger(R.integer.ioStatsRefreshRateSeconds);
activityHandlerForFlashWearChanges =
resources.getString(R.string.activityHandlerForFlashWearChanges);
intentReceiverForUnacceptableIoMetrics =
@@ -624,25 +663,24 @@
@Override
public String toString() {
- return String.format(
- "acceptableBytesWrittenPerSample = %d, " +
- "acceptableFsyncCallsPerSample = %d, " +
- "acceptableHoursPerOnePercentFlashWear = %d, " +
- "activityHandlerForFlashWearChanges = %s, " +
- "intentReceiverForUnacceptableIoMetrics = %s, " +
- "ioStatsNumSamplesToStore = %d, " +
- "ioStatsRefreshRateMs = %d, " +
- "maxExcessiveIoSamplesInWindow = %d, " +
- "uptimeIntervalBetweenUptimeDataWriteMs = %d",
- acceptableBytesWrittenPerSample,
- acceptableFsyncCallsPerSample,
- acceptableHoursPerOnePercentFlashWear,
- activityHandlerForFlashWearChanges,
- intentReceiverForUnacceptableIoMetrics,
- ioStatsNumSamplesToStore,
- ioStatsRefreshRateMs,
- maxExcessiveIoSamplesInWindow,
- uptimeIntervalBetweenUptimeDataWriteMs);
+ return String.format("acceptableBytesWrittenPerSample = %d, "
+ + "acceptableFsyncCallsPerSample = %d, "
+ + "acceptableHoursPerOnePercentFlashWear = %d, "
+ + "activityHandlerForFlashWearChanges = %s, "
+ + "intentReceiverForUnacceptableIoMetrics = %s, "
+ + "ioStatsNumSamplesToStore = %d, "
+ + "ioStatsRefreshRateMs = %d, "
+ + "maxExcessiveIoSamplesInWindow = %d, "
+ + "uptimeIntervalBetweenUptimeDataWriteMs = %d",
+ acceptableBytesWrittenPerSample,
+ acceptableFsyncCallsPerSample,
+ acceptableHoursPerOnePercentFlashWear,
+ activityHandlerForFlashWearChanges,
+ intentReceiverForUnacceptableIoMetrics,
+ ioStatsNumSamplesToStore,
+ ioStatsRefreshRateMs,
+ maxExcessiveIoSamplesInWindow,
+ uptimeIntervalBetweenUptimeDataWriteMs);
}
}
}