MultiHal 2.0 - setOperationMode and init direct channel flags

Implement setOperationMode method of HalProxy object and initialized the
direct channel flag for the sensors list. Also create some unit tests to
test both of these new additions in HalProxy_test.cpp.

Bug: 136511617
Test: Tested using unit tests and tested on device with both fake
      subhals

Change-Id: I4e39ca0e94b3e109706d628612d1db9c98aca053
diff --git a/sensors/2.0/multihal/Android.bp b/sensors/2.0/multihal/Android.bp
index 44dddfd..697bf9e 100644
--- a/sensors/2.0/multihal/Android.bp
+++ b/sensors/2.0/multihal/Android.bp
@@ -45,6 +45,7 @@
     ],
     init_rc: ["android.hardware.sensors@2.0-service-multihal.rc"],
     vintf_fragments: ["android.hardware.sensors@2.0-multihal.xml"],
+    cflags: ["-DLOG_TAG=\"SensorsMultiHal\""],
 }
 
 cc_library_headers {
diff --git a/sensors/2.0/multihal/HalProxy.cpp b/sensors/2.0/multihal/HalProxy.cpp
index d8bde83..28dae93 100644
--- a/sensors/2.0/multihal/HalProxy.cpp
+++ b/sensors/2.0/multihal/HalProxy.cpp
@@ -69,8 +69,8 @@
 };
 
 HalProxy::HalProxy() {
-    const char* kMultiHalConfigFilePath = "/vendor/etc/sensors/hals.conf";
-    initializeSubHalListFromConfigFile(kMultiHalConfigFilePath);
+    const char* kMultiHalConfigFile = "/vendor/etc/sensors/hals.conf";
+    initializeSubHalListFromConfigFile(kMultiHalConfigFile);
     initializeSensorList();
 }
 
@@ -88,9 +88,27 @@
     return Void();
 }
 
