diff options
author | Eric Laurent <elaurent@google.com> | 2014-04-18 17:40:41 -0700 |
---|---|---|
committer | Eric Laurent <elaurent@google.com> | 2014-06-04 15:34:20 -0700 |
commit | b7a11d83f749ad0200778c4815e907d011d4b5d3 (patch) | |
tree | 28c744c0e8fe9c1f198ddd0999b4c5d95de9610a | |
parent | 24c01a8417fe195e5ba2187dbbdf9bd1e3a6553f (diff) | |
download | frameworks_av-b7a11d83f749ad0200778c4815e907d011d4b5d3.zip frameworks_av-b7a11d83f749ad0200778c4815e907d011d4b5d3.tar.gz frameworks_av-b7a11d83f749ad0200778c4815e907d011d4b5d3.tar.bz2 |
add sound trigger native service
Change-Id: I0cd954c1c7d28a334e786d0004431d4f6a1227ec
-rw-r--r-- | include/soundtrigger/ISoundTrigger.h | 59 | ||||
-rw-r--r-- | include/soundtrigger/ISoundTriggerClient.h | 49 | ||||
-rw-r--r-- | include/soundtrigger/ISoundTriggerHwService.h | 57 | ||||
-rw-r--r-- | include/soundtrigger/SoundTrigger.h | 75 | ||||
-rw-r--r-- | include/soundtrigger/SoundTriggerCallback.h | 40 | ||||
-rw-r--r-- | media/mediaserver/Android.mk | 6 | ||||
-rw-r--r-- | media/mediaserver/main_mediaserver.cpp | 2 | ||||
-rw-r--r-- | services/soundtrigger/Android.mk | 41 | ||||
-rw-r--r-- | services/soundtrigger/SoundTriggerHwService.cpp | 566 | ||||
-rw-r--r-- | services/soundtrigger/SoundTriggerHwService.h | 185 | ||||
-rw-r--r-- | soundtrigger/Android.mk | 38 | ||||
-rw-r--r-- | soundtrigger/ISoundTrigger.cpp | 177 | ||||
-rw-r--r-- | soundtrigger/ISoundTriggerClient.cpp | 75 | ||||
-rw-r--r-- | soundtrigger/ISoundTriggerHwService.cpp | 150 | ||||
-rw-r--r-- | soundtrigger/SoundTrigger.cpp | 253 |
15 files changed, 1771 insertions, 2 deletions
diff --git a/include/soundtrigger/ISoundTrigger.h b/include/soundtrigger/ISoundTrigger.h new file mode 100644 index 0000000..5fd8eb2 --- /dev/null +++ b/include/soundtrigger/ISoundTrigger.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_ISOUNDTRIGGER_H +#define ANDROID_HARDWARE_ISOUNDTRIGGER_H + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> +#include <binder/IMemory.h> +#include <system/sound_trigger.h> + +namespace android { + +class ISoundTrigger : public IInterface +{ +public: + DECLARE_META_INTERFACE(SoundTrigger); + + virtual void detach() = 0; + + virtual status_t loadSoundModel(const sp<IMemory>& modelMemory, + sound_model_handle_t *handle) = 0; + + virtual status_t unloadSoundModel(sound_model_handle_t handle) = 0; + + virtual status_t startRecognition(sound_model_handle_t handle, + const sp<IMemory>& dataMemory) = 0; + virtual status_t stopRecognition(sound_model_handle_t handle) = 0; + +}; + +// ---------------------------------------------------------------------------- + +class BnSoundTrigger: public BnInterface<ISoundTrigger> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_ISOUNDTRIGGER_H diff --git a/include/soundtrigger/ISoundTriggerClient.h b/include/soundtrigger/ISoundTriggerClient.h new file mode 100644 index 0000000..7f86d02 --- /dev/null +++ b/include/soundtrigger/ISoundTriggerClient.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_ISOUNDTRIGGER_CLIENT_H +#define ANDROID_HARDWARE_ISOUNDTRIGGER_CLIENT_H + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +namespace android { + +class ISoundTriggerClient : public IInterface +{ +public: + + DECLARE_META_INTERFACE(SoundTriggerClient); + + virtual void onRecognitionEvent(const sp<IMemory>& eventMemory) = 0; + +}; + +// ---------------------------------------------------------------------------- + +class BnSoundTriggerClient : public BnInterface<ISoundTriggerClient> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_ISOUNDTRIGGER_CLIENT_H diff --git a/include/soundtrigger/ISoundTriggerHwService.h b/include/soundtrigger/ISoundTriggerHwService.h new file mode 100644 index 0000000..05a764a --- /dev/null +++ b/include/soundtrigger/ISoundTriggerHwService.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_ISOUNDTRIGGER_SERVICE_H +#define ANDROID_HARDWARE_ISOUNDTRIGGER_SERVICE_H + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> +#include <system/sound_trigger.h> + +namespace android { + +class ISoundTrigger; +class ISoundTriggerClient; + +class ISoundTriggerHwService : public IInterface +{ +public: + + DECLARE_META_INTERFACE(SoundTriggerHwService); + + virtual status_t listModules(struct sound_trigger_module_descriptor *modules, + uint32_t *numModules) = 0; + + virtual status_t attach(const sound_trigger_module_handle_t handle, + const sp<ISoundTriggerClient>& client, + sp<ISoundTrigger>& module) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnSoundTriggerHwService: public BnInterface<ISoundTriggerHwService> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_ISOUNDTRIGGER_SERVICE_H diff --git a/include/soundtrigger/SoundTrigger.h b/include/soundtrigger/SoundTrigger.h new file mode 100644 index 0000000..1f7f286 --- /dev/null +++ b/include/soundtrigger/SoundTrigger.h @@ -0,0 +1,75 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_SOUNDTRIGGER_H +#define ANDROID_HARDWARE_SOUNDTRIGGER_H + +#include <binder/IBinder.h> +#include <soundtrigger/SoundTriggerCallback.h> +#include <soundtrigger/ISoundTrigger.h> +#include <soundtrigger/ISoundTriggerHwService.h> +#include <soundtrigger/ISoundTriggerClient.h> +#include <system/sound_trigger.h> + +namespace android { + +class MemoryDealer; + +class SoundTrigger : public BnSoundTriggerClient, + public IBinder::DeathRecipient +{ +public: + static status_t listModules(struct sound_trigger_module_descriptor *modules, + uint32_t *numModules); + static sp<SoundTrigger> attach(const sound_trigger_module_handle_t module, + const sp<SoundTriggerCallback>& callback); + + virtual ~SoundTrigger(); + + void detach(); + + status_t loadSoundModel(const sp<IMemory>& modelMemory, + sound_model_handle_t *handle); + + status_t unloadSoundModel(sound_model_handle_t handle); + + status_t startRecognition(sound_model_handle_t handle, const sp<IMemory>& dataMemory); + status_t stopRecognition(sound_model_handle_t handle); + + // BpSoundTriggerClient + virtual void onRecognitionEvent(const sp<IMemory>& eventMemory); + + //IBinder::DeathRecipient + virtual void binderDied(const wp<IBinder>& who); + + static status_t stringToGuid(const char *str, sound_trigger_uuid_t *guid); + static status_t guidToString(const sound_trigger_uuid_t *guid, + char *str, size_t maxLen); + +private: + SoundTrigger(sound_trigger_module_handle_t module, + const sp<SoundTriggerCallback>&); + static const sp<ISoundTriggerHwService>& getSoundTriggerHwService(); + + Mutex mLock; + sp<ISoundTrigger> mISoundTrigger; + const sound_trigger_module_handle_t mModule; + sp<SoundTriggerCallback> mCallback; +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_SOUNDTRIGGER_H diff --git a/include/soundtrigger/SoundTriggerCallback.h b/include/soundtrigger/SoundTriggerCallback.h new file mode 100644 index 0000000..8a5ba02 --- /dev/null +++ b/include/soundtrigger/SoundTriggerCallback.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_SOUNDTRIGGER_CALLBACK_H +#define ANDROID_HARDWARE_SOUNDTRIGGER_CALLBACK_H + +#include <utils/RefBase.h> +#include <system/sound_trigger.h> + +namespace android { + +class SoundTriggerCallback : public RefBase +{ +public: + + SoundTriggerCallback() {} + virtual ~SoundTriggerCallback() {} + + virtual void onRecognitionEvent(struct sound_trigger_recognition_event *event) = 0; + + virtual void onServiceDied() = 0; + +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_SOUNDTRIGGER_CALLBACK_H diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk index 786bf0d..3a280f0 100644 --- a/media/mediaserver/Android.mk +++ b/media/mediaserver/Android.mk @@ -25,7 +25,8 @@ LOCAL_SHARED_LIBRARIES := \ libmediaplayerservice \ libutils \ liblog \ - libbinder + libbinder \ + libsoundtriggerservice LOCAL_STATIC_LIBRARIES := \ libregistermsext @@ -36,7 +37,8 @@ LOCAL_C_INCLUDES := \ frameworks/av/services/audioflinger \ frameworks/av/services/audiopolicy \ frameworks/av/services/camera/libcameraservice \ - $(call include-path-for, audio-utils) + $(call include-path-for, audio-utils) \ + frameworks/av/services/soundtrigger LOCAL_MODULE:= mediaserver LOCAL_32_BIT_ONLY := true diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp index a347951..af1c9e6 100644 --- a/media/mediaserver/main_mediaserver.cpp +++ b/media/mediaserver/main_mediaserver.cpp @@ -34,6 +34,7 @@ #include "MediaLogService.h" #include "MediaPlayerService.h" #include "AudioPolicyService.h" +#include "SoundTriggerHwService.h" using namespace android; @@ -128,6 +129,7 @@ int main(int argc __unused, char** argv) MediaPlayerService::instantiate(); CameraService::instantiate(); AudioPolicyService::instantiate(); + SoundTriggerHwService::instantiate(); registerExtensions(); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); 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 <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 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 <utils/Vector.h> +//#include <binder/AppOpsManager.h> +#include <binder/MemoryDealer.h> +#include <binder/BinderService.h> +#include <binder/IAppOpsCallback.h> +#include <soundtrigger/ISoundTriggerHwService.h> +#include <soundtrigger/ISoundTrigger.h> +#include <soundtrigger/ISoundTriggerClient.h> +#include <system/sound_trigger.h> +#include <hardware/sound_trigger.h> + +namespace android { + +class MemoryHeapBase; + +class SoundTriggerHwService : + public BinderService<SoundTriggerHwService>, + public BnSoundTriggerHwService +{ + friend class BinderService<SoundTriggerHwService>; +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<ISoundTriggerClient>& client, + sp<ISoundTrigger>& module); + + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + + virtual status_t dump(int fd, const Vector<String16>& args); + + class Model : public RefBase { + public: + + enum { + STATE_IDLE, + STATE_ACTIVE + }; + + Model(sound_model_handle_t handle); + ~Model() {} + + sp<IMemory> allocateMemory(size_t size); + void deallocateMemory(); + + sound_model_handle_t mHandle; + int mState; + audio_io_handle_t mInputHandle; + audio_session_t mCaptureSession; + sp<MemoryDealer> mMemoryDealer; + }; + + class Module : public virtual RefBase, + public BnSoundTrigger, + public IBinder::DeathRecipient { + public: + + Module(const sp<SoundTriggerHwService>& service, + sound_trigger_hw_device* hwDevice, + sound_trigger_module_descriptor descriptor, + const sp<ISoundTriggerClient>& client); + + virtual ~Module(); + + virtual void detach(); + + virtual status_t loadSoundModel(const sp<IMemory>& 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<IMemory>& dataMemory); + virtual status_t stopRecognition(sound_model_handle_t handle); + + virtual status_t dump(int fd, const Vector<String16>& args); + + + sound_trigger_hw_device *hwDevice() const { return mHwDevice; } + struct sound_trigger_module_descriptor descriptor() { return mDescriptor; } + void setClient(sp<ISoundTriggerClient> client) { mClient = client; } + void clearClient() { mClient.clear(); } + sp<ISoundTriggerClient> client() { return mClient; } + + void sendRecognitionEvent(struct sound_trigger_recognition_event *event); + void onRecognitionEvent(sp<IMemory> eventMemory); + + sp<Model> getModel(sound_model_handle_t handle); + + // IBinder::DeathRecipient implementation + virtual void binderDied(const wp<IBinder> &who); + + private: + Mutex mLock; + wp<SoundTriggerHwService> mService; + struct sound_trigger_hw_device* mHwDevice; + struct sound_trigger_module_descriptor mDescriptor; + sp<ISoundTriggerClient> mClient; + DefaultKeyedVector< sound_model_handle_t, sp<Model> > mModels; + }; // class Module + + class RecognitionEvent : public RefBase { + public: + + RecognitionEvent(sp<IMemory> eventMemory, wp<Module> module); + + virtual ~RecognitionEvent(); + + sp<IMemory> mEventMemory; + wp<Module> mModule; + }; + + class CallbackThread : public Thread { + public: + + CallbackThread(const wp<SoundTriggerHwService>& service); + + virtual ~CallbackThread(); + + // Thread virtuals + virtual bool threadLoop(); + + // RefBase + virtual void onFirstRef(); + + void exit(); + void sendRecognitionEvent(const sp<RecognitionEvent>& event); + + private: + wp<SoundTriggerHwService> mService; + Condition mCallbackCond; + Mutex mCallbackLock; + Vector< sp<RecognitionEvent> > mEventQueue; + }; + + void detachModule(sp<Module> module); + + static void recognitionCallback(struct sound_trigger_recognition_event *event, void *cookie); + void sendRecognitionEvent(const sp<RecognitionEvent>& event); + void onRecognitionEvent(const sp<RecognitionEvent>& 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<Module> > mModules; + sp<CallbackThread> mCallbackThread; +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H diff --git a/soundtrigger/Android.mk b/soundtrigger/Android.mk new file mode 100644 index 0000000..d91c4c2 --- /dev/null +++ b/soundtrigger/Android.mk @@ -0,0 +1,38 @@ +# 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) + +LOCAL_SRC_FILES:= \ + SoundTrigger.cpp \ + ISoundTrigger.cpp \ + ISoundTriggerClient.cpp \ + ISoundTriggerHwService.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + liblog \ + libbinder \ + libhardware + +#LOCAL_C_INCLUDES += \ + system/media/camera/include \ + system/media/private/camera/include + +LOCAL_MODULE:= libsoundtrigger + +include $(BUILD_SHARED_LIBRARY) diff --git a/soundtrigger/ISoundTrigger.cpp b/soundtrigger/ISoundTrigger.cpp new file mode 100644 index 0000000..42280d1 --- /dev/null +++ b/soundtrigger/ISoundTrigger.cpp @@ -0,0 +1,177 @@ +/* +** +** 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. +*/ + +#define LOG_TAG "ISoundTrigger" +#include <utils/Log.h> +#include <utils/Errors.h> +#include <binder/IMemory.h> +#include <soundtrigger/ISoundTrigger.h> +#include <soundtrigger/ISoundTriggerHwService.h> +#include <soundtrigger/ISoundTriggerClient.h> +#include <system/sound_trigger.h> + +namespace android { + +enum { + DETACH = IBinder::FIRST_CALL_TRANSACTION, + LOAD_SOUND_MODEL, + UNLOAD_SOUND_MODEL, + START_RECOGNITION, + STOP_RECOGNITION, +}; + +class BpSoundTrigger: public BpInterface<ISoundTrigger> +{ +public: + BpSoundTrigger(const sp<IBinder>& impl) + : BpInterface<ISoundTrigger>(impl) + { + } + + void detach() + { + ALOGV("detach"); + Parcel data, reply; + data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor()); + remote()->transact(DETACH, data, &reply); + } + + status_t loadSoundModel(const sp<IMemory>& modelMemory, + sound_model_handle_t *handle) + { + if (modelMemory == 0 || handle == NULL) { + return BAD_VALUE; + } + Parcel data, reply; + data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor()); + data.writeStrongBinder(modelMemory->asBinder()); + status_t status = remote()->transact(LOAD_SOUND_MODEL, data, &reply); + if (status != NO_ERROR || + (status = (status_t)reply.readInt32()) != NO_ERROR) { + return status; + } + reply.read(handle, sizeof(sound_model_handle_t)); + return status; + } + + virtual status_t unloadSoundModel(sound_model_handle_t handle) + { + Parcel data, reply; + data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor()); + data.write(&handle, sizeof(sound_model_handle_t)); + status_t status = remote()->transact(UNLOAD_SOUND_MODEL, data, &reply); + if (status != NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + + virtual status_t startRecognition(sound_model_handle_t handle, + const sp<IMemory>& dataMemory) + { + Parcel data, reply; + data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor()); + data.write(&handle, sizeof(sound_model_handle_t)); + if (dataMemory == 0) { + data.writeInt32(0); + } else { + data.writeInt32(dataMemory->size()); + } + data.writeStrongBinder(dataMemory->asBinder()); + status_t status = remote()->transact(START_RECOGNITION, data, &reply); + if (status != NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + + virtual status_t stopRecognition(sound_model_handle_t handle) + { + Parcel data, reply; + data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor()); + data.write(&handle, sizeof(sound_model_handle_t)); + status_t status = remote()->transact(STOP_RECOGNITION, data, &reply); + if (status != NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + +}; + +IMPLEMENT_META_INTERFACE(SoundTrigger, "android.hardware.ISoundTrigger"); + +// ---------------------------------------------------------------------- + +status_t BnSoundTrigger::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case DETACH: { + ALOGV("DETACH"); + CHECK_INTERFACE(ISoundTrigger, data, reply); + detach(); + return NO_ERROR; + } break; + case LOAD_SOUND_MODEL: { + CHECK_INTERFACE(ISoundTrigger, data, reply); + sp<IMemory> modelMemory = interface_cast<IMemory>( + data.readStrongBinder()); + sound_model_handle_t handle; + status_t status = loadSoundModel(modelMemory, &handle); + reply->writeInt32(status); + if (status == NO_ERROR) { + reply->write(&handle, sizeof(sound_model_handle_t)); + } + return NO_ERROR; + } + case UNLOAD_SOUND_MODEL: { + CHECK_INTERFACE(ISoundTrigger, data, reply); + sound_model_handle_t handle; + data.read(&handle, sizeof(sound_model_handle_t)); + status_t status = unloadSoundModel(handle); + reply->writeInt32(status); + return NO_ERROR; + } + case START_RECOGNITION: { + CHECK_INTERFACE(ISoundTrigger, data, reply); + sound_model_handle_t handle; + data.read(&handle, sizeof(sound_model_handle_t)); + sp<IMemory> dataMemory; + if (data.readInt32() != 0) { + dataMemory = interface_cast<IMemory>(data.readStrongBinder()); + } + status_t status = startRecognition(handle, dataMemory); + reply->writeInt32(status); + return NO_ERROR; + } + case STOP_RECOGNITION: { + CHECK_INTERFACE(ISoundTrigger, data, reply); + sound_model_handle_t handle; + data.read(&handle, sizeof(sound_model_handle_t)); + status_t status = stopRecognition(handle); + reply->writeInt32(status); + return NO_ERROR; + } + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/soundtrigger/ISoundTriggerClient.cpp b/soundtrigger/ISoundTriggerClient.cpp new file mode 100644 index 0000000..1d0c0ec --- /dev/null +++ b/soundtrigger/ISoundTriggerClient.cpp @@ -0,0 +1,75 @@ +/* +** +** 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. +*/ + +#include <stdint.h> +#include <sys/types.h> +#include <binder/IMemory.h> +#include <binder/Parcel.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <soundtrigger/ISoundTriggerClient.h> + +namespace android { + +enum { + ON_RECOGNITION_EVENT = IBinder::FIRST_CALL_TRANSACTION, +}; + +class BpSoundTriggerClient: public BpInterface<ISoundTriggerClient> +{ + +public: + BpSoundTriggerClient(const sp<IBinder>& impl) + : BpInterface<ISoundTriggerClient>(impl) + { + } + + virtual void onRecognitionEvent(const sp<IMemory>& eventMemory) + { + Parcel data, reply; + data.writeInterfaceToken(ISoundTriggerClient::getInterfaceDescriptor()); + data.writeStrongBinder(eventMemory->asBinder()); + remote()->transact(ON_RECOGNITION_EVENT, + data, + &reply); + } +}; + +IMPLEMENT_META_INTERFACE(SoundTriggerClient, + "android.hardware.ISoundTriggerClient"); + +// ---------------------------------------------------------------------- + +status_t BnSoundTriggerClient::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case ON_RECOGNITION_EVENT: { + CHECK_INTERFACE(ISoundTriggerClient, data, reply); + sp<IMemory> eventMemory = interface_cast<IMemory>( + data.readStrongBinder()); + onRecognitionEvent(eventMemory); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/soundtrigger/ISoundTriggerHwService.cpp b/soundtrigger/ISoundTriggerHwService.cpp new file mode 100644 index 0000000..c9a0c24 --- /dev/null +++ b/soundtrigger/ISoundTriggerHwService.cpp @@ -0,0 +1,150 @@ +/* +** +** 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. +*/ + +#define LOG_TAG "BpSoundTriggerHwService" +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Errors.h> + +#include <stdint.h> +#include <sys/types.h> +#include <binder/IMemory.h> +#include <binder/Parcel.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> + +#include <soundtrigger/ISoundTriggerHwService.h> +#include <soundtrigger/ISoundTrigger.h> +#include <soundtrigger/ISoundTriggerClient.h> + +namespace android { + +enum { + LIST_MODULES = IBinder::FIRST_CALL_TRANSACTION, + ATTACH, +}; + +class BpSoundTriggerHwService: public BpInterface<ISoundTriggerHwService> +{ +public: + BpSoundTriggerHwService(const sp<IBinder>& impl) + : BpInterface<ISoundTriggerHwService>(impl) + { + } + + virtual status_t listModules(struct sound_trigger_module_descriptor *modules, + uint32_t *numModules) + { + if (numModules == NULL || (*numModules != 0 && modules == NULL)) { + return BAD_VALUE; + } + Parcel data, reply; + data.writeInterfaceToken(ISoundTriggerHwService::getInterfaceDescriptor()); + unsigned int numModulesReq = (modules == NULL) ? 0 : *numModules; + data.writeInt32(numModulesReq); + status_t status = remote()->transact(LIST_MODULES, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + *numModules = (unsigned int)reply.readInt32(); + } + ALOGV("listModules() status %d got *numModules %d", status, *numModules); + if (status == NO_ERROR) { + if (numModulesReq > *numModules) { + numModulesReq = *numModules; + } + if (numModulesReq > 0) { + reply.read(modules, numModulesReq * sizeof(struct sound_trigger_module_descriptor)); + } + } + return status; + } + + virtual status_t attach(const sound_trigger_module_handle_t handle, + const sp<ISoundTriggerClient>& client, + sp<ISoundTrigger>& module) + { + Parcel data, reply; + data.writeInterfaceToken(ISoundTriggerHwService::getInterfaceDescriptor()); + data.write(&handle, sizeof(sound_trigger_module_handle_t)); + data.writeStrongBinder(client->asBinder()); + remote()->transact(ATTACH, data, &reply); + status_t status = reply.readInt32(); + if (reply.readInt32() != 0) { + module = interface_cast<ISoundTrigger>(reply.readStrongBinder()); + } + return status; + } + +}; + +IMPLEMENT_META_INTERFACE(SoundTriggerHwService, "android.hardware.ISoundTriggerHwService"); + +// ---------------------------------------------------------------------- + +status_t BnSoundTriggerHwService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case LIST_MODULES: { + CHECK_INTERFACE(ISoundTriggerHwService, data, reply); + unsigned int numModulesReq = data.readInt32(); + unsigned int numModules = numModulesReq; + struct sound_trigger_module_descriptor *modules = + (struct sound_trigger_module_descriptor *)calloc(numModulesReq, + sizeof(struct sound_trigger_module_descriptor)); + status_t status = listModules(modules, &numModules); + reply->writeInt32(status); + reply->writeInt32(numModules); + ALOGV("LIST_MODULES status %d got numModules %d", status, numModules); + + if (status == NO_ERROR) { + if (numModulesReq > numModules) { + numModulesReq = numModules; + } + reply->write(modules, + numModulesReq * sizeof(struct sound_trigger_module_descriptor)); + } + free(modules); + return NO_ERROR; + } + + case ATTACH: { + CHECK_INTERFACE(ISoundTriggerHwService, data, reply); + sound_trigger_module_handle_t handle; + data.read(&handle, sizeof(sound_trigger_module_handle_t)); + sp<ISoundTriggerClient> client = + interface_cast<ISoundTriggerClient>(data.readStrongBinder()); + sp<ISoundTrigger> module; + status_t status = attach(handle, client, module); + reply->writeInt32(status); + if (module != 0) { + reply->writeInt32(1); + reply->writeStrongBinder(module->asBinder()); + } else { + reply->writeInt32(0); + } + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/soundtrigger/SoundTrigger.cpp b/soundtrigger/SoundTrigger.cpp new file mode 100644 index 0000000..e43acd0 --- /dev/null +++ b/soundtrigger/SoundTrigger.cpp @@ -0,0 +1,253 @@ +/* +** +** 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 "SoundTrigger" +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/threads.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/IMemory.h> + +#include <soundtrigger/SoundTrigger.h> +#include <soundtrigger/ISoundTrigger.h> +#include <soundtrigger/ISoundTriggerHwService.h> +#include <soundtrigger/ISoundTriggerClient.h> +#include <soundtrigger/SoundTriggerCallback.h> + +namespace android { + +namespace { + sp<ISoundTriggerHwService> gSoundTriggerHwService; + const int kSoundTriggerHwServicePollDelay = 500000; // 0.5s + const char* kSoundTriggerHwServiceName = "media.sound_trigger_hw"; + Mutex gLock; + + class DeathNotifier : public IBinder::DeathRecipient + { + public: + DeathNotifier() { + } + + virtual void binderDied(const wp<IBinder>& who __unused) { + ALOGV("binderDied"); + Mutex::Autolock _l(gLock); + gSoundTriggerHwService.clear(); + ALOGW("Sound trigger service died!"); + } + }; + + sp<DeathNotifier> gDeathNotifier; +}; // namespace anonymous + +const sp<ISoundTriggerHwService>& SoundTrigger::getSoundTriggerHwService() +{ + Mutex::Autolock _l(gLock); + if (gSoundTriggerHwService.get() == 0) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder; + do { + binder = sm->getService(String16(kSoundTriggerHwServiceName)); + if (binder != 0) { + break; + } + ALOGW("SoundTriggerHwService not published, waiting..."); + usleep(kSoundTriggerHwServicePollDelay); + } while(true); + if (gDeathNotifier == NULL) { + gDeathNotifier = new DeathNotifier(); + } + binder->linkToDeath(gDeathNotifier); + gSoundTriggerHwService = interface_cast<ISoundTriggerHwService>(binder); + } + ALOGE_IF(gSoundTriggerHwService == 0, "no SoundTriggerHwService!?"); + return gSoundTriggerHwService; +} + +// Static methods +status_t SoundTrigger::listModules(struct sound_trigger_module_descriptor *modules, + uint32_t *numModules) +{ + ALOGV("listModules()"); + const sp<ISoundTriggerHwService>& service = getSoundTriggerHwService(); + if (service == 0) { + return NO_INIT; + } + return service->listModules(modules, numModules); +} + +sp<SoundTrigger> SoundTrigger::attach(const sound_trigger_module_handle_t module, + const sp<SoundTriggerCallback>& callback) +{ + ALOGV("attach()"); + sp<SoundTrigger> soundTrigger; + const sp<ISoundTriggerHwService>& service = getSoundTriggerHwService(); + if (service == 0) { + return soundTrigger; + } + soundTrigger = new SoundTrigger(module, callback); + status_t status = service->attach(module, soundTrigger, soundTrigger->mISoundTrigger); + + if (status == NO_ERROR && soundTrigger->mISoundTrigger != 0) { + soundTrigger->mISoundTrigger->asBinder()->linkToDeath(soundTrigger); + } else { + ALOGW("Error %d connecting to sound trigger service", status); + soundTrigger.clear(); + } + return soundTrigger; +} + + +// SoundTrigger +SoundTrigger::SoundTrigger(sound_trigger_module_handle_t module, + const sp<SoundTriggerCallback>& callback) + : mModule(module), mCallback(callback) +{ +} + +SoundTrigger::~SoundTrigger() +{ + if (mISoundTrigger != 0) { + mISoundTrigger->detach(); + } +} + + +void SoundTrigger::detach() { + ALOGV("detach()"); + Mutex::Autolock _l(mLock); + mCallback.clear(); + if (mISoundTrigger != 0) { + mISoundTrigger->detach(); + mISoundTrigger->asBinder()->unlinkToDeath(this); + mISoundTrigger = 0; + } +} + +status_t SoundTrigger::loadSoundModel(const sp<IMemory>& modelMemory, + sound_model_handle_t *handle) +{ + Mutex::Autolock _l(mLock); + if (mISoundTrigger == 0) { + return NO_INIT; + } + + return mISoundTrigger->loadSoundModel(modelMemory, handle); +} + +status_t SoundTrigger::unloadSoundModel(sound_model_handle_t handle) +{ + Mutex::Autolock _l(mLock); + if (mISoundTrigger == 0) { + return NO_INIT; + } + return mISoundTrigger->unloadSoundModel(handle); +} + +status_t SoundTrigger::startRecognition(sound_model_handle_t handle, + const sp<IMemory>& dataMemory) +{ + Mutex::Autolock _l(mLock); + if (mISoundTrigger == 0) { + return NO_INIT; + } + return mISoundTrigger->startRecognition(handle, dataMemory); +} + +status_t SoundTrigger::stopRecognition(sound_model_handle_t handle) +{ + Mutex::Autolock _l(mLock); + if (mISoundTrigger == 0) { + return NO_INIT; + } + return mISoundTrigger->stopRecognition(handle); +} + +// BpSoundTriggerClient +void SoundTrigger::onRecognitionEvent(const sp<IMemory>& eventMemory) +{ + Mutex::Autolock _l(mLock); + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + return; + } + + if (mCallback != 0) { + mCallback->onRecognitionEvent( + (struct sound_trigger_recognition_event *)eventMemory->pointer()); + } +} + + +//IBinder::DeathRecipient +void SoundTrigger::binderDied(const wp<IBinder>& who __unused) { + Mutex::Autolock _l(mLock); + ALOGW("SoundTrigger server binder Died "); + mISoundTrigger = 0; + if (mCallback != 0) { + mCallback->onServiceDied(); + } +} + +status_t SoundTrigger::stringToGuid(const char *str, sound_trigger_uuid_t *guid) +{ + if (str == NULL || guid == NULL) { + return BAD_VALUE; + } + + int tmp[10]; + + if (sscanf(str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", + tmp, tmp+1, tmp+2, tmp+3, tmp+4, tmp+5, tmp+6, tmp+7, tmp+8, tmp+9) < 10) { + return BAD_VALUE; + } + guid->timeLow = (uint32_t)tmp[0]; + guid->timeMid = (uint16_t)tmp[1]; + guid->timeHiAndVersion = (uint16_t)tmp[2]; + guid->clockSeq = (uint16_t)tmp[3]; + guid->node[0] = (uint8_t)tmp[4]; + guid->node[1] = (uint8_t)tmp[5]; + guid->node[2] = (uint8_t)tmp[6]; + guid->node[3] = (uint8_t)tmp[7]; + guid->node[4] = (uint8_t)tmp[8]; + guid->node[5] = (uint8_t)tmp[9]; + + return NO_ERROR; +} + +status_t SoundTrigger::guidToString(const sound_trigger_uuid_t *guid, char *str, size_t maxLen) +{ + if (guid == NULL || str == NULL) { + return BAD_VALUE; + } + + snprintf(str, maxLen, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", + guid->timeLow, + guid->timeMid, + guid->timeHiAndVersion, + guid->clockSeq, + guid->node[0], + guid->node[1], + guid->node[2], + guid->node[3], + guid->node[4], + guid->node[5]); + + return NO_ERROR; +} + +}; // namespace android |