Merge "Enable data injection mode in SensorService."

This commit is contained in:
Aravind Akella 2015-04-25 01:40:34 +00:00 committed by Android (Google) Code Review
commit cc60dc2128
10 changed files with 239 additions and 50 deletions

View File

@ -38,7 +38,9 @@ public:
DECLARE_META_INTERFACE(SensorServer);
virtual Vector<Sensor> getSensorList() = 0;
virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName) = 0;
virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
int mode) = 0;
virtual status_t enableDataInjection(int enable) = 0;
};
// ----------------------------------------------------------------------------

View File

@ -77,6 +77,8 @@ public:
status_t flush() const;
// Send an ack for every wake_up sensor event that is set to WAKE_UP_SENSOR_EVENT_NEEDS_ACK.
void sendAck(const ASensorEvent* events, int count);
status_t injectSensorEvent(const ASensorEvent& event);
private:
sp<Looper> getLooper() const;
sp<ISensorEventConnection> mSensorEventConnection;

View File

@ -53,7 +53,8 @@ public:
ssize_t getSensorList(Sensor const* const** list) const;
Sensor const* getDefaultSensor(int type);
sp<SensorEventQueue> createEventQueue(String8 packageName = String8(""));
sp<SensorEventQueue> createEventQueue(String8 packageName = String8(""), int mode = 0);
ssize_t enableDataInjection(bool enable);
private:
// DeathRecipient interface

View File

@ -35,6 +35,7 @@ namespace android {
enum {
GET_SENSOR_LIST = IBinder::FIRST_CALL_TRANSACTION,
CREATE_SENSOR_EVENT_CONNECTION,
ENABLE_DATA_INJECTION
};
class BpSensorServer : public BpInterface<ISensorServer>
@ -63,14 +64,24 @@ public:
return v;
}
virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName)
virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
int mode)
{
Parcel data, reply;
data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
data.writeString8(packageName);
data.writeInt32(mode);
remote()->transact(CREATE_SENSOR_EVENT_CONNECTION, data, &reply);
return interface_cast<ISensorEventConnection>(reply.readStrongBinder());
}
virtual status_t enableDataInjection(int enable) {
Parcel data, reply;
data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
data.writeInt32(enable);
remote()->transact(ENABLE_DATA_INJECTION, data, &reply);
return reply.readInt32();
}
};
// Out-of-line virtual method definition to trigger vtable emission in this
@ -98,10 +109,18 @@ status_t BnSensorServer::onTransact(
case CREATE_SENSOR_EVENT_CONNECTION: {
CHECK_INTERFACE(ISensorServer, data, reply);
String8 packageName = data.readString8();
sp<ISensorEventConnection> connection(createSensorEventConnection(packageName));
int32_t mode = data.readInt32();
sp<ISensorEventConnection> connection(createSensorEventConnection(packageName, mode));
reply->writeStrongBinder(IInterface::asBinder(connection));
return NO_ERROR;
}
case ENABLE_DATA_INJECTION: {
CHECK_INTERFACE(ISensorServer, data, reply);
int32_t enable = data.readInt32();
status_t ret = enableDataInjection(enable);
reply->writeInt32(static_cast<int32_t>(ret));
return NO_ERROR;
}
}
return BBinder::onTransact(code, data, reply, flags);
}

View File

