diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp index c4f28082c819b91b81b4e463a633a179cac6f3fb..bb6e8515f10bda7e2e46594373ed5bff9bd9d077 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 resolveAttributionSource( const AttributionSourceState& callerAttributionSource) { AttributionSourceState nextAttributionSource = callerAttributionSource; @@ -113,7 +132,7 @@ std::optional resolveAttributionSource( return std::optional{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 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 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 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 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 0b3a3f96cd8a80d3c8fbdf6000ca86cab6a7c2d7..b1746607c74a940c12e91f1f0e5444e557f325d1 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 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); diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 19510456cc9f591c74237088fad533c09c3f0a71..d41a12acfff1cb698e1f8268eeea5e959cc13609 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& SYSTEM_USAGES = { AUDIO_USAGE_CALL_ASSISTANT, AUDIO_USAGE_EMERGENCY, @@ -807,13 +812,12 @@ Status AudioPolicyService::startInput(int32_t portIdAidl) std::stringstream msg; msg << "Audio recording on session " << client->session; + const auto permitted = 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)) { + && 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); @@ -832,13 +836,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(); @@ -924,7 +932,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); @@ -953,7 +963,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 e95147edba55a551bd3f9438d6ad61348b7f6d06..728cc424485e6547a17acd371e18ea81bfe2e6f9 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -64,6 +64,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) \ @@ -1196,9 +1200,9 @@ void AudioPolicyService::setAppState_l(sp 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 a89a84d7d487a8d883c0573e35614855405d4b6a..00b43a85baf170c97a4c7a98807e637c1a1a532f 100644 --- a/services/audiopolicy/service/AudioRecordClient.cpp +++ b/services/audiopolicy/service/AudioRecordClient.cpp @@ -18,14 +18,17 @@ #include "AudioRecordClient.h" #include "AudioPolicyService.h" +#include "binder/AppOpsManager.h" +#include "mediautils/ServiceUtilities.h" + +#include namespace android::media::audiopolicy { using android::AudioPolicyService; namespace { -bool isAppOpSource(audio_source_t source) -{ +bool isAppOpSource(audio_source_t source) { switch (source) { case AUDIO_SOURCE_FM_TUNER: case AUDIO_SOURCE_ECHO_REFERENCE: @@ -45,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; @@ -54,7 +57,46 @@ int getTargetSdkForPackageName(std::string_view packageName) { bool doesPackageTargetAtLeastU(std::string_view packageName) { return getTargetSdkForPackageName(packageName) >= __ANDROID_API_U__; } -} + +class AttrSourceItr { + public: + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = AttributionSourceState; + using pointer = const value_type*; + using reference = const value_type&; + + AttrSourceItr() : mAttr(nullptr) {} + + AttrSourceItr(const AttributionSourceState& attr) : mAttr(&attr) {} + + reference operator*() const { return *mAttr; } + pointer operator->() const { return mAttr; } + + AttrSourceItr& operator++() { + mAttr = !mAttr->next.empty() ? mAttr->next.data() : nullptr; + return *this; + } + + AttrSourceItr operator++(int) { + AttrSourceItr tmp = *this; + ++(*this); + return tmp; + } + + friend bool operator==(const AttrSourceItr& a, const AttrSourceItr& b) { + return a.mAttr == b.mAttr; + } + + friend bool operator!=(const AttrSourceItr& a, const AttrSourceItr& b) { + return !operator==(a, b); + } + + static AttrSourceItr end() { return AttrSourceItr{}; } +private: + const AttributionSourceState * mAttr; +}; +} // anonymous // static sp @@ -78,16 +120,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 commandThread) : - mHasOp(true), mAttributionSource(attributionSource), mAppOp(appOp), - mCommandThread(commandThread) -{ -} +OpRecordAudioMonitor::OpRecordAudioMonitor(const AttributionSourceState& attributionSource, + int32_t appOp, bool shouldMonitorRecord, + wp commandThread) + : mHasOp(true), + mAttributionSource(attributionSource), + mAppOp(appOp), + mShouldMonitorRecord(shouldMonitorRecord), + mCommandThread(commandThread) {} OpRecordAudioMonitor::~OpRecordAudioMonitor() { @@ -103,15 +148,24 @@ void OpRecordAudioMonitor::onFirstRef() mOpCallback = new RecordAudioOpCallback(this); ALOGV("start watching op %d for %s", mAppOp, mAttributionSource.toString().c_str()); - int flags = doesPackageTargetAtLeastU( - mAttributionSource.packageName.value_or("")) ? - AppOpsManager::WATCH_FOREGROUND_CHANGES : 0; - // TODO: We need to always watch AppOpsManager::OP_RECORD_AUDIO too - // since it controls the mic permission for legacy apps. - mAppOpsManager.startWatchingMode(mAppOp, VALUE_OR_FATAL(aidl2legacy_string_view_String16( - mAttributionSource.packageName.value_or(""))), - flags, - mOpCallback); + int flags = doesPackageTargetAtLeastU(mAttributionSource.packageName.value_or("")) + ? AppOpsManager::WATCH_FOREGROUND_CHANGES + : 0; + + const auto reg = [&](int32_t op) { + std::for_each(AttrSourceItr{mAttributionSource}, AttrSourceItr::end(), + [&](const auto& attr) { + mAppOpsManager.startWatchingMode( + op, + VALUE_OR_FATAL(aidl2legacy_string_view_String16( + attr.packageName.value_or(""))), + flags, mOpCallback); + }); + }; + reg(mAppOp); + if (mAppOp != AppOpsManager::OP_RECORD_AUDIO && mShouldMonitorRecord) { + reg(AppOpsManager::OP_RECORD_AUDIO); + } } bool OpRecordAudioMonitor::hasOp() const { @@ -124,14 +178,20 @@ bool OpRecordAudioMonitor::hasOp() const { // due to the UID in createIfNeeded(). As a result for those record track, it's: // - not called from constructor, // - not called from RecordAudioOpCallback because the callback is not installed in this case -void OpRecordAudioMonitor::checkOp(bool updateUidStates) -{ - // TODO: We need to always check AppOpsManager::OP_RECORD_AUDIO too - // since it controls the mic permission for legacy apps. - const int32_t mode = mAppOpsManager.checkOp(mAppOp, - mAttributionSource.uid, VALUE_OR_FATAL(aidl2legacy_string_view_String16( - mAttributionSource.packageName.value_or("")))); - const bool hasIt = (mode == AppOpsManager::MODE_ALLOWED); +void OpRecordAudioMonitor::checkOp(bool updateUidStates) { + const auto check = [&](int32_t op) -> bool { + return std::all_of( + AttrSourceItr{mAttributionSource}, AttrSourceItr::end(), [&](const auto& x) { + return mAppOpsManager.checkOp(op, x.uid, + VALUE_OR_FATAL(aidl2legacy_string_view_String16( + x.packageName.value_or("")))) == + AppOpsManager::MODE_ALLOWED; + }); + }; + bool hasIt = check(mAppOp); + if (mAppOp != AppOpsManager::OP_RECORD_AUDIO && mShouldMonitorRecord) { + hasIt = hasIt && check(AppOpsManager::OP_RECORD_AUDIO); + } // verbose logging only log when appOp changed ALOGI_IF(hasIt != mHasOp.load(), "App op %d missing, %ssilencing record %s", @@ -161,4 +221,4 @@ void OpRecordAudioMonitor::RecordAudioOpCallback::opChanged(int32_t op, } } -} // android::media::audiopolicy::internal +} // namespace android::media::audiopolicy diff --git a/services/audiopolicy/service/AudioRecordClient.h b/services/audiopolicy/service/AudioRecordClient.h index d3be31618c4809d119249deef4051d1e652793d4..1c733d951758b17c3a304c02f10ab3cda3f6fe60 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 commandThread); + bool shouldMonitorRecord, + wp commandThread); void onFirstRef() override; @@ -68,6 +69,7 @@ private: std::atomic_bool mHasOp; const AttributionSourceState mAttributionSource; const int32_t mAppOp; + const bool mShouldMonitorRecord; wp mCommandThread; };