Use Health service to fetch wear information.
Test: Unit-test CarStorageMonitoringTest
Bug: 118391646
Change-Id: Id1a52ed2ed3d6d0167022d8838ee14be1d69860b
diff --git a/service/Android.mk b/service/Android.mk
index 39c7386..fa4ba77 100644
--- a/service/Android.mk
+++ b/service/Android.mk
@@ -46,6 +46,8 @@
android.hidl.base-V1.0-java \
android.hardware.automotive.audiocontrol-V1.0-java \
android.hardware.automotive.vehicle-V2.0-java \
+ android.hardware.health-V1.0-java \
+ android.hardware.health-V2.0-java \
vehicle-hal-support-lib \
car-frameworks-service \
car-systemtest \
@@ -76,6 +78,8 @@
android.hidl.base-V1.0-java \
android.hardware.automotive.audiocontrol-V1.0-java \
android.hardware.automotive.vehicle-V2.0-java \
+ android.hardware.health-V1.0-java \
+ android.hardware.health-V2.0-java \
vehicle-hal-support-lib \
car-systemtest \
com.android.car.procfsinspector-client \
diff --git a/service/src/com/android/car/storagemonitoring/HealthServiceWearInfoProvider.java b/service/src/com/android/car/storagemonitoring/HealthServiceWearInfoProvider.java
new file mode 100644
index 0000000..f0b8b97
--- /dev/null
+++ b/service/src/com/android/car/storagemonitoring/HealthServiceWearInfoProvider.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.storagemonitoring;
+
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.hardware.health.V2_0.IHealth;
+import android.hardware.health.V2_0.Result;
+import android.hardware.health.V2_0.StorageInfo;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.MutableInt;
+
+import com.android.car.CarLog;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/**
+ * Loads wear information from the Health service.
+ */
+public class HealthServiceWearInfoProvider implements WearInformationProvider {
+
+ private static final String INSTANCE_HEALTHD = "backup";
+ private static final String INSTANCE_VENDOR = "default";
+
+ private static final List<String> sAllInstances =
+ Arrays.asList(INSTANCE_VENDOR, INSTANCE_HEALTHD);
+
+ private IHealthSupplier mHealthSupplier;
+
+ public HealthServiceWearInfoProvider() {
+ mHealthSupplier = new IHealthSupplier() {};
+ }
+
+ @Nullable
+ @Override
+ public WearInformation load() {
+ IHealth healthService = getHealthService();
+ final MutableInt success = new MutableInt(Result.NOT_SUPPORTED);
+ final MutableInt foundInternalStorageDeviceInfo = new MutableInt(0);
+ final MutableInt lifetimeA = new MutableInt(0);
+ final MutableInt lifetimeB = new MutableInt(0);
+ final MutableInt preEol = new MutableInt(0);
+
+ final IHealth.getStorageInfoCallback getStorageInfoCallback =
+ new IHealth.getStorageInfoCallback() {
+ @Override
+ public void onValues(int result, ArrayList<StorageInfo> value) {
+ success.value = result;
+ if (result == Result.SUCCESS) {
+ int len = value.size();
+ for (int i = 0; i < len; i++) {
+ StorageInfo value2 = value.get(i);
+ if (value2.attr.isInternal) {
+ lifetimeA.value = value2.lifetimeA;
+ lifetimeB.value = value2.lifetimeB;
+ preEol.value = value2.eol;
+ foundInternalStorageDeviceInfo.value = 1;
+ }
+ }
+ }
+ }};
+
+ if (healthService == null) {
+ Log.w(CarLog.TAG_STORAGE, "No health service is available to fetch wear information.");
+ return null;
+ }
+
+ try {
+ healthService.getStorageInfo(getStorageInfoCallback);
+ } catch (Exception e) {
+ Log.w(CarLog.TAG_STORAGE, "Failed to get storage information from"
+ + "health service, exception :" + e);
+ return null;
+ }
+
+ if (success.value != Result.SUCCESS) {
+ Log.w(CarLog.TAG_STORAGE, "Health service returned result :" + success.value);
+ return null;
+ } else if (foundInternalStorageDeviceInfo.value == 0) {
+ Log.w(CarLog.TAG_STORAGE, "Failed to find storage information for"
+ + "internal storage device");
+ return null;
+ } else {
+ return new WearInformation(lifetimeA.value, lifetimeB.value,
+ preEol.value);
+ }
+ }
+
+ @Nullable
+ private IHealth getHealthService() {
+ for (String name : sAllInstances) {
+ IHealth newService = null;
+ try {
+ newService = mHealthSupplier.get(name);
+ } catch (Exception e) {
+ /* ignored, handled below */
+ }
+ if (newService != null) {
+ return newService;
+ }
+ }
+ return null;
+ }
+
+ @TestApi
+ public void setHealthSupplier(IHealthSupplier healthSupplier) {
+ mHealthSupplier = healthSupplier;
+ }
+
+ /**
+ * Supplier of services.
+ * Must not return null; throw {@link NoSuchElementException} if a service is not available.
+ */
+ interface IHealthSupplier {
+ default IHealth get(String name) throws NoSuchElementException, RemoteException {
+ return IHealth.getService(name, false /* retry */);
+ }
+ }
+}
diff --git a/service/src/com/android/car/systeminterface/StorageMonitoringInterface.java b/service/src/com/android/car/systeminterface/StorageMonitoringInterface.java
index c0cceb0..78d6b13 100644
--- a/service/src/com/android/car/systeminterface/StorageMonitoringInterface.java
+++ b/service/src/com/android/car/systeminterface/StorageMonitoringInterface.java
@@ -17,6 +17,7 @@
package com.android.car.systeminterface;
import com.android.car.storagemonitoring.EMmcWearInformationProvider;
+import com.android.car.storagemonitoring.HealthServiceWearInfoProvider;
import com.android.car.storagemonitoring.LifetimeWriteInfoProvider;
import com.android.car.storagemonitoring.ProcfsUidIoStatsProvider;
import com.android.car.storagemonitoring.SysfsLifetimeWriteInfoProvider;
@@ -31,7 +32,8 @@
default WearInformationProvider[] getFlashWearInformationProviders() {
return new WearInformationProvider[] {
new EMmcWearInformationProvider(),
- new UfsWearInformationProvider()
+ new UfsWearInformationProvider(),
+ new HealthServiceWearInfoProvider()
};
}
diff --git a/tests/carservice_unit_test/src/com/android/car/storagemonitoring/CarStorageMonitoringTest.java b/tests/carservice_unit_test/src/com/android/car/storagemonitoring/CarStorageMonitoringTest.java
index deb24f6..50490e9 100644
--- a/tests/carservice_unit_test/src/com/android/car/storagemonitoring/CarStorageMonitoringTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/storagemonitoring/CarStorageMonitoringTest.java
@@ -16,20 +16,32 @@
package com.android.car.storagemonitoring;
-import android.car.storagemonitoring.IoStatsEntry;
+import static org.mockito.Mockito.*;
+
import android.car.storagemonitoring.IoStats;
+import android.car.storagemonitoring.IoStatsEntry;
import android.car.storagemonitoring.LifetimeWriteInfo;
import android.car.storagemonitoring.UidIoRecord;
import android.car.storagemonitoring.WearEstimate;
import android.car.storagemonitoring.WearEstimateChange;
+import android.hardware.health.V2_0.IHealth;
+import android.hardware.health.V2_0.IHealth.getStorageInfoCallback;
+import android.hardware.health.V2_0.Result;
+import android.hardware.health.V2_0.StorageInfo;
import android.os.Parcel;
import android.test.suitebuilder.annotation.MediumTest;
-
-import android.util.SparseArray;
-import com.android.car.test.utils.TemporaryDirectory;
-import com.android.car.test.utils.TemporaryFile;
import android.util.JsonReader;
import android.util.JsonWriter;
+import android.util.SparseArray;
+
+import com.android.car.test.utils.TemporaryDirectory;
+import com.android.car.test.utils.TemporaryFile;
+
+import junit.framework.TestCase;
+
+import org.json.JSONObject;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.io.FileWriter;
import java.io.StringReader;
@@ -40,8 +52,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import junit.framework.TestCase;
-import org.json.JSONObject;
+
/**
* Tests the storage monitoring API in CarService.
@@ -50,6 +61,14 @@
public class CarStorageMonitoringTest extends TestCase {
static final String TAG = CarStorageMonitoringTest.class.getSimpleName();
+ @Mock private IHealth mMockedHal;
+ @Mock private HealthServiceWearInfoProvider.IHealthSupplier mHealthServiceSupplier;
+
+ @Override
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
public void testEMmcWearInformationProvider() throws Exception {
try (TemporaryFile lifetimeFile = new TemporaryFile(TAG)) {
try (TemporaryFile eolFile = new TemporaryFile(TAG)) {
@@ -93,6 +112,32 @@
}
}
+ public void testHealthServiceWearInformationProvider() throws Exception {
+ StorageInfo storageInfo = new StorageInfo();
+ storageInfo.eol = WearInformation.PRE_EOL_INFO_NORMAL;
+ storageInfo.lifetimeA = 3;
+ storageInfo.lifetimeB = WearInformation.UNKNOWN_LIFETIME_ESTIMATE;
+ storageInfo.attr.isInternal = true;
+ HealthServiceWearInfoProvider wearInfoProvider = new HealthServiceWearInfoProvider();
+ wearInfoProvider.setHealthSupplier(mHealthServiceSupplier);
+
+ doReturn(mMockedHal)
+ .when(mHealthServiceSupplier).get(anyString());
+ doAnswer((invocation) -> {
+ ArrayList<StorageInfo> list = new ArrayList<StorageInfo>();
+ list.add(storageInfo);
+ ((IHealth.getStorageInfoCallback) invocation.getArguments()[0])
+ .onValues(Result.SUCCESS, list);
+ return null;
+ }).when(mMockedHal).getStorageInfo(any(getStorageInfoCallback.class));
+ WearInformation wearInformation = wearInfoProvider.load();
+
+ assertNotNull(wearInformation);
+ assertEquals(storageInfo.lifetimeA, wearInformation.lifetimeEstimateA);
+ assertEquals(storageInfo.lifetimeB, wearInformation.lifetimeEstimateB);
+ assertEquals(storageInfo.eol, wearInformation.preEolInfo);
+ }
+
public void testWearEstimateEquality() {
WearEstimate wearEstimate1 = new WearEstimate(10, 20);
WearEstimate wearEstimate2 = new WearEstimate(10, 20);