-Return<Result> HalProxy::setOperationMode(OperationMode /* mode */) {
-    // TODO: Proxy API call to all sub-HALs and return appropriate result.
-    return Result::INVALID_OPERATION;
+Return<Result> HalProxy::setOperationMode(OperationMode mode) {
+    Result result = Result::OK;
+    size_t subHalIndex;
+    for (subHalIndex = 0; subHalIndex < mSubHalList.size(); subHalIndex++) {
+        ISensorsSubHal* subHal = mSubHalList[subHalIndex];
+        result = subHal->setOperationMode(mode);
+        if (result != Result::OK) {
+            ALOGE("setOperationMode failed for SubHal: %s", subHal->getName().c_str());
+            break;
+        }
+    }
+    if (result != Result::OK) {
+        // Reset the subhal operation modes that have been flipped
+        for (size_t i = 0; i < subHalIndex; i++) {
+            ISensorsSubHal* subHal = mSubHalList[i];
+            subHal->setOperationMode(mCurrentOperationMode);
+        }
+    } else {
+        mCurrentOperationMode = mode;
+    }
+    return result;
 }
 
 Return<Result> HalProxy::activate(int32_t sensorHandle, bool enabled) {
@@ -190,27 +208,27 @@
 void HalProxy::initializeSubHalListFromConfigFile(const char* configFileName) {
     std::ifstream subHalConfigStream(configFileName);
     if (!subHalConfigStream) {
-        LOG_FATAL("Failed to load subHal config file: %s", configFileName);
+        ALOGE("Failed to load subHal config file: %s", configFileName);
     } else {
         std::string subHalLibraryFile;
         while (subHalConfigStream >> subHalLibraryFile) {
             void* handle = dlopen(subHalLibraryFile.c_str(), RTLD_NOW);
             if (handle == nullptr) {
-                LOG_FATAL("dlopen failed for library: %s", subHalLibraryFile.c_str());
+                ALOGE("dlopen failed for library: %s", subHalLibraryFile.c_str());
             } else {
                 SensorsHalGetSubHalFunc* sensorsHalGetSubHalPtr =
                         (SensorsHalGetSubHalFunc*)dlsym(handle, "sensorsHalGetSubHal");
                 if (sensorsHalGetSubHalPtr == nullptr) {
-                    LOG_FATAL("Failed to locate sensorsHalGetSubHal function for library: %s",
-                              subHalLibraryFile.c_str());
+                    ALOGE("Failed to locate sensorsHalGetSubHal function for library: %s",
+                          subHalLibraryFile.c_str());
                 } else {
                     std::function<SensorsHalGetSubHalFunc> sensorsHalGetSubHal =
                             *sensorsHalGetSubHalPtr;
                     uint32_t version;
                     ISensorsSubHal* subHal = sensorsHalGetSubHal(&version);
                     if (version != SUB_HAL_2_0_VERSION) {
-                        LOG_FATAL("SubHal version was not 2.0 for library: %s",
-                                  subHalLibraryFile.c_str());
+                        ALOGE("SubHal version was not 2.0 for library: %s",
+                              subHalLibraryFile.c_str());
                     } else {
                         ALOGV("Loaded SubHal from library: %s", subHalLibraryFile.c_str());
                         mSubHalList.push_back(subHal);
@@ -231,6 +249,7 @@
                 } else {
                     ALOGV("Loaded sensor: %s", sensor.name.c_str());
                     sensor.sensorHandle |= (subHalIndex << 24);
+                    setDirectChannelFlags(&sensor, subHal);
                     mSensorList.push_back(sensor);
                 }
             }
@@ -241,6 +260,20 @@
     }
 }
 
+void HalProxy::setDirectChannelFlags(SensorInfo* sensorInfo, ISensorsSubHal* subHal) {
+    bool sensorSupportsDirectChannel =
+            (sensorInfo->flags & (V1_0::SensorFlagBits::MASK_DIRECT_REPORT |
+                                  V1_0::SensorFlagBits::MASK_DIRECT_CHANNEL)) != 0;
+    if (mDirectChannelSubHal == nullptr && sensorSupportsDirectChannel) {
+        mDirectChannelSubHal = subHal;
+    } else if (mDirectChannelSubHal != nullptr && subHal != mDirectChannelSubHal) {
+        // disable direct channel capability for sensors in subHals that are not
+        // the only one we will enable
+        sensorInfo->flags &= ~(V1_0::SensorFlagBits::MASK_DIRECT_REPORT |
+                               V1_0::SensorFlagBits::MASK_DIRECT_CHANNEL);
+    }
+}
+
 ISensorsSubHal* HalProxy::getSubHalForSensorHandle(uint32_t sensorHandle) {
     return mSubHalList[static_cast<size_t>(sensorHandle >> 24)];
 }
diff --git a/sensors/2.0/multihal/include/HalProxy.h b/sensors/2.0/multihal/include/HalProxy.h
index b0261c8..24b5081 100644
--- a/sensors/2.0/multihal/include/HalProxy.h
+++ b/sensors/2.0/multihal/include/HalProxy.h
@@ -125,10 +125,16 @@
      * well as the modified sensor handle for the framework.
      *
      * The subhal index is encoded in the first byte of the sensor handle and
-     * the remaining bytes are generated by the subhal.
+     * the remaining bytes are generated by the subhal to identify the sensor.
      */
     std::vector<SensorInfo> mSensorList;
 
+    //! The current operation mode for all subhals.
+    OperationMode mCurrentOperationMode = OperationMode::NORMAL;
+
+    //! The single subHal that supports directChannel reporting.
+    ISensorsSubHal* mDirectChannelSubHal = nullptr;
+
     /**
      * Initialize the list of SubHal objects in mSubHalList by reading from dynamic libraries
      * listed in a config file.
@@ -141,6 +147,17 @@
      */
     void initializeSensorList();
 
+    /**
+     * Clear direct channel flags if the HalProxy has already chosen a subhal as its direct channel
+     * subhal. Set the directChannelSubHal pointer to the subHal passed in if this is the first
+     * direct channel enabled sensor seen.
+     *
+     * @param sensorInfo The SensorInfo object that may be altered to have direct channel support
+     *    disabled.
+     * @param subHal The subhal pointer that the current sensorInfo object came from.
+     */
+    void setDirectChannelFlags(SensorInfo* sensorInfo, ISensorsSubHal* subHal);
+
     /*
      * Get the subhal pointer which can be found by indexing into the mSubHalList vector
      * using the index from the first byte of sensorHandle.
diff --git a/sensors/2.0/multihal/service.cpp b/sensors/2.0/multihal/service.cpp
index 995cf3c..ef77048 100644
--- a/sensors/2.0/multihal/service.cpp
+++ b/sensors/2.0/multihal/service.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "android.hardware.sensors@2.0-service"
-
 #include <android/hardware/sensors/2.0/ISensors.h>
 #include <hidl/HidlTransportSupport.h>
 #include <log/log.h>
diff --git a/sensors/2.0/multihal/tests/HalProxy_test.cpp b/sensors/2.0/multihal/tests/HalProxy_test.cpp
index 28004d0..1e1f9e9 100644
--- a/sensors/2.0/multihal/tests/HalProxy_test.cpp
+++ b/sensors/2.0/multihal/tests/HalProxy_test.cpp
@@ -15,33 +15,56 @@
 
 #include <gtest/gtest.h>
 
+#include <android/hardware/sensors/2.0/types.h>
+
 #include "HalProxy.h"
 #include "SensorsSubHal.h"
 
 #include <vector>
 
+namespace {
+
+using ::android::hardware::sensors::V1_0::SensorFlagBits;
 using ::android::hardware::sensors::V1_0::SensorInfo;
 using ::android::hardware::sensors::V1_0::SensorType;
 using ::android::hardware::sensors::V2_0::implementation::HalProxy;
 using ::android::hardware::sensors::V2_0::subhal::implementation::AllSensorsSubHal;
+using ::android::hardware::sensors::V2_0::subhal::implementation::
+        AllSupportDirectChannelSensorsSubHal;
 using ::android::hardware::sensors::V2_0::subhal::implementation::ContinuousSensorsSubHal;
+using ::android::hardware::sensors::V2_0::subhal::implementation::
+        DoesNotSupportDirectChannelSensorsSubHal;
 using ::android::hardware::sensors::V2_0::subhal::implementation::OnChangeSensorsSubHal;
 using ::android::hardware::sensors::V2_0::subhal::implementation::SensorsSubHal;
 
-// TODO: Add more interesting tests such as
-//     - verify setOperationMode invokes all subhals
-//     - verify if a subhal fails to change operation mode, that state is reset properly
-//     - Available sensors are obtained during initialization
-//
-// You can run this suite using "atest android.hardware.sensors@2.0-halproxy-unit-tests".
-//
-// See https://source.android.com/compatibility/tests/development/native-func-e2e.md for more info
-// on how tests are set up and for information on the gtest framework itself.
+using ::android::hardware::sensors::V2_0::subhal::implementation::
+        SetOperationModeFailingSensorsSubHal;
 
 // Helper declarations follow
+
+/**
+ * Tests that for each SensorInfo object from a proxy getSensorsList call each corresponding
+ * object from a subhal getSensorsList call has the same type and its last 3 bytes are the
+ * same for sensorHandle field.
+ *
+ * @param proxySensorsList The list of SensorInfo objects from the proxy.getSensorsList callback.
+ * @param subHalSenosrsList The list of SensorInfo objects from the subHal.getSensorsList callback.
+ */
 void testSensorsListFromProxyAndSubHal(const std::vector<SensorInfo>& proxySensorsList,
                                        const std::vector<SensorInfo>& subHalSensorsList);
 
+/**
+ * Tests that there is exactly one subhal that allows its sensors to have direct channel enabled.
+ * Therefore, all SensorInfo objects that are not from the enabled subhal should be disabled for
+ * direct channel.
+ *
+ * @param sensorsList The SensorInfo object list from proxy.getSensorsList call.
+ * @param enabledSubHalIndex The index of the subhal in the halproxy that is expected to be
+ *     enabled.
+ */
+void testSensorsListForOneDirectChannelEnabledSubHal(const std::vector<SensorInfo>& sensorsList,
+                                                     size_t enabledSubHalIndex);
+
 // Tests follow
 TEST(HalProxyTest, GetSensorsListOneSubHalTest) {
     AllSensorsSubHal subHal;
@@ -76,6 +99,63 @@
     testSensorsListFromProxyAndSubHal(proxySensorsList, combinedSubHalSensorsList);
 }
 
+TEST(HalProxyTest, SetOperationModeTwoSubHalSuccessTest) {
+    ContinuousSensorsSubHal subHal1;
+    OnChangeSensorsSubHal subHal2;
+
+    std::vector<ISensorsSubHal*> fakeSubHals{&subHal1, &subHal2};
+    HalProxy proxy(fakeSubHals);
+
+    EXPECT_EQ(subHal1.getOperationMode(), OperationMode::NORMAL);
+    EXPECT_EQ(subHal2.getOperationMode(), OperationMode::NORMAL);
+
+    Result result = proxy.setOperationMode(OperationMode::DATA_INJECTION);
+
+    EXPECT_EQ(result, Result::OK);
+    EXPECT_EQ(subHal1.getOperationMode(), OperationMode::DATA_INJECTION);
+    EXPECT_EQ(subHal2.getOperationMode(), OperationMode::DATA_INJECTION);
+}
+
+TEST(HalProxyTest, SetOperationModeTwoSubHalFailTest) {
+    AllSensorsSubHal subHal1;
+    SetOperationModeFailingSensorsSubHal subHal2;
+
+    std::vector<ISensorsSubHal*> fakeSubHals{&subHal1, &subHal2};
+    HalProxy proxy(fakeSubHals);
+
+    EXPECT_EQ(subHal1.getOperationMode(), OperationMode::NORMAL);
+    EXPECT_EQ(subHal2.getOperationMode(), OperationMode::NORMAL);
+
+    Result result = proxy.setOperationMode(OperationMode::DATA_INJECTION);
+
+    EXPECT_NE(result, Result::OK);
+    EXPECT_EQ(subHal1.getOperationMode(), OperationMode::NORMAL);
+    EXPECT_EQ(subHal2.getOperationMode(), OperationMode::NORMAL);
+}
+
+TEST(HalProxyTest, InitDirectChannelTwoSubHalsUnitTest) {
+    AllSupportDirectChannelSensorsSubHal subHal1;
+    AllSupportDirectChannelSensorsSubHal subHal2;
+
+    std::vector<ISensorsSubHal*> fakeSubHals{&subHal1, &subHal2};
+    HalProxy proxy(fakeSubHals);
+
+    proxy.getSensorsList([&](const auto& sensorsList) {
+        testSensorsListForOneDirectChannelEnabledSubHal(sensorsList, 0);
+    });
+}
+
+TEST(HalProxyTest, InitDirectChannelThreeSubHalsUnitTest) {
+    DoesNotSupportDirectChannelSensorsSubHal subHal1;
+    AllSupportDirectChannelSensorsSubHal subHal2, subHal3;
+    std::vector<ISensorsSubHal*> fakeSubHals{&subHal1, &subHal2, &subHal3};
+    HalProxy proxy(fakeSubHals);
+
+    proxy.getSensorsList([&](const auto& sensorsList) {
+        testSensorsListForOneDirectChannelEnabledSubHal(sensorsList, 1);
+    });
+}
+
 // Helper implementations follow
 void testSensorsListFromProxyAndSubHal(const std::vector<SensorInfo>& proxySensorsList,
                                        const std::vector<SensorInfo>& subHalSensorsList) {
@@ -88,4 +168,23 @@
         EXPECT_EQ(proxySensor.type, subHalSensor.type);
         EXPECT_EQ(proxySensor.sensorHandle & 0x00FFFFFF, subHalSensor.sensorHandle);
     }
-}
\ No newline at end of file
+}
+
+void testSensorsListForOneDirectChannelEnabledSubHal(const std::vector<SensorInfo>& sensorsList,
+                                                     size_t enabledSubHalIndex) {
+    for (const SensorInfo& sensor : sensorsList) {
+        size_t subHalIndex = static_cast<size_t>(sensor.sensorHandle >> 24);
+        if (subHalIndex == enabledSubHalIndex) {
+            // First subhal should have been picked as the direct channel subhal
+            // and so have direct channel enabled on all of its sensors
+            EXPECT_NE(sensor.flags & SensorFlagBits::MASK_DIRECT_REPORT, 0);
+            EXPECT_NE(sensor.flags & SensorFlagBits::MASK_DIRECT_CHANNEL, 0);
+        } else {
+            // All other subhals should have direct channel disabled for all sensors
+            EXPECT_EQ(sensor.flags & SensorFlagBits::MASK_DIRECT_REPORT, 0);
+            EXPECT_EQ(sensor.flags & SensorFlagBits::MASK_DIRECT_CHANNEL, 0);
+        }
+    }
+}
+
+}  // namespace
diff --git a/sensors/2.0/multihal/tests/fake_subhal/SensorsSubHal.cpp b/sensors/2.0/multihal/tests/fake_subhal/SensorsSubHal.cpp
index fe47161..d581c49 100644
--- a/sensors/2.0/multihal/tests/fake_subhal/SensorsSubHal.cpp
+++ b/sensors/2.0/multihal/tests/fake_subhal/SensorsSubHal.cpp
@@ -68,6 +68,7 @@
     for (auto sensor : mSensors) {
         sensor.second->setOperationMode(mode);
     }
+    mCurrentOperationMode = mode;
     return Result::OK;
 }
 
