C++程序  |  438行  |  14.53 KB

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