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