Merge "Add skeleton for multihal 2.0"
diff --git a/sensors/2.0/multihal/Android.bp b/sensors/2.0/multihal/Android.bp
new file mode 100644
index 0000000..4dec768
--- /dev/null
+++ b/sensors/2.0/multihal/Android.bp
@@ -0,0 +1,38 @@
+//
+// 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.
+
+cc_binary {
+    name: "android.hardware.sensors@2.0-service.multihal",
+    defaults: ["hidl_defaults"],
+    vendor: true,
+    relative_install_path: "hw",
+    srcs: [
+        "service.cpp",
+        "HalProxy.cpp",
+    ],
+    init_rc: ["android.hardware.sensors@2.0-service-multihal.rc"],
+    shared_libs: [
+        "android.hardware.sensors@1.0",
+        "android.hardware.sensors@2.0",
+        "libcutils",
+        "libfmq",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libpower",
+        "libutils",
+    ],
+    vintf_fragments: ["android.hardware.sensors@2.0-multihal.xml"],
+}
diff --git a/sensors/2.0/multihal/HalProxy.cpp b/sensors/2.0/multihal/HalProxy.cpp
new file mode 100644
index 0000000..31f8a18
--- /dev/null
+++ b/sensors/2.0/multihal/HalProxy.cpp
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+
+#include "HalProxy.h"
+
+#include <android/hardware/sensors/2.0/types.h>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_0 {
+namespace implementation {
+
+// TODO: Use this wake lock name as the prefix to all sensors HAL wake locks acquired.
+// constexpr const char* kWakeLockName = "SensorsHAL_WAKEUP";
+
+// TODO: Use the following class as a starting point for implementing the full HalProxyCallback
+// along with being inspiration for how to implement the ScopedWakelock class.
+/**
+ * Callback class used to provide the HalProxy with the index of which subHal is invoking
+ */
+class SensorsCallbackProxy : public ISensorsCallback {
+  public:
+    SensorsCallbackProxy(wp<HalProxy>& halProxy, int32_t subHalIndex)
+        : mHalProxy(halProxy), mSubHalIndex(subHalIndex) {}
+
+    Return<void> onDynamicSensorsConnected(
+            const hidl_vec<SensorInfo>& dynamicSensorsAdded) override {
+        sp<HalProxy> halProxy(mHalProxy.promote());
+        if (halProxy != nullptr) {
+            return halProxy->onDynamicSensorsConnected(dynamicSensorsAdded, mSubHalIndex);
+        }
+        return Return<void>();
+    }
+
+    Return<void> onDynamicSensorsDisconnected(
+            const hidl_vec<int32_t>& dynamicSensorHandlesRemoved) override {
+        sp<HalProxy> halProxy(mHalProxy.promote());
+        if (halProxy != nullptr) {
+            return halProxy->onDynamicSensorsDisconnected(dynamicSensorHandlesRemoved,
+                                                          mSubHalIndex);
+        }
+        return Return<void>();
+    }
+
+  private:
+    wp<HalProxy>& mHalProxy;
+    int32_t mSubHalIndex;
+};
+
+HalProxy::HalProxy() {
+    // TODO: Initialize all sub-HALs and discover sensors.
+}
+
+HalProxy::~HalProxy() {
+    // TODO: Join any running threads and clean up FMQs and any other allocated
+    // state.
+}
+
+Return<void> HalProxy::getSensorsList(getSensorsList_cb /* _hidl_cb */) {
+    // TODO: Output sensors list created as part of HalProxy().
+    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::activate(int32_t /* sensorHandle */, bool /* enabled */) {
+    // TODO: Proxy API call to appropriate sub-HAL.
+    return Result::INVALID_OPERATION;
+}
+
+Return<Result> HalProxy::initialize(
+        const ::android::hardware::MQDescriptorSync<Event>& eventQueueDescriptor,
+        const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+        const sp<ISensorsCallback>& sensorsCallback) {
+    Result result = Result::OK;
+
+    // TODO: clean up sensor requests, if not already done elsewhere through a death recipient, and
+    // clean up any other resources that exist (FMQs, flags, threads, etc.)
+
+    mDynamicSensorsCallback = sensorsCallback;
+
+    // Create the Event FMQ from the eventQueueDescriptor. Reset the read/write positions.
+    mEventQueue =
+            std::make_unique<EventMessageQueue>(eventQueueDescriptor, true /* resetPointers */);
+
+    // Create the EventFlag that is used to signal to the framework that sensor events have been
+    // written to the Event FMQ
+    if (EventFlag::createEventFlag(mEventQueue->getEventFlagWord(), &mEventQueueFlag) != OK) {
+        result = Result::BAD_VALUE;
+    }
+
+    // Create the Wake Lock FMQ that is used by the framework to communicate whenever WAKE_UP
+    // events have been successfully read and handled by the framework.
+    mWakeLockQueue =
+            std::make_unique<WakeLockMessageQueue>(wakeLockDescriptor, true /* resetPointers */);
+
+    if (!mDynamicSensorsCallback || !mEventQueue || !mWakeLockQueue || mEventQueueFlag == nullptr) {
+        result = Result::BAD_VALUE;
+    }
+
+    // TODO: start threads to read wake locks and process events from sub HALs.
+
+    return result;
+}
+
+Return<Result> HalProxy::batch(int32_t /* sensorHandle */, int64_t /* samplingPeriodNs */,
+                               int64_t /* maxReportLatencyNs */) {
+    // TODO: Proxy API call to appropriate sub-HAL.
+    return Result::INVALID_OPERATION;
+}
+
+Return<Result> HalProxy::flush(int32_t /* sensorHandle */) {
+    // TODO: Proxy API call to appropriate sub-HAL.
+    return Result::INVALID_OPERATION;
+}
+
+Return<Result> HalProxy::injectSensorData(const Event& /* event */) {
+    // TODO: Proxy API call to appropriate sub-HAL.
+    return Result::INVALID_OPERATION;
+}
+
+Return<void> HalProxy::registerDirectChannel(const SharedMemInfo& /* mem */,
+                                             registerDirectChannel_cb _hidl_cb) {
+    // TODO: During init, discover the first sub-HAL in the config that has sensors with direct
+    // channel support, if any, and proxy the API call there.
+    _hidl_cb(Result::INVALID_OPERATION, -1 /* channelHandle */);
+    return Return<void>();
+}
+
+Return<Result> HalProxy::unregisterDirectChannel(int32_t /* channelHandle */) {
+    // TODO: During init, discover the first sub-HAL in the config that has sensors with direct
+    // channel support, if any, and proxy the API call there.
+    return Result::INVALID_OPERATION;
+}
+
+Return<void> HalProxy::configDirectReport(int32_t /* sensorHandle */, int32_t /* channelHandle */,
+                                          RateLevel /* rate */, configDirectReport_cb _hidl_cb) {
+    // TODO: During init, discover the first sub-HAL in the config that has sensors with direct
+    // channel support, if any, and proxy the API call there.
+    _hidl_cb(Result::INVALID_OPERATION, 0 /* reportToken */);
+    return Return<void>();
+}
+
+Return<void> HalProxy::debug(const hidl_handle& /* fd */, const hidl_vec<hidl_string>& /* args */) {
+    // TODO: output debug information
+    return Return<void>();
+}
+
+Return<void> HalProxy::onDynamicSensorsConnected(
+        const hidl_vec<SensorInfo>& /* dynamicSensorsAdded */, int32_t /* subHalIndex */) {
+    // TODO: Map the SensorInfo to the global list and then invoke the framework's callback.
+    return Return<void>();
+}
+
+Return<void> HalProxy::onDynamicSensorsDisconnected(
+        const hidl_vec<int32_t>& /* dynamicSensorHandlesRemoved */, int32_t /* subHalIndex */) {
+    // TODO: Unmap the SensorInfo from the global list and then invoke the framework's callback.
+    return Return<void>();
+}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace sensors
+}  // namespace hardware
+}  // namespace android
diff --git a/sensors/2.0/multihal/HalProxy.h b/sensors/2.0/multihal/HalProxy.h
new file mode 100644
index 0000000..b9855a6
--- /dev/null
+++ b/sensors/2.0/multihal/HalProxy.h
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "SubHal.h"
+
+#include <android/hardware/sensors/2.0/ISensors.h>
+#include <fmq/MessageQueue.h>
+#include <hardware_legacy/power.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::EventFlag;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::MQDescriptor;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+struct HalProxy : public ISensors {
+    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;
+
+    HalProxy();
+    ~HalProxy();
+
+    // Methods from ::android::hardware::sensors::V2_0::ISensors follow.
+    Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
+
+    Return<Result> setOperationMode(OperationMode mode) override;
+
+    Return<Result> activate(int32_t sensorHandle, bool enabled) override;
+
+    Return<Result> initialize(
+            const ::android::hardware::MQDescriptorSync<Event>& eventQueueDescriptor,
+            const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+            const sp<ISensorsCallback>& sensorsCallback) override;
+
+    Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+                         int64_t maxReportLatencyNs) override;
+
+    Return<Result> flush(int32_t sensorHandle) override;
+
+    Return<Result> injectSensorData(const Event& event) override;
+
+    Return<void> registerDirectChannel(const SharedMemInfo& mem,
+                                       registerDirectChannel_cb _hidl_cb) override;
+
+    Return<Result> unregisterDirectChannel(int32_t channelHandle) override;
+
+    Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate,
+                                    configDirectReport_cb _hidl_cb) override;
+
+    Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override;
+
+    // Below methods from ::android::hardware::sensors::V2_0::ISensorsCaback with a minor change
+    // to pass in the sub-HAL index. While the above methods are invoked from the sensors framework
+    // via the binder, these methods are invoked from a callback provided to sub-HALs inside the
+    // same process as the HalProxy, but potentially running on different threads.
+    Return<void> onDynamicSensorsConnected(const hidl_vec<SensorInfo>& dynamicSensorsAdded,
+                                           int32_t subHalIndex);
+
+    Return<void> onDynamicSensorsDisconnected(const hidl_vec<int32_t>& dynamicSensorHandlesRemoved,
+                                              int32_t subHalIndex);
+
+  private:
+    using EventMessageQueue = MessageQueue<Event, kSynchronizedReadWrite>;
+    using WakeLockMessageQueue = MessageQueue<uint32_t, kSynchronizedReadWrite>;
+
+    /**
+     * The Event FMQ where sensor events are written
+     */
+    std::unique_ptr<EventMessageQueue> mEventQueue;
+
+    /**
+     * The Wake Lock FMQ that is read to determine when the framework has handled WAKE_UP events
+     */
+    std::unique_ptr<WakeLockMessageQueue> mWakeLockQueue;
+
+    /**
+     * Event Flag to signal to the framework when sensor events are available to be read
+     */
+    EventFlag* mEventQueueFlag;
+
+    /**
+     * Callback to the sensors framework to inform it that new sensors have been added or removed.
+     */
+    sp<ISensorsCallback> mDynamicSensorsCallback;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace sensors
+}  // namespace hardware
+}  // namespace android
diff --git a/sensors/2.0/multihal/OWNERS b/sensors/2.0/multihal/OWNERS
new file mode 100644
index 0000000..e955670
--- /dev/null
+++ b/sensors/2.0/multihal/OWNERS
@@ -0,0 +1,3 @@
+arthuri@google.com
+bduddie@google.com
+stange@google.com
\ No newline at end of file
diff --git a/sensors/2.0/multihal/SubHal.h b/sensors/2.0/multihal/SubHal.h
new file mode 100644
index 0000000..152f91d
--- /dev/null
+++ b/sensors/2.0/multihal/SubHal.h
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/hardware/sensors/1.0/types.h>
+#include <android/hardware/sensors/2.0/ISensors.h>
+
+#include <vector>
+
+using ::android::hardware::sensors::V1_0::Event;
+using ::android::hardware::sensors::V1_0::Result;
+using ::android::hardware::sensors::V1_0::SensorInfo;
+
+// Indicates the current version of the multiHAL interface formatted as (HAL major version) << 24 |
+// (HAL minor version) << 16 | (multiHAL version)
+#define SUB_HAL_2_0_VERSION 0x02000000
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_0 {
+namespace implementation {
+
+/**
+ * Wrapper around wake lock acquisition functions (acquire/release_wake_lock) that provides a
+ * RAII-style mechanism for keeping a wake lock held for the duration of a scoped block.
+ * When a ScopedWakelock is created, it increments the reference count stored in the HalProxy
+ * for the sub-HALs specific wake lock, acquiring the wake lock if necessary. When the object goes
+ * out of scope, the ref count is decremented, potentially releasing the wake lock if no other
+ * references to the wake lock exist.
+ *
+ * This class is allocated through the createScopedWakelock callback inside the IHalProxyCallback
+ * provided to sub-HALs during initialization and should be used for all wake lock acquisition
+ * inside of the sub-HAL to ensure wake locks are not held indefinitely.
+ *
+ * The most prevalent use case for this class will be for posting events to the framework through
+ * the postEvents HalProxy callback. The expectation is that sub-HALs will create this
+ * ScopedWakelock through the createScopedWakelock upon receiving a sensor events. The lock boolean
+ * provided to createScopedWakelock will be set the according to whether the sensor events are
+ * from wakeup sensors. Then, the sub-HAL will perform any processing necessary before invoking the
+ * postEvents callback passing in the previously created ScopedWakelock. At this point, ownership
+ * of the object will be passed to the HalProxy that will then be responsible for ensuring any
+ * wake locks continue to be held, if necessary.
+ */
+class ScopedWakelock {
+  public:
+    ScopedWakelock(ScopedWakelock&&) = default;
+    ScopedWakelock& operator=(ScopedWakelock&&) = default;
+    virtual ~ScopedWakelock() { mLocked = false; };
+
+    bool isLocked() const { return mLocked; }
+
+  protected:
+    bool mLocked;
+
+  private:
+    // TODO: Mark HalProxy's subclass of ScopedWakelock as a friend so that it can be initialized.
+    ScopedWakelock();
+    ScopedWakelock(const ScopedWakelock&) = delete;
+    ScopedWakelock& operator=(const ScopedWakelock&) = delete;
+};
+
+/**
+ * Interface that contains several callbacks into the HalProxy class to communicate dynamic sensor
+ * changes and sensor events to the framework and acquire wake locks. The HalProxy will ensure
+ * callbacks occurring at the same time from multiple sub-HALs are synchronized in a safe, efficient
+ * manner.
+ */
+class IHalProxyCallback : public ISensorsCallback {
+  public:
+    /**
+     * Thread-safe callback used to post events to the HalProxy. Sub-HALs should invoke this
+     * whenever new sensor events need to be delivered to the sensors framework. Once invoked, the
+     * HalProxy will attempt to send events to the sensors framework using a blocking write with a
+     * 5 second timeout. This write may be done asynchronously if the queue used to communicate
+     * with the framework is full to avoid blocking sub-HALs for the length of the timeout. If the
+     * write fails, the events will be dropped and any wake locks held will be released.
+     *
+     * The provided ScopedWakelock must be locked if the events are from wakeup sensors. If it's
+     * not locked accordingly, the HalProxy will crash as this indicates the sub-HAL isn't compliant
+     * with the sensors HAL 2.0 specification. Additionally, since ScopedWakelock isn't copyable,
+     * the HalProxy will take ownership of the wake lock given when this method is invoked. Once the
+     * method returns, the HalProxy will handle holding the wake lock, if necessary, until the
+     * framework has successfully processed any wakeup events.
+     *
+     * No return type is used for this callback to avoid sub-HALs trying to resend events when
+     * writes fail. Writes should only fail when the framework is under inordinate stress which will
+     * likely result in a framework restart so retrying will likely only result in overloading the
+     * HalProxy. Sub-HALs should always assume that the write was a success and perform any
+     * necessary cleanup. Additionally, the HalProxy will ensure it logs any errors (through ADB and
+     * bug reports) it encounters during delivery to ensure it's obvious that a failure occurred.
+     *
+     * @param events the events that should be sent to the sensors framework
+     * @param wakelock ScopedWakelock that should be locked to send events from wake sensors and
+     *     unlocked otherwise.
+     */
+    virtual void postEvents(const std::vector<Event>& events, ScopedWakelock wakelock) = 0;
+
+    /**
+     * Initializes a ScopedWakelock on the stack that, when locked, will increment the reference
+     * count for the sub-HAL's wake lock managed inside the HalProxy. See the ScopedWakelock class
+     * definition for how it should be used.
+     *
+     * @param lock whether the ScopedWakelock should be locked before it's returned.
+     * @return the created ScopedWakelock
+     */
+    virtual ScopedWakelock createScopedWakelock(bool lock) = 0;
+};
+
+/**
+ * ISensorsSubHal is an interface that sub-HALs must implement in order to be compliant with
+ * multihal 2.0 and in order for the HalProxy to successfully load and communicate with the sub-HAL.
+ *
+ * Any vendor wishing to implement this interface and support multihal 2.0 will need to create a
+ * dynamic library that exposes sensorsHalGetSubHal (defined below). This library will be loaded by
+ * the HalProxy when the sensors HAL is initialized and then the HalProxy will retrieve the vendor's
+ * implementation of sensorsHalGetSubHal.
+ *
+ * With the exception of the initialize method, ISensorsSubHal will implement the ISensors.hal spec.
+ * Any sensor handles given to the HalProxy, either through getSensorsList() or the
+ * onDynamicSensors(Dis)Connected callbacks, will be translated to avoid clashing with other sub-HAL
+ * handles. To achieve this, the HalProxy will use the upper byte to store the sub-HAL index and
+ * sub-HALs can continue to use the lower 3 bytes of the handle.
+ */
+class ISensorsSubHal : public ISensors {
+    // The ISensors version of initialize isn't used for multihal. Instead, sub-HALs must implement
+    // the version below to allow communciation logic to centralized in the HalProxy
+    Return<Result> initialize(
+            const ::android::hardware::MQDescriptorSync<Event>& /* eventQueueDescriptor */,
+            const ::android::hardware::MQDescriptorSync<uint32_t>& /* wakeLockDescriptor */,
+            const sp<ISensorsCallback>& /* sensorsCallback */) final {
+        return Result::INVALID_OPERATION;
+    }
+
+    /**
+     * Method defined in ::android::hidl::base::V1_0::IBase.
+     *
+     * This method should write debug information to hidl_handle that is useful for debugging
+     * issues. Suggestions include:
+     * - Sensor info including handle values and any other state available in the SensorInfo class
+     * - List of active sensors and their current sampling period and reporting latency
+     * - Information about pending flush requests
+     * - Current operating mode
+     * - Currently registered direct channel info
+     * - A history of any of the above
+     */
+    virtual Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) = 0;
+
+    /**
+     * @return A human-readable name for use in wake locks and logging.
+     */
+    virtual const std::string& getName() = 0;
+
+    /**
+     * First method invoked on the sub-HAL after it's allocated through sensorsHalGetSubHal() by the
+     * HalProxy. Sub-HALs should use this to initialize any state and retain the callback given in
+     * order to communicate with the HalProxy.
+     *
+     * @param halProxyCallback callback used to inform the HalProxy when a dynamic sensor's state
+     *     changes, new sensor events should be sent to the framework, and when a new ScopedWakelock
+     *     should be created.
+     * @return result OK on success
+     */
+    virtual Return<Result> initialize(const sp<IHalProxyCallback>& halProxyCallback) = 0;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace sensors
+}  // namespace hardware
+}  // namespace android
+
+using ::android::hardware::sensors::V2_0::implementation::ISensorsSubHal;
+
+/**
+ * Function that must be exported so the HalProxy class can invoke it on the sub-HAL dynamic
+ * library. This function will only be invoked once at initialization time.
+ *
+ * NOTE: The supported sensors HAL version must match SUB_HAL_2_0_VERSION exactly or the HalProxy
+ * will fail to initialize.
+ *
+ * @param uint32_t when this function returns, this parameter must contain the HAL version that
+ *     this sub-HAL supports. To support this version of multi-HAL, this must be set to
+ *     SUB_HAL_2_0_VERSION.
+ * @return A statically allocated, valid ISensorsSubHal implementation.
+ */
+__attribute__((visibility("default"))) extern "C" ISensorsSubHal* sensorsHalGetSubHal(
+        uint32_t* version);
diff --git a/sensors/2.0/multihal/android.hardware.sensors@2.0-multihal.xml b/sensors/2.0/multihal/android.hardware.sensors@2.0-multihal.xml
new file mode 100644
index 0000000..a771100
--- /dev/null
+++ b/sensors/2.0/multihal/android.hardware.sensors@2.0-multihal.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.sensors</name>
+        <transport>hwbinder</transport>
+        <version>2.0</version>
+        <interface>
+            <name>ISensors</name>
+            <instance>multihal</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/sensors/2.0/multihal/android.hardware.sensors@2.0-service-multihal.rc b/sensors/2.0/multihal/android.hardware.sensors@2.0-service-multihal.rc
new file mode 100644
index 0000000..1671689
--- /dev/null
+++ b/sensors/2.0/multihal/android.hardware.sensors@2.0-service-multihal.rc
@@ -0,0 +1,6 @@
+service vendor.sensors-hal-2-0-multihal /vendor/bin/hw/android.hardware.sensors@2.0-service.multihal
+    class hal
+    user system
+    group system
+    capabilities BLOCK_SUSPEND
+    rlimit rtprio 10 10
diff --git a/sensors/2.0/multihal/service.cpp b/sensors/2.0/multihal/service.cpp
new file mode 100644
index 0000000..995cf3c
--- /dev/null
+++ b/sensors/2.0/multihal/service.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#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>
+#include <utils/StrongPointer.h>
+#include "HalProxy.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::hardware::sensors::V2_0::ISensors;
+using android::hardware::sensors::V2_0::implementation::HalProxy;
+
+int main(int /* argc */, char** /* argv */) {
+    configureRpcThreadpool(1, true);
+
+    android::sp<ISensors> halProxy = new HalProxy();
+    if (halProxy->registerAsService() != ::android::OK) {
+        ALOGE("Failed to register Sensors HAL instance");
+        return -1;
+    }
+
+    joinRpcThreadpool();
+    return 1;  // joinRpcThreadpool shouldn't exit
+}