437 lines
14 KiB
C++
437 lines
14 KiB
C++
/*
|
|
* Copyright (C) 2015 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 <cstdlib>
|
|
#include <inttypes.h>
|
|
|
|
#define LOG_TAG "ActivityRecognitionHAL"
|
|
#include <utils/Log.h>
|
|
|
|
#include <media/stagefright/foundation/ADebug.h>
|
|
|
|
#include "activity.h"
|
|
|
|
using namespace android;
|
|
|
|
static const int kVersionMajor = 1;
|
|
static const int kVersionMinor = 0;
|
|
|
|
// The maximum delta between events at which point their timestamps are to be
|
|
// considered equal.
|
|
static const int64_t kEventTimestampThresholdNanos = 100000000; // 100ms.
|
|
static const int64_t kMaxEventAgeNanos = 10000000000; // 10000ms.
|
|
static const useconds_t kFlushDelayMicros = 10000; // 10ms.
|
|
|
|
static const char *const kActivityList[] = {
|
|
ACTIVITY_TYPE_IN_VEHICLE,
|
|
ACTIVITY_TYPE_ON_BICYCLE,
|
|
ACTIVITY_TYPE_WALKING,
|
|
ACTIVITY_TYPE_RUNNING,
|
|
ACTIVITY_TYPE_STILL,
|
|
ACTIVITY_TYPE_TILTING
|
|
};
|
|
|
|
static const int kActivitySensorMap[ARRAY_SIZE(kActivityList)][2] = {
|
|
{ COMMS_SENSOR_ACTIVITY_IN_VEHICLE_START,
|
|
COMMS_SENSOR_ACTIVITY_IN_VEHICLE_STOP, },
|
|
{ COMMS_SENSOR_ACTIVITY_ON_BICYCLE_START,
|
|
COMMS_SENSOR_ACTIVITY_ON_BICYCLE_STOP, },
|
|
{ COMMS_SENSOR_ACTIVITY_WALKING_START,
|
|
COMMS_SENSOR_ACTIVITY_WALKING_STOP, },
|
|
{ COMMS_SENSOR_ACTIVITY_RUNNING_START,
|
|
COMMS_SENSOR_ACTIVITY_RUNNING_STOP, },
|
|
{ COMMS_SENSOR_ACTIVITY_STILL_START,
|
|
COMMS_SENSOR_ACTIVITY_STILL_STOP, },
|
|
{ COMMS_SENSOR_ACTIVITY_TILTING,
|
|
COMMS_SENSOR_ACTIVITY_TILTING, },
|
|
};
|
|
|
|
// The global ActivityContext singleton.
|
|
static ActivityContext *gActivityContext = NULL;
|
|
|
|
static int ActivityClose(struct hw_device_t *) {
|
|
ALOGI("close_activity");
|
|
delete gActivityContext;
|
|
gActivityContext = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static void RegisterActivityCallbackWrapper(
|
|
const struct activity_recognition_device *,
|
|
const activity_recognition_callback_procs_t *callback) {
|
|
gActivityContext->registerActivityCallback(callback);
|
|
}
|
|
|
|
static int EnableActivityEventWrapper(
|
|
const struct activity_recognition_device *,
|
|
uint32_t activity_handle,
|
|
uint32_t event_type,
|
|
int64_t max_batch_report_latency_ns) {
|
|
return gActivityContext->enableActivityEvent(activity_handle, event_type,
|
|
max_batch_report_latency_ns);
|
|
}
|
|
|
|
static int DisableActivityEventWrapper(
|
|
const struct activity_recognition_device *,
|
|
uint32_t activity_handle,
|
|
uint32_t event_type) {
|
|
return gActivityContext->disableActivityEvent(activity_handle, event_type);
|
|
}
|
|
|
|
static int FlushWrapper(const struct activity_recognition_device *) {
|
|
return gActivityContext->flush();
|
|
}
|
|
|
|
ActivityContext::ActivityContext(const struct hw_module_t *module)
|
|
: mHubConnection(HubConnection::getInstance()),
|
|
mCallback(NULL),
|
|
mNewestPublishedEventIndexIsKnown(false),
|
|
mNewestPublishedEventIndex(0),
|
|
mNewestPublishedTimestamp(0),
|
|
mOutstandingFlushEvents(0) {
|
|
memset(&device, 0, sizeof(device));
|
|
|
|
device.common.tag = HARDWARE_DEVICE_TAG;
|
|
device.common.version = ACTIVITY_RECOGNITION_API_VERSION_0_1;
|
|
device.common.module = const_cast<hw_module_t *>(module);
|
|
device.common.close = ActivityClose;
|
|
device.register_activity_callback = RegisterActivityCallbackWrapper;
|
|
device.enable_activity_event = EnableActivityEventWrapper;
|
|
device.disable_activity_event = DisableActivityEventWrapper;
|
|
device.flush = FlushWrapper;
|
|
|
|
if (getHubAlive()) {
|
|
mHubConnection->setActivityCallback(this);
|
|
|
|
// Reset the system to a known good state by disabling all transitions.
|
|
for (int i = COMMS_SENSOR_ACTIVITY_FIRST;
|
|
i <= COMMS_SENSOR_ACTIVITY_LAST; i++) {
|
|
mHubConnection->queueActivate(i, false /* enable */);
|
|
}
|
|
}
|
|
}
|
|
|
|
ActivityContext::~ActivityContext() {
|
|
mHubConnection->setActivityCallback(NULL);
|
|
}
|
|
|
|
/*
|
|
* Obtain the activity handle for a given activity sensor index.
|
|
*/
|
|
static int GetActivityHandleFromSensorIndex(int sensorIndex) {
|
|
int normalizedSensorIndex = sensorIndex - COMMS_SENSOR_ACTIVITY_FIRST;
|
|
return normalizedSensorIndex / 2;
|
|
}
|
|
|
|
/*
|
|
* Obtain the activity type for a given activity sensor index.
|
|
*/
|
|
static int GetActivityTypeFromSensorIndex(int sensorIndex) {
|
|
int normalizedSensorIndex = sensorIndex - COMMS_SENSOR_ACTIVITY_FIRST;
|
|
return (normalizedSensorIndex % 2) + 1;
|
|
}
|
|
|
|
void ActivityContext::PublishUnpublishedEvents() {
|
|
if (mUnpublishedEvents.empty()) {
|
|
return;
|
|
}
|
|
|
|
while (mUnpublishedEvents.size() > 0) {
|
|
bool eventWasPublished = false;
|
|
|
|
for (size_t i = 0; i < mUnpublishedEvents.size(); i++) {
|
|
const ActivityEvent *event = &mUnpublishedEvents[i];
|
|
if (event->eventIndex == (uint8_t)(mNewestPublishedEventIndex + 1)) {
|
|
PublishEvent(*event);
|
|
eventWasPublished = true;
|
|
mUnpublishedEvents.removeAt(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!eventWasPublished) {
|
|
ALOGD("Waiting on unpublished events");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ActivityContext::PublishEvent(const ActivityEvent& event) {
|
|
activity_event_t halEvent;
|
|
memset(&halEvent, 0, sizeof(halEvent));
|
|
|
|
int64_t timestampDelta = event.whenNs - mNewestPublishedTimestamp;
|
|
if (std::abs(timestampDelta) > kEventTimestampThresholdNanos) {
|
|
mNewestPublishedTimestamp = event.whenNs;
|
|
}
|
|
|
|
halEvent.activity = GetActivityHandleFromSensorIndex(event.sensorIndex);
|
|
halEvent.timestamp = mNewestPublishedTimestamp;
|
|
|
|
if (event.sensorIndex == COMMS_SENSOR_ACTIVITY_TILTING) {
|
|
ALOGD("Publishing tilt event (enter/exit)");
|
|
|
|
// Publish two events (enter/exit) for TILTING events.
|
|
halEvent.event_type = ACTIVITY_EVENT_ENTER;
|
|
(*mCallback->activity_callback)(mCallback, &halEvent, 1);
|
|
|
|
halEvent.event_type = ACTIVITY_EVENT_EXIT;
|
|
} else {
|
|
ALOGD("Publishing event - activity_handle: %d, event_type: %d"
|
|
", timestamp: %" PRIu64,
|
|
halEvent.activity, halEvent.event_type, halEvent.timestamp);
|
|
|
|
// Just a single event is required for all other activity types.
|
|
halEvent.event_type = GetActivityTypeFromSensorIndex(event.sensorIndex);
|
|
}
|
|
|
|
(*mCallback->activity_callback)(mCallback, &halEvent, 1);
|
|
mNewestPublishedEventIndex = event.eventIndex;
|
|
mNewestPublishedEventIndexIsKnown = true;
|
|
}
|
|
|
|
void ActivityContext::DiscardExpiredUnpublishedEvents(uint64_t whenNs) {
|
|
// Determine the current oldest buffered event.
|
|
uint64_t oldestEventTimestamp = UINT64_MAX;
|
|
for (size_t i = 0; i < mUnpublishedEvents.size(); i++) {
|
|
const ActivityEvent *event = &mUnpublishedEvents[i];
|
|
if (event->whenNs < oldestEventTimestamp) {
|
|
oldestEventTimestamp = event->whenNs;
|
|
}
|
|
}
|
|
|
|
// If the age of the oldest buffered event is too large an AR sample
|
|
// has been lost. When this happens all AR transitions are set to
|
|
// ACTIVITY_EVENT_EXIT and the event ordering logic is reset.
|
|
if (oldestEventTimestamp != UINT64_MAX
|
|
&& (whenNs - oldestEventTimestamp) > kMaxEventAgeNanos) {
|
|
ALOGD("Lost event detected, discarding buffered events");
|
|
|
|
// Publish stop events for all activity types except for TILTING.
|
|
for (uint32_t activity = 0;
|
|
activity < (ARRAY_SIZE(kActivityList) - 1); activity++) {
|
|
activity_event_t halEvent;
|
|
memset(&halEvent, 0, sizeof(halEvent));
|
|
|
|
halEvent.activity = activity;
|
|
halEvent.timestamp = oldestEventTimestamp;
|
|
halEvent.event_type = ACTIVITY_EVENT_EXIT;
|
|
(*mCallback->activity_callback)(mCallback, &halEvent, 1);
|
|
}
|
|
|
|
// Reset the event reordering logic.
|
|
OnSensorHubReset();
|
|
}
|
|
}
|
|
|
|
void ActivityContext::OnActivityEvent(int sensorIndex, uint8_t eventIndex,
|
|
uint64_t whenNs) {
|
|
ALOGD("OnActivityEvent sensorIndex = %d, eventIndex = %" PRIu8
|
|
", whenNs = %" PRIu64, sensorIndex, eventIndex, whenNs);
|
|
|
|
Mutex::Autolock autoLock(mCallbackLock);
|
|
if (!mCallback) {
|
|
return;
|
|
}
|
|
|
|
DiscardExpiredUnpublishedEvents(whenNs);
|
|
|
|
ActivityEvent event = {
|
|
.eventIndex = eventIndex,
|
|
.sensorIndex = sensorIndex,
|
|
.whenNs = whenNs,
|
|
};
|
|
|
|
if (!mNewestPublishedEventIndexIsKnown
|
|
|| eventIndex == (uint8_t)(mNewestPublishedEventIndex + 1)) {
|
|
PublishEvent(event);
|
|
PublishUnpublishedEvents();
|
|
} else {
|
|
ALOGD("OnActivityEvent out of order, pushing back");
|
|
mUnpublishedEvents.push(event);
|
|
}
|
|
}
|
|
|
|
void ActivityContext::OnFlush() {
|
|
// Once the number of outstanding flush events has reached zero, publish an
|
|
// event via the AR HAL.
|
|
Mutex::Autolock autoLock(mCallbackLock);
|
|
if (!mCallback) {
|
|
return;
|
|
}
|
|
|
|
// For each flush event from the sensor hub, decrement the counter of
|
|
// outstanding flushes.
|
|
mOutstandingFlushEvents--;
|
|
if (mOutstandingFlushEvents > 0) {
|
|
ALOGV("OnFlush with %d outstanding flush events", mOutstandingFlushEvents);
|
|
return;
|
|
} else if (mOutstandingFlushEvents < 0) {
|
|
// This can happen on app start.
|
|
ALOGD("more flush events received than requested");
|
|
mOutstandingFlushEvents = 0;
|
|
}
|
|
|
|
activity_event_t ev = {
|
|
.event_type = ACTIVITY_EVENT_FLUSH_COMPLETE,
|
|
.activity = 0,
|
|
.timestamp = 0ll,
|
|
};
|
|
|
|
(*mCallback->activity_callback)(mCallback, &ev, 1);
|
|
ALOGD("OnFlush published");
|
|
}
|
|
|
|
void ActivityContext::OnSensorHubReset() {
|
|
// Reset the unpublished event queue and clear the last known published
|
|
// event index.
|
|
mUnpublishedEvents.clear();
|
|
mNewestPublishedEventIndexIsKnown = false;
|
|
mOutstandingFlushEvents = 0;
|
|
mNewestPublishedTimestamp = 0;
|
|
}
|
|
|
|
void ActivityContext::registerActivityCallback(
|
|
const activity_recognition_callback_procs_t *callback) {
|
|
ALOGI("registerActivityCallback");
|
|
|
|
Mutex::Autolock autoLock(mCallbackLock);
|
|
mCallback = callback;
|
|
}
|
|
|
|
/*
|
|
* Returns a sensor index for a given activity handle and transition type.
|
|
*/
|
|
int GetActivitySensorForHandleAndType(uint32_t activity_handle,
|
|
uint32_t event_type) {
|
|
// Ensure that the requested activity index is valid.
|
|
if (activity_handle >= ARRAY_SIZE(kActivityList)) {
|
|
return 0;
|
|
}
|
|
|
|
// Ensure that the event type is either an ENTER or EXIT.
|
|
if (event_type < ACTIVITY_EVENT_ENTER || event_type > ACTIVITY_EVENT_EXIT) {
|
|
return 0;
|
|
}
|
|
|
|
return kActivitySensorMap[activity_handle][event_type - 1];
|
|
}
|
|
|
|
int ActivityContext::enableActivityEvent(uint32_t activity_handle,
|
|
uint32_t event_type, int64_t max_report_latency_ns) {
|
|
ALOGI("enableActivityEvent - activity_handle: %" PRIu32
|
|
", event_type: %" PRIu32 ", latency: %" PRId64,
|
|
activity_handle, event_type, max_report_latency_ns);
|
|
|
|
int sensor_index = GetActivitySensorForHandleAndType(activity_handle,
|
|
event_type);
|
|
if (sensor_index <= 0) {
|
|
ALOGE("Enabling invalid activity_handle: %" PRIu32
|
|
", event_type: %" PRIu32, activity_handle, event_type);
|
|
return 1;
|
|
}
|
|
|
|
mHubConnection->queueBatch(sensor_index, 1000000, max_report_latency_ns);
|
|
mHubConnection->queueActivate(sensor_index, true /* enable */);
|
|
return 0;
|
|
}
|
|
|
|
int ActivityContext::disableActivityEvent(uint32_t activity_handle,
|
|
uint32_t event_type) {
|
|
ALOGI("disableActivityEvent");
|
|
|
|
// Obtain the sensor index for the requested activity and transition types.
|
|
int sensor_index = kActivitySensorMap[activity_handle][event_type - 1];
|
|
if (sensor_index > 0) {
|
|
mHubConnection->queueActivate(sensor_index, false /* enable */);
|
|
} else {
|
|
ALOGE("Disabling invalid activity_handle: %" PRIu32
|
|
", event_type: %" PRIu32, activity_handle, event_type);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ActivityContext::flush() {
|
|
{
|
|
// Aquire a lock for the mOutstandingFlushEvents shared state. OnFlush
|
|
// modifies this value as flush results are returned. Nested scope is
|
|
// used here to control the lifecycle of the lock as OnFlush may be
|
|
// invoked before this method returns.
|
|
Mutex::Autolock autoLock(mCallbackLock);
|
|
mOutstandingFlushEvents +=
|
|
(COMMS_SENSOR_ACTIVITY_LAST - COMMS_SENSOR_ACTIVITY_FIRST) + 1;
|
|
}
|
|
|
|
// Flush all activity sensors.
|
|
for (int i = COMMS_SENSOR_ACTIVITY_FIRST;
|
|
i <= COMMS_SENSOR_ACTIVITY_LAST; i++) {
|
|
mHubConnection->queueFlush(i);
|
|
usleep(kFlushDelayMicros);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool ActivityContext::getHubAlive() {
|
|
return mHubConnection->initCheck() == OK
|
|
&& mHubConnection->getAliveCheck() == OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int open_activity(
|
|
const struct hw_module_t *module,
|
|
const char *,
|
|
struct hw_device_t **dev) {
|
|
ALOGI("open_activity");
|
|
|
|
gActivityContext = new ActivityContext(module);
|
|
*dev = &gActivityContext->device.common;
|
|
return 0;
|
|
}
|
|
|
|
static struct hw_module_methods_t activity_module_methods = {
|
|
.open = open_activity
|
|
};
|
|
|
|
static int get_activity_list(struct activity_recognition_module *,
|
|
char const* const **activity_list) {
|
|
ALOGI("get_activity_list");
|
|
|
|
if (gActivityContext != NULL && gActivityContext->getHubAlive()) {
|
|
*activity_list = kActivityList;
|
|
return sizeof(kActivityList) / sizeof(kActivityList[0]);
|
|
} else {
|
|
*activity_list = {};
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
struct activity_recognition_module HAL_MODULE_INFO_SYM = {
|
|
.common = {
|
|
.tag = HARDWARE_MODULE_TAG,
|
|
.version_major = kVersionMajor,
|
|
.version_minor = kVersionMinor,
|
|
.id = ACTIVITY_RECOGNITION_HARDWARE_MODULE_ID,
|
|
.name = "Google Activity Recognition module",
|
|
.author = "Google",
|
|
.methods = &activity_module_methods,
|
|
.dso = NULL,
|
|
.reserved = {0},
|
|
},
|
|
.get_supported_activities_list = get_activity_list,
|
|
};
|