Add support for treble sound trigger HAL

First implementation of Treble HAL in sound trigger hardware service.

If ENABLE_TREBLE build option is true, the treble HAL and HW
sevice is used. Otherwise the legacy HW module is loaded.

Bug: 30222631

Change-Id: Ibe5be680b7b7a3b261dd62913869e0bb412f438b
diff --git a/services/soundtrigger/SoundTriggerHalHidl.cpp b/services/soundtrigger/SoundTriggerHalHidl.cpp
new file mode 100644
index 0000000..6fe719e
--- /dev/null
+++ b/services/soundtrigger/SoundTriggerHalHidl.cpp
@@ -0,0 +1,628 @@
+/*
+ * 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 "SoundTriggerHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include "SoundTriggerHalHidl.h"
+#include <hidl/IServiceManager.h>
+#include <hwbinder/IPCThreadState.h>
+#include <hwbinder/ProcessState.h>
+
+namespace android {
+
+using android::hardware::Return;
+using android::hardware::ProcessState;
+using android::hardware::audio::common::V2_0::AudioDevice;
+
+pthread_once_t SoundTriggerHalHidl::sOnceControl = PTHREAD_ONCE_INIT;
+
+void SoundTriggerHalHidl::sOnceInit()
+{
+    ProcessState::self()->setThreadPoolMaxThreadCount(1);
+    ProcessState::self()->startThreadPool();
+}
+
+/* static */
+sp<SoundTriggerHalInterface> SoundTriggerHalInterface::connectModule(const char *moduleName)
+{
+    return new SoundTriggerHalHidl(moduleName);
+}
+
+int SoundTriggerHalHidl::getProperties(struct sound_trigger_properties *properties)
+{
+    sp<ISoundTriggerHw> soundtrigger = getService();
+    if (soundtrigger == 0) {
+        return -ENODEV;
+    }
+
+    ISoundTriggerHw::Properties halProperties;
+    Return<void> hidlReturn;
+    int32_t halReturn;
+    {
+        AutoMutex lock(mHalLock);
+        hidlReturn = soundtrigger->getProperties([&](int rc, auto res) {
+            halReturn = rc;
+            halProperties = res;
+            ALOGI("getProperties res implementor %s", res.implementor.c_str());
+        });
+    }
+
+    int ret = 0;
+    if (hidlReturn.getStatus().isOk()) {
+        convertPropertiesFromHal(properties, &halProperties);
+    } else {
+        ret = (int)hidlReturn.getStatus().transactionError();
+        if (ret == -EPIPE) {
+            clearService();
+        }
+    }
+
+    return ret;
+}
+
+int SoundTriggerHalHidl::loadSoundModel(struct sound_trigger_sound_model *sound_model,
+                        sound_model_callback_t callback,
+                        void *cookie,
+                        sound_model_handle_t *handle)
+{
+    if (handle == NULL) {
+        return -EINVAL;
+    }
+
+    sp<ISoundTriggerHw> soundtrigger = getService();
+    if (soundtrigger == 0) {
+        return -ENODEV;
+    }
+
+    uint32_t modelId;
+    {
+        AutoMutex lock(mLock);
+        do {
+            modelId = nextUniqueId();
+            ALOGI("loadSoundModel modelId %u", modelId);
+            sp<SoundModel> model = mSoundModels.valueFor(modelId);
+            ALOGI("loadSoundModel model %p", model.get());
+        } while (mSoundModels.valueFor(modelId) != 0 && modelId != 0);
+    }
+    LOG_ALWAYS_FATAL_IF(modelId == 0,
+                        "loadSoundModel(): wrap around in sound model IDs, num loaded models %zd",
+                        mSoundModels.size());
+
+    ISoundTriggerHw::SoundModel *halSoundModel =
+            convertSoundModelToHal(sound_model);
+    if (halSoundModel == NULL) {
+        return -EINVAL;
+    }
+
+    Return<void> hidlReturn;
+    int32_t halReturn;
+    SoundModelHandle halHandle;
+    {
+        AutoMutex lock(mHalLock);
+        if (sound_model->type == SOUND_MODEL_TYPE_KEYPHRASE) {
+            hidlReturn = soundtrigger->loadPhraseSoundModel(
+                    *(const ISoundTriggerHw::PhraseSoundModel *)halSoundModel,
+                    this, modelId, [&](int32_t retval, auto res) {
+                halReturn = retval;
+                halHandle = res;
+            });
+
+        } else {
+            hidlReturn = soundtrigger->loadSoundModel(*halSoundModel,
+                    this, modelId, [&](int32_t retval, auto res) {
+                halReturn = retval;
+                halHandle = res;
+            });
+        }
+    }
+
+    delete halSoundModel;
+
+    int ret = 0;
+    if (hidlReturn.getStatus().isOk()) {
+        AutoMutex lock(mLock);
+        *handle = (sound_model_handle_t)modelId;
+        sp<SoundModel> model = new SoundModel(*handle, callback, cookie, halHandle);
+        mSoundModels.add(*handle, model);
+    } else {
+        ret = (int)hidlReturn.getStatus().transactionError();
+        ALOGE("loadSoundModel error %d", ret);
+        if (ret == -EPIPE) {
+            clearService();
+        }
+    }
+
+
+    return ret;
+}
+
+int SoundTriggerHalHidl::unloadSoundModel(sound_model_handle_t handle)
+{
+    sp<ISoundTriggerHw> soundtrigger = getService();
+    if (soundtrigger == 0) {
+        return -ENODEV;
+    }
+
+    sp<SoundModel> model = removeModel(handle);
+    if (model == 0) {
+        ALOGE("unloadSoundModel model not found for handle %u", handle);
+        return -EINVAL;
+    }
+
+    Return<int32_t> halReturn(0);
+    {
+        AutoMutex lock(mHalLock);
+        halReturn = soundtrigger->unloadSoundModel(model->mHalHandle);
+    }
+
+    int ret = (int)halReturn.getStatus().transactionError();
+    ALOGE_IF(ret != 0, "unloadSoundModel error %d", ret);
+    if (ret == -EPIPE) {
+        clearService();
+    }
+
+    return ret;
+}
+
+int SoundTriggerHalHidl::startRecognition(sound_model_handle_t handle,
+                         const struct sound_trigger_recognition_config *config,
+                         recognition_callback_t callback,
+                         void *cookie)
+{
+    sp<ISoundTriggerHw> soundtrigger = getService();
+    if (soundtrigger == 0) {
+        return -ENODEV;
+    }
+
+    sp<SoundModel> model = getModel(handle);
+    if (model == 0) {
+        ALOGE("startRecognition model not found for handle %u", handle);
+        return -EINVAL;
+    }
+
+    model->mRecognitionCallback = callback;
+    model->mRecognitionCookie = cookie;
+
+    ISoundTriggerHw::RecognitionConfig *halConfig =
+            convertRecognitionConfigToHal(config);
+
+    Return<int32_t> halReturn(0);
+    {
+        AutoMutex lock(mHalLock);
+        halReturn = soundtrigger->startRecognition(model->mHalHandle, *halConfig, this, handle);
+    }
+
+    delete halConfig;
+
+    int ret = (int)halReturn.getStatus().transactionError();
+    ALOGE_IF(ret != 0, "startRecognition error %d", ret);
+    if (ret == -EPIPE) {
+        clearService();
+    }
+    return ret;
+}
+
+int SoundTriggerHalHidl::stopRecognition(sound_model_handle_t handle)
+{
+    sp<ISoundTriggerHw> soundtrigger = getService();
+    if (soundtrigger == 0) {
+        return -ENODEV;
+    }
+
+    sp<SoundModel> model = getModel(handle);
+    if (model == 0) {
+        ALOGE("stopRecognition model not found for handle %u", handle);
+        return -EINVAL;
+    }
+
+    Return<int32_t> halReturn(0);
+    {
+        AutoMutex lock(mHalLock);
+        halReturn = soundtrigger->stopRecognition(model->mHalHandle);
+    }
+
+    int ret = (int)halReturn.getStatus().transactionError();
+    ALOGE_IF(ret != 0, "stopRecognition error %d", ret);
+    if (ret == -EPIPE) {
+        clearService();
+    }
+    return ret;
+}
+
+int SoundTriggerHalHidl::stopAllRecognitions()
+{
+    sp<ISoundTriggerHw> soundtrigger = getService();
+    if (soundtrigger == 0) {
+        return -ENODEV;
+    }
+
+    Return<int32_t> halReturn(0);
+    {
+        AutoMutex lock(mHalLock);
+        Return<int32_t> halReturn = soundtrigger->stopAllRecognitions();
+    }
+
+    int ret = (int)halReturn.getStatus().transactionError();
+    ALOGE_IF(ret != 0, "stopAllRecognitions error %d", ret);
+    if (ret == -EPIPE) {
+        clearService();
+    }
+    return ret;
+}
+
+SoundTriggerHalHidl::SoundTriggerHalHidl(const char *moduleName)
+    : mModuleName(moduleName), mNextUniqueId(1)
+{
+}
+
+void SoundTriggerHalHidl::onFirstRef()
+{
+    pthread_once(&sOnceControl, &sOnceInit);
+}
+
+SoundTriggerHalHidl::~SoundTriggerHalHidl()
+{
+}
+
+sp<ISoundTriggerHw> SoundTriggerHalHidl::getService()
+{
+    AutoMutex lock(mLock);
+    if (mISoundTrigger == 0) {
+        if (mModuleName == NULL) {
+            mModuleName = "primary";
+        }
+        std::string serviceName = "sound_trigger.";
+        serviceName.append(mModuleName);
+        mISoundTrigger = ISoundTriggerHw::getService(serviceName);
+    }
+    return mISoundTrigger;
+}
+
+void SoundTriggerHalHidl::clearService()
+{
+    AutoMutex lock(mLock);
+    mISoundTrigger = 0;
+}
+
+sp<SoundTriggerHalHidl::SoundModel> SoundTriggerHalHidl::getModel(sound_model_handle_t handle)
+{
+    AutoMutex lock(mLock);
+    return mSoundModels.valueFor(handle);
+}
+
+sp<SoundTriggerHalHidl::SoundModel> SoundTriggerHalHidl::removeModel(sound_model_handle_t handle)
+{
+    AutoMutex lock(mLock);
+    sp<SoundModel> model = mSoundModels.valueFor(handle);
+    mSoundModels.removeItem(handle);
+    return model;
+}
+
+uint32_t SoundTriggerHalHidl::nextUniqueId()
+{
+    return (uint32_t) atomic_fetch_add_explicit(&mNextUniqueId,
+                (uint_fast32_t) 1, memory_order_acq_rel);
+}
+
+void SoundTriggerHalHidl::convertUuidToHal(Uuid *halUuid,
+                                           const struct sound_trigger_uuid_s *uuid)
+{
+    halUuid->timeLow = uuid->timeLow;
+    halUuid->timeMid = uuid->timeMid;
+    halUuid->versionAndTimeHigh = uuid->timeHiAndVersion;
+    halUuid->variantAndClockSeqHigh = uuid->clockSeq;
+    memcpy(halUuid->node.data(), &uuid->node[0], sizeof(uuid->node));
+}
+
+void SoundTriggerHalHidl::convertUuidFromHal(struct sound_trigger_uuid_s *uuid,
+                                             const Uuid *halUuid)
+{
+    uuid->timeLow = halUuid->timeLow;
+    uuid->timeMid = halUuid->timeMid;
+    uuid->timeHiAndVersion = halUuid->versionAndTimeHigh;
+    uuid->clockSeq = halUuid->variantAndClockSeqHigh;
+    memcpy(&uuid->node[0], halUuid->node.data(), sizeof(uuid->node));
+}
+
+void SoundTriggerHalHidl::convertPropertiesFromHal(
+        struct sound_trigger_properties *properties,
+        const ISoundTriggerHw::Properties *halProperties)
+{
+    strlcpy(properties->implementor,
+            halProperties->implementor.c_str(), SOUND_TRIGGER_MAX_STRING_LEN);
+    strlcpy(properties->description,
+            halProperties->description.c_str(), SOUND_TRIGGER_MAX_STRING_LEN);
+    properties->version = halProperties->version;
+    convertUuidFromHal(&properties->uuid, &halProperties->uuid);
+    properties->max_sound_models = halProperties->maxSoundModels;
+    properties->max_key_phrases = halProperties->maxKeyPhrases;
+    properties->max_users = halProperties->maxUsers;
+    properties->recognition_modes = halProperties->recognitionModes;
+    properties->capture_transition = (bool)halProperties->captureTransition;
+    properties->max_buffer_ms = halProperties->maxBufferMs;
+    properties->concurrent_capture = (bool)halProperties->concurrentCapture;
+    properties->trigger_in_event = (bool)halProperties->triggerInEvent;
+    properties->power_consumption_mw = halProperties->powerConsumptionMw;
+}
+
+void SoundTriggerHalHidl::convertTriggerPhraseToHal(
+        ISoundTriggerHw::Phrase *halTriggerPhrase,
+        const struct sound_trigger_phrase *triggerPhrase)
+{
+    halTriggerPhrase->id = triggerPhrase->id;
+    halTriggerPhrase->recognitionModes = triggerPhrase->recognition_mode;
+    halTriggerPhrase->users.setToExternal((uint32_t *)&triggerPhrase->users[0], triggerPhrase->num_users);
+    halTriggerPhrase->locale = triggerPhrase->locale;
+    halTriggerPhrase->text = triggerPhrase->text;
+}
+
+ISoundTriggerHw::SoundModel *SoundTriggerHalHidl::convertSoundModelToHal(
+        const struct sound_trigger_sound_model *soundModel)
+{
+    ISoundTriggerHw::SoundModel *halModel = NULL;
+    if (soundModel->type == SOUND_MODEL_TYPE_KEYPHRASE) {
+        ISoundTriggerHw::PhraseSoundModel *halKeyPhraseModel =
+                new ISoundTriggerHw::PhraseSoundModel();
+        struct sound_trigger_phrase_sound_model *keyPhraseModel =
+                (struct sound_trigger_phrase_sound_model *)soundModel;
+        ISoundTriggerHw::Phrase *halPhrases =
+                new ISoundTriggerHw::Phrase[keyPhraseModel->num_phrases];
+
+
+        for (unsigned int i = 0; i < keyPhraseModel->num_phrases; i++) {
+            convertTriggerPhraseToHal(&halPhrases[i],
+                                      &keyPhraseModel->phrases[i]);
+        }
+        halKeyPhraseModel->phrases.setToExternal(halPhrases, keyPhraseModel->num_phrases);
+        // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
+        halKeyPhraseModel->phrases.resize(keyPhraseModel->num_phrases);
+
+        delete[] halPhrases;
+
+        halModel = (ISoundTriggerHw::SoundModel *)halKeyPhraseModel;
+    } else {
+        halModel = new ISoundTriggerHw::SoundModel();
+    }
+    halModel->type = (SoundModelType)soundModel->type;
+    convertUuidToHal(&halModel->uuid, &soundModel->uuid);
+    convertUuidToHal(&halModel->vendorUuid, &soundModel->vendor_uuid);
+    halModel->data.setToExternal((uint8_t *)soundModel + soundModel->data_offset, soundModel->data_size);
+    halModel->data.resize(soundModel->data_size);
+
+    return halModel;
+}
+
+void SoundTriggerHalHidl::convertPhraseRecognitionExtraToHal(
+        PhraseRecognitionExtra *halExtra,
+        const struct sound_trigger_phrase_recognition_extra *extra)
+{
+    halExtra->id = extra->id;
+    halExtra->recognitionModes = extra->recognition_modes;
+    halExtra->confidenceLevel = extra->confidence_level;
+    ConfidenceLevel *halLevels =
+            new ConfidenceLevel[extra->num_levels];
+    for (unsigned int i = 0; i < extra->num_levels; i++) {
+        halLevels[i].userId = extra->levels[i].user_id;
+        halLevels[i].levelPercent = extra->levels[i].level;
+    }
+    halExtra->levels.setToExternal(halLevels, extra->num_levels);
+    // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
+    halExtra->levels.resize(extra->num_levels);
+
+    delete[] halLevels;
+}
+
+
+ISoundTriggerHw::RecognitionConfig *SoundTriggerHalHidl::convertRecognitionConfigToHal(
+        const struct sound_trigger_recognition_config *config)
+{
+    ISoundTriggerHw::RecognitionConfig *halConfig =
+            new ISoundTriggerHw::RecognitionConfig();
+
+    halConfig->captureHandle = config->capture_handle;
+    halConfig->captureDevice = (AudioDevice)config->capture_device;
+    halConfig->captureRequested = (uint32_t)config->capture_requested;
+
+    PhraseRecognitionExtra *halExtras =
+            new PhraseRecognitionExtra[config->num_phrases];
+
+    for (unsigned int i = 0; i < config->num_phrases; i++) {
+        convertPhraseRecognitionExtraToHal(&halExtras[i],
+                                  &config->phrases[i]);
+    }
+    halConfig->phrases.setToExternal(halExtras, config->num_phrases);
+    // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
+    halConfig->phrases.resize(config->num_phrases);
+
+    delete[] halExtras;
+
+    halConfig->data.setToExternal((uint8_t *)config + config->data_offset, config->data_size);
+
+    return halConfig;
+}
+
+
+// ISoundTriggerHwCallback
+::android::hardware::Return<void> SoundTriggerHalHidl::recognitionCallback(
+        const ISoundTriggerHwCallback::RecognitionEvent& halEvent,
+        CallbackCookie cookie)
+{
+    sp<SoundModel> model;
+    {
+        AutoMutex lock(mLock);
+        model = mSoundModels.valueFor((SoundModelHandle)cookie);
+        if (model == 0) {
+            return Return<void>();
+        }
+    }
+    struct sound_trigger_recognition_event *event = convertRecognitionEventFromHal(&halEvent);
+    if (event == NULL) {
+        return Return<void>();
+    }
+    event->model = model->mHandle;
+    model->mRecognitionCallback(event, model->mRecognitionCookie);
+
+    free(event);
+
+    return Return<void>();
+}
+
+::android::hardware::Return<void> SoundTriggerHalHidl::phraseRecognitionCallback(
+        const ISoundTriggerHwCallback::PhraseRecognitionEvent& halEvent,
+        CallbackCookie cookie)
+{
+    sp<SoundModel> model;
+    {
+        AutoMutex lock(mLock);
+        model = mSoundModels.valueFor((SoundModelHandle)cookie);
+        if (model == 0) {
+            return Return<void>();
+        }
+    }
+
+    struct sound_trigger_recognition_event *event = convertRecognitionEventFromHal(
+                                   (const ISoundTriggerHwCallback::RecognitionEvent *)&halEvent);
+    if (event == NULL) {
+        return Return<void>();
+    }
+
+    event->model = model->mHandle;
+    model->mRecognitionCallback(event, model->mRecognitionCookie);
+
+    free(event);
+
+    return Return<void>();
+}
+
+::android::hardware::Return<void> SoundTriggerHalHidl::soundModelCallback(
+        const ISoundTriggerHwCallback::ModelEvent& halEvent,
+        CallbackCookie cookie)
+{
+    sp<SoundModel> model;
+    {
+        AutoMutex lock(mLock);
+        model = mSoundModels.valueFor((SoundModelHandle)cookie);
+        if (model == 0) {
+            return Return<void>();
+        }
+    }
+
+    struct sound_trigger_model_event *event = convertSoundModelEventFromHal(&halEvent);
+    if (event == NULL) {
+        return Return<void>();
+    }
+
+    event->model = model->mHandle;
+    model->mSoundModelCallback(event, model->mSoundModelCookie);
+
+    free(event);
+
+    return Return<void>();
+}
+
+
+struct sound_trigger_model_event *SoundTriggerHalHidl::convertSoundModelEventFromHal(
+                                              const ISoundTriggerHwCallback::ModelEvent *halEvent)
+{
+    struct sound_trigger_model_event *event = (struct sound_trigger_model_event *)malloc(
+            sizeof(struct sound_trigger_model_event) +
+            halEvent->data.size());
+    if (event == NULL) {
+        return NULL;
+    }
+
+    event->status = (int)halEvent->status;
+    // event->model to be set by caller
+    event->data_offset = sizeof(struct sound_trigger_model_event);
+    event->data_size = halEvent->data.size();
+    uint8_t *dst = (uint8_t *)event + event->data_offset;
+    uint8_t *src = (uint8_t *)&halEvent->data[0];
+    memcpy(dst, src, halEvent->data.size());
+
+    return event;
+}
+
+void SoundTriggerHalHidl::convertPhraseRecognitionExtraFromHal(
+        struct sound_trigger_phrase_recognition_extra *extra,
+        const PhraseRecognitionExtra *halExtra)
+{
+    extra->id = halExtra->id;
+    extra->recognition_modes = halExtra->recognitionModes;
+    extra->confidence_level = halExtra->confidenceLevel;
+
+    size_t i;
+    for (i = 0; i < halExtra->levels.size() && i < SOUND_TRIGGER_MAX_USERS; i++) {
+        extra->levels[i].user_id = halExtra->levels[i].userId;
+        extra->levels[i].level = halExtra->levels[i].levelPercent;
+    }
+    extra->num_levels = (unsigned int)i;
+}
+
+
+struct sound_trigger_recognition_event *SoundTriggerHalHidl::convertRecognitionEventFromHal(
+        const ISoundTriggerHwCallback::RecognitionEvent *halEvent)
+{
+    struct sound_trigger_recognition_event *event;
+
+    if (halEvent->type == SoundModelType::KEYPHRASE) {
+        struct sound_trigger_phrase_recognition_event *phraseEvent =
+                (struct sound_trigger_phrase_recognition_event *)malloc(
+                        sizeof(struct sound_trigger_phrase_recognition_event) +
+                        halEvent->data.size());
+        if (phraseEvent == NULL) {
+            return NULL;
+        }
+        const ISoundTriggerHwCallback::PhraseRecognitionEvent *halPhraseEvent =
+                (const ISoundTriggerHwCallback::PhraseRecognitionEvent *)halEvent;
+
+        for (unsigned int i = 0; i < halPhraseEvent->phraseExtras.size(); i++) {
+            convertPhraseRecognitionExtraFromHal(&phraseEvent->phrase_extras[i],
+                                                 &halPhraseEvent->phraseExtras[i]);
+        }
+        phraseEvent->num_phrases = halPhraseEvent->phraseExtras.size();
+        event = (struct sound_trigger_recognition_event *)phraseEvent;
+        event->data_offset = sizeof(sound_trigger_phrase_recognition_event);
+    } else {
+        event = (struct sound_trigger_recognition_event *)malloc(
+                sizeof(struct sound_trigger_recognition_event) + halEvent->data.size());
+        if (event == NULL) {
+            return NULL;
+        }
+        event->data_offset = sizeof(sound_trigger_recognition_event);
+    }
+    event->status = (int)halEvent->status;
+    event->type = (sound_trigger_sound_model_type_t)halEvent->type;
+    // event->model to be set by caller
+    event->capture_available = (bool)halEvent->captureAvailable;
+    event->capture_session = halEvent->captureSession;
+    event->capture_delay_ms = halEvent->captureDelayMs;
+    event->capture_preamble_ms = halEvent->capturePreambleMs;
+    event->trigger_in_data = (bool)halEvent->triggerInData;
+    event->audio_config.sample_rate = halEvent->audioConfig.sampleRateHz;
+    event->audio_config.channel_mask = (audio_channel_mask_t)halEvent->audioConfig.channelMask;
+    event->audio_config.format = (audio_format_t)halEvent->audioConfig.format;
+
+    event->data_size = halEvent->data.size();
+    uint8_t *dst = (uint8_t *)event + event->data_offset;
+    uint8_t *src = (uint8_t *)&halEvent->data[0];
+    memcpy(dst, src, halEvent->data.size());
+
+    return event;
+}
+
+} // namespace android