diff options
| -rw-r--r-- | media/utils/ServiceUtilities.cpp | 143 | ||||
| -rw-r--r-- | media/utils/include/mediautils/ServiceUtilities.h | 4 | ||||
| -rw-r--r-- | services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp | 40 | ||||
| -rw-r--r-- | services/audiopolicy/service/AudioPolicyService.cpp | 10 | ||||
| -rw-r--r-- | services/audiopolicy/service/AudioRecordClient.cpp | 26 | ||||
| -rw-r--r-- | services/audiopolicy/service/AudioRecordClient.h | 4 |
6 files changed, 159 insertions, 68 deletions
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp index 4ee45c795e..92385f3203 100644 --- a/media/utils/ServiceUtilities.cpp +++ b/media/utils/ServiceUtilities.cpp @@ -40,6 +40,11 @@ namespace android { +namespace { +constexpr auto PERMISSION_GRANTED = permission::PermissionChecker::PERMISSION_GRANTED; +constexpr auto PERMISSION_HARD_DENIED = permission::PermissionChecker::PERMISSION_HARD_DENIED; +} + using content::AttributionSourceState; static const String16 sAndroidPermissionRecordAudio("android.permission.RECORD_AUDIO"); @@ -71,19 +76,33 @@ static String16 resolveCallingPackage(PermissionController& permissionController int32_t getOpForSource(audio_source_t source) { switch (source) { - case AUDIO_SOURCE_HOTWORD: - return AppOpsManager::OP_RECORD_AUDIO_HOTWORD; + case AUDIO_SOURCE_FM_TUNER: + return AppOpsManager::OP_NONE; case AUDIO_SOURCE_ECHO_REFERENCE: // fallthrough case AUDIO_SOURCE_REMOTE_SUBMIX: return AppOpsManager::OP_RECORD_AUDIO_OUTPUT; case AUDIO_SOURCE_VOICE_DOWNLINK: return AppOpsManager::OP_RECORD_INCOMING_PHONE_AUDIO; + case AUDIO_SOURCE_HOTWORD: + return AppOpsManager::OP_RECORD_AUDIO_HOTWORD; case AUDIO_SOURCE_DEFAULT: default: return AppOpsManager::OP_RECORD_AUDIO; } } +bool isRecordOpRequired(audio_source_t source) { + switch (source) { + case AUDIO_SOURCE_FM_TUNER: + case AUDIO_SOURCE_ECHO_REFERENCE: // fallthrough + case AUDIO_SOURCE_REMOTE_SUBMIX: + case AUDIO_SOURCE_VOICE_DOWNLINK: + return false; + default: + return true; + } +} + std::optional<AttributionSourceState> resolveAttributionSource( const AttributionSourceState& callerAttributionSource) { AttributionSourceState nextAttributionSource = callerAttributionSource; @@ -113,7 +132,7 @@ std::optional<AttributionSourceState> resolveAttributionSource( return std::optional<AttributionSourceState>{myAttributionSource}; } -static bool checkRecordingInternal(const AttributionSourceState& attributionSource, +static int checkRecordingInternal(const AttributionSourceState& attributionSource, const String16& msg, bool start, audio_source_t source) { // Okay to not track in app ops as audio server or media server is us and if // device is rooted security model is considered compromised. @@ -121,39 +140,74 @@ static bool checkRecordingInternal(const AttributionSourceState& attributionSour // user is active, but it is a core system service so let it through. // TODO(b/141210120): UserManager.DISALLOW_RECORD_AUDIO should not affect system user 0 uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(attributionSource.uid)); - if (isAudioServerOrMediaServerOrSystemServerOrRootUid(uid)) return true; - - // We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder) - // may open a record track on behalf of a client. Note that pid may be a tid. - // IMPORTANT: DON'T USE PermissionCache - RUNTIME PERMISSIONS CHANGE. - const std::optional<AttributionSourceState> resolvedAttributionSource = - resolveAttributionSource(attributionSource); - if (!resolvedAttributionSource.has_value()) { - return false; - } + if (isAudioServerOrMediaServerOrSystemServerOrRootUid(uid)) return PERMISSION_GRANTED; const int32_t attributedOpCode = getOpForSource(source); + if (isRecordOpRequired(source)) { + // We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder) + // may open a record track on behalf of a client. Note that pid may be a tid. + // IMPORTANT: DON'T USE PermissionCache - RUNTIME PERMISSIONS CHANGE. + std::optional<AttributionSourceState> resolvedAttributionSource = + resolveAttributionSource(attributionSource); + if (!resolvedAttributionSource.has_value()) { + return PERMISSION_HARD_DENIED; + } - permission::PermissionChecker permissionChecker; - bool permitted = false; - if (start) { - permitted = (permissionChecker.checkPermissionForStartDataDeliveryFromDatasource( - sAndroidPermissionRecordAudio, resolvedAttributionSource.value(), msg, - attributedOpCode) != permission::PermissionChecker::PERMISSION_HARD_DENIED); + permission::PermissionChecker permissionChecker; + int permitted; + if (start) { + // Do a double-check, where we first check without actually starting in order to handle + // the behavior of AppOps where ops are sometimes started but paused for SOFT_DENIED. + // Since there is no way to maintain reference consensus due to this behavior, avoid + // starting an op when a restriction is in place by first checking. In the case where we + // startOp would fail, call a noteOp (which will also fail) instead. This preserves + // behavior that is reliant on listening to op rejected events (such as the hint + // dialogue to unmute the microphone). Technically racy, but very unlikely. + // + // TODO(b/294609684) To be removed when the pause state for an OP is removed. + permitted = permissionChecker.checkPermissionForPreflightFromDatasource( + sAndroidPermissionRecordAudio, resolvedAttributionSource.value(), msg, + attributedOpCode); + if (permitted == PERMISSION_GRANTED) { + permitted = permissionChecker.checkPermissionForStartDataDeliveryFromDatasource( + sAndroidPermissionRecordAudio, resolvedAttributionSource.value(), msg, + attributedOpCode); + } else { + // intentionally don't set permitted + permissionChecker.checkPermissionForDataDeliveryFromDatasource( + sAndroidPermissionRecordAudio, resolvedAttributionSource.value(), msg, + attributedOpCode); + } + } else { + permitted = permissionChecker.checkPermissionForPreflightFromDatasource( + sAndroidPermissionRecordAudio, resolvedAttributionSource.value(), msg, + attributedOpCode); + } + + return permitted; } else { - permitted = (permissionChecker.checkPermissionForPreflightFromDatasource( - sAndroidPermissionRecordAudio, resolvedAttributionSource.value(), msg, - attributedOpCode) != permission::PermissionChecker::PERMISSION_HARD_DENIED); + if (attributedOpCode == AppOpsManager::OP_NONE) return PERMISSION_GRANTED; // nothing to do + AppOpsManager ap{}; + PermissionController pc{}; + return ap.startOpNoThrow( + attributedOpCode, attributionSource.uid, + resolveCallingPackage(pc, + String16{attributionSource.packageName.value_or("").c_str()}, + attributionSource.uid), + false, + attributionSource.attributionTag.has_value() + ? String16{attributionSource.attributionTag.value().c_str()} + : String16{}, + msg); } - - return permitted; } bool recordingAllowed(const AttributionSourceState& attributionSource, audio_source_t source) { - return checkRecordingInternal(attributionSource, String16(), /*start*/ false, source); + return checkRecordingInternal(attributionSource, String16(), /*start*/ false, source) != + PERMISSION_HARD_DENIED; } -bool startRecording(const AttributionSourceState& attributionSource, const String16& msg, +int startRecording(const AttributionSourceState& attributionSource, const String16& msg, audio_source_t source) { return checkRecordingInternal(attributionSource, msg, /*start*/ true, source); } @@ -164,19 +218,32 @@ void finishRecording(const AttributionSourceState& attributionSource, audio_sour uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(attributionSource.uid)); if (isAudioServerOrMediaServerOrSystemServerOrRootUid(uid)) return; - // We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder) - // may open a record track on behalf of a client. Note that pid may be a tid. - // IMPORTANT: DON'T USE PermissionCache - RUNTIME PERMISSIONS CHANGE. - const std::optional<AttributionSourceState> resolvedAttributionSource = - resolveAttributionSource(attributionSource); - if (!resolvedAttributionSource.has_value()) { - return; - } - const int32_t attributedOpCode = getOpForSource(source); - permission::PermissionChecker permissionChecker; - permissionChecker.finishDataDeliveryFromDatasource(attributedOpCode, - resolvedAttributionSource.value()); + if (isRecordOpRequired(source)) { + // We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder) + // may open a record track on behalf of a client. Note that pid may be a tid. + // IMPORTANT: DON'T USE PermissionCache - RUNTIME PERMISSIONS CHANGE. + const std::optional<AttributionSourceState> resolvedAttributionSource = + resolveAttributionSource(attributionSource); + if (!resolvedAttributionSource.has_value()) { + return; + } + + permission::PermissionChecker permissionChecker; + permissionChecker.finishDataDeliveryFromDatasource(attributedOpCode, + resolvedAttributionSource.value()); + } else { + if (attributedOpCode == AppOpsManager::OP_NONE) return; // nothing to do + AppOpsManager ap{}; + PermissionController pc{}; + ap.finishOp(attributedOpCode, attributionSource.uid, + resolveCallingPackage( + pc, String16{attributionSource.packageName.value_or("").c_str()}, + attributionSource.uid), + attributionSource.attributionTag.has_value() + ? String16{attributionSource.attributionTag.value().c_str()} + : String16{}); + } } bool captureAudioOutputAllowed(const AttributionSourceState& attributionSource) { diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h index 46a70ddad5..71341aae89 100644 --- a/media/utils/include/mediautils/ServiceUtilities.h +++ b/media/utils/include/mediautils/ServiceUtilities.h @@ -87,7 +87,7 @@ static inline bool isAudioServerOrMediaServerUid(uid_t uid) { bool recordingAllowed(const AttributionSourceState& attributionSource, audio_source_t source = AUDIO_SOURCE_DEFAULT); -bool startRecording(const AttributionSourceState& attributionSource, +int startRecording(const AttributionSourceState& attributionSource, const String16& msg, audio_source_t source); void finishRecording(const AttributionSourceState& attributionSource, audio_source_t source); std::optional<AttributionSourceState> resolveAttributionSource( @@ -114,6 +114,8 @@ void anonymizeBluetoothAddress(char *address); int32_t getOpForSource(audio_source_t source); +bool isRecordOpRequired(audio_source_t source); + AttributionSourceState getCallingAttributionSource(); status_t checkIMemory(const sp<IMemory>& iMemory); diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 3ba55f350a..5d4f6e78e4 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -62,6 +62,11 @@ using media::audio::common::AudioUsage; using media::audio::common::AudioUuid; using media::audio::common::Int; +namespace { +constexpr auto PERMISSION_HARD_DENIED = permission::PermissionChecker::PERMISSION_HARD_DENIED; +constexpr auto PERMISSION_GRANTED = permission::PermissionChecker::PERMISSION_GRANTED; +} + const std::vector<audio_usage_t>& SYSTEM_USAGES = { AUDIO_USAGE_CALL_ASSISTANT, AUDIO_USAGE_EMERGENCY, @@ -782,13 +787,12 @@ Status AudioPolicyService::startInput(int32_t portIdAidl) std::stringstream msg; msg << "Audio recording on session " << client->session; + const auto permitted = isAudioServerOrMediaServerUid(client->attributionSource.uid) && + startRecording(client->attributionSource, String16(msg.str().c_str()), + client->attributes.source); + // check calling permissions - if (!isAudioServerOrMediaServerUid(client->attributionSource.uid) - && !(startRecording(client->attributionSource, String16(msg.str().c_str()), - client->attributes.source) - || client->attributes.source == AUDIO_SOURCE_FM_TUNER - || client->attributes.source == AUDIO_SOURCE_REMOTE_SUBMIX - || client->attributes.source == AUDIO_SOURCE_ECHO_REFERENCE)) { + if (permitted == PERMISSION_HARD_DENIED) { ALOGE("%s permission denied: recording not allowed for attribution source %s", __func__, client->attributionSource.toString().c_str()); return binderStatusFromStatusT(PERMISSION_DENIED); @@ -807,13 +811,17 @@ Status AudioPolicyService::startInput(int32_t portIdAidl) return binderStatusFromStatusT(INVALID_OPERATION); } - // Force the possibly silenced client to be unsilenced since we just called - // startRecording (i.e. we have assumed it is unsilenced). - // At this point in time, the client is inactive, so no calls to appops are sent in - // setAppState_l. - // This ensures existing clients have the same behavior as new clients (starting unsilenced). + // Force the possibly silenced client to match the state on the appops side + // following the call to startRecording (i.e. unsilenced iff call succeeded) + // At this point in time, the client is inactive, so no calls to appops are + // sent in setAppState_l. This ensures existing clients have the same + // behavior as new clients. // TODO(b/282076713) - setAppState_l(client, APP_STATE_TOP); + if (permitted == PERMISSION_GRANTED) { + setAppState_l(client, APP_STATE_TOP); + } else { + setAppState_l(client, APP_STATE_IDLE); + } client->active = true; client->startTimeNs = systemTime(); @@ -899,7 +907,9 @@ Status AudioPolicyService::startInput(int32_t portIdAidl) client->active = false; client->startTimeNs = 0; updateUidStates_l(); - finishRecording(client->attributionSource, client->attributes.source); + if (!client->silenced) { + finishRecording(client->attributionSource, client->attributes.source); + } } return binderStatusFromStatusT(status); @@ -928,7 +938,9 @@ Status AudioPolicyService::stopInput(int32_t portIdAidl) updateUidStates_l(); // finish the recording app op - finishRecording(client->attributionSource, client->attributes.source); + if (!client->silenced) { + finishRecording(client->attributionSource, client->attributes.source); + } AutoCallerClear acc; return binderStatusFromStatusT(mAudioPolicyManager->stopInput(portId)); } diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index 09089cd445..7854efa437 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -63,6 +63,10 @@ static const nsecs_t kAudioCommandTimeoutNs = seconds(3); // 3 seconds static const String16 sManageAudioPolicyPermission("android.permission.MANAGE_AUDIO_POLICY"); +namespace { +constexpr auto PERMISSION_GRANTED = permission::PermissionChecker::PERMISSION_GRANTED; +} + // Creates an association between Binder code to name for IAudioPolicyService. #define IAUDIOPOLICYSERVICE_BINDER_METHOD_MACRO_LIST \ BINDER_METHOD_ENTRY(onNewAudioModulesAvailable) \ @@ -1158,9 +1162,9 @@ void AudioPolicyService::setAppState_l(sp<AudioRecordClient> client, app_state_t } else { std::stringstream msg; msg << "Audio recording un-silenced on session " << client->session; - if (!startRecording(client->attributionSource, String16(msg.str().c_str()), - client->attributes.source)) { - silenced = true; + if (startRecording(client->attributionSource, String16(msg.str().c_str()), + client->attributes.source) != PERMISSION_GRANTED) { + return; } } } diff --git a/services/audiopolicy/service/AudioRecordClient.cpp b/services/audiopolicy/service/AudioRecordClient.cpp index 44cbd871f2..26f92370e9 100644 --- a/services/audiopolicy/service/AudioRecordClient.cpp +++ b/services/audiopolicy/service/AudioRecordClient.cpp @@ -19,6 +19,7 @@ #include "AudioRecordClient.h" #include "AudioPolicyService.h" #include "binder/AppOpsManager.h" +#include "mediautils/ServiceUtilities.h" #include <algorithm> @@ -47,7 +48,7 @@ int getTargetSdkForPackageName(std::string_view packageName) { if (pm != nullptr) { const auto status = pm->getTargetSdkVersionForPackage( String16{packageName.data(), packageName.size()}, &targetSdk); - return status.isOk() ? targetSdk : -1; + return status.isOk() ? targetSdk : __ANDROID_API_FUTURE__; } } return targetSdk; @@ -120,16 +121,19 @@ OpRecordAudioMonitor::createIfNeeded( || attributionSource.packageName.value().size() == 0) { return nullptr; } - return new OpRecordAudioMonitor(attributionSource, getOpForSource(attr.source), commandThread); + return new OpRecordAudioMonitor(attributionSource, getOpForSource(attr.source), + isRecordOpRequired(attr.source), + commandThread); } -OpRecordAudioMonitor::OpRecordAudioMonitor( - const AttributionSourceState& attributionSource, int32_t appOp, - wp<AudioPolicyService::AudioCommandThread> commandThread) : - mHasOp(true), mAttributionSource(attributionSource), mAppOp(appOp), - mCommandThread(commandThread) -{ -} +OpRecordAudioMonitor::OpRecordAudioMonitor(const AttributionSourceState& attributionSource, + int32_t appOp, bool shouldMonitorRecord, + wp<AudioPolicyService::AudioCommandThread> commandThread) + : mHasOp(true), + mAttributionSource(attributionSource), + mAppOp(appOp), + mShouldMonitorRecord(shouldMonitorRecord), + mCommandThread(commandThread) {} OpRecordAudioMonitor::~OpRecordAudioMonitor() { @@ -160,7 +164,7 @@ void OpRecordAudioMonitor::onFirstRef() }); }; reg(mAppOp); - if (mAppOp != AppOpsManager::OP_RECORD_AUDIO) { + if (mAppOp != AppOpsManager::OP_RECORD_AUDIO && mShouldMonitorRecord) { reg(AppOpsManager::OP_RECORD_AUDIO); } } @@ -186,7 +190,7 @@ void OpRecordAudioMonitor::checkOp(bool updateUidStates) { }); }; bool hasIt = check(mAppOp); - if (mAppOp != AppOpsManager::OP_RECORD_AUDIO) { + if (mAppOp != AppOpsManager::OP_RECORD_AUDIO && mShouldMonitorRecord) { hasIt = hasIt && check(AppOpsManager::OP_RECORD_AUDIO); } // verbose logging only log when appOp changed diff --git a/services/audiopolicy/service/AudioRecordClient.h b/services/audiopolicy/service/AudioRecordClient.h index d3be31618c..1c733d9517 100644 --- a/services/audiopolicy/service/AudioRecordClient.h +++ b/services/audiopolicy/service/AudioRecordClient.h @@ -43,7 +43,8 @@ public: private: OpRecordAudioMonitor(const AttributionSourceState& attributionSource, int32_t appOp, - wp<AudioPolicyService::AudioCommandThread> commandThread); + bool shouldMonitorRecord, + wp<AudioPolicyService::AudioCommandThread> commandThread); void onFirstRef() override; @@ -68,6 +69,7 @@ private: std::atomic_bool mHasOp; const AttributionSourceState mAttributionSource; const int32_t mAppOp; + const bool mShouldMonitorRecord; wp<AudioPolicyService::AudioCommandThread> mCommandThread; }; |
