• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.cts.verifier.audio;
18 
19 import android.media.AudioAttributes;
20 import android.media.AudioDeviceCallback;
21 import android.media.AudioDeviceInfo;
22 import android.media.AudioManager;
23 import android.media.AudioMixerAttributes;
24 import android.os.Bundle;
25 import android.os.Handler;
26 import android.os.HandlerThread;
27 import android.widget.TextView;
28 
29 import com.android.cts.verifier.PassFailButtons;
30 import com.android.cts.verifier.R;
31 
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.concurrent.CountDownLatch;
35 import java.util.concurrent.Executors;
36 import java.util.concurrent.TimeUnit;
37 import java.util.concurrent.atomic.AtomicBoolean;
38 
39 /**
40  * PreferredMixerAttributesTestActivity is used to test if the Android device supports setting
41  * preferred mixer attributes for USB devices.
42  */
43 public class PreferredMixerAttributesTestActivity extends PassFailButtons.Activity {
44     private static final String TAG = "PreferredMixerAttributesTestActivity";
45 
46     private static final long TIMEOUT_MS = 1000;
47 
48     private AudioManager mAudioManager;
49     private List<AudioDeviceInfo> mUsbDevices = new ArrayList<>();
50 
51     private HandlerThread mPreferredMixerAttrTestThread;
52     private Handler mPreferredMixerAttrTestHandler;
53 
54     private TextView mUsbStatusTextView;
55     private TextView mTestResultTextView;
56     private TextView mFailureMsgTextView;
57 
58     private TestAudioDeviceCallback mAudioDeviceCallback;
59 
60     @Override
onCreate(Bundle savedInstanceState)61     protected void onCreate(Bundle savedInstanceState) {
62         super.onCreate(savedInstanceState);
63         setContentView(R.layout.audio_preferred_mixer_attributes);
64 
65         mAudioManager = getSystemService(AudioManager.class);
66 
67         mUsbStatusTextView = (TextView) findViewById(R.id.usbDeviceConnectionStatus);
68         mTestResultTextView = (TextView) findViewById(R.id.preferredMixerAttributesTestStatus);
69         mFailureMsgTextView = (TextView) findViewById(R.id.preferredMixerAttributesTestFailure);
70 
71         setInfoResources(R.string.audio_preferred_mixer_attributes_test,
72                 R.string.audio_preferred_mixer_attributes_test_info, -1);
73         setPassFailButtonClickListeners();
74 
75         mAudioDeviceCallback = new TestAudioDeviceCallback();
76     }
77 
78     @Override
onResume()79     public void onResume() {
80         super.onResume();
81 
82         startBackgroundThread();
83         mAudioManager.registerAudioDeviceCallback(
84                 mAudioDeviceCallback, mPreferredMixerAttrTestHandler);
85         detectUsbDeviceConnectionAndRunTest();
86     }
87 
88     @Override
onPause()89     public void onPause() {
90         mAudioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
91         stopBackgroundThread();
92         super.onPause();
93     }
94 
startBackgroundThread()95     private void startBackgroundThread() {
96         mPreferredMixerAttrTestThread = new HandlerThread("PreferredMixerAttrBackground");
97         mPreferredMixerAttrTestThread.start();
98         mPreferredMixerAttrTestHandler = new Handler(mPreferredMixerAttrTestThread.getLooper());
99     }
100 
stopBackgroundThread()101     private void stopBackgroundThread() {
102         mPreferredMixerAttrTestThread.quitSafely();
103         try {
104             mPreferredMixerAttrTestThread.join();
105             mPreferredMixerAttrTestThread = null;
106             mPreferredMixerAttrTestHandler = null;
107         } catch (InterruptedException e) {
108             e.printStackTrace();
109         }
110     }
111 
detectUsbDeviceConnectionAndRunTest()112     private void detectUsbDeviceConnectionAndRunTest() {
113         if (!detectUSBDevice()) {
114             updateUI(R.string.audio_preferred_mixer_attributes_test_connect_usb_device,
115                     R.string.empty,
116                     R.string.empty,
117                     false /*passButtonEnabled*/);
118             return;
119         }
120         updateUI(R.string.audio_preferred_mixer_attributes_test_usb_device_connected,
121                 R.string.audio_preferred_mixer_attributes_test_running,
122                 R.string.empty,
123                 false /*passButtonEnabled*/);
124         mPreferredMixerAttrTestHandler.post(new Runnable() {
125             @Override
126             public void run() {
127                 displayTestResult();
128             }
129         });
130     }
131 
displayTestResult()132     private void displayTestResult() {
133         for (AudioDeviceInfo device : mUsbDevices) {
134             List<AudioMixerAttributes> supportedMixerAttrs =
135                     mAudioManager.getSupportedMixerAttributes(device);
136             if (supportedMixerAttrs.isEmpty()) {
137                 updateUI(R.string.audio_preferred_mixer_attributes_test_usb_device_connected,
138                         R.string.result_failure,
139                         R.string.audio_can_not_set_preferred_mixer_attributes,
140                         false /*passButtonEnabled*/);
141                 return;
142             }
143             AudioAttributes attr = new AudioAttributes.Builder()
144                     .setUsage(AudioAttributes.USAGE_MEDIA).build();
145             MyPreferredMixerAttributesListener listener =
146                     new MyPreferredMixerAttributesListener(attr, device.getId());
147             mAudioManager.addOnPreferredMixerAttributesChangedListener(
148                     Executors.newSingleThreadExecutor(), listener);
149             for (AudioMixerAttributes mixerAttr : supportedMixerAttrs) {
150                 listener.reset();
151                 if (!mAudioManager.setPreferredMixerAttributes(attr, device, mixerAttr)) {
152                     testComplete(
153                             R.string.audio_preferred_mixer_attributes_test_usb_device_connected,
154                             R.string.result_failure,
155                             R.string.audio_set_preferred_mixer_attributes_failed,
156                             false /*passButtonEnabled*/,
157                             listener);
158                     return;
159                 }
160                 listener.await(TIMEOUT_MS);
161                 if (!listener.isPreferredMixerAttributesChanged()) {
162                     testComplete(
163                             R.string.audio_preferred_mixer_attributes_test_usb_device_connected,
164                             R.string.result_failure,
165                             R.string.audio_no_callback_for_preferred_mixer_attributes_changed,
166                             false /*passButtonEnabled*/,
167                             listener);
168                     return;
169                 }
170                 if (!mixerAttr.equals(mAudioManager.getPreferredMixerAttributes(attr, device))) {
171                     testComplete(
172                             R.string.audio_preferred_mixer_attributes_test_usb_device_connected,
173                             R.string.result_failure,
174                             R.string.audio_get_preferred_mixer_attributes_not_equal,
175                             false /*passButtonEnabled*/,
176                             listener);
177                     return;
178                 }
179                 listener.reset();
180                 if (!mAudioManager.clearPreferredMixerAttributes(attr, device)) {
181                     testComplete(
182                             R.string.audio_preferred_mixer_attributes_test_usb_device_connected,
183                             R.string.result_failure,
184                             R.string.audio_clear_preferred_mixer_attributes_failed,
185                             false /*passButtonEnabled*/,
186                             listener);
187                     return;
188                 }
189                 listener.await(TIMEOUT_MS);
190                 if (!listener.isPreferredMixerAttributesChanged()) {
191                     testComplete(
192                             R.string.audio_preferred_mixer_attributes_test_usb_device_connected,
193                             R.string.result_failure,
194                             R.string.audio_no_callback_for_preferred_mixer_attributes_changed,
195                             false /*passButtonEnabled*/,
196                             listener);
197                     return;
198                 }
199                 if (mAudioManager.getPreferredMixerAttributes(attr, device) != null) {
200                     testComplete(
201                             R.string.audio_preferred_mixer_attributes_test_usb_device_connected,
202                             R.string.result_failure,
203                             R.string.audio_get_preferred_mixer_attributes_should_be_null,
204                             false /*passButtonEnabled*/,
205                             listener);
206                     return;
207                 }
208             }
209             mAudioManager.removeOnPreferredMixerAttributesChangedListener(listener);
210         }
211         testComplete(R.string.audio_preferred_mixer_attributes_test_usb_device_connected,
212                 R.string.result_success,
213                 R.string.empty,
214                 true /*passButtonEnabled*/,
215                 null /*listener*/);
216     }
217 
testComplete(int usbStatusResId, int testStatusResId, int failureMsgResId, boolean passButtonEnabled, MyPreferredMixerAttributesListener listener)218     private void testComplete(int usbStatusResId, int testStatusResId, int failureMsgResId,
219             boolean passButtonEnabled, MyPreferredMixerAttributesListener listener) {
220         if (listener != null) {
221             mAudioManager.removeOnPreferredMixerAttributesChangedListener(listener);
222         }
223         updateUI(usbStatusResId, testStatusResId, failureMsgResId, passButtonEnabled);
224     }
225 
updateUI(int usbStatusResId, int testStatusResId, int failureMsgResId, boolean passButtonEnabled)226     private void updateUI(int usbStatusResId, int testStatusResId, int failureMsgResId,
227             boolean passButtonEnabled) {
228         runOnUiThread(new Runnable() {
229             @Override
230             public void run() {
231                 mUsbStatusTextView.setText(usbStatusResId);
232                 mTestResultTextView.setText(testStatusResId);
233                 mFailureMsgTextView.setText(failureMsgResId);
234                 getPassButton().setEnabled(passButtonEnabled);
235             }
236         });
237     }
238 
detectUSBDevice()239     private boolean detectUSBDevice() {
240         mUsbDevices.clear();
241         AudioDeviceInfo[] deviceInfos = mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL);
242         for (AudioDeviceInfo deviceInfo : deviceInfos) {
243             if (deviceInfo.isSink() && (deviceInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE
244                     || deviceInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET)) {
245                 mUsbDevices.add(deviceInfo);
246             }
247         }
248         return !mUsbDevices.isEmpty();
249     }
250 
251     private class TestAudioDeviceCallback extends AudioDeviceCallback {
onAudioDevicesAdded(AudioDeviceInfo[] addedDevices)252         public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
253             detectUsbDeviceConnectionAndRunTest();
254         }
255 
onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices)256         public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
257             detectUsbDeviceConnectionAndRunTest();
258         }
259     }
260 
261     private final class MyPreferredMixerAttributesListener
262             implements AudioManager.OnPreferredMixerAttributesChangedListener {
263         private final AudioAttributes mAttr;
264         private final int mDeviceId;
265 
266         private CountDownLatch mCountDownLatch;
267         private AtomicBoolean mIsCalled = new AtomicBoolean(false);
268 
MyPreferredMixerAttributesListener(AudioAttributes attr, int deviceId)269         MyPreferredMixerAttributesListener(AudioAttributes attr, int deviceId) {
270             mAttr = attr;
271             mDeviceId = deviceId;
272             reset();
273         }
274 
275         @Override
onPreferredMixerAttributesChanged(AudioAttributes attributes, AudioDeviceInfo device, AudioMixerAttributes mixerAttributes)276         public void onPreferredMixerAttributesChanged(AudioAttributes attributes,
277                 AudioDeviceInfo device, AudioMixerAttributes mixerAttributes) {
278             if (device.getId() == mDeviceId && mAttr.equals(attributes)) {
279                 mIsCalled.set(true);
280             }
281             mCountDownLatch.countDown();
282         }
283 
reset()284         public void reset() {
285             mIsCalled.set(false);
286             mCountDownLatch = new CountDownLatch(1);
287         }
288 
await(long timeoutMs)289         void await(long timeoutMs) {
290             try {
291                 mCountDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS);
292             } catch (InterruptedException e) {
293             }
294         }
295 
isPreferredMixerAttributesChanged()296         public boolean isPreferredMixerAttributesChanged() {
297             return mIsCalled.get();
298         }
299     }
300 }
301