blob: f09e79ef5479282252eb2a991a5955c1dbdea779 [file] [log] [blame]
/*
* 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 <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <pthread.h>
#include <binder/IServiceManager.h>
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
#include <cutils/atomic.h>
#include <cutils/properties.h>
#include <hardware/hardware.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include "SoundTriggerHwService.h"
#include <system/sound_trigger.h>
#include <hardware/sound_trigger.h>
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<ISoundTriggerClient> client;
sp<Module> 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<ISoundTriggerClient>& client,
sp<ISoundTrigger>& 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> module = mModules.valueAt(index);
module->setClient(client);
client->asBinder()->linkToDeath(module);
moduleInterface = module;
return NO_ERROR;
}
void SoundTriggerHwService::detachModule(sp<Module> 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<String16>& 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<RecognitionEvent>& event)
{
mCallbackThread->sendRecognitionEvent(event);
}
void SoundTriggerHwService::onRecognitionEvent(const sp<RecognitionEvent>& event)
{
ALOGV("onRecognitionEvent");
sp<Module> 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<SoundTriggerHwService>& 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<RecognitionEvent> event;
sp<SoundTriggerHwService> 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<SoundTriggerHwService::RecognitionEvent>& event)
{
AutoMutex lock(mCallbackLock);
mEventQueue.add(event);
mCallbackCond.signal();
}
SoundTriggerHwService::RecognitionEvent::RecognitionEvent(
sp<IMemory> eventMemory,
wp<Module> module)
: mEventMemory(eventMemory), mModule(module)
{
}
SoundTriggerHwService::RecognitionEvent::~RecognitionEvent()
{
}
#undef LOG_TAG
#define LOG_TAG "SoundTriggerHwService::Module"
SoundTriggerHwService::Module::Module(const sp<SoundTriggerHwService>& service,
sound_trigger_hw_device* hwDevice,
sound_trigger_module_descriptor descriptor,
const sp<ISoundTriggerClient>& 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> 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<SoundTriggerHwService> service = mService.promote();
if (service == 0) {
return;
}
service->detachModule(this);
}
status_t SoundTriggerHwService::Module::loadSoundModel(const sp<IMemory>& 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<IMemory>& 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> 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> 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<SoundTriggerHwService> service;
sp<IMemory> eventMemory;
ALOGV("sendRecognitionEvent for model %d", event->model);
{
AutoMutex lock(mLock);
sp<Model> 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<IMemory> 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> 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::Model> SoundTriggerHwService::Module::getModel(
sound_model_handle_t handle)
{
sp<Model> model;
ssize_t index = mModels.indexOfKey(handle);
if (index >= 0) {
model = mModels.valueAt(index);
}
return model;
}
void SoundTriggerHwService::Module::binderDied(
const wp<IBinder> &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<IMemory> SoundTriggerHwService::Model::allocateMemory(size_t size)
{
sp<IMemory> 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<String16>& args __unused) {
String8 result;
return NO_ERROR;
}
}; // namespace android