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/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index a672521..ea6ca39 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -376,15 +376,17 @@
return PERMISSION_DENIED;
}
+ bool canCaptureOutput = captureAudioOutputAllowed(pid, uid);
if ((attr->source == AUDIO_SOURCE_VOICE_UPLINK ||
attr->source == AUDIO_SOURCE_VOICE_DOWNLINK ||
attr->source == AUDIO_SOURCE_VOICE_CALL ||
attr->source == AUDIO_SOURCE_ECHO_REFERENCE) &&
- !captureAudioOutputAllowed(pid, uid)) {
+ !canCaptureOutput) {
return PERMISSION_DENIED;
}
- if ((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed(pid, uid)) {
+ bool canCaptureHotword = captureHotwordAllowed(pid, uid);
+ if ((attr->source == AUDIO_SOURCE_HOTWORD) && !canCaptureHotword) {
return BAD_VALUE;
}
@@ -415,7 +417,7 @@
case AudioPolicyInterface::API_INPUT_TELEPHONY_RX:
// FIXME: use the same permission as for remote submix for now.
case AudioPolicyInterface::API_INPUT_MIX_CAPTURE:
- if (!captureAudioOutputAllowed(pid, uid)) {
+ if (!canCaptureOutput) {
ALOGE("getInputForAttr() permission denied: capture not allowed");
status = PERMISSION_DENIED;
}
@@ -442,7 +444,8 @@
}
sp<AudioRecordClient> client = new AudioRecordClient(*attr, *input, uid, pid, session,
- *selectedDeviceId, opPackageName);
+ *selectedDeviceId, opPackageName,
+ canCaptureOutput, canCaptureHotword);
mAudioRecordClients.add(*portId, client);
}
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;
}
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index a2e75cd..160f70f 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -753,13 +753,17 @@
AudioRecordClient(const audio_attributes_t attributes,
const audio_io_handle_t io, uid_t uid, pid_t pid,
const audio_session_t session, const audio_port_handle_t deviceId,
- const String16& opPackageName) :
+ const String16& opPackageName,
+ bool canCaptureOutput, bool canCaptureHotword) :
AudioClient(attributes, io, uid, pid, session, deviceId),
- opPackageName(opPackageName), startTimeNs(0) {}
+ opPackageName(opPackageName), startTimeNs(0),
+ canCaptureOutput(canCaptureOutput), canCaptureHotword(canCaptureHotword) {}
~AudioRecordClient() override = default;
const String16 opPackageName; // client package name
nsecs_t startTimeNs;
+ const bool canCaptureOutput;
+ const bool canCaptureHotword;
};
// --- AudioPlaybackClient ---