/* * Copyright (C) 2018 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 "SoundTriggerHw" #include "SoundTriggerHw.h" #include #include #include #include using android::hardware::hidl_memory; using android::hidl::allocator::V1_0::IAllocator; using android::hidl::memory::V1_0::IMemory; namespace android { namespace hardware { namespace soundtrigger { namespace V2_2 { namespace implementation { /** * According to the HIDL C++ Users Guide: client and server implementations * should never directly refer to anything other than the interface header * generated from the HIDL definition file (ie. ISoundTriggerHw.hal), so * this V2_2 implementation copies the V2_0 and V2_1 implementations and * then adds the new V2_2 implementation. */ // Begin V2_0 implementation, copied from // hardware/interfaces/soundtrigger/2.0/default/SoundTriggerHalImpl.cpp // static void soundModelCallback_(struct sound_trigger_model_event* halEvent, void* cookie) { if (halEvent == NULL) { ALOGW("soundModelCallback called with NULL event"); return; } sp client = wp(static_cast(cookie)) .promote(); if (client == 0) { ALOGW("soundModelCallback called on stale client"); return; } if (halEvent->model != client->getHalHandle()) { ALOGW("soundModelCallback call with wrong handle %d on client with handle %d", (int)halEvent->model, (int)client->getHalHandle()); return; } client->soundModelCallback(halEvent); } // static void recognitionCallback_(struct sound_trigger_recognition_event* halEvent, void* cookie) { if (halEvent == NULL) { ALOGW("recognitionCallback call NULL event"); return; } sp client = wp(static_cast(cookie)) .promote(); if (client == 0) { ALOGW("recognitionCallback called on stale client"); return; } client->recognitionCallback(halEvent); } Return SoundTriggerHw::getProperties(ISoundTriggerHw::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 SoundTriggerHw::doLoadSoundModel(const V2_0::ISoundTriggerHw::SoundModel& soundModel, sp client) { int32_t ret = 0; struct sound_trigger_sound_model* halSoundModel; 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; } sound_model_handle_t halHandle; ret = mHwDevice->load_sound_model(mHwDevice, halSoundModel, soundModelCallback_, client.get(), &halHandle); free(halSoundModel); if (ret != 0) { goto exit; } client->setHalHandle(halHandle); { AutoMutex lock(mLock); mClients.add(client->getId(), client); } exit: return ret; } Return SoundTriggerHw::loadSoundModel(const V2_0::ISoundTriggerHw::SoundModel& soundModel, const sp& callback, V2_0::ISoundTriggerHwCallback::CallbackCookie cookie, ISoundTriggerHw::loadSoundModel_cb _hidl_cb) { sp client = new SoundModelClient_2_0(nextUniqueModelId(), cookie, callback); _hidl_cb(doLoadSoundModel(soundModel, client), client->getId()); return Void(); } Return SoundTriggerHw::loadPhraseSoundModel( const V2_0::ISoundTriggerHw::PhraseSoundModel& soundModel, const sp& callback, V2_0::ISoundTriggerHwCallback::CallbackCookie cookie, ISoundTriggerHw::loadPhraseSoundModel_cb _hidl_cb) { sp client = new SoundModelClient_2_0(nextUniqueModelId(), cookie, callback); _hidl_cb(doLoadSoundModel((const V2_0::ISoundTriggerHw::SoundModel&)soundModel, client), client->getId()); return Void(); } Return SoundTriggerHw::unloadSoundModel(int32_t modelHandle) { int32_t ret; sp 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->getHalHandle()); mClients.removeItem(modelHandle); exit: return ret; } Return SoundTriggerHw::startRecognition( int32_t modelHandle, const V2_0::ISoundTriggerHw::RecognitionConfig& config, const sp& /* callback */, int32_t /* cookie */) { int32_t ret; sp 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((const V2_0::ISoundTriggerHw::RecognitionConfig*)&config); if (halConfig == NULL) { ret = -EINVAL; goto exit; } ret = mHwDevice->start_recognition(mHwDevice, client->getHalHandle(), halConfig, recognitionCallback_, client.get()); free(halConfig); exit: return ret; } Return SoundTriggerHw::stopRecognition(int32_t modelHandle) { int32_t ret; sp 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->getHalHandle()); exit: return ret; } Return SoundTriggerHw::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; } SoundTriggerHw::SoundTriggerHw() : mModuleName("primary"), mHwDevice(NULL), mNextModelId(1) {} void SoundTriggerHw::onFirstRef() { const hw_module_t* mod; int rc; 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); } SoundTriggerHw::~SoundTriggerHw() { if (mHwDevice != NULL) { sound_trigger_hw_device_close(mHwDevice); } } uint32_t SoundTriggerHw::nextUniqueModelId() { uint32_t modelId = 0; { AutoMutex lock(mLock); do { modelId = atomic_fetch_add_explicit(&mNextModelId, (uint_fast32_t)1, memory_order_acq_rel); } while (mClients.valueFor(modelId) != 0 && modelId != 0); } LOG_ALWAYS_FATAL_IF(modelId == 0, "wrap around in sound model IDs, num loaded models %zu", mClients.size()); return modelId; } void SoundTriggerHw::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 SoundTriggerHw::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 SoundTriggerHw::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 SoundTriggerHw::convertTriggerPhraseToHal(struct sound_trigger_phrase* halTriggerPhrase, const ISoundTriggerHw::Phrase* triggerPhrase) { halTriggerPhrase->id = triggerPhrase->id; halTriggerPhrase->recognition_mode = triggerPhrase->recognitionModes; unsigned int i; halTriggerPhrase->num_users = std::min((int)triggerPhrase->users.size(), SOUND_TRIGGER_MAX_USERS); for (i = 0; i < halTriggerPhrase->num_users; i++) { halTriggerPhrase->users[i] = triggerPhrase->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* SoundTriggerHw::convertSoundModelToHal( const V2_0::ISoundTriggerHw::SoundModel* soundModel) { struct sound_trigger_sound_model* halModel = NULL; if (soundModel->type == V2_0::SoundModelType::KEYPHRASE) { size_t allocSize = sizeof(struct sound_trigger_phrase_sound_model) + soundModel->data.size(); struct sound_trigger_phrase_sound_model* halKeyPhraseModel = static_cast(malloc(allocSize)); LOG_ALWAYS_FATAL_IF(halKeyPhraseModel == NULL, "malloc failed for size %zu in convertSoundModelToHal PHRASE", allocSize); const V2_0::ISoundTriggerHw::PhraseSoundModel* keyPhraseModel = reinterpret_cast(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(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(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(halModel) + halModel->data_offset; const uint8_t* src = reinterpret_cast(&soundModel->data[0]); memcpy(dst, src, soundModel->data.size()); return halModel; } void SoundTriggerHw::convertPhraseRecognitionExtraToHal( struct sound_trigger_phrase_recognition_extra* halExtra, const V2_0::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* SoundTriggerHw::convertRecognitionConfigToHal( const V2_0::ISoundTriggerHw::RecognitionConfig* config) { size_t allocSize = sizeof(struct sound_trigger_recognition_config) + config->data.size(); struct sound_trigger_recognition_config* halConfig = static_cast(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(halConfig) + halConfig->data_offset; const uint8_t* src = reinterpret_cast(&config->data[0]); memcpy(dst, src, config->data.size()); return halConfig; } // static void SoundTriggerHw::convertSoundModelEventFromHal( V2_0::ISoundTriggerHwCallback::ModelEvent* event, const struct sound_trigger_model_event* halEvent) { event->status = (V2_0::ISoundTriggerHwCallback::SoundModelStatus)halEvent->status; // event->model to be remapped by called event->data.setToExternal( const_cast(reinterpret_cast(halEvent)) + halEvent->data_offset, halEvent->data_size); } // static void SoundTriggerHw::convertPhaseRecognitionEventFromHal( V2_0::ISoundTriggerHwCallback::PhraseRecognitionEvent* event, const struct sound_trigger_phrase_recognition_event* halEvent) { event->phraseExtras.resize(halEvent->num_phrases); for (unsigned int i = 0; i < halEvent->num_phrases; i++) { convertPhraseRecognitionExtraFromHal(&event->phraseExtras[i], &halEvent->phrase_extras[i]); } convertRecognitionEventFromHal(&event->common, &halEvent->common); } // static void SoundTriggerHw::convertRecognitionEventFromHal( V2_0::ISoundTriggerHwCallback::RecognitionEvent* event, const struct sound_trigger_recognition_event* halEvent) { event->status = static_cast(halEvent->status); event->type = static_cast(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(reinterpret_cast(halEvent)) + halEvent->data_offset, halEvent->data_size); } // static void SoundTriggerHw::convertPhraseRecognitionExtraFromHal( V2_0::PhraseRecognitionExtra* extra, const struct sound_trigger_phrase_recognition_extra* halExtra) { extra->id = halExtra->id; extra->recognitionModes = halExtra->recognition_modes; extra->confidenceLevel = halExtra->confidence_level; extra->levels.resize(halExtra->num_levels); for (unsigned int i = 0; i < halExtra->num_levels; i++) { extra->levels[i].userId = halExtra->levels[i].user_id; extra->levels[i].levelPercent = halExtra->levels[i].level; } } void SoundTriggerHw::SoundModelClient_2_0::recognitionCallback( struct sound_trigger_recognition_event* halEvent) { if (halEvent->type == SOUND_MODEL_TYPE_KEYPHRASE) { V2_0::ISoundTriggerHwCallback::PhraseRecognitionEvent event; convertPhaseRecognitionEventFromHal( &event, reinterpret_cast(halEvent)); event.common.model = mId; mCallback->phraseRecognitionCallback(event, mCookie); } else { V2_0::ISoundTriggerHwCallback::RecognitionEvent event; convertRecognitionEventFromHal(&event, halEvent); event.model = mId; mCallback->recognitionCallback(event, mCookie); } } void SoundTriggerHw::SoundModelClient_2_0::soundModelCallback( struct sound_trigger_model_event* halEvent) { V2_0::ISoundTriggerHwCallback::ModelEvent event; convertSoundModelEventFromHal(&event, halEvent); event.model = mId; mCallback->soundModelCallback(event, mCookie); } // Begin V2_1 implementation, copied from // hardware/interfaces/soundtrigger/2.1/default/SoundTriggerHw.cpp namespace { // Backs up by the vector with the contents of shared memory. // It is assumed that the passed hidl_vector is empty, so it's // not cleared if the memory is a null object. // The caller needs to keep the returned sp as long as // the data is needed. std::pair> memoryAsVector(const hidl_memory& m, hidl_vec* vec) { sp memory; if (m.size() == 0) { return std::make_pair(true, memory); } memory = mapMemory(m); if (memory != nullptr) { memory->read(); vec->setToExternal(static_cast(static_cast(memory->getPointer())), memory->getSize()); return std::make_pair(true, memory); } ALOGE("%s: Could not map HIDL memory to IMemory", __func__); return std::make_pair(false, memory); } // Moves the data from the vector into allocated shared memory, // emptying the vector. // It is assumed that the passed hidl_memory is a null object, so it's // not reset if the vector is empty. // The caller needs to keep the returned sp as long as // the data is needed. std::pair> moveVectorToMemory(hidl_vec* v, hidl_memory* mem) { sp memory; if (v->size() == 0) { return std::make_pair(true, memory); } sp ashmem = IAllocator::getService("ashmem"); if (ashmem == 0) { ALOGE("Failed to retrieve ashmem allocator service"); return std::make_pair(false, memory); } bool success = false; Return r = ashmem->allocate(v->size(), [&](bool s, const hidl_memory& m) { success = s; if (success) *mem = m; }); if (r.isOk() && success) { memory = hardware::mapMemory(*mem); if (memory != 0) { memory->update(); memcpy(memory->getPointer(), v->data(), v->size()); memory->commit(); v->resize(0); return std::make_pair(true, memory); } else { ALOGE("Failed to map allocated ashmem"); } } else { ALOGE("Failed to allocate %llu bytes from ashmem", (unsigned long long)v->size()); } return std::make_pair(false, memory); } } // namespace Return SoundTriggerHw::loadSoundModel_2_1( const V2_1::ISoundTriggerHw::SoundModel& soundModel, const sp& callback, int32_t cookie, V2_1::ISoundTriggerHw::loadSoundModel_2_1_cb _hidl_cb) { // It is assumed that legacy data vector is empty, thus making copy is cheap. V2_0::ISoundTriggerHw::SoundModel soundModel_2_0(soundModel.header); auto result = memoryAsVector(soundModel.data, &soundModel_2_0.data); if (result.first) { sp client = new SoundModelClient_2_1(nextUniqueModelId(), cookie, callback); _hidl_cb(doLoadSoundModel(soundModel_2_0, client), client->getId()); return Void(); } _hidl_cb(-ENOMEM, 0); return Void(); } Return SoundTriggerHw::loadPhraseSoundModel_2_1( const V2_1::ISoundTriggerHw::PhraseSoundModel& soundModel, const sp& callback, int32_t cookie, V2_1::ISoundTriggerHw::loadPhraseSoundModel_2_1_cb _hidl_cb) { V2_0::ISoundTriggerHw::PhraseSoundModel soundModel_2_0; // It is assumed that legacy data vector is empty, thus making copy is cheap. soundModel_2_0.common = soundModel.common.header; // Avoid copying phrases data. soundModel_2_0.phrases.setToExternal( const_cast(soundModel.phrases.data()), soundModel.phrases.size()); auto result = memoryAsVector(soundModel.common.data, &soundModel_2_0.common.data); if (result.first) { sp client = new SoundModelClient_2_1(nextUniqueModelId(), cookie, callback); _hidl_cb(doLoadSoundModel((const V2_0::ISoundTriggerHw::SoundModel&)soundModel_2_0, client), client->getId()); return Void(); } _hidl_cb(-ENOMEM, 0); return Void(); } Return SoundTriggerHw::startRecognition_2_1( int32_t modelHandle, const V2_1::ISoundTriggerHw::RecognitionConfig& config, const sp& callback, int32_t cookie) { // It is assumed that legacy data vector is empty, thus making copy is cheap. V2_0::ISoundTriggerHw::RecognitionConfig config_2_0(config.header); auto result = memoryAsVector(config.data, &config_2_0.data); return result.first ? startRecognition(modelHandle, config_2_0, callback, cookie) : Return(-ENOMEM); } void SoundTriggerHw::SoundModelClient_2_1::recognitionCallback( struct sound_trigger_recognition_event* halEvent) { if (halEvent->type == SOUND_MODEL_TYPE_KEYPHRASE) { V2_0::ISoundTriggerHwCallback::PhraseRecognitionEvent event_2_0; convertPhaseRecognitionEventFromHal( &event_2_0, reinterpret_cast(halEvent)); event_2_0.common.model = mId; V2_1::ISoundTriggerHwCallback::PhraseRecognitionEvent event; event.phraseExtras.setToExternal(event_2_0.phraseExtras.data(), event_2_0.phraseExtras.size()); auto result = moveVectorToMemory(&event_2_0.common.data, &event.common.data); if (result.first) { // The data vector is now empty, thus copying is cheap. event.common.header = event_2_0.common; mCallback->phraseRecognitionCallback_2_1(event, mCookie); } } else { V2_1::ISoundTriggerHwCallback::RecognitionEvent event; convertRecognitionEventFromHal(&event.header, halEvent); event.header.model = mId; auto result = moveVectorToMemory(&event.header.data, &event.data); if (result.first) { mCallback->recognitionCallback_2_1(event, mCookie); } } } void SoundTriggerHw::SoundModelClient_2_1::soundModelCallback( struct sound_trigger_model_event* halEvent) { V2_1::ISoundTriggerHwCallback::ModelEvent event; convertSoundModelEventFromHal(&event.header, halEvent); event.header.model = mId; auto result = moveVectorToMemory(&event.header.data, &event.data); if (result.first) { mCallback->soundModelCallback_2_1(event, mCookie); } } // Begin V2_2 implementation Return SoundTriggerHw::getModelState(int32_t modelHandle) { sp client; if (mHwDevice == NULL) { return -ENODEV; } { AutoMutex lock(mLock); client = mClients.valueFor(modelHandle); if (client == 0) { return -ENOSYS; } } if (mHwDevice->common.version < SOUND_TRIGGER_DEVICE_API_VERSION_1_2) { ALOGE("Get model state not supported"); return -ENODEV; } if (mHwDevice->get_model_state == NULL) { ALOGE("Failed to get model state from device, no such method"); return -ENODEV; } return mHwDevice->get_model_state(mHwDevice, client->getHalHandle()); } // Methods from ::android::hidl::base::V1_0::IBase follow. ISoundTriggerHw* HIDL_FETCH_ISoundTriggerHw(const char* /* name */) { return new SoundTriggerHw(); } } // namespace implementation } // namespace V2_2 } // namespace soundtrigger } // namespace hardware } // namespace android