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 /soundtrigger | |
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
Diffstat (limited to 'soundtrigger')
-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 |
5 files changed, 693 insertions, 0 deletions
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 |