audio: add audio HAL service and sound trigger HAL implementation
Audio HAL service is common to audio, audio effect, sound trigger and
bradcast radio HALs.
Bug: 32022304
Change-Id: I3952b9c6de3a6b34e757942120320cf8ae036ed5
diff --git a/soundtrigger/2.0/default/SoundTriggerHalImpl.cpp b/soundtrigger/2.0/default/SoundTriggerHalImpl.cpp
new file mode 100644
index 0000000..1ae996a
--- /dev/null
+++ b/soundtrigger/2.0/default/SoundTriggerHalImpl.cpp
@@ -0,0 +1,595 @@
+/*
+ * Copyright (C) 2016 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 "SoundTriggerHalImpl"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include "SoundTriggerHalImpl.h"
+
+
+namespace android {
+namespace hardware {
+namespace soundtrigger {
+namespace V2_0 {
+namespace implementation {
+
+// static
+void SoundTriggerHalImpl::soundModelCallback(struct sound_trigger_model_event *halEvent,
+ void *cookie)
+{
+ if (halEvent == NULL) {
+ ALOGW("soundModelCallback called with NULL event");
+ return;
+ }
+ sp<SoundModelClient> client =
+ wp<SoundModelClient>(static_cast<SoundModelClient *>(cookie)).promote();
+ if (client == 0) {
+ ALOGW("soundModelCallback called on stale client");
+ return;
+ }
+ if (halEvent->model != client->mHalHandle) {
+ ALOGW("soundModelCallback call with wrong handle %d on client with handle %d",
+ (int)halEvent->model, (int)client->mHalHandle);
+ return;
+ }
+
+ ISoundTriggerHwCallback::ModelEvent event;
+ convertSoundModelEventFromHal(&event, halEvent);
+ event.model = client->mId;
+
+ client->mCallback->soundModelCallback(event, client->mCookie);
+}
+
+// static
+void SoundTriggerHalImpl::recognitionCallback(struct sound_trigger_recognition_event *halEvent,
+ void *cookie)
+{
+ if (halEvent == NULL) {
+ ALOGW("recognitionCallback call NULL event");
+ return;
+ }
+ sp<SoundModelClient> client =
+ wp<SoundModelClient>(static_cast<SoundModelClient *>(cookie)).promote();
+ if (client == 0) {
+ ALOGW("soundModelCallback called on stale client");
+ return;
+ }
+
+ ISoundTriggerHwCallback::RecognitionEvent *event = convertRecognitionEventFromHal(halEvent);
+ event->model = client->mId;
+ if (halEvent->type == SOUND_MODEL_TYPE_KEYPHRASE) {
+ client->mCallback->phraseRecognitionCallback(
+ *(reinterpret_cast<ISoundTriggerHwCallback::PhraseRecognitionEvent *>(event)),
+ client->mCookie);
+ } else {
+ client->mCallback->recognitionCallback(*event, client->mCookie);
+ }
+ delete event;
+}
+
+
+
+// Methods from ::android::hardware::soundtrigger::V2_0::ISoundTriggerHw follow.
+Return<void> SoundTriggerHalImpl::getProperties(getProperties_cb _hidl_cb)
+{
+ ALOGV("getProperties() mHwDevice %p", mHwDevice);
+ int ret;
+ struct sound_trigger_properties halProperties;
+ ISoundTriggerHw::Properties properties;
+
+ if (mHwDevice == NULL) {
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ ret = mHwDevice->get_properties(mHwDevice, &halProperties);
+
+ convertPropertiesFromHal(&properties, &halProperties);
+
+ ALOGV("getProperties implementor %s recognitionModes %08x",
+ properties.implementor.c_str(), properties.recognitionModes);
+
+exit:
+ _hidl_cb(ret, properties);
+ return Void();
+}
+
+int SoundTriggerHalImpl::doLoadSoundModel(const ISoundTriggerHw::SoundModel& soundModel,
+ const sp<ISoundTriggerHwCallback>& callback,
+ ISoundTriggerHwCallback::CallbackCookie cookie,
+ uint32_t *modelId)
+{
+ int32_t ret = 0;
+ struct sound_trigger_sound_model *halSoundModel;
+ *modelId = 0;
+ sp<SoundModelClient> client;
+
+ ALOGV("doLoadSoundModel() data size %zu", soundModel.data.size());
+
+ if (mHwDevice == NULL) {
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ halSoundModel = convertSoundModelToHal(&soundModel);
+ if (halSoundModel == NULL) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ {
+ AutoMutex lock(mLock);
+ do {
+ *modelId = nextUniqueId();
+ } while (mClients.valueFor(*modelId) != 0 && *modelId != 0);
+ }
+ LOG_ALWAYS_FATAL_IF(*modelId == 0,
+ "wrap around in sound model IDs, num loaded models %d", mClients.size());
+
+ client = new SoundModelClient(*modelId, callback, cookie);
+
+ ret = mHwDevice->load_sound_model(mHwDevice, halSoundModel, soundModelCallback,
+ client.get(), &client->mHalHandle);
+
+ free(halSoundModel);
+
+ if (ret != 0) {
+ goto exit;
+ }
+
+ {
+ AutoMutex lock(mLock);
+ mClients.add(*modelId, client);
+ }
+
+exit:
+ return ret;
+}
+
+Return<void> SoundTriggerHalImpl::loadSoundModel(const ISoundTriggerHw::SoundModel& soundModel,
+ const sp<ISoundTriggerHwCallback>& callback,
+ ISoundTriggerHwCallback::CallbackCookie cookie,
+ loadSoundModel_cb _hidl_cb)
+{
+ uint32_t modelId = 0;
+ int32_t ret = doLoadSoundModel(soundModel, callback, cookie, &modelId);
+
+ _hidl_cb(ret, modelId);
+ return Void();
+}
+
+Return<void> SoundTriggerHalImpl::loadPhraseSoundModel(
+ const ISoundTriggerHw::PhraseSoundModel& soundModel,
+ const sp<ISoundTriggerHwCallback>& callback,
+ ISoundTriggerHwCallback::CallbackCookie cookie,
+ ISoundTriggerHw::loadPhraseSoundModel_cb _hidl_cb)
+{
+ uint32_t modelId = 0;
+ int32_t ret = doLoadSoundModel((const ISoundTriggerHw::SoundModel&)soundModel,
+ callback, cookie, &modelId);
+
+ _hidl_cb(ret, modelId);
+ return Void();
+}
+
+Return<int32_t> SoundTriggerHalImpl::unloadSoundModel(SoundModelHandle modelHandle)
+{
+ int32_t ret;
+ sp<SoundModelClient> client;
+
+ if (mHwDevice == NULL) {
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ {
+ AutoMutex lock(mLock);
+ client = mClients.valueFor(modelHandle);
+ if (client == 0) {
+ ret = -ENOSYS;
+ goto exit;
+ }
+ }
+
+ ret = mHwDevice->unload_sound_model(mHwDevice, client->mHalHandle);
+
+ mClients.removeItem(modelHandle);
+
+exit:
+ return ret;
+}
+
+Return<int32_t> SoundTriggerHalImpl::startRecognition(SoundModelHandle modelHandle,
+ const ISoundTriggerHw::RecognitionConfig& config,
+ const sp<ISoundTriggerHwCallback>& callback __unused,
+ ISoundTriggerHwCallback::CallbackCookie cookie __unused)
+{
+ int32_t ret;
+ sp<SoundModelClient> client;
+ struct sound_trigger_recognition_config *halConfig;
+
+ if (mHwDevice == NULL) {
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ {
+ AutoMutex lock(mLock);
+ client = mClients.valueFor(modelHandle);
+ if (client == 0) {
+ ret = -ENOSYS;
+ goto exit;
+ }
+ }
+
+
+ halConfig = convertRecognitionConfigToHal(&config);
+
+ if (halConfig == NULL) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ ret = mHwDevice->start_recognition(mHwDevice, client->mHalHandle, halConfig,
+ recognitionCallback, client.get());
+
+ free(halConfig);
+
+exit:
+ return ret;
+}
+
+Return<int32_t> SoundTriggerHalImpl::stopRecognition(SoundModelHandle modelHandle)
+{
+ int32_t ret;
+ sp<SoundModelClient> client;
+ if (mHwDevice == NULL) {
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ {
+ AutoMutex lock(mLock);
+ client = mClients.valueFor(modelHandle);
+ if (client == 0) {
+ ret = -ENOSYS;
+ goto exit;
+ }
+ }
+
+ ret = mHwDevice->stop_recognition(mHwDevice, client->mHalHandle);
+
+exit:
+ return ret;
+}
+
+Return<int32_t> SoundTriggerHalImpl::stopAllRecognitions()
+{
+ int32_t ret;
+ if (mHwDevice == NULL) {
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ if (mHwDevice->common.version >= SOUND_TRIGGER_DEVICE_API_VERSION_1_1 &&
+ mHwDevice->stop_all_recognitions) {
+ ret = mHwDevice->stop_all_recognitions(mHwDevice);
+ } else {
+ ret = -ENOSYS;
+ }
+exit:
+ return ret;
+}
+
+SoundTriggerHalImpl::SoundTriggerHalImpl(const char *moduleName)
+ : mModuleName(moduleName), mNextModelId(1)
+{
+}
+
+void SoundTriggerHalImpl::onFirstRef()
+{
+ const hw_module_t *mod;
+ int rc;
+
+ if (mModuleName == NULL || strlen(mModuleName) == 0) {
+ mModuleName = "primary";
+ }
+ rc = hw_get_module_by_class(SOUND_TRIGGER_HARDWARE_MODULE_ID, mModuleName, &mod);
+ if (rc != 0) {
+ ALOGE("couldn't load sound trigger module %s.%s (%s)",
+ SOUND_TRIGGER_HARDWARE_MODULE_ID, mModuleName, strerror(-rc));
+ return;
+ }
+ rc = sound_trigger_hw_device_open(mod, &mHwDevice);
+ if (rc != 0) {
+ ALOGE("couldn't open sound trigger hw device in %s.%s (%s)",
+ SOUND_TRIGGER_HARDWARE_MODULE_ID, mModuleName, strerror(-rc));
+ mHwDevice = NULL;
+ return;
+ }
+ if (mHwDevice->common.version < SOUND_TRIGGER_DEVICE_API_VERSION_1_0 ||
+ mHwDevice->common.version > SOUND_TRIGGER_DEVICE_API_VERSION_CURRENT) {
+ ALOGE("wrong sound trigger hw device version %04x", mHwDevice->common.version);
+ sound_trigger_hw_device_close(mHwDevice);
+ mHwDevice = NULL;
+ return;
+ }
+
+ ALOGI("onFirstRef() mModuleName %s mHwDevice %p", mModuleName, mHwDevice);
+}
+
+SoundTriggerHalImpl::~SoundTriggerHalImpl()
+{
+ if (mHwDevice != NULL) {
+ sound_trigger_hw_device_close(mHwDevice);
+ }
+}
+
+uint32_t SoundTriggerHalImpl::nextUniqueId()
+{
+ return (uint32_t) atomic_fetch_add_explicit(&mNextModelId,
+ (uint_fast32_t) 1, memory_order_acq_rel);
+}
+
+void SoundTriggerHalImpl::convertUuidFromHal(Uuid *uuid,
+ const sound_trigger_uuid_t *halUuid)
+{
+ uuid->timeLow = halUuid->timeLow;
+ uuid->timeMid = halUuid->timeMid;
+ uuid->versionAndTimeHigh = halUuid->timeHiAndVersion;
+ uuid->variantAndClockSeqHigh = halUuid->clockSeq;
+ memcpy(&uuid->node[0], &halUuid->node[0], 6);
+}
+
+void SoundTriggerHalImpl::convertUuidToHal(sound_trigger_uuid_t *halUuid,
+ const Uuid *uuid)
+{
+ halUuid->timeLow = uuid->timeLow;
+ halUuid->timeMid = uuid->timeMid;
+ halUuid->timeHiAndVersion = uuid->versionAndTimeHigh;
+ halUuid->clockSeq = uuid->variantAndClockSeqHigh;
+ memcpy(&halUuid->node[0], &uuid->node[0], 6);
+}
+
+void SoundTriggerHalImpl::convertPropertiesFromHal(
+ ISoundTriggerHw::Properties *properties,
+ const struct sound_trigger_properties *halProperties)
+{
+ properties->implementor = halProperties->implementor;
+ properties->description = halProperties->description;
+ properties->version = halProperties->version;
+ convertUuidFromHal(&properties->uuid, &halProperties->uuid);
+ properties->maxSoundModels = halProperties->max_sound_models;
+ properties->maxKeyPhrases = halProperties->max_key_phrases;
+ properties->maxUsers = halProperties->max_users;
+ properties->recognitionModes = halProperties->recognition_modes;
+ properties->captureTransition = halProperties->capture_transition;
+ properties->maxBufferMs = halProperties->max_buffer_ms;
+ properties->concurrentCapture = halProperties->concurrent_capture;
+ properties->triggerInEvent = halProperties->trigger_in_event;
+ properties->powerConsumptionMw = halProperties->power_consumption_mw;
+
+}
+
+void SoundTriggerHalImpl::convertTriggerPhraseToHal(
+ struct sound_trigger_phrase *halTriggerPhrase,
+ const ISoundTriggerHw::Phrase *triggerPhrase)
+{
+ halTriggerPhrase->id = triggerPhrase->id;
+ halTriggerPhrase->recognition_mode = triggerPhrase->recognitionModes;
+ unsigned int i;
+ for (i = 0; i < triggerPhrase->users.size(); i++) {
+ halTriggerPhrase->users[i] = triggerPhrase->users[i];
+ }
+ halTriggerPhrase->num_users = i;
+
+ strlcpy(halTriggerPhrase->locale,
+ triggerPhrase->locale.c_str(), SOUND_TRIGGER_MAX_LOCALE_LEN);
+ strlcpy(halTriggerPhrase->text,
+ triggerPhrase->text.c_str(), SOUND_TRIGGER_MAX_STRING_LEN);
+}
+
+struct sound_trigger_sound_model *SoundTriggerHalImpl::convertSoundModelToHal(
+ const ISoundTriggerHw::SoundModel *soundModel)
+{
+ struct sound_trigger_sound_model *halModel = NULL;
+ if (soundModel->type == SoundModelType::KEYPHRASE) {
+ size_t allocSize =
+ sizeof(struct sound_trigger_phrase_sound_model) + soundModel->data.size();
+ struct sound_trigger_phrase_sound_model *halKeyPhraseModel =
+ static_cast<struct sound_trigger_phrase_sound_model *>(malloc(allocSize));
+ LOG_ALWAYS_FATAL_IF(halKeyPhraseModel == NULL,
+ "malloc failed for size %zu in convertSoundModelToHal PHRASE", allocSize);
+
+ const ISoundTriggerHw::PhraseSoundModel *keyPhraseModel =
+ reinterpret_cast<const ISoundTriggerHw::PhraseSoundModel *>(soundModel);
+
+ size_t i;
+ for (i = 0; i < keyPhraseModel->phrases.size() && i < SOUND_TRIGGER_MAX_PHRASES; i++) {
+ convertTriggerPhraseToHal(&halKeyPhraseModel->phrases[i],
+ &keyPhraseModel->phrases[i]);
+ }
+ halKeyPhraseModel->num_phrases = (unsigned int)i;
+ halModel = reinterpret_cast<struct sound_trigger_sound_model *>(halKeyPhraseModel);
+ halModel->data_offset = sizeof(struct sound_trigger_phrase_sound_model);
+ } else {
+ size_t allocSize =
+ sizeof(struct sound_trigger_sound_model) + soundModel->data.size();
+ halModel = static_cast<struct sound_trigger_sound_model *>(malloc(allocSize));
+ LOG_ALWAYS_FATAL_IF(halModel == NULL,
+ "malloc failed for size %zu in convertSoundModelToHal GENERIC",
+ allocSize);
+
+ halModel->data_offset = sizeof(struct sound_trigger_sound_model);
+ }
+ halModel->type = (sound_trigger_sound_model_type_t)soundModel->type;
+ convertUuidToHal(&halModel->uuid, &soundModel->uuid);
+ convertUuidToHal(&halModel->vendor_uuid, &soundModel->vendorUuid);
+ halModel->data_size = soundModel->data.size();
+ uint8_t *dst = reinterpret_cast<uint8_t *>(halModel) + halModel->data_offset;
+ const uint8_t *src = reinterpret_cast<const uint8_t *>(&soundModel->data[0]);
+ memcpy(dst, src, soundModel->data.size());
+
+ return halModel;
+}
+
+void SoundTriggerHalImpl::convertPhraseRecognitionExtraToHal(
+ struct sound_trigger_phrase_recognition_extra *halExtra,
+ const PhraseRecognitionExtra *extra)
+{
+ halExtra->id = extra->id;
+ halExtra->recognition_modes = extra->recognitionModes;
+ halExtra->confidence_level = extra->confidenceLevel;
+
+ unsigned int i;
+ for (i = 0; i < extra->levels.size() && i < SOUND_TRIGGER_MAX_USERS; i++) {
+ halExtra->levels[i].user_id = extra->levels[i].userId;
+ halExtra->levels[i].level = extra->levels[i].levelPercent;
+ }
+ halExtra->num_levels = i;
+}
+
+struct sound_trigger_recognition_config *SoundTriggerHalImpl::convertRecognitionConfigToHal(
+ const ISoundTriggerHw::RecognitionConfig *config)
+{
+ size_t allocSize = sizeof(struct sound_trigger_recognition_config) + config->data.size();
+ struct sound_trigger_recognition_config *halConfig =
+ static_cast<struct sound_trigger_recognition_config *>(malloc(allocSize));
+
+ LOG_ALWAYS_FATAL_IF(halConfig == NULL,
+ "malloc failed for size %zu in convertRecognitionConfigToHal",
+ allocSize);
+
+ halConfig->capture_handle = (audio_io_handle_t)config->captureHandle;
+ halConfig->capture_device = (audio_devices_t)config->captureDevice;
+ halConfig->capture_requested = config->captureRequested;
+
+ unsigned int i;
+ for (i = 0; i < config->phrases.size() && i < SOUND_TRIGGER_MAX_PHRASES; i++) {
+ convertPhraseRecognitionExtraToHal(&halConfig->phrases[i],
+ &config->phrases[i]);
+ }
+ halConfig->num_phrases = i;
+
+ halConfig->data_offset = sizeof(struct sound_trigger_recognition_config);
+ halConfig->data_size = config->data.size();
+ uint8_t *dst = reinterpret_cast<uint8_t *>(halConfig) + halConfig->data_offset;
+ const uint8_t *src = reinterpret_cast<const uint8_t *>(&config->data[0]);
+ memcpy(dst, src, config->data.size());
+ return halConfig;
+}
+
+// static
+void SoundTriggerHalImpl::convertSoundModelEventFromHal(ISoundTriggerHwCallback::ModelEvent *event,
+ const struct sound_trigger_model_event *halEvent)
+{
+ event->status = (ISoundTriggerHwCallback::SoundModelStatus)halEvent->status;
+ // event->model to be remapped by called
+ event->data.setToExternal(
+ const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(halEvent)) + halEvent->data_offset,
+ halEvent->data_size);
+}
+
+// static
+ISoundTriggerHwCallback::RecognitionEvent *SoundTriggerHalImpl::convertRecognitionEventFromHal(
+ const struct sound_trigger_recognition_event *halEvent)
+{
+ ISoundTriggerHwCallback::RecognitionEvent * event;
+
+ if (halEvent->type == SOUND_MODEL_TYPE_KEYPHRASE) {
+ const struct sound_trigger_phrase_recognition_event *halPhraseEvent =
+ reinterpret_cast<const struct sound_trigger_phrase_recognition_event *>(halEvent);
+ ISoundTriggerHwCallback::PhraseRecognitionEvent *phraseEvent =
+ new ISoundTriggerHwCallback::PhraseRecognitionEvent();
+
+ PhraseRecognitionExtra *phraseExtras =
+ new PhraseRecognitionExtra[halPhraseEvent->num_phrases];
+ for (unsigned int i = 0; i < halPhraseEvent->num_phrases; i++) {
+ convertPhraseRecognitionExtraFromHal(&phraseExtras[i],
+ &halPhraseEvent->phrase_extras[i]);
+ }
+ phraseEvent->phraseExtras.setToExternal(phraseExtras, halPhraseEvent->num_phrases);
+ // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
+ phraseEvent->phraseExtras.resize(halPhraseEvent->num_phrases);
+ delete[] phraseExtras;
+ event = reinterpret_cast<ISoundTriggerHwCallback::RecognitionEvent *>(phraseEvent);
+ } else {
+ event = new ISoundTriggerHwCallback::RecognitionEvent();
+ }
+
+ event->status = static_cast<ISoundTriggerHwCallback::RecognitionStatus>(halEvent->status);
+ event->type = static_cast<SoundModelType>(halEvent->type);
+ // event->model to be remapped by called
+ event->captureAvailable = halEvent->capture_available;
+ event->captureSession = halEvent->capture_session;
+ event->captureDelayMs = halEvent->capture_delay_ms;
+ event->capturePreambleMs = halEvent->capture_preamble_ms;
+ event->triggerInData = halEvent->trigger_in_data;
+ event->audioConfig.sampleRateHz = halEvent->audio_config.sample_rate;
+ event->audioConfig.channelMask =
+ (audio::common::V2_0::AudioChannelMask)halEvent->audio_config.channel_mask;
+ event->audioConfig.format = (audio::common::V2_0::AudioFormat)halEvent->audio_config.format;
+ event->data.setToExternal(
+ const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(halEvent)) + halEvent->data_offset,
+ halEvent->data_size);
+
+ return event;
+}
+
+// static
+void SoundTriggerHalImpl::convertPhraseRecognitionExtraFromHal(
+ PhraseRecognitionExtra *extra,
+ const struct sound_trigger_phrase_recognition_extra *halExtra)
+{
+ extra->id = halExtra->id;
+ extra->recognitionModes = halExtra->recognition_modes;
+ extra->confidenceLevel = halExtra->confidence_level;
+
+ ConfidenceLevel *levels =
+ new ConfidenceLevel[halExtra->num_levels];
+ for (unsigned int i = 0; i < halExtra->num_levels; i++) {
+ levels[i].userId = halExtra->levels[i].user_id;
+ levels[i].levelPercent = halExtra->levels[i].level;
+ }
+ extra->levels.setToExternal(levels, halExtra->num_levels);
+ // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
+ extra->levels.resize(halExtra->num_levels);
+ delete[] levels;
+}
+
+ISoundTriggerHw *HIDL_FETCH_ISoundTriggerHw(const char *name)
+{
+ if (name != NULL) {
+ if (strncmp(SOUND_TRIGGER_HARDWARE_MODULE_ID, name,
+ strlen(SOUND_TRIGGER_HARDWARE_MODULE_ID)) != 0) {
+ return NULL;
+ }
+ name = strchr(name, '.');
+ if (name == NULL) {
+ return NULL;
+ }
+ name++;
+ }
+ return new SoundTriggerHalImpl(name);
+}
+} // namespace implementation
+} // namespace V2_0
+} // namespace soundtrigger
+} // namespace hardware
+} // namespace android
+
+
+