Dynamic sensor manager -- implementation of basic sensor daemon

Library to handle dynamic sensor connection. There are two way to use
this: as hal extension or standalone hal module.

In hal extension mode: add libdynamic_sensor_ext in dependency of hal,
instantiate DynamicSensorManager with appropriate parameters. Then
for all sensor requests, if the handle is owned by dynamic sensor
manager, forward the request.

In standalone mode, add sensor.dynamic_sensor_hal into device make
file. Usually, this also means multihal is necessary. Add
sensor.dynamic_sensor_hal into multihal configuration file.

A dummy sensor module is included for testing.

Test: tested with cts dynamics sensor related test and demo app.
      also verified sensor basic operation with sensor logger.

Change-Id: I16612935fc21b06c173aca875401ece37c6bde01
diff --git a/CleanSpec.mk b/CleanSpec.mk
index b84e1b6..1c64bfc 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -47,3 +47,13 @@
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
+
+$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib/libdynamic_sensor_ext.so)
+$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib64/libdynamic_sensor_ext.so)
+$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib/hw/sensors.dynamic_sensor_hal.so)
+$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib64/hw/sensors.dynamic_sensor_hal.so)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
diff --git a/modules/sensors/dynamic_sensor/Android.mk b/modules/sensors/dynamic_sensor/Android.mk
index 07bc5e8..38a658a 100644
--- a/modules/sensors/dynamic_sensor/Android.mk
+++ b/modules/sensors/dynamic_sensor/Android.mk
@@ -33,11 +33,15 @@
 LOCAL_MODULE := libdynamic_sensor_ext
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE_OWNER := google
+LOCAL_PROPRIETARY_MODULE := true
 
 LOCAL_CFLAGS += $(COMMON_CFLAGS) -DLOG_TAG=\"DynamicSensorExt\"
 
 LOCAL_SRC_FILES := \
+    BaseDynamicSensorDaemon.cpp \
     BaseSensorObject.cpp \
+    ConnectionDetector.cpp \
+    DummyDynamicAccelDaemon.cpp \
     DynamicSensorManager.cpp \
     RingBuffer.cpp
 
@@ -58,11 +62,15 @@
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE_OWNER := google
+LOCAL_PROPRIETARY_MODULE := true
 
 LOCAL_CFLAGS += $(COMMON_CFLAGS) -DLOG_TAG=\"DynamicSensorHal\"
 
 LOCAL_SRC_FILES := \
+    BaseDynamicSensorDaemon.cpp \
     BaseSensorObject.cpp \
+    ConnectionDetector.cpp \
+    DummyDynamicAccelDaemon.cpp \
     DynamicSensorManager.cpp \
     RingBuffer.cpp \
     sensors.cpp
@@ -70,7 +78,7 @@
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
     libutils \
-    liblog \
+    liblog
 
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
 
diff --git a/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.cpp b/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.cpp
new file mode 100644
index 0000000..f5f642c
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 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 "BaseDynamicSensorDaemon.h"
+#include "DynamicSensorManager.h"
+#include "utils/Log.h"
+
+namespace android {
+namespace SensorHalExt {
+
+bool BaseDynamicSensorDaemon::onConnectionChange(const std::string &deviceKey, bool connected) {
+    bool ret = false;
+    auto i = mDevices.find(deviceKey);
+    if (connected) {
+        if (i == mDevices.end()) {
+            ALOGV("device %s is connected", deviceKey.c_str());
+            BaseSensorObject* s = createSensor(deviceKey);
+            if (s) {
+                mDevices.emplace(deviceKey, sp<BaseSensorObject>(s));
+                mManager.registerSensor(s);
+                ALOGV("device %s is registered", deviceKey.c_str());
+                ret = true;
+            }
+        } else {
+            ALOGD("device %s already added and is connected again, ignore", deviceKey.c_str());
+        }
+    } else {
+        ALOGV("device %s is disconnected", deviceKey.c_str());
+        if (i != mDevices.end()) {
+            mManager.unregisterSensor(i->second.get());
+            mDevices.erase(i);
+            ALOGV("device %s is unregistered", deviceKey.c_str());
+            ret = true;
+        } else {
+            ALOGD("device not found in registry");
+        }
+    }
+
+    return ret;
+}
+} // namespace SensorHalExt
+} // namespace android
+
diff --git a/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.h b/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.h
index 29cf7a4..b698b7d 100644
--- a/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.h
+++ b/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.h
@@ -17,7 +17,11 @@
 #ifndef ANDROID_SENSORHAL_EXT_BASE_DYNAMIC_SENSOR_DAEMON_H
 #define ANDROID_SENSORHAL_EXT_BASE_DYNAMIC_SENSOR_DAEMON_H
 
