1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.audiopolicytest; 18 19 import static android.media.AudioFormat.CHANNEL_OUT_MONO; 20 import static android.media.AudioFormat.CHANNEL_OUT_STEREO; 21 import static android.media.AudioFormat.ENCODING_PCM_16BIT; 22 import static android.media.audiopolicy.AudioMixingRule.MIX_ROLE_INJECTOR; 23 import static android.media.audiopolicy.AudioMixingRule.MIX_ROLE_PLAYERS; 24 import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_AUDIO_SESSION_ID; 25 import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_UID; 26 27 import static org.junit.Assert.assertEquals; 28 import static org.junit.Assert.assertThrows; 29 30 import android.media.AudioFormat; 31 import android.media.AudioSystem; 32 import android.media.audiopolicy.AudioMix; 33 import android.media.audiopolicy.AudioMixingRule; 34 import android.media.audiopolicy.AudioPolicyConfig; 35 import android.os.Parcel; 36 import android.platform.test.annotations.Presubmit; 37 38 import androidx.test.ext.junit.runners.AndroidJUnit4; 39 40 import com.google.common.testing.EqualsTester; 41 42 import org.junit.Test; 43 import org.junit.runner.RunWith; 44 45 import java.util.ArrayList; 46 import java.util.List; 47 48 /** 49 * Unit tests for AudioMix. 50 * 51 * Run with "atest AudioMixUnitTests". 52 */ 53 @Presubmit 54 @RunWith(AndroidJUnit4.class) 55 public class AudioMixUnitTests { 56 private static final AudioFormat OUTPUT_FORMAT_STEREO_44KHZ_PCM = 57 new AudioFormat.Builder() 58 .setSampleRate(44000) 59 .setChannelMask(CHANNEL_OUT_STEREO) 60 .setEncoding(ENCODING_PCM_16BIT).build(); 61 private static final AudioFormat OUTPUT_FORMAT_MONO_16KHZ_PCM = 62 new AudioFormat.Builder() 63 .setSampleRate(16000) 64 .setChannelMask(CHANNEL_OUT_MONO) 65 .setEncoding(ENCODING_PCM_16BIT).build(); 66 private static final AudioFormat INPUT_FORMAT_MONO_16KHZ_PCM = 67 new AudioFormat.Builder() 68 .setSampleRate(16000) 69 .setChannelMask(AudioFormat.CHANNEL_IN_MONO) 70 .setEncoding(ENCODING_PCM_16BIT).build(); 71 72 @Test testEquals()73 public void testEquals() { 74 final EqualsTester equalsTester = new EqualsTester(); 75 76 // --- Equality group 1 77 final AudioMix playbackAudioMixWithSessionId42AndUid123 = 78 new AudioMix.Builder(new AudioMixingRule.Builder() 79 .setTargetMixRole(MIX_ROLE_PLAYERS) 80 .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42) 81 .addMixRule(RULE_MATCH_UID, 123).build()) 82 .setFormat(OUTPUT_FORMAT_STEREO_44KHZ_PCM) 83 .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build(); 84 final AudioMix playbackAudioMixWithUid123AndSessionId42 = 85 new AudioMix.Builder(new AudioMixingRule.Builder() 86 .setTargetMixRole(MIX_ROLE_PLAYERS) 87 .addMixRule(RULE_MATCH_UID, 123) 88 .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42).build()) 89 .setFormat(OUTPUT_FORMAT_STEREO_44KHZ_PCM) 90 .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build(); 91 equalsTester.addEqualityGroup( 92 playbackAudioMixWithSessionId42AndUid123, 93 playbackAudioMixWithUid123AndSessionId42, 94 writeToAndFromParcel(playbackAudioMixWithSessionId42AndUid123), 95 writeToAndFromParcel(playbackAudioMixWithUid123AndSessionId42)); 96 97 // --- Equality group 2 98 final AudioMix recordingAudioMixWithSessionId42AndUid123 = 99 new AudioMix.Builder(new AudioMixingRule.Builder() 100 .setTargetMixRole(MIX_ROLE_INJECTOR) 101 .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42) 102 .addMixRule(RULE_MATCH_UID, 123).build()) 103 .setFormat(INPUT_FORMAT_MONO_16KHZ_PCM) 104 .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build(); 105 final AudioMix recordingAudioMixWithUid123AndSessionId42 = 106 new AudioMix.Builder(new AudioMixingRule.Builder() 107 .setTargetMixRole(MIX_ROLE_INJECTOR) 108 .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42) 109 .addMixRule(RULE_MATCH_UID, 123).build()) 110 .setFormat(INPUT_FORMAT_MONO_16KHZ_PCM) 111 .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build(); 112 equalsTester.addEqualityGroup(recordingAudioMixWithSessionId42AndUid123, 113 recordingAudioMixWithUid123AndSessionId42, 114 writeToAndFromParcel(recordingAudioMixWithSessionId42AndUid123), 115 writeToAndFromParcel(recordingAudioMixWithUid123AndSessionId42)); 116 117 // --- Equality group 3 118 final AudioMix recordingAudioMixWithSessionId42AndUid123Render = 119 new AudioMix.Builder(new AudioMixingRule.Builder() 120 .setTargetMixRole(MIX_ROLE_INJECTOR) 121 .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42) 122 .addMixRule(RULE_MATCH_UID, 123).build()) 123 .setFormat(INPUT_FORMAT_MONO_16KHZ_PCM) 124 .setRouteFlags( 125 AudioMix.ROUTE_FLAG_LOOP_BACK | AudioMix.ROUTE_FLAG_RENDER).build(); 126 equalsTester.addEqualityGroup(recordingAudioMixWithSessionId42AndUid123Render, 127 writeToAndFromParcel(recordingAudioMixWithSessionId42AndUid123Render)); 128 129 // --- Equality group 4 130 final AudioMix playbackAudioMixWithUid123 = 131 new AudioMix.Builder(new AudioMixingRule.Builder() 132 .setTargetMixRole(MIX_ROLE_PLAYERS) 133 .addMixRule(RULE_MATCH_UID, 123).build()) 134 .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM) 135 .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build(); 136 equalsTester.addEqualityGroup(playbackAudioMixWithUid123, 137 writeToAndFromParcel(playbackAudioMixWithUid123)); 138 139 // --- Equality group 5 140 final AudioMix playbackAudioMixWithUid42 = 141 new AudioMix.Builder(new AudioMixingRule.Builder() 142 .setTargetMixRole(MIX_ROLE_PLAYERS) 143 .addMixRule(RULE_MATCH_UID, 42).build()) 144 .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM) 145 .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build(); 146 equalsTester.addEqualityGroup(playbackAudioMixWithUid42, 147 writeToAndFromParcel(playbackAudioMixWithUid42)); 148 149 equalsTester.testEquals(); 150 } 151 152 @Test buildRenderToRemoteSubmix_success()153 public void buildRenderToRemoteSubmix_success() { 154 final String deviceAddress = "address"; 155 final AudioMix audioMix = new AudioMix.Builder(new AudioMixingRule.Builder() 156 .setTargetMixRole(MIX_ROLE_PLAYERS) 157 .addMixRule(RULE_MATCH_UID, 42).build()) 158 .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM) 159 .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER) 160 .setDevice(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX, /*address=*/deviceAddress).build(); 161 162 assertEquals(deviceAddress, audioMix.getRegistration()); 163 assertEquals(OUTPUT_FORMAT_MONO_16KHZ_PCM, audioMix.getFormat()); 164 assertEquals(AudioMix.ROUTE_FLAG_RENDER, audioMix.getRouteFlags()); 165 } 166 167 @Test buildLoopbackAndRenderToRemoteSubmix_success()168 public void buildLoopbackAndRenderToRemoteSubmix_success() { 169 final String deviceAddress = "address"; 170 final AudioMix audioMix = new AudioMix.Builder(new AudioMixingRule.Builder() 171 .setTargetMixRole(MIX_ROLE_PLAYERS) 172 .addMixRule(RULE_MATCH_UID, 42).build()) 173 .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM) 174 .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK_RENDER) 175 .setDevice(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX, /*address=*/deviceAddress).build(); 176 177 assertEquals(deviceAddress, audioMix.getRegistration()); 178 assertEquals(OUTPUT_FORMAT_MONO_16KHZ_PCM, audioMix.getFormat()); 179 assertEquals(AudioMix.ROUTE_FLAG_LOOP_BACK_RENDER, audioMix.getRouteFlags()); 180 } 181 182 @Test buildRenderToSpeaker_success()183 public void buildRenderToSpeaker_success() { 184 final AudioMix audioMix = new AudioMix.Builder(new AudioMixingRule.Builder() 185 .setTargetMixRole(MIX_ROLE_PLAYERS) 186 .addMixRule(RULE_MATCH_UID, 42).build()) 187 .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM) 188 .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER) 189 .setDevice(AudioSystem.DEVICE_OUT_SPEAKER, /*address=*/"").build(); 190 191 assertEquals(OUTPUT_FORMAT_MONO_16KHZ_PCM, audioMix.getFormat()); 192 assertEquals(AudioMix.ROUTE_FLAG_RENDER, audioMix.getRouteFlags()); 193 } 194 195 @Test buildLoopbackForPlayerMix_success()196 public void buildLoopbackForPlayerMix_success() { 197 final AudioMix audioMix = new AudioMix.Builder(new AudioMixingRule.Builder() 198 .setTargetMixRole(MIX_ROLE_PLAYERS) 199 .addMixRule(RULE_MATCH_UID, 42).build()) 200 .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM) 201 .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build(); 202 203 assertEquals(OUTPUT_FORMAT_MONO_16KHZ_PCM, audioMix.getFormat()); 204 assertEquals(AudioMix.ROUTE_FLAG_LOOP_BACK, audioMix.getRouteFlags()); 205 } 206 207 @Test buildLoopbackWithDevice_throws()208 public void buildLoopbackWithDevice_throws() { 209 assertThrows(IllegalArgumentException.class, () -> new AudioMix.Builder( 210 new AudioMixingRule.Builder() 211 .setTargetMixRole(MIX_ROLE_PLAYERS) 212 .addMixRule(RULE_MATCH_UID, 42).build()) 213 .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM) 214 .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK) 215 .setDevice(AudioSystem.DEVICE_OUT_SPEAKER, /*address=*/"").build()); 216 } 217 218 @Test buildRenderWithoutDevice_throws()219 public void buildRenderWithoutDevice_throws() { 220 assertThrows(IllegalArgumentException.class, () -> new AudioMix.Builder( 221 new AudioMixingRule.Builder() 222 .setTargetMixRole(MIX_ROLE_PLAYERS) 223 .addMixRule(RULE_MATCH_UID, 42).build()) 224 .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM) 225 .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER).build()); 226 } 227 228 229 writeToAndFromParcel(AudioMix audioMix)230 private static AudioMix writeToAndFromParcel(AudioMix audioMix) { 231 AudioPolicyConfig apc = new AudioPolicyConfig(new ArrayList<>(List.of(audioMix))); 232 Parcel parcel = Parcel.obtain(); 233 apc.writeToParcel(parcel, /*flags=*/0); 234 parcel.setDataPosition(0); 235 AudioMix unmarshalledMix = 236 AudioPolicyConfig.CREATOR.createFromParcel(parcel).getMixes().get(0); 237 parcel.recycle(); 238 return unmarshalledMix; 239 } 240 } 241