Merge changes Ib4ee69ec,I39af1ea6
* changes: Fix mix role deduction and validation in AudioMixingRule.Builder Fix AudioMixingRule validation
This commit is contained in:
committed by
Android (Google) Code Review
commit
bb5f51b1d7
@@ -30,8 +30,10 @@ import android.util.Log;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
@@ -50,10 +52,10 @@ import java.util.Objects;
|
||||
@SystemApi
|
||||
public class AudioMixingRule {
|
||||
|
||||
private AudioMixingRule(int mixType, ArrayList<AudioMixMatchCriterion> criteria,
|
||||
private AudioMixingRule(int mixType, Collection<AudioMixMatchCriterion> criteria,
|
||||
boolean allowPrivilegedMediaPlaybackCapture,
|
||||
boolean voiceCommunicationCaptureAllowed) {
|
||||
mCriteria = criteria;
|
||||
mCriteria = new ArrayList<>(criteria);
|
||||
mTargetMixType = mixType;
|
||||
mAllowPrivilegedPlaybackCapture = allowPrivilegedMediaPlaybackCapture;
|
||||
mVoiceCommunicationCaptureAllowed = voiceCommunicationCaptureAllowed;
|
||||
@@ -140,6 +142,20 @@ public class AudioMixingRule {
|
||||
return Objects.hash(mAttr, mIntProp, mRule);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (object == null || this.getClass() != object.getClass()) {
|
||||
return false;
|
||||
}
|
||||
if (object == this) {
|
||||
return true;
|
||||
}
|
||||
AudioMixMatchCriterion other = (AudioMixMatchCriterion) object;
|
||||
return mRule == other.mRule
|
||||
&& mIntProp == other.mIntProp
|
||||
&& Objects.equals(mAttr, other.mAttr);
|
||||
}
|
||||
|
||||
void writeToParcel(Parcel dest) {
|
||||
dest.writeInt(mRule);
|
||||
final int match_rule = mRule & ~RULE_EXCLUSION_MASK;
|
||||
@@ -192,15 +208,6 @@ public class AudioMixingRule {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean areCriteriaEquivalent(ArrayList<AudioMixMatchCriterion> cr1,
|
||||
ArrayList<AudioMixMatchCriterion> cr2) {
|
||||
if (cr1 == null || cr2 == null) return false;
|
||||
if (cr1 == cr2) return true;
|
||||
if (cr1.size() != cr2.size()) return false;
|
||||
//TODO iterate over rules to check they contain the same criterion
|
||||
return (cr1.hashCode() == cr2.hashCode());
|
||||
}
|
||||
|
||||
private final int mTargetMixType;
|
||||
int getTargetMixType() {
|
||||
return mTargetMixType;
|
||||
@@ -286,9 +293,9 @@ public class AudioMixingRule {
|
||||
|
||||
final AudioMixingRule that = (AudioMixingRule) o;
|
||||
return (this.mTargetMixType == that.mTargetMixType)
|
||||
&& (areCriteriaEquivalent(this.mCriteria, that.mCriteria)
|
||||
&& this.mAllowPrivilegedPlaybackCapture == that.mAllowPrivilegedPlaybackCapture
|
||||
&& this.mVoiceCommunicationCaptureAllowed
|
||||
&& Objects.equals(mCriteria, that.mCriteria)
|
||||
&& (this.mAllowPrivilegedPlaybackCapture == that.mAllowPrivilegedPlaybackCapture)
|
||||
&& (this.mVoiceCommunicationCaptureAllowed
|
||||
== that.mVoiceCommunicationCaptureAllowed);
|
||||
}
|
||||
|
||||
@@ -372,7 +379,7 @@ public class AudioMixingRule {
|
||||
* Builder class for {@link AudioMixingRule} objects
|
||||
*/
|
||||
public static class Builder {
|
||||
private ArrayList<AudioMixMatchCriterion> mCriteria;
|
||||
private final Set<AudioMixMatchCriterion> mCriteria;
|
||||
private int mTargetMixType = AudioMix.MIX_TYPE_INVALID;
|
||||
private boolean mAllowPrivilegedMediaPlaybackCapture = false;
|
||||
// This value should be set internally according to a permission check
|
||||
@@ -382,7 +389,7 @@ public class AudioMixingRule {
|
||||
* Constructs a new Builder with no rules.
|
||||
*/
|
||||
public Builder() {
|
||||
mCriteria = new ArrayList<AudioMixMatchCriterion>();
|
||||
mCriteria = new HashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -547,7 +554,12 @@ public class AudioMixingRule {
|
||||
throw new IllegalArgumentException("Illegal argument for mix role");
|
||||
}
|
||||
|
||||
Log.i("AudioMixingRule", "Builder setTargetMixRole " + mixRole);
|
||||
if (mCriteria.stream().map(AudioMixMatchCriterion::getRule)
|
||||
.anyMatch(mixRole == MIX_ROLE_PLAYERS
|
||||
? AudioMixingRule::isRecorderRule : AudioMixingRule::isPlayerRule)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Target mix role is not compatible with mix rules.");
|
||||
}
|
||||
mTargetMixType = mixRole == MIX_ROLE_INJECTOR
|
||||
? AudioMix.MIX_TYPE_RECORDERS : AudioMix.MIX_TYPE_PLAYERS;
|
||||
return this;
|
||||
@@ -604,17 +616,15 @@ public class AudioMixingRule {
|
||||
*/
|
||||
private Builder addRuleInternal(AudioAttributes attrToMatch, Integer intProp, int rule)
|
||||
throws IllegalArgumentException {
|
||||
// as rules are added to the Builder, we verify they are consistent with the type
|
||||
// of mix being built. When adding the first rule, the mix type is MIX_TYPE_INVALID.
|
||||
// If mix type is invalid and added rule is valid only for the players / recorders,
|
||||
// adjust the mix type accordingly.
|
||||
// Otherwise, if the mix type was already deduced or set explicitly, verify the rule
|
||||
// is valid for the mix type.
|
||||
if (mTargetMixType == AudioMix.MIX_TYPE_INVALID) {
|
||||
if (isPlayerRule(rule)) {
|
||||
mTargetMixType = AudioMix.MIX_TYPE_PLAYERS;
|
||||
} else if (isRecorderRule(rule)) {
|
||||
mTargetMixType = AudioMix.MIX_TYPE_RECORDERS;
|
||||
} else {
|
||||
// For rules which are not player or recorder specific (e.g. RULE_MATCH_UID),
|
||||
// the default mix type is MIX_TYPE_PLAYERS.
|
||||
mTargetMixType = AudioMix.MIX_TYPE_PLAYERS;
|
||||
}
|
||||
} else if ((isPlayerRule(rule) && (mTargetMixType != AudioMix.MIX_TYPE_PLAYERS))
|
||||
|| (isRecorderRule(rule)) && (mTargetMixType != AudioMix.MIX_TYPE_RECORDERS))
|
||||
@@ -622,75 +632,13 @@ public class AudioMixingRule {
|
||||
throw new IllegalArgumentException("Incompatible rule for mix");
|
||||
}
|
||||
synchronized (mCriteria) {
|
||||
Iterator<AudioMixMatchCriterion> crIterator = mCriteria.iterator();
|
||||
final int match_rule = rule & ~RULE_EXCLUSION_MASK;
|
||||
while (crIterator.hasNext()) {
|
||||
final AudioMixMatchCriterion criterion = crIterator.next();
|
||||
|
||||
if ((criterion.mRule & ~RULE_EXCLUSION_MASK) != match_rule) {
|
||||
continue; // The two rules are not of the same type
|
||||
}
|
||||
switch (match_rule) {
|
||||
case RULE_MATCH_ATTRIBUTE_USAGE:
|
||||
// "usage"-based rule
|
||||
if (criterion.mAttr.getSystemUsage() == attrToMatch.getSystemUsage()) {
|
||||
if (criterion.mRule == rule) {
|
||||
// rule already exists, we're done
|
||||
return this;
|
||||
} else {
|
||||
// criterion already exists with a another rule,
|
||||
// it is incompatible
|
||||
throw new IllegalArgumentException("Contradictory rule exists"
|
||||
+ " for " + attrToMatch);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
|
||||
// "capture preset"-base rule
|
||||
if (criterion.mAttr.getCapturePreset() == attrToMatch.getCapturePreset()) {
|
||||
if (criterion.mRule == rule) {
|
||||
// rule already exists, we're done
|
||||
return this;
|
||||
} else {
|
||||
// criterion already exists with a another rule,
|
||||
// it is incompatible
|
||||
throw new IllegalArgumentException("Contradictory rule exists"
|
||||
+ " for " + attrToMatch);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RULE_MATCH_UID:
|
||||
// "usage"-based rule
|
||||
if (criterion.mIntProp == intProp.intValue()) {
|
||||
if (criterion.mRule == rule) {
|
||||
// rule already exists, we're done
|
||||
return this;
|
||||
} else {
|
||||
// criterion already exists with a another rule,
|
||||
// it is incompatible
|
||||
throw new IllegalArgumentException("Contradictory rule exists"
|
||||
+ " for UID " + intProp);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RULE_MATCH_USERID:
|
||||
// "userid"-based rule
|
||||
if (criterion.mIntProp == intProp.intValue()) {
|
||||
if (criterion.mRule == rule) {
|
||||
// rule already exists, we're done
|
||||
return this;
|
||||
} else {
|
||||
// criterion already exists with a another rule,
|
||||
// it is incompatible
|
||||
throw new IllegalArgumentException("Contradictory rule exists"
|
||||
+ " for userId " + intProp);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
int oppositeRule = rule ^ RULE_EXCLUSION_MASK;
|
||||
if (mCriteria.stream().anyMatch(criterion -> criterion.mRule == oppositeRule)) {
|
||||
throw new IllegalArgumentException("AudioMixingRule cannot contain RULE_MATCH_*"
|
||||
+ " and RULE_EXCLUDE_* for the same dimension.");
|
||||
}
|
||||
// rule didn't exist, add it
|
||||
switch (match_rule) {
|
||||
int ruleWithoutExclusion = rule & ~RULE_EXCLUSION_MASK;
|
||||
switch (ruleWithoutExclusion) {
|
||||
case RULE_MATCH_ATTRIBUTE_USAGE:
|
||||
case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
|
||||
mCriteria.add(new AudioMixMatchCriterion(attrToMatch, rule));
|
||||
@@ -734,8 +682,11 @@ public class AudioMixingRule {
|
||||
* @return a new {@link AudioMixingRule} object
|
||||
*/
|
||||
public AudioMixingRule build() {
|
||||
return new AudioMixingRule(mTargetMixType, mCriteria,
|
||||
mAllowPrivilegedMediaPlaybackCapture, mVoiceCommunicationCaptureAllowed);
|
||||
return new AudioMixingRule(
|
||||
mTargetMixType == AudioMix.MIX_TYPE_INVALID
|
||||
? AudioMix.MIX_TYPE_PLAYERS : mTargetMixType,
|
||||
mCriteria, mAllowPrivilegedMediaPlaybackCapture,
|
||||
mVoiceCommunicationCaptureAllowed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ android_test {
|
||||
"androidx.test.ext.junit",
|
||||
"androidx.test.rules",
|
||||
"guava",
|
||||
"hamcrest-library",
|
||||
"platform-test-annotations",
|
||||
],
|
||||
platform_apis: true,
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.audiopolicytest;
|
||||
|
||||
import static android.media.AudioAttributes.USAGE_MEDIA;
|
||||
import static android.media.MediaRecorder.AudioSource.VOICE_RECOGNITION;
|
||||
import static android.media.audiopolicy.AudioMixingRule.MIX_ROLE_INJECTOR;
|
||||
import static android.media.audiopolicy.AudioMixingRule.MIX_ROLE_PLAYERS;
|
||||
import static android.media.audiopolicy.AudioMixingRule.RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET;
|
||||
import static android.media.audiopolicy.AudioMixingRule.RULE_EXCLUDE_ATTRIBUTE_USAGE;
|
||||
import static android.media.audiopolicy.AudioMixingRule.RULE_EXCLUDE_UID;
|
||||
import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET;
|
||||
import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE;
|
||||
import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_UID;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
|
||||
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.audiopolicy.AudioMixingRule;
|
||||
import android.media.audiopolicy.AudioMixingRule.AudioMixMatchCriterion;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.hamcrest.CustomTypeSafeMatcher;
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Unit tests for AudioPolicy.
|
||||
*
|
||||
* Run with "atest AudioMixingRuleUnitTests".
|
||||
*/
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AudioMixingRuleUnitTests {
|
||||
private static final AudioAttributes USAGE_MEDIA_AUDIO_ATTRIBUTES =
|
||||
new AudioAttributes.Builder().setUsage(USAGE_MEDIA).build();
|
||||
private static final AudioAttributes CAPTURE_PRESET_VOICE_RECOGNITION_AUDIO_ATTRIBUTES =
|
||||
new AudioAttributes.Builder().setCapturePreset(VOICE_RECOGNITION).build();
|
||||
private static final int TEST_UID = 42;
|
||||
private static final int OTHER_UID = 77;
|
||||
|
||||
@Test
|
||||
public void testConstructValidRule() {
|
||||
AudioMixingRule rule = new AudioMixingRule.Builder()
|
||||
.addMixRule(RULE_MATCH_ATTRIBUTE_USAGE, USAGE_MEDIA_AUDIO_ATTRIBUTES)
|
||||
.addMixRule(RULE_MATCH_UID, TEST_UID)
|
||||
.build();
|
||||
|
||||
// Based on the rules, the mix type should fall back to MIX_ROLE_PLAYERS,
|
||||
// since the rules are valid for both MIX_ROLE_PLAYERS & MIX_ROLE_INJECTOR.
|
||||
assertEquals(rule.getTargetMixRole(), MIX_ROLE_PLAYERS);
|
||||
assertThat(rule.getCriteria(), containsInAnyOrder(
|
||||
isAudioMixMatchUsageCriterion(USAGE_MEDIA),
|
||||
isAudioMixMatchUidCriterion(TEST_UID)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructRuleWithConflictingCriteriaFails() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new AudioMixingRule.Builder()
|
||||
.addMixRule(RULE_MATCH_ATTRIBUTE_USAGE, USAGE_MEDIA_AUDIO_ATTRIBUTES)
|
||||
.addMixRule(RULE_MATCH_UID, TEST_UID)
|
||||
// Conflicts with previous criterion.
|
||||
.addMixRule(RULE_EXCLUDE_UID, OTHER_UID)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRuleBuilderDedupsCriteria() {
|
||||
AudioMixingRule rule = new AudioMixingRule.Builder()
|
||||
.addMixRule(RULE_MATCH_ATTRIBUTE_USAGE, USAGE_MEDIA_AUDIO_ATTRIBUTES)
|
||||
.addMixRule(RULE_MATCH_UID, TEST_UID)
|
||||
// Identical to previous criterion.
|
||||
.addMixRule(RULE_MATCH_UID, TEST_UID)
|
||||
// Identical to first criterion.
|
||||
.addMixRule(RULE_MATCH_ATTRIBUTE_USAGE, USAGE_MEDIA_AUDIO_ATTRIBUTES)
|
||||
.build();
|
||||
|
||||
assertThat(rule.getCriteria(), hasSize(2));
|
||||
assertThat(rule.getCriteria(), containsInAnyOrder(
|
||||
isAudioMixMatchUsageCriterion(USAGE_MEDIA),
|
||||
isAudioMixMatchUidCriterion(TEST_UID)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failsWhenAddAttributeRuleCalledWithInvalidType() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new AudioMixingRule.Builder()
|
||||
// Rule match attribute usage requires AudioAttributes, not
|
||||
// just the int enum value of the usage.
|
||||
.addMixRule(RULE_MATCH_ATTRIBUTE_USAGE, USAGE_MEDIA)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failsWhenExcludeAttributeRuleCalledWithInvalidType() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new AudioMixingRule.Builder()
|
||||
// Rule match attribute usage requires AudioAttributes, not
|
||||
// just the int enum value of the usage.
|
||||
.excludeMixRule(RULE_MATCH_ATTRIBUTE_USAGE, USAGE_MEDIA)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failsWhenAddIntRuleCalledWithInvalidType() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new AudioMixingRule.Builder()
|
||||
// Rule match uid requires Integer not AudioAttributes.
|
||||
.addMixRule(RULE_MATCH_UID, USAGE_MEDIA_AUDIO_ATTRIBUTES)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failsWhenExcludeIntRuleCalledWithInvalidType() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new AudioMixingRule.Builder()
|
||||
// Rule match uid requires Integer not AudioAttributes.
|
||||
.excludeMixRule(RULE_MATCH_UID, USAGE_MEDIA_AUDIO_ATTRIBUTES)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void injectorMixTypeDeductionWithGenericRuleSucceeds() {
|
||||
AudioMixingRule rule = new AudioMixingRule.Builder()
|
||||
// UID rule can be used both with MIX_ROLE_PLAYERS and MIX_ROLE_INJECTOR.
|
||||
.addMixRule(RULE_MATCH_UID, TEST_UID)
|
||||
// Capture preset rule is only valid for injector, MIX_ROLE_INJECTOR should
|
||||
// be deduced.
|
||||
.addMixRule(RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET,
|
||||
CAPTURE_PRESET_VOICE_RECOGNITION_AUDIO_ATTRIBUTES)
|
||||
.build();
|
||||
|
||||
assertEquals(rule.getTargetMixRole(), MIX_ROLE_INJECTOR);
|
||||
assertThat(rule.getCriteria(), containsInAnyOrder(
|
||||
isAudioMixMatchUidCriterion(TEST_UID),
|
||||
isAudioMixMatchCapturePresetCriterion(VOICE_RECOGNITION)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void settingTheMixTypeToIncompatibleInjectorMixFails() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new AudioMixingRule.Builder()
|
||||
.addMixRule(RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET,
|
||||
CAPTURE_PRESET_VOICE_RECOGNITION_AUDIO_ATTRIBUTES)
|
||||
// Capture preset cannot be defined for MIX_ROLE_PLAYERS.
|
||||
.setTargetMixRole(MIX_ROLE_PLAYERS)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addingPlayersOnlyRuleWithInjectorsOnlyRuleFails() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new AudioMixingRule.Builder()
|
||||
// MIX_ROLE_PLAYERS only rule.
|
||||
.addMixRule(RULE_MATCH_ATTRIBUTE_USAGE, USAGE_MEDIA_AUDIO_ATTRIBUTES)
|
||||
// MIX ROLE_INJECTOR only rule.
|
||||
.addMixRule(RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET,
|
||||
CAPTURE_PRESET_VOICE_RECOGNITION_AUDIO_ATTRIBUTES)
|
||||
.build());
|
||||
}
|
||||
|
||||
|
||||
private static Matcher isAudioMixUidCriterion(int uid, boolean exclude) {
|
||||
return new CustomTypeSafeMatcher<AudioMixMatchCriterion>("uid mix criterion") {
|
||||
@Override
|
||||
public boolean matchesSafely(AudioMixMatchCriterion item) {
|
||||
int expectedRule = exclude ? RULE_EXCLUDE_UID : RULE_MATCH_UID;
|
||||
return item.getRule() == expectedRule && item.getIntProp() == uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeMismatchSafely(
|
||||
AudioMixMatchCriterion item, Description mismatchDescription) {
|
||||
mismatchDescription.appendText(
|
||||
String.format("is not %s criterion with uid %d",
|
||||
exclude ? "exclude" : "match", uid));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static Matcher isAudioMixMatchUidCriterion(int uid) {
|
||||
return isAudioMixUidCriterion(uid, /*exclude=*/ false);
|
||||
}
|
||||
|
||||
private static Matcher isAudioMixCapturePresetCriterion(int audioSource, boolean exclude) {
|
||||
return new CustomTypeSafeMatcher<AudioMixMatchCriterion>("uid mix criterion") {
|
||||
@Override
|
||||
public boolean matchesSafely(AudioMixMatchCriterion item) {
|
||||
int expectedRule = exclude
|
||||
? RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET
|
||||
: RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET;
|
||||
AudioAttributes attributes = item.getAudioAttributes();
|
||||
return item.getRule() == expectedRule
|
||||
&& attributes != null && attributes.getCapturePreset() == audioSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeMismatchSafely(
|
||||
AudioMixMatchCriterion item, Description mismatchDescription) {
|
||||
mismatchDescription.appendText(
|
||||
String.format("is not %s criterion with capture preset %d",
|
||||
exclude ? "exclude" : "match", audioSource));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static Matcher isAudioMixMatchCapturePresetCriterion(int audioSource) {
|
||||
return isAudioMixCapturePresetCriterion(audioSource, /*exclude=*/ false);
|
||||
}
|
||||
|
||||
private static Matcher isAudioMixUsageCriterion(int usage, boolean exclude) {
|
||||
return new CustomTypeSafeMatcher<AudioMixMatchCriterion>("usage mix criterion") {
|
||||
@Override
|
||||
public boolean matchesSafely(AudioMixMatchCriterion item) {
|
||||
int expectedRule =
|
||||
exclude ? RULE_EXCLUDE_ATTRIBUTE_USAGE : RULE_MATCH_ATTRIBUTE_USAGE;
|
||||
AudioAttributes attributes = item.getAudioAttributes();
|
||||
return item.getRule() == expectedRule
|
||||
&& attributes != null && attributes.getUsage() == usage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeMismatchSafely(
|
||||
AudioMixMatchCriterion item, Description mismatchDescription) {
|
||||
mismatchDescription.appendText(
|
||||
String.format("is not %s criterion with usage %d",
|
||||
exclude ? "exclude" : "match", usage));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static Matcher isAudioMixMatchUsageCriterion(int usage) {
|
||||
return isAudioMixUsageCriterion(usage, /*exclude=*/ false);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user