audio policy: fix several problems with capture policy
- Fix accessibility service not able to get same policy as regular apps
when not using VOICE_RECOGNITION source b/126785297
- Make sure two captures from same app do not silence each other
(e.g two privacy sensitive captures concurrently)
- Ignore clients in idle state when considering priority active clients in
silencing logic.
- Prevent capture while in call if privileged permission CAPTURE_AUDIO_OUTPUT
is not granted
- Allow capture concurrently with privacy sensitive capture or call if
permission CAPTURE_AUDIO_OUTPUT is granted.
- Do not silence virtual sources when sensor privacy is enabled.
- Consider system persistent service as top app state.
Bug: 128419018
Bug: 126785297
Test: audio smoke tests
Change-Id: I7f8c7c511c674d4d46a815eaa7e9cf95e95cc3f9
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 8cbf3af..e858e8d 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -414,32 +414,35 @@
{
// Go over all active clients and allow capture (does not force silence) in the
// following cases:
-// The client is the assistant
+// Another client in the same UID has already been allowed to capture
+// OR The client is the assistant
// AND an accessibility service is on TOP
// AND the source is VOICE_RECOGNITION or HOTWORD
// OR uses VOICE_RECOGNITION AND is on TOP OR latest started
// OR uses HOTWORD
-// AND there is no privacy sensitive active capture
+// AND there is no active privacy sensitive capture or call
+// OR client has CAPTURE_AUDIO_OUTPUT privileged permission
// OR The client is an accessibility service
// AND is on TOP OR latest started
// AND the source is VOICE_RECOGNITION or HOTWORD
-// OR the source is one of: AUDIO_SOURCE_VOICE_DOWNLINK, AUDIO_SOURCE_VOICE_UPLINK,
-// AUDIO_SOURCE_VOICE_CALL
+// OR the client source is virtual (remote submix, call audio TX or RX...)
// OR Any other client
// AND The assistant is not on TOP
-// AND is on TOP OR latest started
-// AND there is no privacy sensitive active capture
+// AND there is no active privacy sensitive capture or call
+// OR client has CAPTURE_AUDIO_OUTPUT privileged permission
//TODO: mamanage pre processing effects according to use case priority
sp<AudioRecordClient> topActive;
sp<AudioRecordClient> latestActive;
sp<AudioRecordClient> latestSensitiveActive;
+
nsecs_t topStartNs = 0;
nsecs_t latestStartNs = 0;
nsecs_t latestSensitiveStartNs = 0;
bool isA11yOnTop = mUidPolicy->isA11yOnTop();
bool isAssistantOnTop = false;
bool isSensitiveActive = false;
+ bool isInCall = mPhoneState == AUDIO_MODE_IN_CALL;
// if Sensor Privacy is enabled then all recordings should be silenced.
if (mSensorPrivacyPolicy->isSensorPrivacyEnabled()) {
@@ -449,15 +452,18 @@
for (size_t i =0; i < mAudioRecordClients.size(); i++) {
sp<AudioRecordClient> current = mAudioRecordClients[i];
- if (!current->active) continue;
- if (isPrivacySensitiveSource(current->attributes.source)) {
- if (current->startTimeNs > latestSensitiveStartNs) {
- latestSensitiveActive = current;
- latestSensitiveStartNs = current->startTimeNs;
- }
- isSensitiveActive = true;
+ if (!current->active) {
+ continue;
}
- if (mUidPolicy->getUidState(current->uid) == ActivityManager::PROCESS_STATE_TOP) {
+
+ app_state_t appState = apmStatFromAmState(mUidPolicy->getUidState(current->uid));
+ // clients which app is in IDLE state are not eligible for top active or
+ // latest active
+ if (appState == APP_STATE_IDLE) {
+ continue;
+ }
+
+ if (appState == APP_STATE_TOP) {
if (current->startTimeNs > topStartNs) {
topActive = current;
topStartNs = current->startTimeNs;
@@ -470,72 +476,105 @@
latestActive = current;
latestStartNs = current->startTimeNs;
}
+ if (isPrivacySensitiveSource(current->attributes.source)) {
+ if (current->startTimeNs > latestSensitiveStartNs) {
+ latestSensitiveActive = current;
+ latestSensitiveStartNs = current->startTimeNs;
+ }
+ isSensitiveActive = true;
+ }
}
- if (topActive == nullptr && latestActive == nullptr) {
- return;
+ // if no active client with UI on Top, consider latest active as top
+ if (topActive == nullptr) {
+ topActive = latestActive;
}
- if (topActive != nullptr) {
- latestActive = nullptr;
- }
+ std::vector<uid_t> enabledUids;
for (size_t i =0; i < mAudioRecordClients.size(); i++) {
sp<AudioRecordClient> current = mAudioRecordClients[i];
- if (!current->active) continue;
+ if (!current->active) {
+ continue;
+ }
+
+ // keep capture allowed if another client with the same UID has already
+ // been allowed to capture
+ if (std::find(enabledUids.begin(), enabledUids.end(), current->uid)
+ != enabledUids.end()) {
+ continue;
+ }
audio_source_t source = current->attributes.source;
- bool isOnTop = current == topActive;
- bool isLatest = current == latestActive;
- bool isLatestSensitive = current == latestSensitiveActive;
- bool forceIdle = true;
+ bool isTopOrLatestActive = topActive == nullptr ? false : current->uid == topActive->uid;
+ bool isLatestSensitive = latestSensitiveActive == nullptr ?
+ false : current->uid == latestSensitiveActive->uid;
+
+ // By default allow capture if:
+ // The assistant is not on TOP
+ // AND there is no active privacy sensitive capture or call
+ // OR client has CAPTURE_AUDIO_OUTPUT privileged permission
+ bool allowCapture = !isAssistantOnTop
+ && !(isSensitiveActive && !(isLatestSensitive || current->canCaptureOutput))
+ && !(isInCall && !current->canCaptureOutput);
if (isVirtualSource(source)) {
- forceIdle = false;
+ // Allow capture for virtual (remote submix, call audio TX or RX...) sources
+ allowCapture = true;
} else if (mUidPolicy->isAssistantUid(current->uid)) {
+ // For assistant allow capture if:
+ // An accessibility service is on TOP
+ // AND the source is VOICE_RECOGNITION or HOTWORD
+ // OR is on TOP OR latest started AND uses VOICE_RECOGNITION
+ // OR uses HOTWORD
+ // AND there is no active privacy sensitive capture or call
+ // OR client has CAPTURE_AUDIO_OUTPUT privileged permission
if (isA11yOnTop) {
if (source == AUDIO_SOURCE_HOTWORD || source == AUDIO_SOURCE_VOICE_RECOGNITION) {
- forceIdle = false;
+ allowCapture = true;
}
} else {
- if ((((isOnTop || isLatest) && source == AUDIO_SOURCE_VOICE_RECOGNITION) ||
- source == AUDIO_SOURCE_HOTWORD) && !isSensitiveActive) {
- forceIdle = false;
+ if (((isTopOrLatestActive && source == AUDIO_SOURCE_VOICE_RECOGNITION) ||
+ source == AUDIO_SOURCE_HOTWORD) &&
+ (!(isSensitiveActive || isInCall) || current->canCaptureOutput)) {
+ allowCapture = true;
}
}
} else if (mUidPolicy->isA11yUid(current->uid)) {
- if ((isOnTop || isLatest) &&
- (source == AUDIO_SOURCE_VOICE_RECOGNITION || source == AUDIO_SOURCE_HOTWORD)) {
- forceIdle = false;
- }
- } else {
- if (!isAssistantOnTop && (isOnTop || isLatest) &&
- (!isSensitiveActive || isLatestSensitive)) {
- forceIdle = false;
+ // For accessibility service allow capture if:
+ // Is on TOP OR latest started
+ // AND the source is VOICE_RECOGNITION or HOTWORD
+ if (isTopOrLatestActive &&
+ (source == AUDIO_SOURCE_VOICE_RECOGNITION || source == AUDIO_SOURCE_HOTWORD)) {
+ allowCapture = true;
}
}
setAppState_l(current->uid,
- forceIdle ? APP_STATE_IDLE :
- apmStatFromAmState(mUidPolicy->getUidState(current->uid)));
+ allowCapture ? apmStatFromAmState(mUidPolicy->getUidState(current->uid)) :
+ APP_STATE_IDLE);
+ if (allowCapture) {
+ enabledUids.push_back(current->uid);
+ }
}
}
void AudioPolicyService::silenceAllRecordings_l() {
for (size_t i = 0; i < mAudioRecordClients.size(); i++) {
sp<AudioRecordClient> current = mAudioRecordClients[i];
- setAppState_l(current->uid, APP_STATE_IDLE);
+ if (!isVirtualSource(current->attributes.source)) {
+ setAppState_l(current->uid, APP_STATE_IDLE);
+ }
}
}
/* static */
app_state_t AudioPolicyService::apmStatFromAmState(int amState) {
- switch (amState) {
- case ActivityManager::PROCESS_STATE_UNKNOWN:
+
+ if (amState == ActivityManager::PROCESS_STATE_UNKNOWN) {
return APP_STATE_IDLE;
- case ActivityManager::PROCESS_STATE_TOP:
- return APP_STATE_TOP;
- default:
- break;
+ } else if (amState <= ActivityManager::PROCESS_STATE_TOP) {
+ // include persistent services
+ return APP_STATE_TOP;
}
return APP_STATE_FOREGROUND;
}