From b7a11d83f749ad0200778c4815e907d011d4b5d3 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 18 Apr 2014 17:40:41 -0700 Subject: add sound trigger native service Change-Id: I0cd954c1c7d28a334e786d0004431d4f6a1227ec --- services/soundtrigger/Android.mk | 41 ++ services/soundtrigger/SoundTriggerHwService.cpp | 566 ++++++++++++++++++++++++ services/soundtrigger/SoundTriggerHwService.h | 185 ++++++++ 3 files changed, 792 insertions(+) create mode 100644 services/soundtrigger/Android.mk create mode 100644 services/soundtrigger/SoundTriggerHwService.cpp create mode 100644 services/soundtrigger/SoundTriggerHwService.h (limited to 'services/soundtrigger') diff --git a/services/soundtrigger/Android.mk b/services/soundtrigger/Android.mk new file mode 100644 index 0000000..b7ccaab --- /dev/null +++ b/services/soundtrigger/Android.mk @@ -0,0 +1,41 @@ +# Copyright 2014 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + + +ifeq ($(SOUND_TRIGGER_USE_STUB_MODULE), 1) + LOCAL_CFLAGS += -DSOUND_TRIGGER_USE_STUB_MODULE +endif + +LOCAL_SRC_FILES:= \ + SoundTriggerHwService.cpp + +LOCAL_SHARED_LIBRARIES:= \ + libui \ + liblog \ + libutils \ + libbinder \ + libcutils \ + libhardware \ + libsoundtrigger + +#LOCAL_C_INCLUDES += \ + + +LOCAL_MODULE:= libsoundtriggerservice + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp new file mode 100644 index 0000000..f09e79e --- /dev/null +++ b/services/soundtrigger/SoundTriggerHwService.cpp @@ -0,0 +1,566 @@ +/* + * Copyright (C) 2014 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. + */ + +#define LOG_TAG "SoundTriggerHwService" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "SoundTriggerHwService.h" +#include +#include + +namespace android { + +#ifdef SOUND_TRIGGER_USE_STUB_MODULE +#define HW_MODULE_PREFIX "stub" +#else +#define HW_MODULE_PREFIX "primary" +#endif + +SoundTriggerHwService::SoundTriggerHwService() + : BnSoundTriggerHwService(), + mNextUniqueId(1) +{ +} + +void SoundTriggerHwService::onFirstRef() +{ + const hw_module_t *mod; + int rc; + sound_trigger_hw_device *dev; + + rc = hw_get_module_by_class(SOUND_TRIGGER_HARDWARE_MODULE_ID, HW_MODULE_PREFIX, &mod); + if (rc != 0) { + ALOGE("couldn't load sound trigger module %s.%s (%s)", + SOUND_TRIGGER_HARDWARE_MODULE_ID, "primary", strerror(-rc)); + return; + } + rc = sound_trigger_hw_device_open(mod, &dev); + if (rc != 0) { + ALOGE("couldn't open sound trigger hw device in %s.%s (%s)", + SOUND_TRIGGER_HARDWARE_MODULE_ID, "primary", strerror(-rc)); + return; + } + if (dev->common.version != SOUND_TRIGGER_DEVICE_API_VERSION_CURRENT) { + ALOGE("wrong sound trigger hw device version %04x", dev->common.version); + return; + } + + sound_trigger_module_descriptor descriptor; + rc = dev->get_properties(dev, &descriptor.properties); + if (rc != 0) { + ALOGE("could not read implementation properties"); + return; + } + descriptor.handle = + (sound_trigger_module_handle_t)android_atomic_inc(&mNextUniqueId); + ALOGI("loaded default module %s, handle %d", descriptor.properties.description, + descriptor.handle); + + sp client; + sp module = new Module(this, dev, descriptor, client); + mModules.add(descriptor.handle, module); + mCallbackThread = new CallbackThread(this); +} + +SoundTriggerHwService::~SoundTriggerHwService() +{ + if (mCallbackThread != 0) { + mCallbackThread->exit(); + } + for (size_t i = 0; i < mModules.size(); i++) { + sound_trigger_hw_device_close(mModules.valueAt(i)->hwDevice()); + } +} + +status_t SoundTriggerHwService::listModules(struct sound_trigger_module_descriptor *modules, + uint32_t *numModules) +{ + ALOGV("listModules"); + AutoMutex lock(mServiceLock); + if (numModules == NULL || (*numModules != 0 && modules == NULL)) { + return BAD_VALUE; + } + size_t maxModules = *numModules; + *numModules = mModules.size(); + for (size_t i = 0; i < mModules.size() && i < maxModules; i++) { + modules[i] = mModules.valueAt(i)->descriptor(); + } + return NO_ERROR; +} + +status_t SoundTriggerHwService::attach(const sound_trigger_module_handle_t handle, + const sp& client, + sp& moduleInterface) +{ + ALOGV("attach module %d", handle); + AutoMutex lock(mServiceLock); + moduleInterface.clear(); + if (client == 0) { + return BAD_VALUE; + } + ssize_t index = mModules.indexOfKey(handle); + if (index < 0) { + return BAD_VALUE; + } + sp module = mModules.valueAt(index); + + module->setClient(client); + client->asBinder()->linkToDeath(module); + moduleInterface = module; + + return NO_ERROR; +} + +void SoundTriggerHwService::detachModule(sp module) { + AutoMutex lock(mServiceLock); + ALOGV("detachModule"); + module->clearClient(); +} + +static const int kDumpLockRetries = 50; +static const int kDumpLockSleep = 60000; + +static bool tryLock(Mutex& mutex) +{ + bool locked = false; + for (int i = 0; i < kDumpLockRetries; ++i) { + if (mutex.tryLock() == NO_ERROR) { + locked = true; + break; + } + usleep(kDumpLockSleep); + } + return locked; +} + +status_t SoundTriggerHwService::dump(int fd, const Vector& args __unused) { + String8 result; + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + result.appendFormat("Permission Denial: can't dump SoundTriggerHwService"); + write(fd, result.string(), result.size()); + } else { + bool locked = tryLock(mServiceLock); + // failed to lock - SoundTriggerHwService is probably deadlocked + if (!locked) { + result.append("SoundTriggerHwService may be deadlocked\n"); + write(fd, result.string(), result.size()); + } + + if (locked) mServiceLock.unlock(); + } + return NO_ERROR; +} + +status_t SoundTriggerHwService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + return BnSoundTriggerHwService::onTransact(code, data, reply, flags); +} + + +// static +void SoundTriggerHwService::recognitionCallback(struct sound_trigger_recognition_event *event, + void *cookie) +{ + Module *module = (Module *)cookie; + if (module == NULL) { + return; + } + module->sendRecognitionEvent(event); +} + + +void SoundTriggerHwService::sendRecognitionEvent(const sp& event) +{ + mCallbackThread->sendRecognitionEvent(event); +} + +void SoundTriggerHwService::onRecognitionEvent(const sp& event) +{ + ALOGV("onRecognitionEvent"); + sp module; + { + AutoMutex lock(mServiceLock); + module = event->mModule.promote(); + if (module == 0) { + return; + } + } + module->onRecognitionEvent(event->mEventMemory); +} + +// static +void SoundTriggerHwService::soundModelCallback(struct sound_trigger_model_event *event __unused, + void *cookie) +{ + Module *module = (Module *)cookie; + +} + +#undef LOG_TAG +#define LOG_TAG "SoundTriggerHwService::CallbackThread" + +SoundTriggerHwService::CallbackThread::CallbackThread(const wp& service) + : mService(service) +{ +} + +SoundTriggerHwService::CallbackThread::~CallbackThread() +{ + mEventQueue.clear(); +} + +void SoundTriggerHwService::CallbackThread::onFirstRef() +{ + run("soundTrigger cbk", ANDROID_PRIORITY_URGENT_AUDIO); +} + +bool SoundTriggerHwService::CallbackThread::threadLoop() +{ + while (!exitPending()) { + sp event; + sp service; + { + Mutex::Autolock _l(mCallbackLock); + while (mEventQueue.isEmpty() && !exitPending()) { + ALOGV("CallbackThread::threadLoop() sleep"); + mCallbackCond.wait(mCallbackLock); + ALOGV("CallbackThread::threadLoop() wake up"); + } + if (exitPending()) { + break; + } + event = mEventQueue[0]; + mEventQueue.removeAt(0); + service = mService.promote(); + } + if (service != 0) { + service->onRecognitionEvent(event); + } + } + return false; +} + +void SoundTriggerHwService::CallbackThread::exit() +{ + Mutex::Autolock _l(mCallbackLock); + requestExit(); + mCallbackCond.broadcast(); +} + +void SoundTriggerHwService::CallbackThread::sendRecognitionEvent( + const sp& event) +{ + AutoMutex lock(mCallbackLock); + mEventQueue.add(event); + mCallbackCond.signal(); +} + +SoundTriggerHwService::RecognitionEvent::RecognitionEvent( + sp eventMemory, + wp module) + : mEventMemory(eventMemory), mModule(module) +{ +} + +SoundTriggerHwService::RecognitionEvent::~RecognitionEvent() +{ +} + +#undef LOG_TAG +#define LOG_TAG "SoundTriggerHwService::Module" + +SoundTriggerHwService::Module::Module(const sp& service, + sound_trigger_hw_device* hwDevice, + sound_trigger_module_descriptor descriptor, + const sp& client) + : mService(service), mHwDevice(hwDevice), mDescriptor(descriptor), + mClient(client) +{ +} + +SoundTriggerHwService::Module::~Module() { +} + +void SoundTriggerHwService::Module::detach() { + ALOGV("detach()"); + { + AutoMutex lock(mLock); + for (size_t i = 0; i < mModels.size(); i++) { + sp model = mModels.valueAt(i); + ALOGV("detach() unloading model %d", model->mHandle); + if (model->mState == Model::STATE_ACTIVE) { + mHwDevice->stop_recognition(mHwDevice, model->mHandle); + model->deallocateMemory(); + } + mHwDevice->unload_sound_model(mHwDevice, model->mHandle); + } + mModels.clear(); + } + if (mClient != 0) { + mClient->asBinder()->unlinkToDeath(this); + } + sp service = mService.promote(); + if (service == 0) { + return; + } + service->detachModule(this); +} + +status_t SoundTriggerHwService::Module::loadSoundModel(const sp& modelMemory, + sound_model_handle_t *handle) +{ + ALOGV("loadSoundModel() handle"); + + if (modelMemory == 0 || modelMemory->pointer() == NULL) { + ALOGE("loadSoundModel() modelMemory is 0 or has NULL pointer()"); + return BAD_VALUE; + } + struct sound_trigger_sound_model *sound_model = + (struct sound_trigger_sound_model *)modelMemory->pointer(); + + AutoMutex lock(mLock); + status_t status = mHwDevice->load_sound_model(mHwDevice, + sound_model, + SoundTriggerHwService::soundModelCallback, + this, + handle); + if (status == NO_ERROR) { + mModels.replaceValueFor(*handle, new Model(*handle)); + } + + return status; +} + +status_t SoundTriggerHwService::Module::unloadSoundModel(sound_model_handle_t handle) +{ + ALOGV("unloadSoundModel() model handle %d", handle); + + AutoMutex lock(mLock); + ssize_t index = mModels.indexOfKey(handle); + if (index < 0) { + return BAD_VALUE; + } + mModels.removeItem(handle); + + return mHwDevice->unload_sound_model(mHwDevice, handle); +} + +status_t SoundTriggerHwService::Module::startRecognition(sound_model_handle_t handle, + const sp& dataMemory) +{ + ALOGV("startRecognition() model handle %d", handle); + + if (dataMemory != 0 && dataMemory->pointer() == NULL) { + ALOGE("startRecognition() dataMemory is non-0 but has NULL pointer()"); + return BAD_VALUE; + + } + AutoMutex lock(mLock); + sp model = getModel(handle); + if (model == 0) { + return BAD_VALUE; + } + + if (model->mState == Model::STATE_ACTIVE) { + return INVALID_OPERATION; + } + model->mState = Model::STATE_ACTIVE; + + char *data = NULL; + unsigned int data_size = 0; + if (dataMemory != 0 && dataMemory->size() != 0) { + data_size = (unsigned int)dataMemory->size(); + data = (char *)dataMemory->pointer(); + ALOGV("startRecognition() data size %d data %d - %d", + data_size, data[0], data[data_size - 1]); + } + + //TODO: get capture handle and device from audio policy service + audio_io_handle_t capture_handle = 0; + return mHwDevice->start_recognition(mHwDevice, handle, capture_handle, AUDIO_DEVICE_NONE, + SoundTriggerHwService::recognitionCallback, + this, + data_size, + data); +} + +status_t SoundTriggerHwService::Module::stopRecognition(sound_model_handle_t handle) +{ + ALOGV("stopRecognition() model handle %d", handle); + + AutoMutex lock(mLock); + sp model = getModel(handle); + if (model == 0) { + return BAD_VALUE; + } + + if (model->mState != Model::STATE_ACTIVE) { + return INVALID_OPERATION; + } + mHwDevice->stop_recognition(mHwDevice, handle); + model->deallocateMemory(); + model->mState = Model::STATE_IDLE; + return NO_ERROR; +} + +void SoundTriggerHwService::Module::sendRecognitionEvent( + struct sound_trigger_recognition_event *event) +{ + sp service; + sp eventMemory; + ALOGV("sendRecognitionEvent for model %d", event->model); + { + AutoMutex lock(mLock); + sp model = getModel(event->model); + if (model == 0) { + return; + } + if (model->mState != Model::STATE_ACTIVE) { + ALOGV("sendRecognitionEvent model->mState %d != Model::STATE_ACTIVE", model->mState); + return; + } + if (mClient == 0) { + return; + } + service = mService.promote(); + if (service == 0) { + return; + } + + //sanitize event + switch (event->type) { + case SOUND_MODEL_TYPE_KEYPHRASE: + ALOGW_IF(event->data_offset != + sizeof(struct sound_trigger_phrase_recognition_event), + "sendRecognitionEvent(): invalid data offset %u for keyphrase event type", + event->data_offset); + event->data_offset = sizeof(struct sound_trigger_phrase_recognition_event); + break; + case SOUND_MODEL_TYPE_UNKNOWN: + ALOGW_IF(event->data_offset != + sizeof(struct sound_trigger_recognition_event), + "sendRecognitionEvent(): invalid data offset %u for unknown event type", + event->data_offset); + event->data_offset = sizeof(struct sound_trigger_recognition_event); + break; + default: + return; + } + + size_t size = event->data_offset + event->data_size; + eventMemory = model->allocateMemory(size); + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + return; + } + memcpy(eventMemory->pointer(), event, size); + } + service->sendRecognitionEvent(new RecognitionEvent(eventMemory, this)); +} + +void SoundTriggerHwService::Module::onRecognitionEvent(sp eventMemory) +{ + ALOGV("Module::onRecognitionEvent"); + + AutoMutex lock(mLock); + + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + return; + } + struct sound_trigger_recognition_event *event = + (struct sound_trigger_recognition_event *)eventMemory->pointer(); + + sp model = getModel(event->model); + if (model == 0) { + ALOGI("%s model == 0", __func__); + return; + } + if (model->mState != Model::STATE_ACTIVE) { + ALOGV("onRecognitionEvent model->mState %d != Model::STATE_ACTIVE", model->mState); + return; + } + if (mClient == 0) { + ALOGI("%s mClient == 0", __func__); + return; + } + mClient->onRecognitionEvent(eventMemory); + model->mState = Model::STATE_IDLE; + model->deallocateMemory(); +} + +sp SoundTriggerHwService::Module::getModel( + sound_model_handle_t handle) +{ + sp model; + ssize_t index = mModels.indexOfKey(handle); + if (index >= 0) { + model = mModels.valueAt(index); + } + return model; +} + +void SoundTriggerHwService::Module::binderDied( + const wp &who __unused) { + ALOGW("client binder died for module %d", mDescriptor.handle); + detach(); +} + + +SoundTriggerHwService::Model::Model(sound_model_handle_t handle) : + mHandle(handle), mState(STATE_IDLE), mInputHandle(AUDIO_IO_HANDLE_NONE), + mCaptureSession(AUDIO_SESSION_ALLOCATE), + mMemoryDealer(new MemoryDealer(sizeof(struct sound_trigger_recognition_event), + "SoundTriggerHwService::Event")) +{ + +} + + +sp SoundTriggerHwService::Model::allocateMemory(size_t size) +{ + sp memory; + if (mMemoryDealer->getMemoryHeap()->getSize() < size) { + mMemoryDealer = new MemoryDealer(size, "SoundTriggerHwService::Event"); + } + memory = mMemoryDealer->allocate(size); + return memory; +} + +void SoundTriggerHwService::Model::deallocateMemory() +{ + mMemoryDealer->deallocate(0); +} + +status_t SoundTriggerHwService::Module::dump(int fd __unused, + const Vector& args __unused) { + String8 result; + return NO_ERROR; +} + +}; // namespace android diff --git a/services/soundtrigger/SoundTriggerHwService.h b/services/soundtrigger/SoundTriggerHwService.h new file mode 100644 index 0000000..377f2a1 --- /dev/null +++ b/services/soundtrigger/SoundTriggerHwService.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H +#define ANDROID_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H + +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +class MemoryHeapBase; + +class SoundTriggerHwService : + public BinderService, + public BnSoundTriggerHwService +{ + friend class BinderService; +public: + class Module; + + static char const* getServiceName() { return "media.sound_trigger_hw"; } + + SoundTriggerHwService(); + virtual ~SoundTriggerHwService(); + + // ISoundTriggerHwService + virtual status_t listModules(struct sound_trigger_module_descriptor *modules, + uint32_t *numModules); + + virtual status_t attach(const sound_trigger_module_handle_t handle, + const sp& client, + sp& module); + + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + + virtual status_t dump(int fd, const Vector& args); + + class Model : public RefBase { + public: + + enum { + STATE_IDLE, + STATE_ACTIVE + }; + + Model(sound_model_handle_t handle); + ~Model() {} + + sp allocateMemory(size_t size); + void deallocateMemory(); + + sound_model_handle_t mHandle; + int mState; + audio_io_handle_t mInputHandle; + audio_session_t mCaptureSession; + sp mMemoryDealer; + }; + + class Module : public virtual RefBase, + public BnSoundTrigger, + public IBinder::DeathRecipient { + public: + + Module(const sp& service, + sound_trigger_hw_device* hwDevice, + sound_trigger_module_descriptor descriptor, + const sp& client); + + virtual ~Module(); + + virtual void detach(); + + virtual status_t loadSoundModel(const sp& modelMemory, + sound_model_handle_t *handle); + + virtual status_t unloadSoundModel(sound_model_handle_t handle); + + virtual status_t startRecognition(sound_model_handle_t handle, + const sp& dataMemory); + virtual status_t stopRecognition(sound_model_handle_t handle); + + virtual status_t dump(int fd, const Vector& args); + + + sound_trigger_hw_device *hwDevice() const { return mHwDevice; } + struct sound_trigger_module_descriptor descriptor() { return mDescriptor; } + void setClient(sp client) { mClient = client; } + void clearClient() { mClient.clear(); } + sp client() { return mClient; } + + void sendRecognitionEvent(struct sound_trigger_recognition_event *event); + void onRecognitionEvent(sp eventMemory); + + sp getModel(sound_model_handle_t handle); + + // IBinder::DeathRecipient implementation + virtual void binderDied(const wp &who); + + private: + Mutex mLock; + wp mService; + struct sound_trigger_hw_device* mHwDevice; + struct sound_trigger_module_descriptor mDescriptor; + sp mClient; + DefaultKeyedVector< sound_model_handle_t, sp > mModels; + }; // class Module + + class RecognitionEvent : public RefBase { + public: + + RecognitionEvent(sp eventMemory, wp module); + + virtual ~RecognitionEvent(); + + sp mEventMemory; + wp mModule; + }; + + class CallbackThread : public Thread { + public: + + CallbackThread(const wp& service); + + virtual ~CallbackThread(); + + // Thread virtuals + virtual bool threadLoop(); + + // RefBase + virtual void onFirstRef(); + + void exit(); + void sendRecognitionEvent(const sp& event); + + private: + wp mService; + Condition mCallbackCond; + Mutex mCallbackLock; + Vector< sp > mEventQueue; + }; + + void detachModule(sp module); + + static void recognitionCallback(struct sound_trigger_recognition_event *event, void *cookie); + void sendRecognitionEvent(const sp& event); + void onRecognitionEvent(const sp& event); + + static void soundModelCallback(struct sound_trigger_model_event *event, void *cookie); + +private: + + virtual void onFirstRef(); + + Mutex mServiceLock; + volatile int32_t mNextUniqueId; + DefaultKeyedVector< sound_trigger_module_handle_t, sp > mModules; + sp mCallbackThread; +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H -- cgit v1.1