@ -149,6 +149,16 @@ status_t SensorEventQueue::setEventRate(Sensor const* sensor, nsecs_t ns) const
return mSensorEventConnection->setEventRate(sensor->getHandle(), ns);
}
status_t SensorEventQueue::injectSensorEvent(const ASensorEvent& event) {
// Blocking call.
ssize_t size = ::send(mSensorChannel->getFd(), &event, sizeof(event), MSG_NOSIGNAL);
if (size < 0) {
ALOGE("injectSensorEvent failure %zd %d", size, mSensorChannel->getFd());
return size;
}
return NO_ERROR;
}
void SensorEventQueue::sendAck(const ASensorEvent* events, int count) {
for (int i = 0; i < count; ++i) {
if (events[i].flags & WAKE_UP_SENSOR_EVENT_NEEDS_ACK) {

View File

@ -100,8 +100,6 @@ status_t SensorManager::assertStateLocked() const {
return NO_ERROR;
}
ssize_t SensorManager::getSensorList(Sensor const* const** list) const
{
Mutex::Autolock _l(mLock);
@ -139,18 +137,17 @@ Sensor const* SensorManager::getDefaultSensor(int type)
return NULL;
}
sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName)
{
sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName, int mode) {
sp<SensorEventQueue> queue;
Mutex::Autolock _l(mLock);
while (assertStateLocked() == NO_ERROR) {
sp<ISensorEventConnection> connection =
mSensorServer->createSensorEventConnection(packageName);
mSensorServer->createSensorEventConnection(packageName, mode);
if (connection == NULL) {
// SensorService just died.
ALOGE("createEventQueue: connection is NULL. SensorService died.");
continue;
// SensorService just died or the app doesn't have required permissions.
ALOGE("createEventQueue: connection is NULL.");
return NULL;
}
queue = new SensorEventQueue(connection);
break;
@ -158,5 +155,13 @@ sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName)
return queue;
}
status_t SensorManager::enableDataInjection(bool enable) {
Mutex::Autolock _l(mLock);
if (assertStateLocked() == NO_ERROR) {
return mSensorServer->enableDataInjection(enable);
}
return INVALID_OPERATION;
}
// ----------------------------------------------------------------------------
}; // namespace android

View File

@ -367,7 +367,7 @@ void SensorDevice::enableAllSensors() {
void SensorDevice::disableAllSensors() {
Mutex::Autolock _l(mLock);
for (size_t i = 0; i< mActivationCount.size(); ++i) {
for (size_t i = 0; i< mActivationCount.size(); ++i) {
const Info& info = mActivationCount.valueAt(i);
// Check if this sensor has been activated previously and disable it.
if (info.batchParams.size() > 0) {
@ -386,6 +386,27 @@ void SensorDevice::disableAllSensors() {
}
}
status_t SensorDevice::injectSensorData(const sensors_event_t *injected_sensor_event, size_t count) {
ALOGD_IF(DEBUG_CONNECTIONS,
"sensor_event handle=%d ts=%lld data=%.2f, %.2f, %.2f %.2f %.2f %.2f",
injected_sensor_event->sensor,
injected_sensor_event->timestamp, injected_sensor_event->data[0],
injected_sensor_event->data[1], injected_sensor_event->data[2],
injected_sensor_event->data[3], injected_sensor_event->data[4],
injected_sensor_event->data[5]);
if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_4) {
return INVALID_OPERATION;
}
return mSensorDevice->inject_sensor_data(mSensorDevice, injected_sensor_event);
}
status_t SensorDevice::setMode(uint32_t mode) {
if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_4) {
return INVALID_OPERATION;
}
return mSensorModule->set_operation_mode(mode);
}
// ---------------------------------------------------------------------------
int SensorDevice::Info::numActiveClients() {

View File

@ -98,9 +98,11 @@ public:
// Call batch with timeout zero instead of calling setDelay() for newer devices.
status_t setDelay(void* ident, int handle, int64_t ns);
status_t flush(void* ident, int handle);
status_t setMode(uint32_t mode);
void disableAllSensors();
void enableAllSensors();
void autoDisable(void *ident, int handle);
status_t injectSensorData(const sensors_event_t *event, size_t count);
void dump(String8& result);
};

View File

@ -64,6 +64,9 @@ namespace android {
*/
const char* SensorService::WAKE_LOCK_NAME = "SensorService";
// Permissions.
static const String16 sDataInjectionPermission("android.permission.HARDWARE_TEST");
static const String16 sDump("android.permission.DUMP");
SensorService::SensorService()
: mInitCheck(NO_INIT), mSocketBufferSize(SOCKET_BUFFER_SIZE_NON_BATCHED),
@ -74,7 +77,6 @@ SensorService::SensorService()
void SensorService::onFirstRef()
{
ALOGD("nuSensorService starting...");
SensorDevice& dev(SensorDevice::getInstance());
if (dev.initCheck() == NO_ERROR) {
@ -190,7 +192,7 @@ void SensorService::onFirstRef()
mSensorEventBuffer = new sensors_event_t[minBufferSize];
mSensorEventScratch = new sensors_event_t[minBufferSize];
mMapFlushEventsToConnections = new SensorEventConnection const * [minBufferSize];
mMode = NORMAL;
mCurrentOperatingMode = NORMAL;
mAckReceiver = new SensorEventAckReceiver(this);
mAckReceiver->run("SensorEventAckReceiver", PRIORITY_URGENT_DISPLAY);
@ -229,8 +231,6 @@ SensorService::~SensorService()
delete mSensorMap.valueAt(i);
}
static const String16 sDump("android.permission.DUMP");
status_t SensorService::dump(int fd, const Vector<String16>& args)
{
String8 result;
@ -245,8 +245,8 @@ status_t SensorService::dump(int fd, const Vector<String16>& args)
}
Mutex::Autolock _l(mLock);
SensorDevice& dev(SensorDevice::getInstance());
if (args[0] == String16("restrict") && mMode == NORMAL) {
mMode = RESTRICTED;
if (args[0] == String16("restrict") && mCurrentOperatingMode == NORMAL) {
mCurrentOperatingMode = RESTRICTED;
dev.disableAllSensors();
// Clear all pending flush connections for all active sensors. If one of the active
// connections has called flush() and the underlying sensor has been disabled before a
@ -254,8 +254,8 @@ status_t SensorService::dump(int fd, const Vector<String16>& args)
for (size_t i=0 ; i< mActiveSensors.size(); ++i) {
mActiveSensors.valueAt(i)->clearAllPendingFlushConnections();
}
} else if (args[0] == String16("enable") && mMode == RESTRICTED) {
mMode = NORMAL;
} else if (args[0] == String16("enable") && mCurrentOperatingMode == RESTRICTED) {
mCurrentOperatingMode = NORMAL;
dev.enableAllSensors();
}
return status_t(NO_ERROR);
@ -363,7 +363,7 @@ status_t SensorService::dump(int fd, const Vector<String16>& args)
mSocketBufferSize/sizeof(sensors_event_t));
result.appendFormat("WakeLock Status: %s \n", mWakeLockAcquired ? "acquired" : "not held");
result.appendFormat("Mode :");
switch(mMode) {
switch(mCurrentOperatingMode) {
case NORMAL:
result.appendFormat(" NORMAL\n");
break;
@ -403,8 +403,9 @@ void SensorService::cleanupAutoDisabledSensorLocked(const sp<SensorEventConnecti
sensor->autoDisable(connection.get(), handle);
cleanupWithoutDisableLocked(connection, handle);
}
}
}
}
}
bool SensorService::threadLoop()
@ -685,13 +686,76 @@ Vector<Sensor> SensorService::getSensorList()
return accessibleSensorList;
}
sp<ISensorEventConnection> SensorService::createSensorEventConnection(const String8& packageName)
{
sp<ISensorEventConnection> SensorService::createSensorEventConnection(const String8& packageName,
int requestedMode) {
// Only 2 modes supported for a SensorEventConnection ... NORMAL and DATA_INJECTION.
if (requestedMode != NORMAL && requestedMode != DATA_INJECTION) {
return NULL;
}
// DATA_INJECTION mode needs to have the required permissions set.
if (requestedMode == DATA_INJECTION && !hasDataInjectionPermissions()) {
return NULL;
}
Mutex::Autolock _l(mLock);
uid_t uid = IPCThreadState::self()->getCallingUid();
sp<SensorEventConnection> result(new SensorEventConnection(this, uid, packageName));
sp<SensorEventConnection> result(new SensorEventConnection(this, uid, packageName,
requestedMode == DATA_INJECTION));
if (requestedMode == DATA_INJECTION) {
if (mActiveConnections.indexOf(result) < 0) {
mActiveConnections.add(result);
}
// Add the associated file descriptor to the Looper for polling whenever there is data to
// be injected.
result->updateLooperRegistration(mLooper);
}
return result;
}
status_t SensorService::enableDataInjection(int requestedMode) {
if (!hasDataInjectionPermissions()) {
return INVALID_OPERATION;
}
Mutex::Autolock _l(mLock);
ALOGD_IF(DEBUG_CONNECTIONS, "SensorService::enableDataInjection %d", requestedMode);
SensorDevice& dev(SensorDevice::getInstance());
status_t err(NO_ERROR);
if (requestedMode == DATA_INJECTION) {
if (mCurrentOperatingMode == NORMAL) {
dev.disableAllSensors();
err = dev.setMode(requestedMode);
if (err == NO_ERROR) {
mCurrentOperatingMode = DATA_INJECTION;
} else {
// Re-enable sensors.
dev.enableAllSensors();
}
} else if (mCurrentOperatingMode == DATA_INJECTION) {
// Already in DATA_INJECTION mode. Treat this as a no_op.
return NO_ERROR;
} else {
// Transition to data injection mode supported only from NORMAL mode.
return INVALID_OPERATION;
}
} else if (requestedMode == NORMAL && mCurrentOperatingMode != NORMAL) {
err = resetToNormalModeLocked();
}
return err;
}
status_t SensorService::resetToNormalMode() {
Mutex::Autolock _l(mLock);
return resetToNormalModeLocked();
}
status_t SensorService::resetToNormalModeLocked() {
SensorDevice& dev(SensorDevice::getInstance());
dev.enableAllSensors();
status_t err = dev.setMode(NORMAL);
mCurrentOperatingMode = NORMAL;
return err;
}
void SensorService::cleanupConnection(SensorEventConnection* c)
{
Mutex::Autolock _l(mLock);
@ -753,7 +817,7 @@ status_t SensorService::enable(const sp<SensorEventConnection>& connection,
}
Mutex::Autolock _l(mLock);
if (mMode == RESTRICTED && !isWhiteListedPackage(connection->getPackageName())) {
if (mCurrentOperatingMode == RESTRICTED && !isWhiteListedPackage(connection->getPackageName())) {
return INVALID_OPERATION;
}
@ -960,6 +1024,15 @@ bool SensorService::verifyCanAccessSensor(const Sensor& sensor, const char* oper
}
}
bool SensorService::hasDataInjectionPermissions() {
if (!PermissionCache::checkCallingPermission(sDataInjectionPermission)) {
ALOGE("Permission Denial trying to activate data injection without"
" the required permission");
return false;
}
return true;
}
void SensorService::checkWakeLockState() {
Mutex::Autolock _l(mLock);
checkWakeLockStateLocked();
@ -1071,9 +1144,10 @@ void SensorService::SensorRecord::clearAllPendingFlushConnections() {
// ---------------------------------------------------------------------------
SensorService::SensorEventConnection::SensorEventConnection(
const sp<SensorService>& service, uid_t uid, String8 packageName)
const sp<SensorService>& service, uid_t uid, String8 packageName, bool isDataInjectionMode)
: mService(service), mUid(uid), mWakeLockRefCount(0), mHasLooperCallbacks(false),
mDead(false), mEventCache(NULL), mCacheSize(0), mMaxCacheSize(0), mPackageName(packageName) {
mDead(false), mEventCache(NULL), mCacheSize(0), mMaxCacheSize(0), mPackageName(packageName),
mDataInjectionMode(isDataInjectionMode) {
mChannel = new BitTube(mService->mSocketBufferSize);
#if DEBUG_CONNECTIONS
mEventsReceived = mEventsSentFromCache = mEventsSent = 0;
@ -1105,6 +1179,7 @@ void SensorService::SensorEventConnection::resetWakeLockRefCount() {
void SensorService::SensorEventConnection::dump(String8& result) {
Mutex::Autolock _l(mConnectionLock);
result.appendFormat("Operating Mode: %s\n", mDataInjectionMode ? "DATA_INJECTION" : "NORMAL");
result.appendFormat("\t%s | WakeLockRefCount %d | uid %d | cache size %d | max cache size %d\n",
mPackageName.string(), mWakeLockRefCount, mUid, mCacheSize, mMaxCacheSize);
for (size_t i = 0; i < mSensorInfo.size(); ++i) {
@ -1190,7 +1265,8 @@ void SensorService::SensorEventConnection::updateLooperRegistration(const sp<Loo
void SensorService::SensorEventConnection::updateLooperRegistrationLocked(
const sp<Looper>& looper) {
bool isConnectionActive = mSensorInfo.size() > 0;
bool isConnectionActive = (mSensorInfo.size() > 0 && !mDataInjectionMode) ||
mDataInjectionMode;
// If all sensors are unregistered OR Looper has encountered an error, we
// can remove the Fd from the Looper if it has been previously added.
if (!isConnectionActive || mDead) {
@ -1204,6 +1280,7 @@ void SensorService::SensorEventConnection::updateLooperRegistrationLocked(
int looper_flags = 0;
if (mCacheSize > 0) looper_flags |= ALOOPER_EVENT_OUTPUT;
if (mDataInjectionMode) looper_flags |= ALOOPER_EVENT_INPUT;
for (size_t i = 0; i < mSensorInfo.size(); ++i) {
const int handle = mSensorInfo.keyAt(i);
if (mService->getSensorFromHandle(handle).isWakeUpSensor()) {
@ -1570,26 +1647,55 @@ int SensorService::SensorEventConnection::handleEvent(int fd, int events, void*
updateLooperRegistrationLocked(mService->getLooper());
}
mService->checkWakeLockState();
if (mDataInjectionMode) {
// If the Looper has encountered some error in data injection mode, reset SensorService
// back to normal mode.
mService->resetToNormalMode();
mDataInjectionMode = false;
}
return 1;
}
if (events & ALOOPER_EVENT_INPUT) {
uint32_t numAcks = 0;
ssize_t ret = ::recv(fd, &numAcks, sizeof(numAcks), MSG_DONTWAIT);
unsigned char buf[sizeof(sensors_event_t)];
ssize_t numBytesRead = ::recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
{
Mutex::Autolock _l(mConnectionLock);
// Sanity check to ensure there are no read errors in recv, numAcks is always
// within the range and not zero. If any of the above don't hold reset mWakeLockRefCount
// to zero.
if (ret != sizeof(numAcks) || numAcks > mWakeLockRefCount || numAcks == 0) {
ALOGE("Looper read error ret=%d numAcks=%d", ret, numAcks);
mWakeLockRefCount = 0;
} else {
mWakeLockRefCount -= numAcks;
}
if (numBytesRead == sizeof(sensors_event_t)) {
if (!mDataInjectionMode) {
ALOGE("Data injected in normal mode, dropping event"
"package=%s uid=%d", mPackageName.string(), mUid);
// Unregister call backs.
return 0;
}
SensorDevice& dev(SensorDevice::getInstance());
sensors_event_t sensor_event;
memset(&sensor_event, 0, sizeof(sensor_event));
memcpy(&sensor_event, buf, sizeof(sensors_event_t));
Sensor sensor = mService->getSensorFromHandle(sensor_event.sensor);
sensor_event.type = sensor.getType();
dev.injectSensorData(&sensor_event, 1);
#if DEBUG_CONNECTIONS
mTotalAcksReceived += numAcks;
++mEventsReceived;
#endif
} else if (numBytesRead == sizeof(uint32_t)) {
uint32_t numAcks = 0;
memcpy(&numAcks, buf, sizeof(numBytesRead));
// Sanity check to ensure there are no read errors in recv, numAcks is always
// within the range and not zero. If any of the above don't hold reset
// mWakeLockRefCount to zero.
if (numAcks > 0 && numAcks < mWakeLockRefCount) {
mWakeLockRefCount -= numAcks;
} else {
mWakeLockRefCount = 0;
}
#if DEBUG_CONNECTIONS
mTotalAcksReceived += numAcks;
#endif
} else {
// Read error, reset wakelock refcount.
mWakeLockRefCount = 0;
}
}
// Check if wakelock can be released by sensorservice. mConnectionLock needs to be released
// here as checkWakeLockState() will need it.

View File

@ -70,21 +70,33 @@ class SensorService :
// The regular operating mode where any application can register/unregister/call flush on
// sensors.
NORMAL = 0,
// This mode is only used for testing purposes. Not all HALs support this mode. In this
// mode, the HAL ignores the sensor data provided by physical sensors and accepts the data
// that is injected from the SensorService as if it were the real sensor data. This mode
// is primarily used for testing various algorithms like vendor provided SensorFusion,
// Step Counter and Step Detector etc. Typically in this mode, there will be a client
// (a SensorEventConnection) which will be injecting sensor data into the HAL. Normal apps
// can unregister and register for any sensor that supports injection. Registering to sensors
// that do not support injection will give an error.
// TODO(aakella) : Allow exactly one client to inject sensor data at a time.
DATA_INJECTION = 1,
// This mode is used only for testing sensors. Each sensor can be tested in isolation with
// the required sampling_rate and maxReportLatency parameters without having to think about
// the data rates requested by other applications. End user devices are always expected to be
// in NORMAL mode. When this mode is first activated, all active sensors from all connections
// are disabled. Calling flush() will return an error. In this mode, only the requests from
// selected apps whose package names are whitelisted are allowed (typically CTS apps). Only
// these apps can register/unregister/call flush() on sensors. If SensorService switches to
// these apps can register/unregister/call flush() on sensors. If SensorService switches to
// NORMAL mode again, all sensors that were previously registered to are activated with the
// corresponding paramaters if the application hasn't unregistered for sensors in the mean
// time.
// NOTE: Non whitelisted app whose sensors were previously deactivated may still receive
// events if a whitelisted app requests data from the same sensor.
RESTRICTED,
// TODO: This mode hasn't been implemented yet.
DATA_INJECTION
RESTRICTED = 2
// State Transitions supported.
// RESTRICTED <--- NORMAL ---> DATA_INJECTION
// ---> <---
};
static const char* WAKE_LOCK_NAME;
@ -100,7 +112,9 @@ class SensorService :
// ISensorServer interface
virtual Vector<Sensor> getSensorList();
virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName);
virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
int requestedMode);
virtual status_t enableDataInjection(int enable);
virtual status_t dump(int fd, const Vector<String16>& args);
class SensorEventConnection : public BnSensorEventConnection, public LooperCallback {
@ -177,6 +191,8 @@ class SensorService :
// mWakeLockRefCount is reset to zero. needsWakeLock method will always return false, if
// this flag is set.
bool mDead;
bool mDataInjectionMode;
struct FlushInfo {
// The number of flush complete events dropped for this sensor is stored here.
// They are sent separately before the next batch of events.
@ -191,14 +207,14 @@ class SensorService :
sensors_event_t *mEventCache;
int mCacheSize, mMaxCacheSize;
String8 mPackageName;
#if DEBUG_CONNECTIONS
int mEventsReceived, mEventsSent, mEventsSentFromCache;
int mTotalAcksNeeded, mTotalAcksReceived;
#endif
public:
SensorEventConnection(const sp<SensorService>& service, uid_t uid, String8 packageName);
SensorEventConnection(const sp<SensorService>& service, uid_t uid, String8 packageName,
bool isDataInjectionMode);
status_t sendEvents(sensors_event_t const* buffer, size_t count,
sensors_event_t* scratch,
@ -257,6 +273,7 @@ class SensorService :
sensors_event_t const* buffer, const int count);
static bool canAccessSensor(const Sensor& sensor);
static bool verifyCanAccessSensor(const Sensor& sensor, const char* operation);
static bool hasDataInjectionPermissions();
// SensorService acquires a partial wakelock for delivering events from wake up sensors. This
// method checks whether all the events from these wake up sensors have been delivered to the
// corresponding applications, if yes the wakelock is released.
@ -290,6 +307,10 @@ class SensorService :
// allowed.
bool isWhiteListedPackage(const String8& packageName);
// Reset the state of SensorService to NORMAL mode.
status_t resetToNormalMode();
status_t resetToNormalModeLocked();
// constants
Vector<Sensor> mSensorList;
Vector<Sensor> mUserSensorListDebug;
@ -311,7 +332,7 @@ class SensorService :
bool mWakeLockAcquired;
sensors_event_t *mSensorEventBuffer, *mSensorEventScratch;
SensorEventConnection const **mMapFlushEventsToConnections;
Mode mMode;
Mode mCurrentOperatingMode;
// The size of this vector is constant, only the items are mutable
KeyedVector<int32_t, sensors_event_t> mLastEventSeen;