audio policy: more concurrent capture fixes
Fix two concurrent capture scenarii when VoIP call is active:
1) when the audio mode is IN_COMMUNICATION, only allow capture
from the app owning the audio mode.
2) fix capture by a privileged app when a privacy sensitive capture is
active: capture was silecend if the privacy sensitive capture was
started after the privileged capture.
Bug: 148368476
Test: Manual test of various VoIP apps and phone calls scenarii
Test: cts AudioRecordTest
Change-Id: I166f91c43ce86bb23c2f171700ece634091caaad
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 99cec5a..1b8c202 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -446,17 +446,19 @@
sp<AudioRecordClient> topActive;
sp<AudioRecordClient> latestActive;
+ sp<AudioRecordClient> topSensitiveActive;
sp<AudioRecordClient> latestSensitiveActive;
nsecs_t topStartNs = 0;
nsecs_t latestStartNs = 0;
+ nsecs_t topSensitiveStartNs = 0;
nsecs_t latestSensitiveStartNs = 0;
bool isA11yOnTop = mUidPolicy->isA11yOnTop();
bool isAssistantOnTop = false;
bool isSensitiveActive = false;
bool isInCall = mPhoneState == AUDIO_MODE_IN_CALL;
- bool rttCallActive =
- (mPhoneState == AUDIO_MODE_IN_CALL || mPhoneState == AUDIO_MODE_IN_COMMUNICATION)
+ bool isInCommunication = mPhoneState == AUDIO_MODE_IN_COMMUNICATION;
+ bool rttCallActive = (isInCall || isInCommunication)
&& mUidPolicy->isRttEnabled();
bool onlyHotwordActive = true;
@@ -479,33 +481,47 @@
continue;
}
- bool isAssistant = mUidPolicy->isAssistantUid(current->uid);
bool isAccessibility = mUidPolicy->isA11yUid(current->uid);
- if (appState == APP_STATE_TOP && !isAccessibility) {
- if (current->startTimeNs > topStartNs) {
- topActive = current;
- topStartNs = current->startTimeNs;
+ // Clients capturing for Accessibility services are not considered
+ // for top or latest active to avoid masking regular clients started before
+ if (!isAccessibility) {
+ bool isAssistant = mUidPolicy->isAssistantUid(current->uid);
+ bool isPrivacySensitive =
+ (current->attributes.flags & AUDIO_FLAG_CAPTURE_PRIVATE) != 0;
+ if (appState == APP_STATE_TOP) {
+ if (isPrivacySensitive) {
+ if (current->startTimeNs > topSensitiveStartNs) {
+ topSensitiveActive = current;
+ topSensitiveStartNs = current->startTimeNs;
+ }
+ } else {
+ if (current->startTimeNs > topStartNs) {
+ topActive = current;
+ topStartNs = current->startTimeNs;
+ }
+ }
+ if (isAssistant) {
+ isAssistantOnTop = true;
+ }
}
- if (isAssistant) {
- isAssistantOnTop = true;
+ // Clients capturing for HOTWORD are not considered
+ // for latest active to avoid masking regular clients started before
+ if (!(current->attributes.source == AUDIO_SOURCE_HOTWORD
+ || ((isA11yOnTop || rttCallActive) && isAssistant))) {
+ if (isPrivacySensitive) {
+ if (current->startTimeNs > latestSensitiveStartNs) {
+ latestSensitiveActive = current;
+ latestSensitiveStartNs = current->startTimeNs;
+ }
+ isSensitiveActive = true;
+ } else {
+ if (current->startTimeNs > latestStartNs) {
+ latestActive = current;
+ latestStartNs = current->startTimeNs;
+ }
+ }
}
}
- // Client capturing for HOTWORD or Accessibility services not considered
- // for latest active to avoid masking regular clients started before
- if (current->startTimeNs > latestStartNs
- && !(current->attributes.source == AUDIO_SOURCE_HOTWORD
- || ((isA11yOnTop || rttCallActive) && isAssistant))
- && !isAccessibility) {
- latestActive = current;
- latestStartNs = current->startTimeNs;
- }
- if ((current->attributes.flags & AUDIO_FLAG_CAPTURE_PRIVATE) != 0) {
- if (current->startTimeNs > latestSensitiveStartNs) {
- latestSensitiveActive = current;
- latestSensitiveStartNs = current->startTimeNs;
- }
- isSensitiveActive = true;
- }
if (current->attributes.source != AUDIO_SOURCE_HOTWORD) {
onlyHotwordActive = false;
}
@@ -514,6 +530,21 @@
// if no active client with UI on Top, consider latest active as top
if (topActive == nullptr) {
topActive = latestActive;
+ topStartNs = latestStartNs;
+ }
+ if (topSensitiveActive == nullptr) {
+ topSensitiveActive = latestSensitiveActive;
+ topSensitiveStartNs = latestSensitiveStartNs;
+ }
+
+ // If both privacy sensitive and regular capture are active:
+ // if the regular capture is privileged
+ // allow concurrency
+ // else
+ // favor the privacy sensitive case
+ if (topActive != nullptr && topSensitiveActive != nullptr
+ && !topActive->canCaptureCallOrOutput) {
+ topActive = nullptr;
}
for (size_t i =0; i < mAudioRecordClients.size(); i++) {
@@ -524,8 +555,17 @@
audio_source_t source = current->attributes.source;
bool isTopOrLatestActive = topActive == nullptr ? false : current->uid == topActive->uid;
- bool isLatestSensitive = latestSensitiveActive == nullptr ?
- false : current->uid == latestSensitiveActive->uid;
+ bool isTopOrLatestSensitive = topSensitiveActive == nullptr ?
+ false : current->uid == topSensitiveActive->uid;
+
+ auto canCaptureIfInCallOrCommunication = [&](const auto &recordClient) {
+ bool canCaptureCall = recordClient->canCaptureCallOrOutput;
+ bool canCaptureCommunication = recordClient->canCaptureCallOrOutput
+ || recordClient->uid == mPhoneStateOwnerUid
+ || isServiceUid(mPhoneStateOwnerUid);
+ return !(isInCall && !canCaptureCall)
+ && !(isInCommunication && !canCaptureCommunication);
+ };
// By default allow capture if:
// The assistant is not on TOP
@@ -533,9 +573,10 @@
// AND there is no active privacy sensitive capture or call
// OR client has CAPTURE_AUDIO_OUTPUT privileged permission
bool allowCapture = !isAssistantOnTop
- && ((isTopOrLatestActive && !isLatestSensitive) || isLatestSensitive)
- && !(isSensitiveActive && !(isLatestSensitive || current->canCaptureCallOrOutput))
- && !(isInCall && !current->canCaptureCallOrOutput);
+ && (isTopOrLatestActive || isTopOrLatestSensitive)
+ && !(isSensitiveActive
+ && !(isTopOrLatestSensitive || current->canCaptureCallOrOutput))
+ && canCaptureIfInCallOrCommunication(current);
if (isVirtualSource(source)) {
// Allow capture for virtual (remote submix, call audio TX or RX...) sources
@@ -554,8 +595,9 @@
}
} else {
if (((isAssistantOnTop && source == AUDIO_SOURCE_VOICE_RECOGNITION) ||
- source == AUDIO_SOURCE_HOTWORD) &&
- (!(isSensitiveActive || isInCall) || current->canCaptureCallOrOutput)) {
+ source == AUDIO_SOURCE_HOTWORD)
+ && !(isSensitiveActive && !current->canCaptureCallOrOutput)
+ && canCaptureIfInCallOrCommunication(current)) {
allowCapture = true;
}
}
@@ -567,7 +609,8 @@
// OR
// Is on TOP AND the source is VOICE_RECOGNITION or HOTWORD
if (!isAssistantOnTop
- && (!(isSensitiveActive || isInCall) || current->canCaptureCallOrOutput)) {
+ && !(isSensitiveActive && !current->canCaptureCallOrOutput)
+ && canCaptureIfInCallOrCommunication(current)) {
allowCapture = true;
}
if (isA11yOnTop) {
@@ -580,7 +623,8 @@
// All active clients are using HOTWORD source
// AND no call is active
// OR client has CAPTURE_AUDIO_OUTPUT privileged permission
- if (onlyHotwordActive && !(isInCall && !current->canCaptureCallOrOutput)) {
+ if (onlyHotwordActive
+ && canCaptureIfInCallOrCommunication(current)) {
allowCapture = true;
}
}