Merge "AudioService: fix playback capture permission check"

This commit is contained in:
Eric Laurent
2021-01-15 10:18:54 +00:00
committed by Android (Google) Code Review
5 changed files with 36 additions and 31 deletions

View File

@@ -1946,7 +1946,7 @@ static jint convertAudioMixToNative(JNIEnv *env,
jobject jRule = env->GetObjectField(jAudioMix, gAudioMixFields.mRule);
jobject jRuleCriteria = env->GetObjectField(jRule, gAudioMixingRuleFields.mCriteria);
nAudioMix->mAllowPrivilegedPlaybackCapture =
nAudioMix->mAllowPrivilegedMediaPlaybackCapture =
env->GetBooleanField(jRule, gAudioMixingRuleFields.mAllowPrivilegedPlaybackCapture);
nAudioMix->mVoiceCommunicationCaptureAllowed =
env->GetBooleanField(jRule, gAudioMixingRuleFields.mVoiceCommunicationCaptureAllowed);

View File

@@ -218,7 +218,7 @@ public class AudioMix {
/** @return an error string if the format would not allow Privileged playbackCapture
* null otherwise
* @hide */
public static String canBeUsedForPrivilegedCapture(AudioFormat format) {
public static String canBeUsedForPrivilegedMediaCapture(AudioFormat format) {
int sampleRate = format.getSampleRate();
if (sampleRate > PRIVILEDGED_CAPTURE_MAX_SAMPLE_RATE || sampleRate <= 0) {
return "Privileged audio capture sample rate " + sampleRate
@@ -448,8 +448,8 @@ public class AudioMix {
}
}
}
if (mRule.allowPrivilegedPlaybackCapture()) {
String error = AudioMix.canBeUsedForPrivilegedCapture(mFormat);
if (mRule.allowPrivilegedMediaPlaybackCapture()) {
String error = AudioMix.canBeUsedForPrivilegedMediaCapture(mFormat);
if (error != null) {
throw new IllegalArgumentException(error);
}

View File

@@ -46,11 +46,11 @@ import java.util.Objects;
public class AudioMixingRule {
private AudioMixingRule(int mixType, ArrayList<AudioMixMatchCriterion> criteria,
boolean allowPrivilegedPlaybackCapture,
boolean allowPrivilegedMediaPlaybackCapture,
boolean voiceCommunicationCaptureAllowed) {
mCriteria = criteria;
mTargetMixType = mixType;
mAllowPrivilegedPlaybackCapture = allowPrivilegedPlaybackCapture;
mAllowPrivilegedPlaybackCapture = allowPrivilegedMediaPlaybackCapture;
mVoiceCommunicationCaptureAllowed = voiceCommunicationCaptureAllowed;
}
@@ -204,13 +204,17 @@ public class AudioMixingRule {
private final ArrayList<AudioMixMatchCriterion> mCriteria;
/** @hide */
public ArrayList<AudioMixMatchCriterion> getCriteria() { return mCriteria; }
/** Indicates that this rule is intended to capture media or game playback by a system component
* with permission CAPTURE_MEDIA_OUTPUT or CAPTURE_AUDIO_OUTPUT.
*/
//TODO b/177061175: rename to mAllowPrivilegedMediaPlaybackCapture
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mAllowPrivilegedPlaybackCapture = false;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mVoiceCommunicationCaptureAllowed = false;
/** @hide */
public boolean allowPrivilegedPlaybackCapture() {
public boolean allowPrivilegedMediaPlaybackCapture() {
return mAllowPrivilegedPlaybackCapture;
}
@@ -311,7 +315,7 @@ public class AudioMixingRule {
public static class Builder {
private ArrayList<AudioMixMatchCriterion> mCriteria;
private int mTargetMixType = AudioMix.MIX_TYPE_INVALID;
private boolean mAllowPrivilegedPlaybackCapture = false;
private boolean mAllowPrivilegedMediaPlaybackCapture = false;
// This value should be set internally according to a permission check
private boolean mVoiceCommunicationCaptureAllowed = false;
@@ -434,7 +438,7 @@ public class AudioMixingRule {
* @return the same Builder instance.
*/
public @NonNull Builder allowPrivilegedPlaybackCapture(boolean allow) {
mAllowPrivilegedPlaybackCapture = allow;
mAllowPrivilegedMediaPlaybackCapture = allow;
return this;
}
@@ -639,7 +643,7 @@ public class AudioMixingRule {
*/
public AudioMixingRule build() {
return new AudioMixingRule(mTargetMixType, mCriteria,
mAllowPrivilegedPlaybackCapture, mVoiceCommunicationCaptureAllowed);
mAllowPrivilegedMediaPlaybackCapture, mVoiceCommunicationCaptureAllowed);
}
}
}

View File

@@ -97,7 +97,7 @@ public class AudioPolicyConfig implements Parcelable {
dest.writeInt(mix.getFormat().getEncoding());
dest.writeInt(mix.getFormat().getChannelMask());
// write opt-out respect
dest.writeBoolean(mix.getRule().allowPrivilegedPlaybackCapture());
dest.writeBoolean(mix.getRule().allowPrivilegedMediaPlaybackCapture());
// write voice communication capture allowed flag
dest.writeBoolean(mix.getRule().voiceCommunicationCaptureAllowed());
// write mix rules
@@ -172,7 +172,7 @@ public class AudioPolicyConfig implements Parcelable {
textDump += " channels=0x";
textDump += Integer.toHexString(mix.getFormat().getChannelMask()).toUpperCase() + "\n";
textDump += " ignore playback capture opt out="
+ mix.getRule().allowPrivilegedPlaybackCapture() + "\n";
+ mix.getRule().allowPrivilegedMediaPlaybackCapture() + "\n";
textDump += " allow voice communication capture="
+ mix.getRule().voiceCommunicationCaptureAllowed() + "\n";
// write mix rules

View File

@@ -8454,28 +8454,29 @@ public class AudioService extends IAudioService.Stub
}
for (AudioMix mix : policyConfig.getMixes()) {
// If mix is requesting privileged capture
if (mix.getRule().allowPrivilegedPlaybackCapture()) {
// then it must have CAPTURE_MEDIA_OUTPUT or CAPTURE_AUDIO_OUTPUT permission
requireCaptureAudioOrMediaOutputPerm |= true;
// and its format must be low quality enough
String error = mix.canBeUsedForPrivilegedCapture(mix.getFormat());
if (error != null) {
Log.e(TAG, error);
if (mix.getRule().allowPrivilegedMediaPlaybackCapture()) {
// then its format must be low quality enough
String privilegedMediaCaptureError =
mix.canBeUsedForPrivilegedMediaCapture(mix.getFormat());
if (privilegedMediaCaptureError != null) {
Log.e(TAG, privilegedMediaCaptureError);
return false;
}
// and it must have CAPTURE_MEDIA_OUTPUT or CAPTURE_AUDIO_OUTPUT permission
requireCaptureAudioOrMediaOutputPerm |= true;
// If mix is trying to excplicitly capture USAGE_VOICE_COMMUNICATION
if (mix.containsMatchAttributeRuleForUsage(
AudioAttributes.USAGE_VOICE_COMMUNICATION)) {
// then it must have CAPTURE_USAGE_VOICE_COMMUNICATION_OUTPUT permission
// Note that for UID, USERID or EXCLDUE rules, the capture will be silenced
// in AudioPolicyMix
if (voiceCommunicationCaptureMixes == null) {
voiceCommunicationCaptureMixes = new ArrayList<AudioMix>();
}
voiceCommunicationCaptureMixes.add(mix);
}
// If mix is trying to explicitly capture USAGE_VOICE_COMMUNICATION
if (mix.containsMatchAttributeRuleForUsage(
AudioAttributes.USAGE_VOICE_COMMUNICATION)
&& (mix.getRouteFlags() == mix.ROUTE_FLAG_LOOP_BACK_RENDER)) {
// It must have CAPTURE_USAGE_VOICE_COMMUNICATION_OUTPUT permission
// Note that for UID, USERID or EXCLDUE rules, the capture will be silenced
// in AudioPolicyMix
if (voiceCommunicationCaptureMixes == null) {
voiceCommunicationCaptureMixes = new ArrayList<AudioMix>();
}
voiceCommunicationCaptureMixes.add(mix);
}
// If mix is RENDER|LOOPBACK, then an audio MediaProjection is enough
@@ -8498,7 +8499,7 @@ public class AudioService extends IAudioService.Stub
if (voiceCommunicationCaptureMixes != null && voiceCommunicationCaptureMixes.size() > 0) {
if (!callerHasPermission(
android.Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT)) {
Log.e(TAG, "Privileged audio capture for voice communication requires "
Log.e(TAG, "Audio capture for voice communication requires "
+ "CAPTURE_VOICE_COMMUNICATION_OUTPUT system permission");
return false;
}