+#include "BaseSensorObject.h"
+
 #include <utils/RefBase.h>
+#include <string>
+#include <unordered_map>
 
 namespace android {
 namespace SensorHalExt {
@@ -28,8 +32,13 @@
 public:
     BaseDynamicSensorDaemon(DynamicSensorManager& manager) : mManager(manager) {}
     virtual ~BaseDynamicSensorDaemon() = default;
+
+    virtual bool onConnectionChange(const std::string &deviceKey, bool connected);
 protected:
-    DynamicSensorManager& mManager;
+    virtual BaseSensorObject * createSensor(const std::string &deviceKey) = 0;
+
+    DynamicSensorManager &mManager;
+    std::unordered_map<std::string, sp<BaseSensorObject> > mDevices;
 };
 
 } // namespace SensorHalExt
diff --git a/modules/sensors/dynamic_sensor/ConnectionDetector.cpp b/modules/sensors/dynamic_sensor/ConnectionDetector.cpp
new file mode 100644
index 0000000..6099493
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/ConnectionDetector.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2017 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 "ConnectionDetector.h"
+
+#include <utils/Log.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <sys/inotify.h>
+#include <sys/socket.h>
+
+#include <sstream>
+
+namespace android {
+namespace SensorHalExt {
+
+// SocketConnectionDetector functions
+SocketConnectionDetector::SocketConnectionDetector(BaseDynamicSensorDaemon *d, int port)
+        : ConnectionDetector(d) {
+    // initialize socket that accept connection to localhost:port
+    mListenFd = ::socket(AF_INET, SOCK_STREAM, 0);
+    if (mListenFd < 0) {
+        ALOGE("Cannot open socket");
+        return;
+    }
+
+    struct sockaddr_in serverAddress = {
+        .sin_family = AF_INET,
+        .sin_port = htons(port),
+        .sin_addr = {
+            .s_addr = htonl(INADDR_LOOPBACK)
+        }
+    };
+
+    ::bind(mListenFd, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
+    if (::listen(mListenFd, 0) != NO_ERROR) {
+        ALOGE("Cannot listen to port %d", port);
+        mListenFd = -1;
+        return;
+    }
+
+    std::ostringstream s;
+    s << "socket:" << port;
+    mDevice = s.str();
+
+    run("ddad_socket");
+}
+
+SocketConnectionDetector::~SocketConnectionDetector() {
+    if (mListenFd >= 0) {
+        requestExitAndWait();
+    }
+}
+
+int SocketConnectionDetector::waitForConnection() {
+    return ::accept(mListenFd, nullptr, nullptr);
+}
+
+void SocketConnectionDetector::waitForDisconnection(int connFd) {
+    char buffer[16];
+    while (::read(connFd, buffer, sizeof(buffer)) > 0) {
+        // discard data but response something to denote thread alive
+        ::write(connFd, ".", 1);
+    }
+    // read failure means disconnection
+    ::close(connFd);
+}
+
+bool SocketConnectionDetector::threadLoop() {
+    while (!Thread::exitPending()) {
+        // block waiting for connection
+        int connFd = waitForConnection();
+
+        if (connFd < 0) {
+            break;
+        }
+
+        ALOGV("Received connection, register dynamic accel sensor");
+        mDaemon->onConnectionChange(mDevice, true);
+
+        waitForDisconnection(connFd);
+        ALOGV("Connection break, unregister dynamic accel sensor");
+        mDaemon->onConnectionChange(mDevice, false);
+    }
+    mDaemon->onConnectionChange(mDevice, false);
+    ALOGD("SocketConnectionDetector thread exited");
+    return false;
+}
+
+// FileConnectionDetector functions
+FileConnectionDetector::FileConnectionDetector (
+        BaseDynamicSensorDaemon *d, const std::string &path, const std::string &regex)
+            : ConnectionDetector(d), mPath(path), mRegex(regex) {
+    mInotifyFd = ::inotify_init1(IN_NONBLOCK);
+    if (mInotifyFd < 0) {
+        ALOGE("Cannot init inotify");
+        return;
+    }
+
+    int wd = ::inotify_add_watch(mInotifyFd, path.c_str(), IN_CREATE | IN_DELETE | IN_MOVED_FROM);
+    if (wd < 0) {
+        ::close(mInotifyFd);
+        mInotifyFd = -1;
+        ALOGE("Cannot setup watch on dir %s", path.c_str());
+        return;
+    }
+
+    mPollFd.fd = wd;
+    mPollFd.events = POLLIN;
+
+    run("ddad_file");
+}
+
+FileConnectionDetector::~FileConnectionDetector() {
+    if (mInotifyFd) {
+        requestExitAndWait();
+        ::close(mInotifyFd);
+    }
+}
+
+bool FileConnectionDetector::matches(const std::string &name) const {
+    return std::regex_match(name, mRegex);
+}
+
+std::string FileConnectionDetector::getFullName(const std::string name) const {
+    return mPath + name;
+}
+
+void FileConnectionDetector::processExistingFiles() const {
+    auto dirp = ::opendir(mPath.c_str());
+    struct dirent *dp;
+    while ((dp = ::readdir(dirp)) != NULL) {
+        const std::string name(dp->d_name);
+        if (matches(name)) {
+            mDaemon->onConnectionChange(getFullName(name), true /*connected*/);
+        }
+    }
+    ::closedir(dirp);
+}
+
+bool FileConnectionDetector::threadLoop() {
+    struct {
+        struct inotify_event e;
+        uint8_t padding[NAME_MAX + 1];
+    } ev;
+
+    processExistingFiles();
+
+    while (!Thread::exitPending()) {
+        int pollNum = ::poll(&mPollFd, 1, -1);
+        if (pollNum == -1) {
+           if (errno == EINTR)
+               continue;
+           ALOGE("inotify poll error: %s", ::strerror(errno));
+        }
+
+        if (pollNum > 0) {
+            if (! (mPollFd.revents & POLLIN)) {
+                continue;
+            }
+
+            /* Inotify events are available */
+            while (true) {
+                /* Read some events. */
+                ssize_t len = ::read(mInotifyFd, &ev, sizeof ev);
+                if (len == -1 && errno != EAGAIN) {
+                    ALOGE("read error: %s", ::strerror(errno));
+                    requestExit();
+                    break;
+                }
+
+                /* If the nonblocking read() found no events to read, then
+                   it returns -1 with errno set to EAGAIN. In that case,
+                   we exit the loop. */
+                if (len <= 0) {
+                    break;
+                }
+
+                if (ev.e.len && !(ev.e.mask & IN_ISDIR)) {
+                    const std::string name(ev.e.name);
+                    ALOGV("device %s state changed", name.c_str());
+                    if (matches(name)) {
+                        if (ev.e.mask & IN_CREATE) {
+                            mDaemon->onConnectionChange(getFullName(name), true /* connected*/);
+                        }
+
+                        if (ev.e.mask & IN_DELETE || ev.e.mask & IN_MOVED_FROM) {
+                            mDaemon->onConnectionChange(getFullName(name), false /* connected*/);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    ALOGD("FileConnectionDetection thread exited");
+    return false;
+}
+} // namespace SensorHalExt
+} // namespace android
diff --git a/modules/sensors/dynamic_sensor/ConnectionDetector.h b/modules/sensors/dynamic_sensor/ConnectionDetector.h
new file mode 100644
index 0000000..712d832
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/ConnectionDetector.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ANDROID_SENSORHAL_EXT_CONNECTION_DETECTOR_H
+#define ANDROID_SENSORHAL_EXT_CONNECTION_DETECTOR_H
+
+#include "BaseDynamicSensorDaemon.h"
+#include <utils/Thread.h>
+
+#include <regex>
+
+#include <poll.h>
+
+namespace android {
+namespace SensorHalExt {
+
+// Abstraction of connection detector: an entity that calls
+// BaseDynamicSensorDaemon::onConnectionChange() when necessary.
+class ConnectionDetector : virtual public RefBase {
+public:
+    ConnectionDetector(BaseDynamicSensorDaemon *d) : mDaemon(d) { }
+    virtual ~ConnectionDetector() = default;
+protected:
+    BaseDynamicSensorDaemon* mDaemon;
+};
+
+// Open a socket that listen to localhost:port and notify sensor daemon of connection and
+// disconnection event when socket is connected or disconnected, respectively. Only one concurrent
+// client is accepted.
+class SocketConnectionDetector : public ConnectionDetector, public Thread {
+public:
+    SocketConnectionDetector(BaseDynamicSensorDaemon *d, int port);
+    virtual ~SocketConnectionDetector();
+private:
+    // implement virtual of Thread
+    virtual bool threadLoop();
+    int waitForConnection();
+    static void waitForDisconnection(int connFd);
+
+    int mListenFd;
+    std::string mDevice;
+};
+
+// Detect file change under path and notify sensor daemon of connection and disconnection event when
+// file is created in or removed from the directory, respectively.
+class FileConnectionDetector : public ConnectionDetector, public Thread {
+public:
+    FileConnectionDetector(
+            BaseDynamicSensorDaemon *d, const std::string &path, const std::string &regex);
+    virtual ~FileConnectionDetector();
+private:
+    // implement virtual of Thread
+    virtual bool threadLoop();
+
+    bool matches(const std::string &name) const;
+    void processExistingFiles() const;
+    std::string getFullName(const std::string name) const;
+
+    std::string mPath;
+    std::regex mRegex;
+    int mInotifyFd;
+    struct pollfd mPollFd;
+};
+
+} // namespace SensorHalExt
+} // namespace android
+
+#endif // ANDROID_SENSORHAL_EXT_DYNAMIC_SENSOR_DAEMON_H
diff --git a/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.cpp b/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.cpp
new file mode 100644
index 0000000..1977967
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2017 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 "BaseSensorObject.h"
+#include "ConnectionDetector.h"
+#include "DummyDynamicAccelDaemon.h"
+#include "DynamicSensorManager.h"
+
+#include <cutils/properties.h>
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
+#include <utils/misc.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <algorithm>            //std::max
+
+#define SYSPROP_PREFIX                  "dynamic_sensor.dummy"
+#define FILE_NAME_BASE                  "dummy_accel_file"
+#define FILE_NAME_REGEX                 ("^" FILE_NAME_BASE "[0-9]$")
+
+namespace android {
+namespace SensorHalExt {
+
+DummyDynamicAccelDaemon::DummyDynamicAccelDaemon(DynamicSensorManager& manager)
+        : BaseDynamicSensorDaemon(manager) {
+    char property[PROPERTY_VALUE_MAX+1];
+
+    property_get(SYSPROP_PREFIX ".file", property, "");
+    if (strcmp(property, "") != 0) {
+        mFileDetector = new FileConnectionDetector(
+                this, std::string(property), std::string(FILE_NAME_REGEX));
+    }
+
+    property_get(SYSPROP_PREFIX ".socket", property, "");
+    if (strcmp(property, "") != 0) {
+        mSocketDetector = new SocketConnectionDetector(this, atoi(property));
+    }
+}
+
+BaseSensorObject * DummyDynamicAccelDaemon::createSensor(const std::string &deviceKey) {
+    if (deviceKey.compare(0, 1, "/") == 0) {
+        // file detector result, deviceKey is file absolute path
+        size_t start = std::max(static_cast<size_t>(0),
+                                deviceKey.length() - (::strlen(FILE_NAME_BASE) + 1));
+        return new DummySensor(deviceKey.substr(start));
+
+    } else if (deviceKey.compare(0, ::strlen("socket:"), "socket:") == 0) {
+        return new DummySensor(deviceKey);
+    } else {
+        // unknown deviceKey
+        return nullptr;
+    }
+}
+
+DummyDynamicAccelDaemon::DummySensor::DummySensor(const std::string &name) : mRunState(false) {
+    mSensorName = "Dummy Accel - " + name;
+    mSensor = (struct sensor_t) {
+        mSensorName.c_str(),
+        "DemoSense, Inc.",
+        1,                                         // version
+        -1,                                        // handle, dummy number here
+        SENSOR_TYPE_ACCELEROMETER,
+        9.8 * 8.0f,                                // maxRange
+        9.8 * 8.0f / 32768.0f,                     // resolution
+        0.5f,                                      // power
+        (int32_t)(1.0E6f / 50),                    // minDelay
+        0,                                         // fifoReservedEventCount
+        0,                                         // fifoMaxEventCount
+        SENSOR_STRING_TYPE_ACCELEROMETER,
+        "",                                        // requiredPermission
+        (long)(1.0E6f / 50),                       // maxDelay
+        SENSOR_FLAG_CONTINUOUS_MODE,
+        { NULL, NULL }
+    };
+    mRunLock.lock();
+    run("DummySensor");
+}
+
+DummyDynamicAccelDaemon::DummySensor::~DummySensor() {
+    requestExitAndWait();
+    // unlock mRunLock so thread can be unblocked
+    mRunLock.unlock();
+}
+
+const sensor_t* DummyDynamicAccelDaemon::DummySensor::getSensor() const {
+    return &mSensor;
+}
+
+void DummyDynamicAccelDaemon::DummySensor::getUuid(uint8_t* uuid) const {
+    // at maximum, there will be always one instance, so we can hardcode
+    size_t hash = std::hash<std::string>()(mSensorName);
+    memset(uuid, 'x', 16);
+    memcpy(uuid, &hash, sizeof(hash));
+}
+
+int DummyDynamicAccelDaemon::DummySensor::enable(bool enable) {
+    std::lock_guard<std::mutex> lk(mLock);
+    if (mRunState != enable) {
+        if (enable) {
+            mRunLock.unlock();
+        } else {
+            mRunLock.lock();
+        }
+        mRunState = enable;
+    }
+    return 0;
+}
+
+int DummyDynamicAccelDaemon::DummySensor::batch(nsecs_t, nsecs_t) {
+    return 0;
+}
+
+void DummyDynamicAccelDaemon::DummySensor::waitUntilNextSample() {
+    // block when disabled (mRunLock locked)
+    mRunLock.lock();
+    mRunLock.unlock();
+
+    if (!Thread::exitPending()) {
+        // sleep 20 ms (50Hz)
+        usleep(20000);
+    }
+}
+
+bool DummyDynamicAccelDaemon::DummySensor::threadLoop() {
+    // designated intialization will leave the unspecified fields zeroed
+    sensors_event_t event = {
+        .version = sizeof(event),
+        .sensor = -1,
+        .type = SENSOR_TYPE_ACCELEROMETER,
+    };
+
+    int64_t startTimeNs = elapsedRealtimeNano();
+
+    ALOGI("Dynamic Dummy Accel started for sensor %s", mSensorName.c_str());
+    while (!Thread::exitPending()) {
+        waitUntilNextSample();
+
+        if (Thread::exitPending()) {
+            break;
+        }
+        int64_t nowTimeNs = elapsedRealtimeNano();
+        float t = (nowTimeNs - startTimeNs) / 1e9f;
+
+        event.data[0] = 2 * ::sin(3 * M_PI * t);
+        event.data[1] = 3 * ::cos(3 * M_PI * t);
+        event.data[2] = 1.5 * ::sin(6 * M_PI * t);
+        event.timestamp = nowTimeNs;
+        generateEvent(event);
+    }
+
+    ALOGI("Dynamic Dummy Accel thread ended for sensor %s", mSensorName.c_str());
+    return false;
+}
+
+} // namespace SensorHalExt
+} // namespace android
+
diff --git a/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.h b/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.h
new file mode 100644
index 0000000..0bffbee
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ANDROID_SENSORHAL_EXT_DUMMY_DYNAMIC_ACCEL_DAEMON_H
+#define ANDROID_SENSORHAL_EXT_DUMMY_DYNAMIC_ACCEL_DAEMON_H
+
+#include "BaseDynamicSensorDaemon.h"
+#include "BaseSensorObject.h"
+
+#include <hardware/sensors.h>
+#include <utils/Thread.h>
+#include <mutex>
+#include <unordered_set>
+
+namespace android {
+namespace SensorHalExt {
+
+class ConnectionDetector;
+
+/**
+ * This daemon simulates dynamic sensor connection without the need of actually connect peripheral
+ * to Android. It is for debugging and testing. It can handle one concurrent connections at maximum.
+ */
+class DummyDynamicAccelDaemon : public BaseDynamicSensorDaemon {
+public:
+    DummyDynamicAccelDaemon(DynamicSensorManager& manager);
+    virtual ~DummyDynamicAccelDaemon() = default;
+private:
+    class DummySensor : public BaseSensorObject, public Thread {
+    public:
+        DummySensor(const std::string &name);
+        ~DummySensor();
+        virtual const sensor_t* getSensor() const;
+        virtual void getUuid(uint8_t* uuid) const;
+        virtual int enable(bool enable);
+        virtual int batch(nsecs_t sample_period, nsecs_t batch_period);
+    private:
+        // implement Thread function
+        virtual bool threadLoop() override;
+
+        void waitUntilNextSample();
+
+        sensor_t mSensor;
+        std::string mSensorName;
+
+        std::mutex mLock;
+        std::mutex mRunLock;
+        bool mRunState;
+    };
+    // implement BaseDynamicSensorDaemon function
+    BaseSensorObject * createSensor(const std::string &deviceKey) override;
+
+    sp<ConnectionDetector> mFileDetector;
+    sp<ConnectionDetector> mSocketDetector;
+};
+} // namespace SensorHalExt
+} // namespace android
+
+#endif // ANDROID_SENSORHAL_EXT_DYNAMIC_SENSOR_DAEMON_H
+
diff --git a/modules/sensors/dynamic_sensor/DynamicSensorManager.cpp b/modules/sensors/dynamic_sensor/DynamicSensorManager.cpp
index d33650c..eb4903d 100644
--- a/modules/sensors/dynamic_sensor/DynamicSensorManager.cpp
+++ b/modules/sensors/dynamic_sensor/DynamicSensorManager.cpp
@@ -16,6 +16,7 @@
 
 #include "BaseDynamicSensorDaemon.h"
 #include "BaseSensorObject.h"
+#include "DummyDynamicAccelDaemon.h"
 #include "DynamicSensorManager.h"
 
 #include <utils/Log.h>
@@ -29,6 +30,7 @@
 DynamicSensorManager* DynamicSensorManager::createInstance(
         int handleBase, int handleCount, SensorEventCallback *callback) {
     auto m = new DynamicSensorManager(handleBase, handleBase + handleCount - 1, callback);
+    m->mDaemonVector.push_back(new DummyDynamicAccelDaemon(*m));
     return m;
 }
 
diff --git a/modules/sensors/dynamic_sensor/sensors.h b/modules/sensors/dynamic_sensor/sensors.h
index be42b67..2fd8708 100644
--- a/modules/sensors/dynamic_sensor/sensors.h
+++ b/modules/sensors/dynamic_sensor/sensors.h
@@ -75,7 +75,6 @@
 
     static int FlushWrapper(struct sensors_poll_device_1 *dev, int handle);
 
-    // default ~16 million handles for dynamic sensor use, can be overriden by system property
     static constexpr int32_t kDynamicHandleBase = 0x10000;
     static constexpr int32_t kDynamicHandleEnd = 0x1000000;
     static constexpr int32_t kMaxDynamicHandleCount = kDynamicHandleEnd - kDynamicHandleBase;