| /* |
| * 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 <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; |
| |
| static const int ACTIVITY_TYPE_TILTING_INDEX = 6; |
| |
| static const char *const kActivityList[] = { |
| ACTIVITY_TYPE_IN_VEHICLE, |
| ACTIVITY_TYPE_ON_BICYCLE, |
| ACTIVITY_TYPE_WALKING, |
| ACTIVITY_TYPE_RUNNING, |
| ACTIVITY_TYPE_STILL, |
| "com.google.android.contexthub.ar.inconsistent", |
| ACTIVITY_TYPE_TILTING |
| }; |
| |
| // The global ActivityContext singleton. |
| static ActivityContext *gActivityContext = NULL; |
| |
| static int ActivityClose(struct hw_device_t *) { |
| 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), |
| mPrevActivity(-1), |
| mInitExitDone(false) { |
| 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); |
| mHubConnection->queueActivate( |
| COMMS_SENSOR_ACTIVITY, false /* enable */); |
| } |
| } |
| |
| ActivityContext::~ActivityContext() { |
| mHubConnection->setActivityCallback(NULL); |
| } |
| |
| void ActivityContext::OnActivityEvent(int activityRaw, uint64_t whenNs) { |
| Mutex::Autolock autoLock(mCallbackLock); |
| if (!mCallback) { |
| return; |
| } |
| |
| ALOGV("activityRaw = %d", activityRaw); |
| |
| if (mPrevActivity >= 0 && mPrevActivity == activityRaw) { |
| // same old, same old... |
| return; |
| } |
| |
| activity_event_t ev[8]; |
| memset(&ev, 0, 8*sizeof(activity_event_t)); |
| int num_events = 0; |
| |
| // exit all other activities when first enabled. |
| if (!mInitExitDone) { |
| mInitExitDone = true; |
| |
| int numActivities = sizeof(kActivityList) / sizeof(kActivityList[0]); |
| for (int i = 0; i < numActivities; ++i) { |
| if ((i == activityRaw) || !isEnabled(i, ACTIVITY_EVENT_EXIT)) { |
| continue; |
| } |
| |
| activity_event_t *curr_ev = &ev[num_events]; |
| curr_ev->event_type = ACTIVITY_EVENT_EXIT; |
| curr_ev->activity = i; |
| curr_ev->timestamp = whenNs; |
| curr_ev->reserved[0] = curr_ev->reserved[1] = curr_ev->reserved[2] = curr_ev->reserved[3] = 0; |
| num_events++; |
| } |
| } |
| |
| // tilt activities do not change the current activity type, but have a |
| // simultaneous enter and exit event type |
| if (activityRaw == ACTIVITY_TYPE_TILTING_INDEX) { |
| if (isEnabled(activityRaw, ACTIVITY_EVENT_ENTER)) { |
| activity_event_t *curr_ev = &ev[num_events]; |
| curr_ev->event_type = ACTIVITY_EVENT_ENTER; |
| curr_ev->activity = activityRaw; |
| curr_ev->timestamp = whenNs; |
| curr_ev->reserved[0] = curr_ev->reserved[1] = curr_ev->reserved[2] = curr_ev->reserved[3] = 0; |
| num_events++; |
| } |
| |
| if (isEnabled(activityRaw, ACTIVITY_EVENT_EXIT)) { |
| activity_event_t *curr_ev = &ev[num_events]; |
| curr_ev->event_type = ACTIVITY_EVENT_EXIT; |
| curr_ev->activity = activityRaw; |
| curr_ev->timestamp = whenNs; |
| curr_ev->reserved[0] = curr_ev->reserved[1] = curr_ev->reserved[2] = curr_ev->reserved[3] = 0; |
| num_events++; |
| } |
| } else { |
| if ((mPrevActivity >= 0) && |
| (isEnabled(mPrevActivity, ACTIVITY_EVENT_EXIT))) { |
| activity_event_t *curr_ev = &ev[num_events]; |
| curr_ev->event_type = ACTIVITY_EVENT_EXIT; |
| curr_ev->activity = mPrevActivity; |
| curr_ev->timestamp = whenNs; |
| curr_ev->reserved[0] = curr_ev->reserved[1] = curr_ev->reserved[2] = curr_ev->reserved[3] = 0; |
| num_events++; |
| } |
| |
| if (isEnabled(activityRaw, ACTIVITY_EVENT_ENTER)) { |
| activity_event_t *curr_ev = &ev[num_events]; |
| curr_ev->event_type = ACTIVITY_EVENT_ENTER; |
| curr_ev->activity = activityRaw; |
| curr_ev->timestamp = whenNs; |
| curr_ev->reserved[0] = curr_ev->reserved[1] = curr_ev->reserved[2] = curr_ev->reserved[3] = 0; |
| num_events++; |
| } |
| |
| mPrevActivity = activityRaw; |
| } |
| |
| if (num_events > 0) { |
| (*mCallback->activity_callback)(mCallback, ev, num_events); |
| } |
| } |
| |
| void ActivityContext::OnFlush() { |
| Mutex::Autolock autoLock(mCallbackLock); |
| if (!mCallback) { |
| return; |
| } |
| |
| activity_event_t ev = { |
| .event_type = ACTIVITY_EVENT_FLUSH_COMPLETE, |
| .activity = 0, |
| .timestamp = 0ll, |
| }; |
| |
| (*mCallback->activity_callback)(mCallback, &ev, 1); |
| } |
| |
| void ActivityContext::registerActivityCallback( |
| const activity_recognition_callback_procs_t *callback) { |
| ALOGI("registerActivityCallback"); |
| |
| Mutex::Autolock autoLock(mCallbackLock); |
| mCallback = callback; |
| } |
| |
| int ActivityContext::enableActivityEvent( |
| uint32_t activity_handle, |
| uint32_t event_type, |
| int64_t max_batch_report_latency_ns) { |
| ALOGI("enableActivityEvent - activity_handle: %" PRIu32 |
| ", event_type: %" PRIu32 ", latency: %" PRId64, |
| activity_handle, event_type, max_batch_report_latency_ns); |
| |
| bool wasEnabled = !mMaxBatchReportLatencyNs.isEmpty(); |
| int64_t prev_latency = calculateReportLatencyNs(); |
| |
| mMaxBatchReportLatencyNs.add( |
| ((uint64_t)activity_handle << 32) | event_type, |
| max_batch_report_latency_ns); |
| |
| if (!wasEnabled) { |
| mPrevActivity = -1; |
| mInitExitDone = false; |
| |
| mHubConnection->queueBatch(COMMS_SENSOR_ACTIVITY, 1000000, |
| max_batch_report_latency_ns); |
| mHubConnection->queueActivate(COMMS_SENSOR_ACTIVITY, true /* enable */); |
| } else if (max_batch_report_latency_ns != prev_latency) { |
| mHubConnection->queueBatch(COMMS_SENSOR_ACTIVITY, 1000000, |
| max_batch_report_latency_ns); |
| } |
| |
| return 0; |
| } |
| |
| int64_t ActivityContext::calculateReportLatencyNs() { |
| int64_t ret = INT64_MAX; |
| |
| for (size_t i = 0 ; i < mMaxBatchReportLatencyNs.size(); ++i) { |
| if (mMaxBatchReportLatencyNs[i] <ret) { |
| ret = mMaxBatchReportLatencyNs[i]; |
| } |
| } |
| return ret; |
| } |
| |
| int ActivityContext::disableActivityEvent( |
| uint32_t activity_handle, uint32_t event_type) { |
| ALOGI("disableActivityEvent"); |
| |
| bool wasEnabled = !mMaxBatchReportLatencyNs.isEmpty(); |
| |
| mMaxBatchReportLatencyNs.removeItem( |
| ((uint64_t)activity_handle << 32) | event_type); |
| |
| bool isEnabled = !mMaxBatchReportLatencyNs.isEmpty(); |
| |
| if (wasEnabled && !isEnabled) { |
| mHubConnection->queueActivate(COMMS_SENSOR_ACTIVITY, false /* enable */); |
| } |
| |
| return 0; |
| } |
| |
| bool ActivityContext::isEnabled( |
| uint32_t activity_handle, uint32_t event_type) const { |
| return mMaxBatchReportLatencyNs.indexOfKey( |
| ((uint64_t)activity_handle << 32) | event_type) >= 0; |
| } |
| |
| int ActivityContext::flush() { |
| mHubConnection->queueFlush(COMMS_SENSOR_ACTIVITY); |
| 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, |
| }; |