/* ** ** Copyright 2010, 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_NDEBUG 0 #define LOG_TAG "AudioEffect" #include #include #include #include #include #include #include #include #include #include #include #define RETURN_STATUS_IF_ERROR(x) \ { \ auto _tmp = (x); \ if (_tmp != OK) return _tmp; \ } namespace android { using aidl_utils::statusTFromBinderStatus; using binder::Status; using media::IAudioPolicyService; namespace { // Copy from a raw pointer + size into a vector of bytes. void appendToBuffer(const void* data, size_t size, std::vector* buffer) { const uint8_t* p = reinterpret_cast(data); buffer->insert(buffer->end(), p, p + size); } } // namespace // --------------------------------------------------------------------------- AudioEffect::AudioEffect(const android::content::AttributionSourceState& attributionSource) : mClientAttributionSource(attributionSource) { } status_t AudioEffect::set(const effect_uuid_t *type, const effect_uuid_t *uuid, int32_t priority, effect_callback_t cbf, void* user, audio_session_t sessionId, audio_io_handle_t io, const AudioDeviceTypeAddr& device, bool probe) { sp iEffect; sp cblk; int enabled; ALOGV("set %p mUserData: %p uuid: %p timeLow %08x", this, user, type, type ? type->timeLow : 0); if (mIEffect != 0) { ALOGW("Effect already in use"); return INVALID_OPERATION; } if (sessionId == AUDIO_SESSION_DEVICE && io != AUDIO_IO_HANDLE_NONE) { ALOGW("IO handle should not be specified for device effect"); return BAD_VALUE; } const sp& audioFlinger = AudioSystem::get_audio_flinger(); if (audioFlinger == 0) { ALOGE("set(): Could not get audioflinger"); return NO_INIT; } if (type == NULL && uuid == NULL) { ALOGW("Must specify at least type or uuid"); return BAD_VALUE; } mProbe = probe; mPriority = priority; mCbf = cbf; mUserData = user; mSessionId = sessionId; memset(&mDescriptor, 0, sizeof(effect_descriptor_t)); mDescriptor.type = *(type != NULL ? type : EFFECT_UUID_NULL); mDescriptor.uuid = *(uuid != NULL ? uuid : EFFECT_UUID_NULL); // TODO b/182392769: use attribution source util mIEffectClient = new EffectClient(this); pid_t pid = IPCThreadState::self()->getCallingPid(); mClientAttributionSource.pid = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(pid)); pid_t uid = IPCThreadState::self()->getCallingUid(); mClientAttributionSource.uid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(uid)); media::CreateEffectRequest request; request.desc = VALUE_OR_RETURN_STATUS( legacy2aidl_effect_descriptor_t_EffectDescriptor(mDescriptor)); request.client = mIEffectClient; request.priority = priority; request.output = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(io)); request.sessionId = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_session_t_int32_t(mSessionId)); request.device = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioDeviceTypeAddress(device)); request.attributionSource = mClientAttributionSource; request.probe = probe; media::CreateEffectResponse response; mStatus = audioFlinger->createEffect(request, &response); if (mStatus == OK) { mId = response.id; enabled = response.enabled; iEffect = response.effect; mDescriptor = VALUE_OR_RETURN_STATUS( aidl2legacy_EffectDescriptor_effect_descriptor_t(response.desc)); } // In probe mode, we stop here and return the status: the IEffect interface to // audio flinger will not be retained. initCheck() will return the creation status // but all other APIs will return invalid operation. if (probe || iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) { char typeBuffer[64] = {}, uuidBuffer[64] = {}; guidToString(type, typeBuffer, sizeof(typeBuffer)); guidToString(uuid, uuidBuffer, sizeof(uuidBuffer)); ALOGE_IF(!probe, "set(): AudioFlinger could not create effect %s / %s, status: %d", type != nullptr ? typeBuffer : "NULL", uuid != nullptr ? uuidBuffer : "NULL", mStatus); if (!probe && iEffect == 0) { mStatus = NO_INIT; } return mStatus; } mEnabled = (volatile int32_t)enabled; if (media::SharedFileRegion shmem; !iEffect->getCblk(&shmem).isOk() || !convertSharedFileRegionToIMemory(shmem, &cblk) || cblk == 0) { mStatus = NO_INIT; ALOGE("Could not get control block"); return mStatus; } mIEffect = iEffect; mCblkMemory = cblk; // TODO: Using unsecurePointer() has some associated security pitfalls // (see declaration for details). // Either document why it is safe in this case or address the // issue (e.g. by copying). mCblk = static_cast(cblk->unsecurePointer()); int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int); mCblk->buffer = (uint8_t *)mCblk + bufOffset; IInterface::asBinder(iEffect)->linkToDeath(mIEffectClient); ALOGV("set() %p OK effect: %s id: %d status %d enabled %d pid %d", this, mDescriptor.name, mId, mStatus, mEnabled, mClientAttributionSource.pid); if (!audio_is_global_session(mSessionId)) { AudioSystem::acquireAudioSessionId(mSessionId, pid, uid); } return mStatus; } status_t AudioEffect::set(const char *typeStr, const char *uuidStr, int32_t priority, effect_callback_t cbf, void* user, audio_session_t sessionId, audio_io_handle_t io, const AudioDeviceTypeAddr& device, bool probe) { effect_uuid_t type; effect_uuid_t *pType = nullptr; effect_uuid_t uuid; effect_uuid_t *pUuid = nullptr; ALOGV("AudioEffect::set string\n - type: %s\n - uuid: %s", typeStr ? typeStr : "nullptr", uuidStr ? uuidStr : "nullptr"); if (stringToGuid(typeStr, &type) == NO_ERROR) { pType = &type; } if (stringToGuid(uuidStr, &uuid) == NO_ERROR) { pUuid = &uuid; } return set(pType, pUuid, priority, cbf, user, sessionId, io, device, probe); } AudioEffect::~AudioEffect() { ALOGV("Destructor %p", this); if (!mProbe && (mStatus == NO_ERROR || mStatus == ALREADY_EXISTS)) { if (!audio_is_global_session(mSessionId)) { AudioSystem::releaseAudioSessionId(mSessionId, VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientAttributionSource.pid))); } if (mIEffect != NULL) { mIEffect->disconnect(); IInterface::asBinder(mIEffect)->unlinkToDeath(mIEffectClient); } mIEffect.clear(); mCblkMemory.clear(); } mIEffectClient.clear(); IPCThreadState::self()->flushCommands(); } status_t AudioEffect::initCheck() const { return mStatus; } // ------------------------------------------------------------------------- effect_descriptor_t AudioEffect::descriptor() const { return mDescriptor; } bool AudioEffect::getEnabled() const { return (mEnabled != 0); } status_t AudioEffect::setEnabled(bool enabled) { if (mProbe) { return INVALID_OPERATION; } if (mStatus != NO_ERROR) { return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus; } status_t status = NO_ERROR; AutoMutex lock(mLock); if (enabled != mEnabled) { Status bs; if (enabled) { ALOGV("enable %p", this); bs = mIEffect->enable(&status); } else { ALOGV("disable %p", this); bs = mIEffect->disable(&status); } if (!bs.isOk()) { status = statusTFromBinderStatus(bs); } if (status == NO_ERROR) { mEnabled = enabled; } } return status; } status_t AudioEffect::command(uint32_t cmdCode, uint32_t cmdSize, void *cmdData, uint32_t *replySize, void *replyData) { if (mProbe) { return INVALID_OPERATION; } if (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS) { ALOGV("command() bad status %d", mStatus); return mStatus; } if (cmdCode == EFFECT_CMD_ENABLE || cmdCode == EFFECT_CMD_DISABLE) { if (mEnabled == (cmdCode == EFFECT_CMD_ENABLE)) { return NO_ERROR; } if (replySize == NULL || *replySize != sizeof(status_t) || replyData == NULL) { return BAD_VALUE; } mLock.lock(); } std::vector data; appendToBuffer(cmdData, cmdSize, &data); status_t status; std::vector response; Status bs = mIEffect->command(cmdCode, data, *replySize, &response, &status); if (!bs.isOk()) { status = statusTFromBinderStatus(bs); } if (status == NO_ERROR) { memcpy(replyData, response.data(), response.size()); *replySize = response.size(); } if (cmdCode == EFFECT_CMD_ENABLE || cmdCode == EFFECT_CMD_DISABLE) { if (status == NO_ERROR) { status = *(status_t *)replyData; } if (status == NO_ERROR) { mEnabled = (cmdCode == EFFECT_CMD_ENABLE); } mLock.unlock(); } return status; } status_t AudioEffect::setParameter(effect_param_t *param) { if (mProbe) { return INVALID_OPERATION; } if (mStatus != NO_ERROR) { return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus; } if (param == NULL || param->psize == 0 || param->vsize == 0) { return BAD_VALUE; } uint32_t psize = ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize; ALOGV("setParameter: param: %d, param2: %d", *(int *)param->data, (param->psize == 8) ? *((int *)param->data + 1): -1); std::vector cmd; appendToBuffer(param, sizeof(effect_param_t) + psize, &cmd); std::vector response; status_t status; Status bs = mIEffect->command(EFFECT_CMD_SET_PARAM, cmd, sizeof(int), &response, &status); if (!bs.isOk()) { status = statusTFromBinderStatus(bs); return status; } assert(response.size() == sizeof(int)); memcpy(¶m->status, response.data(), response.size()); return status; } status_t AudioEffect::setParameterDeferred(effect_param_t *param) { if (mProbe) { return INVALID_OPERATION; } if (mStatus != NO_ERROR) { return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus; } if (param == NULL || param->psize == 0 || param->vsize == 0) { return BAD_VALUE; } Mutex::Autolock _l(mCblk->lock); int psize = ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize; int size = ((sizeof(effect_param_t) + psize - 1) / sizeof(int) + 1) * sizeof(int); if (mCblk->clientIndex + size > EFFECT_PARAM_BUFFER_SIZE) { return NO_MEMORY; } int *p = (int *)(mCblk->buffer + mCblk->clientIndex); *p++ = size; memcpy(p, param, sizeof(effect_param_t) + psize); mCblk->clientIndex += size; return NO_ERROR; } status_t AudioEffect::setParameterCommit() { if (mProbe) { return INVALID_OPERATION; } if (mStatus != NO_ERROR) { return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus; } Mutex::Autolock _l(mCblk->lock); if (mCblk->clientIndex == 0) { return INVALID_OPERATION; } std::vector cmd; std::vector response; status_t status; Status bs = mIEffect->command(EFFECT_CMD_SET_PARAM_COMMIT, cmd, 0, &response, &status); if (!bs.isOk()) { status = statusTFromBinderStatus(bs); } return status; } status_t AudioEffect::getParameter(effect_param_t *param) { if (mProbe) { return INVALID_OPERATION; } if (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS) { return mStatus; } if (param == NULL || param->psize == 0 || param->vsize == 0) { return BAD_VALUE; } ALOGV("getParameter: param: %d, param2: %d", *(int *)param->data, (param->psize == 8) ? *((int *)param->data + 1): -1); uint32_t psize = sizeof(effect_param_t) + ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize; status_t status; std::vector cmd; std::vector response; appendToBuffer(param, sizeof(effect_param_t) + param->psize, &cmd); Status bs = mIEffect->command(EFFECT_CMD_GET_PARAM, cmd, psize, &response, &status); if (!bs.isOk()) { status = statusTFromBinderStatus(bs); return status; } memcpy(param, response.data(), response.size()); return status; } // ------------------------------------------------------------------------- void AudioEffect::binderDied() { ALOGW("IEffect died"); mStatus = DEAD_OBJECT; if (mCbf != NULL) { status_t status = DEAD_OBJECT; mCbf(EVENT_ERROR, mUserData, &status); } mIEffect.clear(); } // ------------------------------------------------------------------------- void AudioEffect::controlStatusChanged(bool controlGranted) { ALOGV("controlStatusChanged %p control %d callback %p mUserData %p", this, controlGranted, mCbf, mUserData); if (controlGranted) { if (mStatus == ALREADY_EXISTS) { mStatus = NO_ERROR; } } else { if (mStatus == NO_ERROR) { mStatus = ALREADY_EXISTS; } } if (mCbf != NULL) { mCbf(EVENT_CONTROL_STATUS_CHANGED, mUserData, &controlGranted); } } void AudioEffect::enableStatusChanged(bool enabled) { ALOGV("enableStatusChanged %p enabled %d mCbf %p", this, enabled, mCbf); if (mStatus == ALREADY_EXISTS) { mEnabled = enabled; if (mCbf != NULL) { mCbf(EVENT_ENABLE_STATUS_CHANGED, mUserData, &enabled); } } } void AudioEffect::commandExecuted(int32_t cmdCode, const std::vector& cmdData, const std::vector& replyData) { if (cmdData.empty() || replyData.empty()) { return; } if (mCbf != NULL && cmdCode == EFFECT_CMD_SET_PARAM) { std::vector cmdDataCopy(cmdData); effect_param_t* cmd = reinterpret_cast(cmdDataCopy.data()); cmd->status = *reinterpret_cast(replyData.data()); mCbf(EVENT_PARAMETER_CHANGED, mUserData, cmd); } } // ------------------------------------------------------------------------- status_t AudioEffect::queryNumberEffects(uint32_t *numEffects) { const sp& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; return af->queryNumberEffects(numEffects); } status_t AudioEffect::queryEffect(uint32_t index, effect_descriptor_t *descriptor) { const sp& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; return af->queryEffect(index, descriptor); } status_t AudioEffect::getEffectDescriptor(const effect_uuid_t *uuid, const effect_uuid_t *type, uint32_t preferredTypeFlag, effect_descriptor_t *descriptor) { const sp& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; return af->getEffectDescriptor(uuid, type, preferredTypeFlag, descriptor); } status_t AudioEffect::queryDefaultPreProcessing(audio_session_t audioSession, effect_descriptor_t *descriptors, uint32_t *count) { if (descriptors == nullptr || count == nullptr) { return BAD_VALUE; } const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; int32_t audioSessionAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_session_t_int32_t(audioSession)); media::Int countAidl; countAidl.value = VALUE_OR_RETURN_STATUS(convertIntegral(*count)); std::vector retAidl; RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( aps->queryDefaultPreProcessing(audioSessionAidl, &countAidl, &retAidl))); *count = VALUE_OR_RETURN_STATUS(convertIntegral(countAidl.value)); RETURN_STATUS_IF_ERROR(convertRange(retAidl.begin(), retAidl.end(), descriptors, aidl2legacy_EffectDescriptor_effect_descriptor_t)); return OK; } status_t AudioEffect::newEffectUniqueId(audio_unique_id_t* id) { const sp& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; *id = af->newAudioUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT); return NO_ERROR; } status_t AudioEffect::addSourceDefaultEffect(const char *typeStr, const String16& opPackageName, const char *uuidStr, int32_t priority, audio_source_t source, audio_unique_id_t *id) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; if (typeStr == NULL && uuidStr == NULL) return BAD_VALUE; // Convert type & uuid from string to effect_uuid_t. effect_uuid_t type; if (typeStr != NULL) { status_t res = stringToGuid(typeStr, &type); if (res != OK) return res; } else { type = *EFFECT_UUID_NULL; } effect_uuid_t uuid; if (uuidStr != NULL) { status_t res = stringToGuid(uuidStr, &uuid); if (res != OK) return res; } else { uuid = *EFFECT_UUID_NULL; } media::AudioUuid typeAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_uuid_t_AudioUuid(type)); media::AudioUuid uuidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_uuid_t_AudioUuid(uuid)); std::string opPackageNameAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_String16_string(opPackageName)); media::AudioSourceType sourceAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_source_t_AudioSourceType(source)); int32_t retAidl; RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( aps->addSourceDefaultEffect(typeAidl, opPackageNameAidl, uuidAidl, priority, sourceAidl, &retAidl))); *id = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_unique_id_t(retAidl)); return OK; } status_t AudioEffect::addStreamDefaultEffect(const char *typeStr, const String16& opPackageName, const char *uuidStr, int32_t priority, audio_usage_t usage, audio_unique_id_t *id) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; if (typeStr == NULL && uuidStr == NULL) return BAD_VALUE; // Convert type & uuid from string to effect_uuid_t. effect_uuid_t type; if (typeStr != NULL) { status_t res = stringToGuid(typeStr, &type); if (res != OK) return res; } else { type = *EFFECT_UUID_NULL; } effect_uuid_t uuid; if (uuidStr != NULL) { status_t res = stringToGuid(uuidStr, &uuid); if (res != OK) return res; } else { uuid = *EFFECT_UUID_NULL; } media::AudioUuid typeAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_uuid_t_AudioUuid(type)); media::AudioUuid uuidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_uuid_t_AudioUuid(uuid)); std::string opPackageNameAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_String16_string(opPackageName)); media::AudioUsage usageAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_usage_t_AudioUsage(usage)); int32_t retAidl; RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( aps->addStreamDefaultEffect(typeAidl, opPackageNameAidl, uuidAidl, priority, usageAidl, &retAidl))); *id = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_unique_id_t(retAidl)); return OK; } status_t AudioEffect::removeSourceDefaultEffect(audio_unique_id_t id) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; int32_t idAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_unique_id_t_int32_t(id)); return statusTFromBinderStatus(aps->removeSourceDefaultEffect(idAidl)); } status_t AudioEffect::removeStreamDefaultEffect(audio_unique_id_t id) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; int32_t idAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_unique_id_t_int32_t(id)); return statusTFromBinderStatus(aps->removeStreamDefaultEffect(idAidl)); } // ------------------------------------------------------------------------- status_t AudioEffect::stringToGuid(const char *str, effect_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 AudioEffect::guidToString(const effect_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