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);