diff options
Diffstat (limited to 'media/utils/ServiceUtilities.cpp')
| -rw-r--r-- | media/utils/ServiceUtilities.cpp | 143 |
1 files changed, 105 insertions, 38 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) { |
