/* * 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, };