@@ -192,6 +193,34 @@
     AddSensor<RelativeHumiditySensor>();
 }
 
+Return<Result> SetOperationModeFailingSensorsSubHal::setOperationMode(OperationMode /*mode*/) {
+    return Result::BAD_VALUE;
+}
+
+Return<void> AllSupportDirectChannelSensorsSubHal::getSensorsList(getSensorsList_cb _hidl_cb) {
+    std::vector<SensorInfo> sensors;
+    for (const auto& sensor : mSensors) {
+        SensorInfo sensorInfo = sensor.second->getSensorInfo();
+        sensorInfo.flags |= V1_0::SensorFlagBits::MASK_DIRECT_CHANNEL;
+        sensorInfo.flags |= V1_0::SensorFlagBits::MASK_DIRECT_REPORT;
+        sensors.push_back(sensorInfo);
+    }
+    _hidl_cb(sensors);
+    return Void();
+}
+
+Return<void> DoesNotSupportDirectChannelSensorsSubHal::getSensorsList(getSensorsList_cb _hidl_cb) {
+    std::vector<SensorInfo> sensors;
+    for (const auto& sensor : mSensors) {
+        SensorInfo sensorInfo = sensor.second->getSensorInfo();
+        sensorInfo.flags &= ~static_cast<uint32_t>(V1_0::SensorFlagBits::MASK_DIRECT_CHANNEL);
+        sensorInfo.flags &= ~static_cast<uint32_t>(V1_0::SensorFlagBits::MASK_DIRECT_REPORT);
+        sensors.push_back(sensorInfo);
+    }
+    _hidl_cb(sensors);
+    return Void();
+}
+
 }  // namespace implementation
 }  // namespace subhal
 }  // namespace V2_0
