xref: /aosp_15_r20/cts/tests/tests/media/audio/src/android/media/audio/cts/AudioTestUtil.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
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 android.media.audio.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 
21 import android.content.Context;
22 import android.content.pm.PackageManager;
23 import android.media.AudioAttributes;
24 import android.media.AudioDeviceInfo;
25 import android.media.AudioManager;
26 import android.media.AudioRecordingConfiguration;
27 import android.media.audiopolicy.AudioProductStrategy;
28 import android.os.PowerManager;
29 
30 import androidx.test.InstrumentationRegistry;
31 
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.concurrent.CountDownLatch;
35 import java.util.concurrent.TimeUnit;
36 import java.util.function.IntSupplier;
37 
38 class AudioTestUtil {
39     // Default matches the invalid (empty) attributes from native.
40     // The difference is the input source default which is not aligned between native and java
41     public static final AudioAttributes DEFAULT_ATTRIBUTES =
42             AudioProductStrategy.getDefaultAttributes();
43     public static final AudioAttributes INVALID_ATTRIBUTES = new AudioAttributes.Builder().build();
44 
45     // Basic Device Attributes
hasAudioOutput(Context context)46     public static boolean hasAudioOutput(Context context) {
47         return context.getPackageManager().hasSystemFeature(
48             PackageManager.FEATURE_AUDIO_OUTPUT);
49     }
50 
hasAudioInput(Context context)51     public static boolean hasAudioInput(Context context) {
52         return context.getPackageManager().hasSystemFeature(
53             PackageManager.FEATURE_MICROPHONE);
54     }
55 
resetVolumeIndex(int indexMin, int indexMax)56     public static int resetVolumeIndex(int indexMin, int indexMax) {
57         return (indexMax + indexMin) / 2;
58     }
59 
incrementVolumeIndex(int index, int indexMin, int indexMax)60     public static int incrementVolumeIndex(int index, int indexMin, int indexMax) {
61         return (index + 1 > indexMax) ? resetVolumeIndex(indexMin, indexMax) : ++index;
62     }
63 
64     //-----------------------------------------------------------------------------------
65 
66     /**
67      * A test helper class to help compare an expected int against the result of an IntSupplier
68      * lambda. It supports specifying a max wait time, broken down into Thread.sleep() of the
69      * given period. The expected value is compared against the result of the lambda every period.
70      * It will assert if the expected value is never returned after the maximum specified time.
71      * Example of how to use:
72      * <pre>
73      *     final SleepAssertIntEquals test = new SleepAssertIntEquals(
74      *             5000, // max sleep duration is 5s
75      *             100,  // test condition will be checked every 100ms
76      *             getContext()); // strictly for the wakelock hold
77      *    // do the operation under test
78      *    mAudioManager.setStreamVolume(STREAM_MUSIC,
79      *             mAudioManager.getMinStreamVolume(STREAM_MUSIC), 0);
80      *    // sleep and check until the volume has changed to what the test expects,
81      *    // it will throw an Exception if that doesn't happen within 5s
82      *    test.assertEqualsSleep( mAudioManager.getMinStreamVolume(STREAM_MUSIC), // expected value
83      *                            () -> mAudioManager.getStreamVolume(STREAM_MUSIC),
84      *                            "Observed volume not at min for MUSIC");
85      * </pre>
86      */
87     public static class SleepAssertIntEquals {
88         final long mMaxWaitMs;
89         final long mPeriodMs;
90         private PowerManager.WakeLock mWakeLock;
91 
92         /**
93          * Constructor for the test utility
94          * @param maxWaitMs the maximum time this test will ever wait
95          * @param periodMs the period to sleep for in between test attempts,
96          *                 must be less than maxWaitMs
97          * @param context not retained, just for obtaining a partial wakelock from PowerManager
98          */
SleepAssertIntEquals(int maxWaitMs, int periodMs, Context context)99         SleepAssertIntEquals(int maxWaitMs, int periodMs, Context context) {
100             if (periodMs >= maxWaitMs) {
101                 throw new IllegalArgumentException("Period must be lower than max wait time");
102             }
103             mMaxWaitMs = maxWaitMs;
104             mPeriodMs = periodMs;
105             PowerManager pm = context.getSystemService(PowerManager.class);
106             mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SleepAssertIntEquals");
107         }
108 
109         /**
110          * Compares the expected against the result of the lambda until they're equals, or unless
111          * the max wait time has elapsed, whichever happens first. On a timeout (int result wasn't
112          * as expected), the method asserts.
113          * @param expected the expected int value in the test
114          * @param result the function returning an int under test
115          * @param message the message to display when asserting
116          * @throws InterruptedException
117          */
assertEqualsSleep(int expected, IntSupplier result, String message)118         public void assertEqualsSleep(int expected, IntSupplier result, String message)
119                 throws InterruptedException {
120             final long endMs = System.currentTimeMillis() + mMaxWaitMs;
121             try {
122                 mWakeLock.acquire();
123                 int actual = Integer.MIN_VALUE;
124                 while (System.currentTimeMillis() < endMs) {
125                     actual = result.getAsInt();
126                     if (actual == expected) {
127                         // test successful, stop
128                         return;
129                     } else {
130                         // wait some more before expecting the test to be successful
131                         Thread.sleep(mPeriodMs);
132                     }
133                 }
134                 assertEquals(message, expected, actual);
135             } finally {
136                 mWakeLock.release();
137             }
138         }
139     }
140 
141     /**
142      * A helper class to use when wanting to block in a test on audio recording starting/stopping
143      */
144     static class AudioRecordingCallbackUtil extends AudioManager.AudioRecordingCallback {
145         boolean mCalled;
146         private final Object mConfigLock = new Object();
147         List<AudioRecordingConfiguration> mConfigs;
148         private final int mTestSource;
149         private final int mTestSession;
150         private CountDownLatch mCountDownLatch;
151 
reset()152         void reset() {
153             mCountDownLatch = new CountDownLatch(1);
154             mCalled = false;
155             synchronized (mConfigLock) {
156                 mConfigs = new ArrayList<AudioRecordingConfiguration>();
157             }
158         }
159 
AudioRecordingCallbackUtil(int session, int source)160         AudioRecordingCallbackUtil(int session, int source) {
161             mTestSource = source;
162             mTestSession = session;
163             reset();
164         }
165 
166         @Override
onRecordingConfigChanged(List<AudioRecordingConfiguration> configs)167         public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {
168             mCalled = true;
169             synchronized (mConfigLock) {
170                 mConfigs = configs;
171             }
172             mCountDownLatch.countDown();
173         }
174 
await(long timeoutMs)175         void await(long timeoutMs) {
176             try {
177                 mCountDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS);
178             } catch (InterruptedException e) {
179             }
180         }
181 
hasRecording(int session, int source)182         boolean hasRecording(int session, int source) {
183             synchronized (mConfigLock) {
184                 for (AudioRecordingConfiguration config : mConfigs) {
185                     if ((config.getClientAudioSessionId() == session)
186                             && (config.getAudioSource() == source)) {
187                         return true;
188                     }
189                 }
190             }
191             return false;
192         }
193     }
194 
195 
196     private static final List<Integer> MEDIA_DEVICE_TYPES = List.of(
197             AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
198             AudioDeviceInfo.TYPE_WIRED_HEADSET,
199             AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
200             AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
201             AudioDeviceInfo.TYPE_USB_HEADSET,
202             AudioDeviceInfo.TYPE_BLE_HEADSET);
203 
getMediaDevices()204     static List<AudioDeviceInfo> getMediaDevices() {
205         AudioManager am = InstrumentationRegistry.getInstrumentation()
206                 .getContext().getSystemService(AudioManager.class);
207 
208         List<AudioDeviceInfo> mediaDevices = new ArrayList();
209         AudioDeviceInfo[] allDevices = am.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
210 
211         for (AudioDeviceInfo device : allDevices) {
212             if (MEDIA_DEVICE_TYPES.contains(device.getType())) {
213                 mediaDevices.add(device);
214             }
215         }
216         return mediaDevices;
217     }
218 }
219