| /* |
| * 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 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::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; |
| } |
| |
| 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() { |
| 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, |
| }; |