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
This commit is contained in:
Peng Xu 2017-01-20 14:24:39 -08:00
parent 3623fbace2
commit 18082bd61f
10 changed files with 627 additions and 3 deletions

View file

@ -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
# ************************************************

View file

@ -33,11 +33,15 @@ include $(CLEAR_VARS)
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 := sensors.dynamic_sensor_hal
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_SRC_FILES := \
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
liblog \
liblog
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)

View file

@ -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

View file

@ -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 @@ class BaseDynamicSensorDaemon : public RefBase {
public:
BaseDynamicSensorDaemon(DynamicSensorManager& manager) : mManager(manager) {}
virtual ~BaseDynamicSensorDaemon() = default;
virtual bool onConnectionChange(const std::string &deviceKey, bool connected);
protected:
virtual BaseSensorObject * createSensor(const std::string &deviceKey) = 0;
DynamicSensorManager &mManager;
std::unordered_map<std::string, sp<BaseSensorObject> > mDevices;
};
} // namespace SensorHalExt

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -16,6 +16,7 @@
#include "BaseDynamicSensorDaemon.h"
#include "BaseSensorObject.h"
#include "DummyDynamicAccelDaemon.h"
#include "DynamicSensorManager.h"
#include <utils/Log.h>
@ -29,6 +30,7 @@ namespace SensorHalExt {
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;
}

View file

@ -75,7 +75,6 @@ private:
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;