/* * 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 <audio_utils/clock.h> #include <system/sound_trigger.h> #include <cutils/atomic.h> #include <cutils/properties.h> #include <hardware/hardware.h> #include <media/AudioSystem.h> #include <mediautils/ServiceUtilities.h> #include <utils/Errors.h> #include <utils/Log.h> #include <binder/IServiceManager.h> #include <binder/MemoryBase.h> #include <binder/MemoryHeapBase.h> #include <system/sound_trigger.h> #include "SoundTriggerHwService.h" #define HW_MODULE_PREFIX "primary" namespace android { SoundTriggerHwService::SoundTriggerHwService() : BnSoundTriggerHwService(), mNextUniqueId(1), mMemoryDealer(new MemoryDealer(1024 * 1024, "SoundTriggerHwService")), mCaptureState(false) { } void SoundTriggerHwService::onFirstRef() { int rc; sp<SoundTriggerHalInterface> halInterface = SoundTriggerHalInterface::connectModule(HW_MODULE_PREFIX); if (halInterface == 0) { ALOGW("could not connect to HAL"); return; } sound_trigger_module_descriptor descriptor; rc = halInterface->getProperties(&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<Module> module = new Module(this, halInterface, descriptor); mModules.add(descriptor.handle, module); mCallbackThread = new CallbackThread(this); } SoundTriggerHwService::~SoundTriggerHwService() { if (mCallbackThread != 0) { mCallbackThread->exit(); } } status_t SoundTriggerHwService::listModules(const String16& opPackageName, struct sound_trigger_module_descriptor *modules, uint32_t *numModules) { ALOGV("listModules"); if (!captureHotwordAllowed(opPackageName, IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid())) { return PERMISSION_DENIED; } 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 String16& opPackageName, const sound_trigger_module_handle_t handle, const sp<ISoundTriggerClient>& client, sp<ISoundTrigger>& moduleInterface) { ALOGV("attach module %d", handle); if (!captureHotwordAllowed(opPackageName, IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid())) { return PERMISSION_DENIED; } 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); sp<ModuleClient> moduleClient = module->addClient(client, opPackageName); if (moduleClient == 0) { return NO_INIT; } moduleClient->setCaptureState_l(mCaptureState); moduleInterface = moduleClient; return NO_ERROR; } status_t SoundTriggerHwService::setCaptureState(bool active) { ALOGV("setCaptureState %d", active); AutoMutex lock(mServiceLock); mCaptureState = active; for (size_t i = 0; i < mModules.size(); i++) { mModules.valueAt(i)->setCaptureState_l(active); } return NO_ERROR; } static const int kDumpLockTimeoutNs = 1 * NANOS_PER_SECOND; static bool dumpTryLock(Mutex& mutex) { status_t err = mutex.timedLock(kDumpLockTimeoutNs); return err == NO_ERROR; } 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 = dumpTryLock(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; } sp<SoundTriggerHwService> service = module->service().promote(); if (service == 0) { return; } service->sendRecognitionEvent(event, module); } sp<IMemory> SoundTriggerHwService::prepareRecognitionEvent( struct sound_trigger_recognition_event *event) { AutoMutex lock(mMemoryDealerLock); sp<IMemory> eventMemory; //sanitize event switch (event->type) { case SOUND_MODEL_TYPE_KEYPHRASE: ALOGW_IF(event->data_size != 0 && event->data_offset != sizeof(struct sound_trigger_phrase_recognition_event), "prepareRecognitionEvent(): 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_GENERIC: ALOGW_IF(event->data_size != 0 && event->data_offset != sizeof(struct sound_trigger_generic_recognition_event), "prepareRecognitionEvent(): invalid data offset %u for generic event type", event->data_offset); event->data_offset = sizeof(struct sound_trigger_generic_recognition_event); break; case SOUND_MODEL_TYPE_UNKNOWN: ALOGW_IF(event->data_size != 0 && event->data_offset != sizeof(struct sound_trigger_recognition_event), "prepareRecognitionEvent(): invalid data offset %u for unknown event type", event->data_offset); event->data_offset = sizeof(struct sound_trigger_recognition_event); break; default: return eventMemory; } size_t size = event->data_offset + event->data_size; eventMemory = mMemoryDealer->allocate(size); if (eventMemory == 0 || eventMemory->pointer() == NULL) { eventMemory.clear(); return eventMemory; } memcpy(eventMemory->pointer(), event, size); return eventMemory; } void SoundTriggerHwService::sendRecognitionEvent(struct sound_trigger_recognition_event *event, Module *module) { if (module == NULL) { return; } sp<IMemory> eventMemory = prepareRecognitionEvent(event); if (eventMemory == 0) { return; } sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_RECOGNITION, eventMemory); callbackEvent->setModule(module); sendCallbackEvent(callbackEvent); } // static void SoundTriggerHwService::soundModelCallback(struct sound_trigger_model_event *event, void *cookie) { Module *module = (Module *)cookie; if (module == NULL) { return; } sp<SoundTriggerHwService> service = module->service().promote(); if (service == 0) { return; } service->sendSoundModelEvent(event, module); } sp<IMemory> SoundTriggerHwService::prepareSoundModelEvent(struct sound_trigger_model_event *event) { AutoMutex lock(mMemoryDealerLock); sp<IMemory> eventMemory; size_t size = event->data_offset + event->data_size; eventMemory = mMemoryDealer->allocate(size); if (eventMemory == 0 || eventMemory->pointer() == NULL) { eventMemory.clear(); return eventMemory; } memcpy(eventMemory->pointer(), event, size); return eventMemory; } void SoundTriggerHwService::sendSoundModelEvent(struct sound_trigger_model_event *event, Module *module) { sp<IMemory> eventMemory = prepareSoundModelEvent(event); if (eventMemory == 0) { return; } sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_SOUNDMODEL, eventMemory); callbackEvent->setModule(module); sendCallbackEvent(callbackEvent); } sp<IMemory> SoundTriggerHwService::prepareServiceStateEvent(sound_trigger_service_state_t state) { AutoMutex lock(mMemoryDealerLock); sp<IMemory> eventMemory; size_t size = sizeof(sound_trigger_service_state_t); eventMemory = mMemoryDealer->allocate(size); if (eventMemory == 0 || eventMemory->pointer() == NULL) { eventMemory.clear(); return eventMemory; } *((sound_trigger_service_state_t *)eventMemory->pointer()) = state; return eventMemory; } void SoundTriggerHwService::sendServiceStateEvent(sound_trigger_service_state_t state, Module *module) { sp<IMemory> eventMemory = prepareServiceStateEvent(state); if (eventMemory == 0) { return; } sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_SERVICE_STATE, eventMemory); callbackEvent->setModule(module); sendCallbackEvent(callbackEvent); } void SoundTriggerHwService::sendServiceStateEvent(sound_trigger_service_state_t state, ModuleClient *moduleClient) { sp<IMemory> eventMemory = prepareServiceStateEvent(state); if (eventMemory == 0) { return; } sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_SERVICE_STATE, eventMemory); callbackEvent->setModuleClient(moduleClient); sendCallbackEvent(callbackEvent); } void SoundTriggerHwService::sendCallbackEvent(const sp<CallbackEvent>& event) { mCallbackThread->sendCallbackEvent(event); } void SoundTriggerHwService::onCallbackEvent(const sp<CallbackEvent>& event) { ALOGV("onCallbackEvent"); sp<Module> module; sp<ModuleClient> moduleClient; { AutoMutex lock(mServiceLock); //CallbackEvent is either for Module or ModuleClient module = event->mModule.promote(); if (module == 0) { moduleClient = event->mModuleClient.promote(); if (moduleClient == 0) { return; } } else { // Sanity check on this being a Module we know about. bool foundModule = false; for (size_t i = 0; i < mModules.size(); i++) { if (mModules.valueAt(i).get() == module.get()) { foundModule = true; break; } } if (!foundModule) { ALOGE("onCallbackEvent for unknown module"); return; } } } if (module != 0) { ALOGV("onCallbackEvent for module"); module->onCallbackEvent(event); } else if (moduleClient != 0) { ALOGV("onCallbackEvent for moduleClient"); moduleClient->onCallbackEvent(event); } { AutoMutex lock(mServiceLock); // clear now to execute with mServiceLock locked event->mMemory.clear(); } } #undef LOG_TAG #define LOG_TAG "SoundTriggerHwService::CallbackThread" SoundTriggerHwService::CallbackThread::CallbackThread(const wp<SoundTriggerHwService>& service) : mService(service) { } SoundTriggerHwService::CallbackThread::~CallbackThread() { while (!mEventQueue.isEmpty()) { mEventQueue[0]->mMemory.clear(); mEventQueue.removeAt(0); } } void SoundTriggerHwService::CallbackThread::onFirstRef() { run("soundTrigger cbk", ANDROID_PRIORITY_URGENT_AUDIO); } bool SoundTriggerHwService::CallbackThread::threadLoop() { while (!exitPending()) { sp<CallbackEvent> 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->onCallbackEvent(event); } } return false; } void SoundTriggerHwService::CallbackThread::exit() { Mutex::Autolock _l(mCallbackLock); requestExit(); mCallbackCond.broadcast(); } void SoundTriggerHwService::CallbackThread::sendCallbackEvent( const sp<SoundTriggerHwService::CallbackEvent>& event) { AutoMutex lock(mCallbackLock); mEventQueue.add(event); mCallbackCond.signal(); } SoundTriggerHwService::CallbackEvent::CallbackEvent(event_type type, sp<IMemory> memory) : mType(type), mMemory(memory) { } SoundTriggerHwService::CallbackEvent::~CallbackEvent() { } #undef LOG_TAG #define LOG_TAG "SoundTriggerHwService::Module" SoundTriggerHwService::Module::Module(const sp<SoundTriggerHwService>& service, const sp<SoundTriggerHalInterface>& halInterface, sound_trigger_module_descriptor descriptor) : mService(service), mHalInterface(halInterface), mDescriptor(descriptor), mServiceState(SOUND_TRIGGER_STATE_NO_INIT) { } SoundTriggerHwService::Module::~Module() { mModuleClients.clear(); } sp<SoundTriggerHwService::ModuleClient> SoundTriggerHwService::Module::addClient(const sp<ISoundTriggerClient>& client, const String16& opPackageName) { AutoMutex lock(mLock); sp<ModuleClient> moduleClient; for (size_t i = 0; i < mModuleClients.size(); i++) { if (mModuleClients[i]->client() == client) { // Client already present, reuse client return moduleClient; } } moduleClient = new ModuleClient(this, client, opPackageName); ALOGV("addClient() client %p", moduleClient.get()); mModuleClients.add(moduleClient); return moduleClient; } void SoundTriggerHwService::Module::detach(const sp<ModuleClient>& moduleClient) { ALOGV("Module::detach()"); Vector<audio_session_t> releasedSessions; { AutoMutex lock(mLock); ssize_t index = -1; for (size_t i = 0; i < mModuleClients.size(); i++) { if (mModuleClients[i] == moduleClient) { index = i; break; } } if (index == -1) { return; } ALOGV("remove client %p", moduleClient.get()); mModuleClients.removeAt(index); // Iterate in reverse order as models are removed from list inside the loop. for (size_t i = mModels.size(); i > 0; i--) { sp<Model> model = mModels.valueAt(i - 1); if (moduleClient == model->mModuleClient) { mModels.removeItemsAt(i - 1); ALOGV("detach() unloading model %d", model->mHandle); if (mHalInterface != 0) { if (model->mState == Model::STATE_ACTIVE) { mHalInterface->stopRecognition(model->mHandle); } mHalInterface->unloadSoundModel(model->mHandle); } releasedSessions.add(model->mCaptureSession); } } } for (size_t i = 0; i < releasedSessions.size(); i++) { // do not call AudioSystem methods with mLock held AudioSystem::releaseSoundTriggerSession(releasedSessions[i]); } } status_t SoundTriggerHwService::Module::loadSoundModel(const sp<IMemory>& modelMemory, sp<ModuleClient> moduleClient, sound_model_handle_t *handle) { ALOGV("loadSoundModel() handle"); if (mHalInterface == 0) { return NO_INIT; } struct sound_trigger_sound_model *sound_model = (struct sound_trigger_sound_model *)modelMemory->pointer(); size_t structSize; if (sound_model->type == SOUND_MODEL_TYPE_KEYPHRASE) { structSize = sizeof(struct sound_trigger_phrase_sound_model); } else { structSize = sizeof(struct sound_trigger_sound_model); } if (sound_model->data_offset < structSize || sound_model->data_size > (UINT_MAX - sound_model->data_offset) || modelMemory->size() < sound_model->data_offset || sound_model->data_size > (modelMemory->size() - sound_model->data_offset)) { android_errorWriteLog(0x534e4554, "30148546"); ALOGE("loadSoundModel() data_size is too big"); return BAD_VALUE; } audio_session_t session; audio_io_handle_t ioHandle; audio_devices_t device; // do not call AudioSystem methods with mLock held status_t status = AudioSystem::acquireSoundTriggerSession(&session, &ioHandle, &device); if (status != NO_ERROR) { return status; } { AutoMutex lock(mLock); if (mModels.size() >= mDescriptor.properties.max_sound_models) { ALOGW("loadSoundModel(): Not loading, max number of models (%d) would be exceeded", mDescriptor.properties.max_sound_models); status = INVALID_OPERATION; goto exit; } status = mHalInterface->loadSoundModel(sound_model, SoundTriggerHwService::soundModelCallback, this, handle); if (status != NO_ERROR) { goto exit; } sp<Model> model = new Model(*handle, session, ioHandle, device, sound_model->type, moduleClient); mModels.replaceValueFor(*handle, model); } exit: if (status != NO_ERROR) { // do not call AudioSystem methods with mLock held AudioSystem::releaseSoundTriggerSession(session); } return status; } status_t SoundTriggerHwService::Module::unloadSoundModel(sound_model_handle_t handle) { ALOGV("unloadSoundModel() model handle %d", handle); status_t status; audio_session_t session; { AutoMutex lock(mLock); if (mHalInterface == 0) { return NO_INIT; } ssize_t index = mModels.indexOfKey(handle); if (index < 0) { return BAD_VALUE; } sp<Model> model = mModels.valueAt(index); mModels.removeItem(handle); if (model->mState == Model::STATE_ACTIVE) { mHalInterface->stopRecognition(model->mHandle); model->mState = Model::STATE_IDLE; } status = mHalInterface->unloadSoundModel(handle); session = model->mCaptureSession; } // do not call AudioSystem methods with mLock held AudioSystem::releaseSoundTriggerSession(session); return status; } status_t SoundTriggerHwService::Module::startRecognition(sound_model_handle_t handle, const sp<IMemory>& dataMemory) { ALOGV("startRecognition() model handle %d", handle); if (mHalInterface == 0) { return NO_INIT; } struct sound_trigger_recognition_config *config = (struct sound_trigger_recognition_config *)dataMemory->pointer(); if (config->data_offset < sizeof(struct sound_trigger_recognition_config) || config->data_size > (UINT_MAX - config->data_offset) || dataMemory->size() < config->data_offset || config->data_size > (dataMemory->size() - config->data_offset)) { ALOGE("startRecognition() data_size is too big"); return BAD_VALUE; } AutoMutex lock(mLock); if (mServiceState == SOUND_TRIGGER_STATE_DISABLED) { return INVALID_OPERATION; } sp<Model> model = getModel(handle); if (model == 0) { return BAD_VALUE; } if (model->mState == Model::STATE_ACTIVE) { return INVALID_OPERATION; } //TODO: get capture handle and device from audio policy service config->capture_handle = model->mCaptureIOHandle; config->capture_device = model->mCaptureDevice; status_t status = mHalInterface->startRecognition(handle, config, SoundTriggerHwService::recognitionCallback, this); if (status == NO_ERROR) { model->mState = Model::STATE_ACTIVE; model->mConfig = *config; } return status; } status_t SoundTriggerHwService::Module::stopRecognition(sound_model_handle_t handle) { ALOGV("stopRecognition() model handle %d", handle); if (mHalInterface == 0) { return NO_INIT; } AutoMutex lock(mLock); sp<Model> model = getModel(handle); if (model == 0) { return BAD_VALUE; } if (model->mState != Model::STATE_ACTIVE) { return INVALID_OPERATION; } mHalInterface->stopRecognition(handle); model->mState = Model::STATE_IDLE; return NO_ERROR; } status_t SoundTriggerHwService::Module::getModelState(sound_model_handle_t handle) { ALOGV("getModelState() model handle %d", handle); if (mHalInterface == 0) { return NO_INIT; } AutoMutex lock(mLock); sp<Model> model = getModel(handle); if (model == 0) { return BAD_VALUE; } if (model->mState != Model::STATE_ACTIVE) { return INVALID_OPERATION; } return mHalInterface->getModelState(handle); } void SoundTriggerHwService::Module::onCallbackEvent(const sp<CallbackEvent>& event) { ALOGV("onCallbackEvent type %d", event->mType); sp<IMemory> eventMemory = event->mMemory; if (eventMemory == 0 || eventMemory->pointer() == NULL) { return; } if (mModuleClients.isEmpty()) { ALOGI("%s no clients", __func__); return; } Vector< sp<ModuleClient> > clients; switch (event->mType) { case CallbackEvent::TYPE_RECOGNITION: { struct sound_trigger_recognition_event *recognitionEvent = (struct sound_trigger_recognition_event *)eventMemory->pointer(); { AutoMutex lock(mLock); sp<Model> model = getModel(recognitionEvent->model); if (model == 0) { ALOGW("%s model == 0", __func__); return; } if (model->mState != Model::STATE_ACTIVE) { ALOGV("onCallbackEvent model->mState %d != Model::STATE_ACTIVE", model->mState); return; } recognitionEvent->capture_session = model->mCaptureSession; model->mState = Model::STATE_IDLE; clients.add(model->mModuleClient); } } break; case CallbackEvent::TYPE_SOUNDMODEL: { struct sound_trigger_model_event *soundmodelEvent = (struct sound_trigger_model_event *)eventMemory->pointer(); { AutoMutex lock(mLock); sp<Model> model = getModel(soundmodelEvent->model); if (model == 0) { ALOGW("%s model == 0", __func__); return; } clients.add(model->mModuleClient); } } break; case CallbackEvent::TYPE_SERVICE_STATE: { { AutoMutex lock(mLock); for (size_t i = 0; i < mModuleClients.size(); i++) { if (mModuleClients[i] != 0) { clients.add(mModuleClients[i]); } } } } break; default: LOG_ALWAYS_FATAL("onCallbackEvent unknown event type %d", event->mType); } for (size_t i = 0; i < clients.size(); i++) { clients[i]->onCallbackEvent(event); } } 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; } // Called with mServiceLock held void SoundTriggerHwService::Module::setCaptureState_l(bool active) { ALOGV("Module::setCaptureState_l %d", active); sp<SoundTriggerHwService> service; sound_trigger_service_state_t state; Vector< sp<IMemory> > events; { AutoMutex lock(mLock); state = (active && !mDescriptor.properties.concurrent_capture) ? SOUND_TRIGGER_STATE_DISABLED : SOUND_TRIGGER_STATE_ENABLED; if (state == mServiceState) { return; } mServiceState = state; service = mService.promote(); if (service == 0) { return; } if (state == SOUND_TRIGGER_STATE_ENABLED) { goto exit; } const bool supports_stop_all = (mHalInterface != 0) && (mHalInterface->stopAllRecognitions() != -ENOSYS); for (size_t i = 0; i < mModels.size(); i++) { sp<Model> model = mModels.valueAt(i); if (model->mState == Model::STATE_ACTIVE) { if (mHalInterface != 0 && !supports_stop_all) { mHalInterface->stopRecognition(model->mHandle); } // keep model in ACTIVE state so that event is processed by onCallbackEvent() if (model->mType == SOUND_MODEL_TYPE_KEYPHRASE) { struct sound_trigger_phrase_recognition_event event; memset(&event, 0, sizeof(struct sound_trigger_phrase_recognition_event)); event.num_phrases = model->mConfig.num_phrases; for (size_t i = 0; i < event.num_phrases; i++) { event.phrase_extras[i] = model->mConfig.phrases[i]; } event.common.status = RECOGNITION_STATUS_ABORT; event.common.type = model->mType; event.common.model = model->mHandle; event.common.data_size = 0; sp<IMemory> eventMemory = service->prepareRecognitionEvent(&event.common); if (eventMemory != 0) { events.add(eventMemory); } } else if (model->mType == SOUND_MODEL_TYPE_GENERIC) { struct sound_trigger_generic_recognition_event event; memset(&event, 0, sizeof(struct sound_trigger_generic_recognition_event)); event.common.status = RECOGNITION_STATUS_ABORT; event.common.type = model->mType; event.common.model = model->mHandle; event.common.data_size = 0; sp<IMemory> eventMemory = service->prepareRecognitionEvent(&event.common); if (eventMemory != 0) { events.add(eventMemory); } } else if (model->mType == SOUND_MODEL_TYPE_UNKNOWN) { struct sound_trigger_phrase_recognition_event event; memset(&event, 0, sizeof(struct sound_trigger_phrase_recognition_event)); event.common.status = RECOGNITION_STATUS_ABORT; event.common.type = model->mType; event.common.model = model->mHandle; event.common.data_size = 0; sp<IMemory> eventMemory = service->prepareRecognitionEvent(&event.common); if (eventMemory != 0) { events.add(eventMemory); } } else { goto exit; } } } } for (size_t i = 0; i < events.size(); i++) { sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_RECOGNITION, events[i]); callbackEvent->setModule(this); service->sendCallbackEvent(callbackEvent); } exit: service->sendServiceStateEvent(state, this); } SoundTriggerHwService::Model::Model(sound_model_handle_t handle, audio_session_t session, audio_io_handle_t ioHandle, audio_devices_t device, sound_trigger_sound_model_type_t type, sp<ModuleClient>& moduleClient) : mHandle(handle), mState(STATE_IDLE), mCaptureSession(session), mCaptureIOHandle(ioHandle), mCaptureDevice(device), mType(type), mModuleClient(moduleClient) { } #undef LOG_TAG #define LOG_TAG "SoundTriggerHwService::ModuleClient" SoundTriggerHwService::ModuleClient::ModuleClient(const sp<Module>& module, const sp<ISoundTriggerClient>& client, const String16& opPackageName) : mModule(module), mClient(client), mOpPackageName(opPackageName) { } void SoundTriggerHwService::ModuleClient::onFirstRef() { sp<IBinder> binder = IInterface::asBinder(mClient); if (binder != 0) { binder->linkToDeath(this); } } SoundTriggerHwService::ModuleClient::~ModuleClient() { } status_t SoundTriggerHwService::ModuleClient::dump(int fd __unused, const Vector<String16>& args __unused) { String8 result; return NO_ERROR; } void SoundTriggerHwService::ModuleClient::detach() { ALOGV("detach()"); if (!captureHotwordAllowed(mOpPackageName, IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid())) { return; } { AutoMutex lock(mLock); if (mClient != 0) { IInterface::asBinder(mClient)->unlinkToDeath(this); mClient.clear(); } } sp<Module> module = mModule.promote(); if (module == 0) { return; } module->detach(this); } status_t SoundTriggerHwService::ModuleClient::loadSoundModel(const sp<IMemory>& modelMemory, sound_model_handle_t *handle) { ALOGV("loadSoundModel() handle"); if (!captureHotwordAllowed(mOpPackageName, IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid())) { return PERMISSION_DENIED; } if (checkIMemory(modelMemory) != NO_ERROR) { return BAD_VALUE; } sp<Module> module = mModule.promote(); if (module == 0) { return NO_INIT; } return module->loadSoundModel(modelMemory, this, handle); } status_t SoundTriggerHwService::ModuleClient::unloadSoundModel(sound_model_handle_t handle) { ALOGV("unloadSoundModel() model handle %d", handle); if (!captureHotwordAllowed(mOpPackageName, IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid())) { return PERMISSION_DENIED; } sp<Module> module = mModule.promote(); if (module == 0) { return NO_INIT; } return module->unloadSoundModel(handle); } status_t SoundTriggerHwService::ModuleClient::startRecognition(sound_model_handle_t handle, const sp<IMemory>& dataMemory) { ALOGV("startRecognition() model handle %d", handle); if (!captureHotwordAllowed(mOpPackageName, IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid())) { return PERMISSION_DENIED; } if (checkIMemory(dataMemory) != NO_ERROR) { return BAD_VALUE; } sp<Module> module = mModule.promote(); if (module == 0) { return NO_INIT; } return module->startRecognition(handle, dataMemory); } status_t SoundTriggerHwService::ModuleClient::stopRecognition(sound_model_handle_t handle) { ALOGV("stopRecognition() model handle %d", handle); if (!captureHotwordAllowed(mOpPackageName, IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid())) { return PERMISSION_DENIED; } sp<Module> module = mModule.promote(); if (module == 0) { return NO_INIT; } return module->stopRecognition(handle); } status_t SoundTriggerHwService::ModuleClient::getModelState(sound_model_handle_t handle) { ALOGV("getModelState() model handle %d", handle); if (!captureHotwordAllowed(mOpPackageName, IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid())) { return PERMISSION_DENIED; } sp<Module> module = mModule.promote(); if (module == 0) { return NO_INIT; } return module->getModelState(handle); } void SoundTriggerHwService::ModuleClient::setCaptureState_l(bool active) { ALOGV("ModuleClient::setCaptureState_l %d", active); sp<SoundTriggerHwService> service; sound_trigger_service_state_t state; sp<Module> module = mModule.promote(); if (module == 0) { return; } { AutoMutex lock(mLock); state = (active && !module->isConcurrentCaptureAllowed()) ? SOUND_TRIGGER_STATE_DISABLED : SOUND_TRIGGER_STATE_ENABLED; service = module->service().promote(); if (service == 0) { return; } } service->sendServiceStateEvent(state, this); } void SoundTriggerHwService::ModuleClient::onCallbackEvent(const sp<CallbackEvent>& event) { ALOGV("ModuleClient onCallbackEvent type %d", event->mType); sp<IMemory> eventMemory = event->mMemory; if (eventMemory == 0 || eventMemory->pointer() == NULL) { return; } sp<ISoundTriggerClient> client; { AutoMutex lock(mLock); client = mClient; } if (client != 0) { switch (event->mType) { case CallbackEvent::TYPE_RECOGNITION: { client->onRecognitionEvent(eventMemory); } break; case CallbackEvent::TYPE_SOUNDMODEL: { client->onSoundModelEvent(eventMemory); } break; case CallbackEvent::TYPE_SERVICE_STATE: { client->onServiceStateChange(eventMemory); } break; default: LOG_ALWAYS_FATAL("onCallbackEvent unknown event type %d", event->mType); } } } void SoundTriggerHwService::ModuleClient::binderDied( const wp<IBinder> &who __unused) { ALOGW("client binder died for client %p", this); detach(); } }; // namespace android