Loading services/audiopolicy/AudioPolicyInterface.h +28 −2 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include <android/media/audio/common/AudioMMapPolicyType.h> #include <android/media/GetInputForAttrResponse.h> #include <android/content/AttributionSourceState.h> #include <error/BinderResult.h> #include <media/AudioCommonTypes.h> #include <media/AudioContainers.h> #include <media/AudioDeviceTypeAddr.h> Loading Loading @@ -182,8 +183,7 @@ public: audio_input_flags_t flags, audio_unique_id_t riid, audio_session_t session, const AttributionSourceState& attributionSource, input_type_t *inputType /* out param */) = 0; const AttributionSourceState& attributionSource) = 0; // indicates to the audio policy manager that the input starts being used. virtual status_t startInput(audio_port_handle_t portId) = 0; // indicates to the audio policy manager that the input stops being used. Loading Loading @@ -636,6 +636,32 @@ public: virtual status_t getMmapPolicyInfos( media::audio::common::AudioMMapPolicyType policyType, std::vector<media::audio::common::AudioMMapPolicyInfo> *policyInfos) = 0; enum class MixType { // e.g. audio recording from a microphone NONE = 0, // used for "remote submix" legacy mode (no DAP), capture of the media to play it remotely CAPTURE, // used for platform audio rerouting, where mixes are handled by external and dynamically // installed policies which reroute audio mixes EXT_POLICY_REROUTE, // used for playback capture with a MediaProjection PUBLIC_CAPTURE_PLAYBACK, // used for capture from telephony RX path TELEPHONY_RX_CAPTURE, }; struct PermissionReqs { media::audio::common::AudioSource source; MixType mixType; uint32_t virtualDeviceId; // Flag based validation bool isHotword; bool isCallRedir; }; virtual error::BinderResult<bool> checkPermissionForInput(const AttributionSourceState& attr, const PermissionReqs& req) = 0; }; // These are the signatures of createAudioPolicyManager/destroyAudioPolicyManager Loading services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp +2 −4 Original line number Diff line number Diff line Loading @@ -295,14 +295,12 @@ bool AudioPolicyManagerFuzzer::getInputForAttr( audio_port_handle_t localPortId; if (!portId) portId = &localPortId; *portId = AUDIO_PORT_HANDLE_NONE; AudioPolicyInterface::input_type_t inputType; AttributionSourceState attributionSource; attributionSource.uid = 0; attributionSource.token = sp<BBinder>::make(); const auto inputRes = mManager->getInputForAttr(attr, input, *selectedDeviceId, config, flags, riid, AUDIO_SESSION_NONE, attributionSource, &inputType); const auto inputRes = mManager->getInputForAttr(attr, input, *selectedDeviceId, config, flags, riid, AUDIO_SESSION_NONE, attributionSource); if (!inputRes.has_value()) return false; if (inputRes->portId == AUDIO_PORT_HANDLE_NONE || inputRes->input == AUDIO_IO_HANDLE_NONE) { Loading services/audiopolicy/managerdefault/AudioPolicyManager.cpp +36 −19 Original line number Diff line number Diff line Loading @@ -2935,8 +2935,7 @@ AudioPolicyManager::getInputForAttr(audio_attributes_t attributes, audio_input_flags_t flags, audio_unique_id_t riid, audio_session_t session, const AttributionSourceState& attributionSource, input_type_t *inputType) const AttributionSourceState& attributionSource) { ALOGV("%s() source %d, sampling rate %d, format %#x, channel mask %#x, session %d, " "flags %#x attributes=%s requested device ID %d", Loading @@ -2957,6 +2956,17 @@ AudioPolicyManager::getInputForAttr(audio_attributes_t attributes, attributes.source = AUDIO_SOURCE_MIC; } using PermissionReqs = AudioPolicyClientInterface::PermissionReqs; using MixType = AudioPolicyClientInterface::MixType; PermissionReqs permReq { .source = legacy2aidl_audio_source_t_AudioSource(attributes.source).value(), .mixType = MixType::NONE, // can be modified .virtualDeviceId = 0, // can be modified .isHotword = (flags & (AUDIO_INPUT_FLAG_HW_HOTWORD | AUDIO_INPUT_FLAG_HOTWORD_TAP | AUDIO_INPUT_FLAG_HW_LOOKBACK)) != 0, .isCallRedir = (attributes.flags & AUDIO_FLAG_CALL_REDIRECTION) != 0, }; // Explicit routing? sp<DeviceDescriptor> explicitRoutingDevice = mAvailableInputDevices.getDeviceFromId(requestedDeviceId); Loading Loading @@ -3000,13 +3010,9 @@ AudioPolicyManager::getInputForAttr(audio_attributes_t attributes, } } } *inputType = API_INPUT_LEGACY; device = inputDesc->getDevice(); ALOGV("%s reusing MMAP input %d for session %d", __FUNCTION__, requestedInput, session); // TODO perm check } else { *inputType = API_INPUT_INVALID; if (attributes.source == AUDIO_SOURCE_REMOTE_SUBMIX && extractAddressFromAudioAttributes(attributes).has_value()) { status_t status = mPolicyMixes.getInputMixForAttr(attributes, &policyMix); Loading @@ -3027,12 +3033,12 @@ AudioPolicyManager::getInputForAttr(audio_attributes_t attributes, } if (is_mix_loopback_render(policyMix->mRouteFlags)) { *inputType = API_INPUT_MIX_PUBLIC_CAPTURE_PLAYBACK; permReq.mixType = MixType::PUBLIC_CAPTURE_PLAYBACK; } else { *inputType = API_INPUT_MIX_EXT_POLICY_REROUTE; permReq.mixType = MixType::EXT_POLICY_REROUTE; } // TODO is this correct? vdi = policyMix->mVirtualDeviceId; permReq.virtualDeviceId = policyMix->mVirtualDeviceId; } else { if (explicitRoutingDevice != nullptr) { device = explicitRoutingDevice; Loading @@ -3050,24 +3056,35 @@ AudioPolicyManager::getInputForAttr(audio_attributes_t attributes, attributes.source))}; } if (device->type() == AUDIO_DEVICE_IN_ECHO_REFERENCE) { *inputType = API_INPUT_MIX_CAPTURE; permReq.mixType = MixType::CAPTURE; } else if (policyMix) { ALOG_ASSERT(policyMix->mMixType == MIX_TYPE_RECORDERS, "Invalid Mix Type"); // there is an external policy, but this input is attached to a mix of recorders, // meaning it receives audio injected into the framework, so the recorder doesn't // know about it and is therefore considered "legacy" *inputType = API_INPUT_LEGACY; vdi = policyMix->mVirtualDeviceId; permReq.mixType = MixType::NONE; permReq.virtualDeviceId = policyMix->mVirtualDeviceId; } else if (audio_is_remote_submix_device(device->type())) { *inputType = API_INPUT_MIX_CAPTURE; permReq.mixType = MixType::CAPTURE; } else if (device->type() == AUDIO_DEVICE_IN_TELEPHONY_RX) { *inputType = API_INPUT_TELEPHONY_RX; permReq.mixType = MixType::TELEPHONY_RX_CAPTURE; } else { *inputType = API_INPUT_LEGACY; permReq.mixType = MixType::NONE; } } // TODO perm check auto permRes = mpClientInterface->checkPermissionForInput(attributionSource, permReq); if (!permRes.has_value()) return base::unexpected {permRes.error()}; if (!permRes.value()) { return base::unexpected{Status::fromExceptionCode( EX_SECURITY, String8::format("%s: %s missing perms for source %d mix %d vdi %d" "hotword? %d callredir? %d", __func__, attributionSource.toString().c_str(), static_cast<int>(permReq.source), static_cast<int>(permReq.mixType), permReq.virtualDeviceId, permReq.isHotword, permReq.isCallRedir))}; } input = getInputForDevice(device, session, attributes, config, flags, policyMix); if (input == AUDIO_IO_HANDLE_NONE) { Loading Loading @@ -3108,14 +3125,14 @@ AudioPolicyManager::getInputForAttr(audio_attributes_t attributes, mEffects.moveEffectsForIo(session, input, &mInputs, mpClientInterface); inputDesc->addClient(clientDesc); ALOGV("getInputForAttr() returns input %d type %d selectedDeviceId %d for port ID %d", input, *inputType, selectedDeviceId, allocatedPortId); ALOGV("getInputForAttr() returns input %d selectedDeviceId %d vdi %d for port ID %d", input, selectedDeviceId, permReq.virtualDeviceId, allocatedPortId); auto ret = media::GetInputForAttrResponse {}; ret.input = input; ret.selectedDeviceId = selectedDeviceId; ret.portId = allocatedPortId; ret.virtualDeviceId = vdi; ret.virtualDeviceId = permReq.virtualDeviceId; ret.config = legacy2aidl_audio_config_base_t_AudioConfigBase(config, true /*isInput*/).value(); return ret; } Loading services/audiopolicy/managerdefault/AudioPolicyManager.h +1 −2 Original line number Diff line number Diff line Loading @@ -144,8 +144,7 @@ public: audio_input_flags_t flags, audio_unique_id_t riid, audio_session_t session, const AttributionSourceState& attributionSource, input_type_t *inputType /* out param */) override; const AttributionSourceState& attributionSource) override; // indicates to the audio policy manager that the input starts being used. virtual status_t startInput(audio_port_handle_t portId); Loading services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +51 −105 Original line number Diff line number Diff line Loading @@ -680,13 +680,14 @@ static bool isLegacyOutputSource(AudioSource source) { } } error::BinderResult<bool> AudioPolicyService::evaluatePermsForSource( const AttributionSourceState& attrSource, AudioSource source, bool isHotword) { error::BinderResult<bool> AudioPolicyService::AudioPolicyClient::checkPermissionForInput( const AttributionSourceState& attrSource, const PermissionReqs& req) { error::BinderResult<bool> permRes = true; const auto check_perm = [&](PermissionEnum perm, uid_t uid) { return getPermissionProvider().checkPermission(perm, uid); return mAudioPolicyService->getPermissionProvider().checkPermission(perm, uid); }; switch (source) { switch (req.source) { case AudioSource::VOICE_UPLINK: case AudioSource::VOICE_DOWNLINK: case AudioSource::VOICE_CALL: Loading Loading @@ -721,14 +722,13 @@ error::BinderResult<bool> AudioPolicyService::evaluatePermsForSource( case AudioSource::VOICE_PERFORMANCE: // No additional check intended case AudioSource::REMOTE_SUBMIX: // special-case checked based on device (evaluatePermsForDevice) // special-case checked based on mix type below break; } bool isAllowed = VALUE_OR_RETURN(permRes); if (!isAllowed) { if (isLegacyOutputSource(source)) { if (!permRes.has_value()) return permRes; if (!permRes.value()) { if (isLegacyOutputSource(req.source)) { permRes = audioserver_permissions() ? check_perm(CAPTURE_AUDIO_OUTPUT, attrSource.uid) : captureAudioOutputAllowed(attrSource); PROPAGATE_FALSEY(permRes); Loading @@ -737,70 +737,61 @@ error::BinderResult<bool> AudioPolicyService::evaluatePermsForSource( } } if (isHotword) { if (req.isHotword) { permRes = audioserver_permissions() ? check_perm(CAPTURE_AUDIO_HOTWORD, attrSource.uid) : captureHotwordAllowed(attrSource); PROPAGATE_FALSEY(permRes); } // All sources which aren't output capture require RECORD as well, // as well as vdi policy mix const auto legacySource = aidl2legacy_AudioSource_audio_source_t(source).value(); if (isRecordOpRequired(legacySource)) { permRes = audioserver_permissions() ? check_perm(RECORD_AUDIO, attrSource.uid) : recordingAllowed(attrSource, legacySource); PROPAGATE_FALSEY(permRes); } return true; } error::BinderResult<bool> AudioPolicyService::evaluatePermsForDevice( const AttributionSourceState& attrSource, AudioSource source, AudioPolicyInterface::input_type_t inputType, uint32_t vdi, bool isCallRedir) { // enforce permission (if any) required for each type of input error::BinderResult<bool> permRes = true; const auto check_perm = [&](PermissionEnum perm, uid_t uid) { return getPermissionProvider().checkPermission(perm, uid); }; // TODO evaluate whether we should be checking call redirection like this bool isAllowedDueToCallPerm = false; if (isCallRedir) { if (req.isCallRedir) { const auto checkCall = audioserver_permissions() ? check_perm(CALL_AUDIO_INTERCEPTION, attrSource.uid) : callAudioInterceptionAllowed(attrSource); isAllowedDueToCallPerm = VALUE_OR_RETURN(checkCall); } switch (inputType) { case AudioPolicyInterface::API_INPUT_MIX_PUBLIC_CAPTURE_PLAYBACK: switch (req.mixType) { case MixType::NONE: break; case MixType::PUBLIC_CAPTURE_PLAYBACK: // this use case has been validated in audio service with a MediaProjection token, // and doesn't rely on regular permissions case AudioPolicyInterface::API_INPUT_LEGACY: // TODO (b/378778313) break; case AudioPolicyInterface::API_INPUT_TELEPHONY_RX: case MixType::TELEPHONY_RX_CAPTURE: if (isAllowedDueToCallPerm) break; // FIXME: use the same permission as for remote submix for now. FALLTHROUGH_INTENDED; case AudioPolicyInterface::API_INPUT_MIX_CAPTURE: case MixType::CAPTURE: permRes = audioserver_permissions() ? check_perm(CAPTURE_AUDIO_OUTPUT, attrSource.uid) : captureAudioOutputAllowed(attrSource); break; case AudioPolicyInterface::API_INPUT_MIX_EXT_POLICY_REROUTE: { case MixType::EXT_POLICY_REROUTE: // TODO intended? if (isAllowedDueToCallPerm) break; permRes = audioserver_permissions() ? check_perm(MODIFY_AUDIO_ROUTING, attrSource.uid) : modifyAudioRoutingAllowed(attrSource); break; } case AudioPolicyInterface::API_INPUT_INVALID: default: LOG_ALWAYS_FATAL("%s encountered an invalid input type %d", __func__, (int)inputType); } PROPAGATE_FALSEY(permRes); if (audiopolicy_flags::record_audio_device_aware_permission()) { // enforce device-aware RECORD_AUDIO permission const auto legacySource = aidl2legacy_AudioSource_audio_source_t(source).value(); return vdi == kDefaultVirtualDeviceId || recordingAllowed(attrSource, vdi, legacySource); // All sources which aren't output capture // AND capture from vdi policy mix (the injected audio is mic data from another device) // REQUIRE RECORD perms const auto legacySource = aidl2legacy_AudioSource_audio_source_t(req.source).value(); if (req.virtualDeviceId != kDefaultVirtualDeviceId) { // TODO assert that this is always a recordOpSource // TODO upcall solution return recordingAllowed(attrSource, req.virtualDeviceId, legacySource); } if (isRecordOpRequired(legacySource)) { permRes = audioserver_permissions() ? check_perm(RECORD_AUDIO, attrSource.uid) : recordingAllowed(attrSource, legacySource); PROPAGATE_FALSEY(permRes); } return true; } Loading Loading @@ -847,19 +838,14 @@ Status AudioPolicyService::getInputForAttr(const media::audio::common::AudioAttr inputSource = AudioSource::MIC; } const bool isHotword = (flags & (AUDIO_INPUT_FLAG_HW_HOTWORD | AUDIO_INPUT_FLAG_HOTWORD_TAP | AUDIO_INPUT_FLAG_HW_LOOKBACK)) != 0; const bool isCallRedir = (attr.flags & AUDIO_FLAG_CALL_REDIRECTION) != 0; const bool canCaptureOutput = audioserver_permissions() ? CHECK_PERM(CAPTURE_AUDIO_OUTPUT, attributionSource.uid) : captureAudioOutputAllowed(attributionSource); //TODO(b/374751406): remove forcing canBypassConcurrentPolicy to canCaptureOutput // once all system apps using CAPTURE_AUDIO_OUTPUT to capture during calls // are updated to use the new CONCURRENT_AUDIO_RECORD_BYPASS permission. bool canBypassConcurrentPolicy = canCaptureOutput; bool canBypassConcurrentPolicy = audioserver_permissions() ? CHECK_PERM(CAPTURE_AUDIO_OUTPUT, attributionSource.uid) : captureAudioOutputAllowed(attributionSource); if (concurrent_audio_record_bypass_permission()) { canBypassConcurrentPolicy = audioserver_permissions() ? CHECK_PERM(BYPASS_CONCURRENT_RECORD_AUDIO_RESTRICTION, Loading @@ -867,55 +853,16 @@ Status AudioPolicyService::getInputForAttr(const media::audio::common::AudioAttr : bypassConcurrentPolicyAllowed(attributionSource); } const bool hasPerm = VALUE_OR_RETURN_STATUS(evaluatePermsForSource( attributionSource, inputSource, isHotword)); if (!hasPerm) { return Status::fromExceptionCode( EX_SECURITY, String8::format("%s: %s missing perms for source %s", __func__, attributionSource.toString().c_str(), toString(inputSource).c_str())); } sp<AudioPolicyEffects> audioPolicyEffects; base::expected<media::GetInputForAttrResponse, std::variant<binder::Status, AudioConfigBase>> res; { AudioPolicyInterface::input_type_t inputType; audio_utils::lock_guard _l(mMutex); { AutoCallerClear acc; // the audio_in_acoustics_t parameter is ignored by get_input() res = mAudioPolicyManager->getInputForAttr(attr, requestedInput, requestedDeviceId, config, flags, riid, session, attributionSource, &inputType); } audioPolicyEffects = mAudioPolicyEffects; if (res.has_value()) { const auto permResult = evaluatePermsForDevice(attributionSource, inputSource, inputType, res->virtualDeviceId, isCallRedir); if (!permResult.has_value()) { AutoCallerClear acc; mAudioPolicyManager->releaseInput(res->portId); return permResult.error(); } else if (!permResult.value()) { AutoCallerClear acc; mAudioPolicyManager->releaseInput(res->portId); return Status::fromExceptionCode( EX_SECURITY, String8::format( "%s: %s missing perms for input type %d, inputSource %d, vdi %d", __func__, attributionSource.toString().c_str(), inputType, inputSource, res->virtualDeviceId)); } } config, flags, riid, session, attributionSource); if (!res.has_value()) { if (res.error().index() == 1) { _aidl_return->config = std::get<1>(res.error()); Loading @@ -925,16 +872,15 @@ Status AudioPolicyService::getInputForAttr(const media::audio::common::AudioAttr } } DeviceIdVector selectedDeviceIds = { res->selectedDeviceId }; sp<AudioRecordClient> client = new AudioRecordClient(attr, res->input, session, res->portId, selectedDeviceIds, attributionSource, res->virtualDeviceId, canBypassConcurrentPolicy, mOutputCommandThread); audioPolicyEffects = mAudioPolicyEffects; sp<AudioRecordClient> client = new AudioRecordClient( attr, res->input, session, res->portId, {res->selectedDeviceId}, attributionSource, res->virtualDeviceId, canBypassConcurrentPolicy, mOutputCommandThread); mAudioRecordClients.add(res->portId, client); } if (audioPolicyEffects != 0) { if (audioPolicyEffects != nullptr) { // create audio pre processors according to input source status_t status = audioPolicyEffects->addInputEffects(res->input, aidl2legacy_AudioSource_audio_source_t(inputSource).value(), session); Loading Loading
services/audiopolicy/AudioPolicyInterface.h +28 −2 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include <android/media/audio/common/AudioMMapPolicyType.h> #include <android/media/GetInputForAttrResponse.h> #include <android/content/AttributionSourceState.h> #include <error/BinderResult.h> #include <media/AudioCommonTypes.h> #include <media/AudioContainers.h> #include <media/AudioDeviceTypeAddr.h> Loading Loading @@ -182,8 +183,7 @@ public: audio_input_flags_t flags, audio_unique_id_t riid, audio_session_t session, const AttributionSourceState& attributionSource, input_type_t *inputType /* out param */) = 0; const AttributionSourceState& attributionSource) = 0; // indicates to the audio policy manager that the input starts being used. virtual status_t startInput(audio_port_handle_t portId) = 0; // indicates to the audio policy manager that the input stops being used. Loading Loading @@ -636,6 +636,32 @@ public: virtual status_t getMmapPolicyInfos( media::audio::common::AudioMMapPolicyType policyType, std::vector<media::audio::common::AudioMMapPolicyInfo> *policyInfos) = 0; enum class MixType { // e.g. audio recording from a microphone NONE = 0, // used for "remote submix" legacy mode (no DAP), capture of the media to play it remotely CAPTURE, // used for platform audio rerouting, where mixes are handled by external and dynamically // installed policies which reroute audio mixes EXT_POLICY_REROUTE, // used for playback capture with a MediaProjection PUBLIC_CAPTURE_PLAYBACK, // used for capture from telephony RX path TELEPHONY_RX_CAPTURE, }; struct PermissionReqs { media::audio::common::AudioSource source; MixType mixType; uint32_t virtualDeviceId; // Flag based validation bool isHotword; bool isCallRedir; }; virtual error::BinderResult<bool> checkPermissionForInput(const AttributionSourceState& attr, const PermissionReqs& req) = 0; }; // These are the signatures of createAudioPolicyManager/destroyAudioPolicyManager Loading
services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp +2 −4 Original line number Diff line number Diff line Loading @@ -295,14 +295,12 @@ bool AudioPolicyManagerFuzzer::getInputForAttr( audio_port_handle_t localPortId; if (!portId) portId = &localPortId; *portId = AUDIO_PORT_HANDLE_NONE; AudioPolicyInterface::input_type_t inputType; AttributionSourceState attributionSource; attributionSource.uid = 0; attributionSource.token = sp<BBinder>::make(); const auto inputRes = mManager->getInputForAttr(attr, input, *selectedDeviceId, config, flags, riid, AUDIO_SESSION_NONE, attributionSource, &inputType); const auto inputRes = mManager->getInputForAttr(attr, input, *selectedDeviceId, config, flags, riid, AUDIO_SESSION_NONE, attributionSource); if (!inputRes.has_value()) return false; if (inputRes->portId == AUDIO_PORT_HANDLE_NONE || inputRes->input == AUDIO_IO_HANDLE_NONE) { Loading
services/audiopolicy/managerdefault/AudioPolicyManager.cpp +36 −19 Original line number Diff line number Diff line Loading @@ -2935,8 +2935,7 @@ AudioPolicyManager::getInputForAttr(audio_attributes_t attributes, audio_input_flags_t flags, audio_unique_id_t riid, audio_session_t session, const AttributionSourceState& attributionSource, input_type_t *inputType) const AttributionSourceState& attributionSource) { ALOGV("%s() source %d, sampling rate %d, format %#x, channel mask %#x, session %d, " "flags %#x attributes=%s requested device ID %d", Loading @@ -2957,6 +2956,17 @@ AudioPolicyManager::getInputForAttr(audio_attributes_t attributes, attributes.source = AUDIO_SOURCE_MIC; } using PermissionReqs = AudioPolicyClientInterface::PermissionReqs; using MixType = AudioPolicyClientInterface::MixType; PermissionReqs permReq { .source = legacy2aidl_audio_source_t_AudioSource(attributes.source).value(), .mixType = MixType::NONE, // can be modified .virtualDeviceId = 0, // can be modified .isHotword = (flags & (AUDIO_INPUT_FLAG_HW_HOTWORD | AUDIO_INPUT_FLAG_HOTWORD_TAP | AUDIO_INPUT_FLAG_HW_LOOKBACK)) != 0, .isCallRedir = (attributes.flags & AUDIO_FLAG_CALL_REDIRECTION) != 0, }; // Explicit routing? sp<DeviceDescriptor> explicitRoutingDevice = mAvailableInputDevices.getDeviceFromId(requestedDeviceId); Loading Loading @@ -3000,13 +3010,9 @@ AudioPolicyManager::getInputForAttr(audio_attributes_t attributes, } } } *inputType = API_INPUT_LEGACY; device = inputDesc->getDevice(); ALOGV("%s reusing MMAP input %d for session %d", __FUNCTION__, requestedInput, session); // TODO perm check } else { *inputType = API_INPUT_INVALID; if (attributes.source == AUDIO_SOURCE_REMOTE_SUBMIX && extractAddressFromAudioAttributes(attributes).has_value()) { status_t status = mPolicyMixes.getInputMixForAttr(attributes, &policyMix); Loading @@ -3027,12 +3033,12 @@ AudioPolicyManager::getInputForAttr(audio_attributes_t attributes, } if (is_mix_loopback_render(policyMix->mRouteFlags)) { *inputType = API_INPUT_MIX_PUBLIC_CAPTURE_PLAYBACK; permReq.mixType = MixType::PUBLIC_CAPTURE_PLAYBACK; } else { *inputType = API_INPUT_MIX_EXT_POLICY_REROUTE; permReq.mixType = MixType::EXT_POLICY_REROUTE; } // TODO is this correct? vdi = policyMix->mVirtualDeviceId; permReq.virtualDeviceId = policyMix->mVirtualDeviceId; } else { if (explicitRoutingDevice != nullptr) { device = explicitRoutingDevice; Loading @@ -3050,24 +3056,35 @@ AudioPolicyManager::getInputForAttr(audio_attributes_t attributes, attributes.source))}; } if (device->type() == AUDIO_DEVICE_IN_ECHO_REFERENCE) { *inputType = API_INPUT_MIX_CAPTURE; permReq.mixType = MixType::CAPTURE; } else if (policyMix) { ALOG_ASSERT(policyMix->mMixType == MIX_TYPE_RECORDERS, "Invalid Mix Type"); // there is an external policy, but this input is attached to a mix of recorders, // meaning it receives audio injected into the framework, so the recorder doesn't // know about it and is therefore considered "legacy" *inputType = API_INPUT_LEGACY; vdi = policyMix->mVirtualDeviceId; permReq.mixType = MixType::NONE; permReq.virtualDeviceId = policyMix->mVirtualDeviceId; } else if (audio_is_remote_submix_device(device->type())) { *inputType = API_INPUT_MIX_CAPTURE; permReq.mixType = MixType::CAPTURE; } else if (device->type() == AUDIO_DEVICE_IN_TELEPHONY_RX) { *inputType = API_INPUT_TELEPHONY_RX; permReq.mixType = MixType::TELEPHONY_RX_CAPTURE; } else { *inputType = API_INPUT_LEGACY; permReq.mixType = MixType::NONE; } } // TODO perm check auto permRes = mpClientInterface->checkPermissionForInput(attributionSource, permReq); if (!permRes.has_value()) return base::unexpected {permRes.error()}; if (!permRes.value()) { return base::unexpected{Status::fromExceptionCode( EX_SECURITY, String8::format("%s: %s missing perms for source %d mix %d vdi %d" "hotword? %d callredir? %d", __func__, attributionSource.toString().c_str(), static_cast<int>(permReq.source), static_cast<int>(permReq.mixType), permReq.virtualDeviceId, permReq.isHotword, permReq.isCallRedir))}; } input = getInputForDevice(device, session, attributes, config, flags, policyMix); if (input == AUDIO_IO_HANDLE_NONE) { Loading Loading @@ -3108,14 +3125,14 @@ AudioPolicyManager::getInputForAttr(audio_attributes_t attributes, mEffects.moveEffectsForIo(session, input, &mInputs, mpClientInterface); inputDesc->addClient(clientDesc); ALOGV("getInputForAttr() returns input %d type %d selectedDeviceId %d for port ID %d", input, *inputType, selectedDeviceId, allocatedPortId); ALOGV("getInputForAttr() returns input %d selectedDeviceId %d vdi %d for port ID %d", input, selectedDeviceId, permReq.virtualDeviceId, allocatedPortId); auto ret = media::GetInputForAttrResponse {}; ret.input = input; ret.selectedDeviceId = selectedDeviceId; ret.portId = allocatedPortId; ret.virtualDeviceId = vdi; ret.virtualDeviceId = permReq.virtualDeviceId; ret.config = legacy2aidl_audio_config_base_t_AudioConfigBase(config, true /*isInput*/).value(); return ret; } Loading
services/audiopolicy/managerdefault/AudioPolicyManager.h +1 −2 Original line number Diff line number Diff line Loading @@ -144,8 +144,7 @@ public: audio_input_flags_t flags, audio_unique_id_t riid, audio_session_t session, const AttributionSourceState& attributionSource, input_type_t *inputType /* out param */) override; const AttributionSourceState& attributionSource) override; // indicates to the audio policy manager that the input starts being used. virtual status_t startInput(audio_port_handle_t portId); Loading
services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +51 −105 Original line number Diff line number Diff line Loading @@ -680,13 +680,14 @@ static bool isLegacyOutputSource(AudioSource source) { } } error::BinderResult<bool> AudioPolicyService::evaluatePermsForSource( const AttributionSourceState& attrSource, AudioSource source, bool isHotword) { error::BinderResult<bool> AudioPolicyService::AudioPolicyClient::checkPermissionForInput( const AttributionSourceState& attrSource, const PermissionReqs& req) { error::BinderResult<bool> permRes = true; const auto check_perm = [&](PermissionEnum perm, uid_t uid) { return getPermissionProvider().checkPermission(perm, uid); return mAudioPolicyService->getPermissionProvider().checkPermission(perm, uid); }; switch (source) { switch (req.source) { case AudioSource::VOICE_UPLINK: case AudioSource::VOICE_DOWNLINK: case AudioSource::VOICE_CALL: Loading Loading @@ -721,14 +722,13 @@ error::BinderResult<bool> AudioPolicyService::evaluatePermsForSource( case AudioSource::VOICE_PERFORMANCE: // No additional check intended case AudioSource::REMOTE_SUBMIX: // special-case checked based on device (evaluatePermsForDevice) // special-case checked based on mix type below break; } bool isAllowed = VALUE_OR_RETURN(permRes); if (!isAllowed) { if (isLegacyOutputSource(source)) { if (!permRes.has_value()) return permRes; if (!permRes.value()) { if (isLegacyOutputSource(req.source)) { permRes = audioserver_permissions() ? check_perm(CAPTURE_AUDIO_OUTPUT, attrSource.uid) : captureAudioOutputAllowed(attrSource); PROPAGATE_FALSEY(permRes); Loading @@ -737,70 +737,61 @@ error::BinderResult<bool> AudioPolicyService::evaluatePermsForSource( } } if (isHotword) { if (req.isHotword) { permRes = audioserver_permissions() ? check_perm(CAPTURE_AUDIO_HOTWORD, attrSource.uid) : captureHotwordAllowed(attrSource); PROPAGATE_FALSEY(permRes); } // All sources which aren't output capture require RECORD as well, // as well as vdi policy mix const auto legacySource = aidl2legacy_AudioSource_audio_source_t(source).value(); if (isRecordOpRequired(legacySource)) { permRes = audioserver_permissions() ? check_perm(RECORD_AUDIO, attrSource.uid) : recordingAllowed(attrSource, legacySource); PROPAGATE_FALSEY(permRes); } return true; } error::BinderResult<bool> AudioPolicyService::evaluatePermsForDevice( const AttributionSourceState& attrSource, AudioSource source, AudioPolicyInterface::input_type_t inputType, uint32_t vdi, bool isCallRedir) { // enforce permission (if any) required for each type of input error::BinderResult<bool> permRes = true; const auto check_perm = [&](PermissionEnum perm, uid_t uid) { return getPermissionProvider().checkPermission(perm, uid); }; // TODO evaluate whether we should be checking call redirection like this bool isAllowedDueToCallPerm = false; if (isCallRedir) { if (req.isCallRedir) { const auto checkCall = audioserver_permissions() ? check_perm(CALL_AUDIO_INTERCEPTION, attrSource.uid) : callAudioInterceptionAllowed(attrSource); isAllowedDueToCallPerm = VALUE_OR_RETURN(checkCall); } switch (inputType) { case AudioPolicyInterface::API_INPUT_MIX_PUBLIC_CAPTURE_PLAYBACK: switch (req.mixType) { case MixType::NONE: break; case MixType::PUBLIC_CAPTURE_PLAYBACK: // this use case has been validated in audio service with a MediaProjection token, // and doesn't rely on regular permissions case AudioPolicyInterface::API_INPUT_LEGACY: // TODO (b/378778313) break; case AudioPolicyInterface::API_INPUT_TELEPHONY_RX: case MixType::TELEPHONY_RX_CAPTURE: if (isAllowedDueToCallPerm) break; // FIXME: use the same permission as for remote submix for now. FALLTHROUGH_INTENDED; case AudioPolicyInterface::API_INPUT_MIX_CAPTURE: case MixType::CAPTURE: permRes = audioserver_permissions() ? check_perm(CAPTURE_AUDIO_OUTPUT, attrSource.uid) : captureAudioOutputAllowed(attrSource); break; case AudioPolicyInterface::API_INPUT_MIX_EXT_POLICY_REROUTE: { case MixType::EXT_POLICY_REROUTE: // TODO intended? if (isAllowedDueToCallPerm) break; permRes = audioserver_permissions() ? check_perm(MODIFY_AUDIO_ROUTING, attrSource.uid) : modifyAudioRoutingAllowed(attrSource); break; } case AudioPolicyInterface::API_INPUT_INVALID: default: LOG_ALWAYS_FATAL("%s encountered an invalid input type %d", __func__, (int)inputType); } PROPAGATE_FALSEY(permRes); if (audiopolicy_flags::record_audio_device_aware_permission()) { // enforce device-aware RECORD_AUDIO permission const auto legacySource = aidl2legacy_AudioSource_audio_source_t(source).value(); return vdi == kDefaultVirtualDeviceId || recordingAllowed(attrSource, vdi, legacySource); // All sources which aren't output capture // AND capture from vdi policy mix (the injected audio is mic data from another device) // REQUIRE RECORD perms const auto legacySource = aidl2legacy_AudioSource_audio_source_t(req.source).value(); if (req.virtualDeviceId != kDefaultVirtualDeviceId) { // TODO assert that this is always a recordOpSource // TODO upcall solution return recordingAllowed(attrSource, req.virtualDeviceId, legacySource); } if (isRecordOpRequired(legacySource)) { permRes = audioserver_permissions() ? check_perm(RECORD_AUDIO, attrSource.uid) : recordingAllowed(attrSource, legacySource); PROPAGATE_FALSEY(permRes); } return true; } Loading Loading @@ -847,19 +838,14 @@ Status AudioPolicyService::getInputForAttr(const media::audio::common::AudioAttr inputSource = AudioSource::MIC; } const bool isHotword = (flags & (AUDIO_INPUT_FLAG_HW_HOTWORD | AUDIO_INPUT_FLAG_HOTWORD_TAP | AUDIO_INPUT_FLAG_HW_LOOKBACK)) != 0; const bool isCallRedir = (attr.flags & AUDIO_FLAG_CALL_REDIRECTION) != 0; const bool canCaptureOutput = audioserver_permissions() ? CHECK_PERM(CAPTURE_AUDIO_OUTPUT, attributionSource.uid) : captureAudioOutputAllowed(attributionSource); //TODO(b/374751406): remove forcing canBypassConcurrentPolicy to canCaptureOutput // once all system apps using CAPTURE_AUDIO_OUTPUT to capture during calls // are updated to use the new CONCURRENT_AUDIO_RECORD_BYPASS permission. bool canBypassConcurrentPolicy = canCaptureOutput; bool canBypassConcurrentPolicy = audioserver_permissions() ? CHECK_PERM(CAPTURE_AUDIO_OUTPUT, attributionSource.uid) : captureAudioOutputAllowed(attributionSource); if (concurrent_audio_record_bypass_permission()) { canBypassConcurrentPolicy = audioserver_permissions() ? CHECK_PERM(BYPASS_CONCURRENT_RECORD_AUDIO_RESTRICTION, Loading @@ -867,55 +853,16 @@ Status AudioPolicyService::getInputForAttr(const media::audio::common::AudioAttr : bypassConcurrentPolicyAllowed(attributionSource); } const bool hasPerm = VALUE_OR_RETURN_STATUS(evaluatePermsForSource( attributionSource, inputSource, isHotword)); if (!hasPerm) { return Status::fromExceptionCode( EX_SECURITY, String8::format("%s: %s missing perms for source %s", __func__, attributionSource.toString().c_str(), toString(inputSource).c_str())); } sp<AudioPolicyEffects> audioPolicyEffects; base::expected<media::GetInputForAttrResponse, std::variant<binder::Status, AudioConfigBase>> res; { AudioPolicyInterface::input_type_t inputType; audio_utils::lock_guard _l(mMutex); { AutoCallerClear acc; // the audio_in_acoustics_t parameter is ignored by get_input() res = mAudioPolicyManager->getInputForAttr(attr, requestedInput, requestedDeviceId, config, flags, riid, session, attributionSource, &inputType); } audioPolicyEffects = mAudioPolicyEffects; if (res.has_value()) { const auto permResult = evaluatePermsForDevice(attributionSource, inputSource, inputType, res->virtualDeviceId, isCallRedir); if (!permResult.has_value()) { AutoCallerClear acc; mAudioPolicyManager->releaseInput(res->portId); return permResult.error(); } else if (!permResult.value()) { AutoCallerClear acc; mAudioPolicyManager->releaseInput(res->portId); return Status::fromExceptionCode( EX_SECURITY, String8::format( "%s: %s missing perms for input type %d, inputSource %d, vdi %d", __func__, attributionSource.toString().c_str(), inputType, inputSource, res->virtualDeviceId)); } } config, flags, riid, session, attributionSource); if (!res.has_value()) { if (res.error().index() == 1) { _aidl_return->config = std::get<1>(res.error()); Loading @@ -925,16 +872,15 @@ Status AudioPolicyService::getInputForAttr(const media::audio::common::AudioAttr } } DeviceIdVector selectedDeviceIds = { res->selectedDeviceId }; sp<AudioRecordClient> client = new AudioRecordClient(attr, res->input, session, res->portId, selectedDeviceIds, attributionSource, res->virtualDeviceId, canBypassConcurrentPolicy, mOutputCommandThread); audioPolicyEffects = mAudioPolicyEffects; sp<AudioRecordClient> client = new AudioRecordClient( attr, res->input, session, res->portId, {res->selectedDeviceId}, attributionSource, res->virtualDeviceId, canBypassConcurrentPolicy, mOutputCommandThread); mAudioRecordClients.add(res->portId, client); } if (audioPolicyEffects != 0) { if (audioPolicyEffects != nullptr) { // create audio pre processors according to input source status_t status = audioPolicyEffects->addInputEffects(res->input, aidl2legacy_AudioSource_audio_source_t(inputSource).value(), session); Loading