diff --git a/sensors/2.0/multihal/tests/fake_subhal/SensorsSubHal.h b/sensors/2.0/multihal/tests/fake_subhal/SensorsSubHal.h
index d94d73d..61caa2c 100644
--- a/sensors/2.0/multihal/tests/fake_subhal/SensorsSubHal.h
+++ b/sensors/2.0/multihal/tests/fake_subhal/SensorsSubHal.h
@@ -29,6 +29,8 @@
 namespace subhal {
 namespace implementation {
 
+using ::android::hardware::sensors::V1_0::OperationMode;
+using ::android::hardware::sensors::V1_0::Result;
 using ::android::hardware::sensors::V2_0::implementation::IHalProxyCallback;
 
 /**
@@ -37,18 +39,18 @@
  */
 class SensorsSubHal : public ISensorsSubHal, public ISensorsEventCallback {
     using Event = ::android::hardware::sensors::V1_0::Event;
-    using OperationMode = ::android::hardware::sensors::V1_0::OperationMode;
     using RateLevel = ::android::hardware::sensors::V1_0::RateLevel;
-    using Result = ::android::hardware::sensors::V1_0::Result;
     using SharedMemInfo = ::android::hardware::sensors::V1_0::SharedMemInfo;
 
   public:
     SensorsSubHal();
 
     // Methods from ::android::hardware::sensors::V2_0::ISensors follow.
-    Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
+    virtual Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
 
-    Return<Result> setOperationMode(OperationMode mode) override;
+    virtual Return<Result> setOperationMode(OperationMode mode) override;
+
+    OperationMode getOperationMode() const { return mCurrentOperationMode; }
 
     Return<Result> activate(int32_t sensorHandle, bool enabled) override;
 
@@ -91,8 +93,19 @@
         mSensors[sensor->getSensorInfo().sensorHandle] = sensor;
     }
 
+    /**
+     * A map of the available sensors
+     */
+    std::map<int32_t, std::shared_ptr<Sensor>> mSensors;
+
   private:
     /**
+     * The current operation mode of the multihal framework. Ensures that all subhals are set to
+     * the same operation mode.
+     */
+    OperationMode mCurrentOperationMode = OperationMode::NORMAL;
+
+    /**
      * Callback used to communicate to the HalProxy when dynamic sensors are connected /
      * disconnected, sensor events need to be sent to the framework, and when a wakelock should be
      * acquired.
@@ -100,11 +113,6 @@
     sp<IHalProxyCallback> mCallback;
 
     /**
-     * A map of the available sensors
-     */
-    std::map<int32_t, std::shared_ptr<Sensor>> mSensors;
-
-    /**
      * The next available sensor handle
      */
     int32_t mNextHandle;
@@ -128,6 +136,21 @@
     AllSensorsSubHal();
 };
 
+class SetOperationModeFailingSensorsSubHal : public AllSensorsSubHal {
+  public:
+    Return<Result> setOperationMode(OperationMode mode) override;
+};
+
+class AllSupportDirectChannelSensorsSubHal : public AllSensorsSubHal {
+  public:
+    Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
+};
+
+class DoesNotSupportDirectChannelSensorsSubHal : public AllSensorsSubHal {
+  public:
+    Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
+};
+
 }  // namespace implementation
 }  // namespace subhal
 }  // namespace V2_0