1 /*
2  * Copyright (C) 2020 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 static com.android.cts.verifier.TestListActivity.sCurrentDisplayMode;
20 import static com.android.cts.verifier.TestListAdapter.setTestNameSuffix;
21 
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.media.AudioDeviceCallback;
27 import android.media.AudioDeviceInfo;
28 import android.media.AudioManager;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.util.Log;
32 import android.widget.TextView;
33 
34 import com.android.compatibility.common.util.CddTest;
35 import com.android.compatibility.common.util.ResultType;
36 import com.android.compatibility.common.util.ResultUnit;
37 import com.android.cts.verifier.CtsVerifierReportLog;
38 import com.android.cts.verifier.PassFailButtons;
39 import com.android.cts.verifier.R;  // needed to access resource in CTSVerifier project namespace.
40 
41 @CddTest(requirement = "7.8.2.2/H-2-1,H-3-1,H-4-2,H-4-3,H-4-4,H-4-5")
42 public class USBAudioPeripheralNotificationsTest extends PassFailButtons.Activity {
43     private static final String
44             TAG = USBAudioPeripheralNotificationsTest.class.getSimpleName();
45 
46     private AudioManager    mAudioManager;
47 
48     private TextView mHeadsetInName;
49     private TextView mHeadsetOutName;
50     private TextView mUsbDeviceInName;
51     private TextView mUsbDeviceOutName;
52 
53     // private TextView mHeadsetPlugText;
54     private TextView mHeadsetPlugMessage;
55 
56     // Intents
57     private HeadsetPlugReceiver mHeadsetPlugReceiver;
58     private boolean mPlugIntentReceived;
59 
60     // Device
61     private AudioDeviceInfo mUsbHeadsetInInfo;
62     private AudioDeviceInfo mUsbHeadsetOutInfo;
63     private AudioDeviceInfo mUsbDeviceInInfo;
64     private AudioDeviceInfo mUsbDeviceOutInfo;
65 
66     private boolean mUsbHeadsetInReceived;
67     private boolean mUsbHeadsetOutReceived;
68     private boolean mUsbDeviceInReceived;
69     private boolean mUsbDeviceOutReceived;
70 
71     @Override
onCreate(Bundle savedInstanceState)72     protected void onCreate(Bundle savedInstanceState) {
73         super.onCreate(savedInstanceState);
74         setContentView(R.layout.uap_notifications_layout);
75 
76         mHeadsetInName = (TextView)findViewById(R.id.uap_messages_headset_in_name);
77         mHeadsetOutName = (TextView)findViewById(R.id.uap_messages_headset_out_name);
78 
79         mUsbDeviceInName = (TextView)findViewById(R.id.uap_messages_usb_device_in_name);
80         mUsbDeviceOutName = (TextView)findViewById(R.id.uap_messages_usb_device__out_name);
81 
82         mHeadsetPlugMessage = (TextView)findViewById(R.id.uap_messages_plug_message);
83 
84         mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);
85         mAudioManager.registerAudioDeviceCallback(new ConnectListener(), new Handler());
86 
87         mHeadsetPlugReceiver = new HeadsetPlugReceiver();
88         IntentFilter filter = new IntentFilter();
89         filter.addAction(Intent.ACTION_HEADSET_PLUG);
90         registerReceiver(mHeadsetPlugReceiver, filter);
91 
92         setInfoResources(R.string.audio_uap_notifications_test, R.string.uapNotificationsTestInfo,
93                 -1);
94 
95         setPassFailButtonClickListeners();
96         getPassButton().setEnabled(false);
97     }
98 
99     //
100     // UI
101     //
showConnectedDevices()102     private void showConnectedDevices() {
103         if (mUsbHeadsetInInfo != null) {
104             mHeadsetInName.setText(
105                     "Headset INPUT Connected " + mUsbHeadsetInInfo.getProductName());
106         } else {
107             mHeadsetInName.setText("");
108         }
109 
110         if (mUsbHeadsetOutInfo != null) {
111             mHeadsetOutName.setText(
112                     "Headset OUTPUT Connected " + mUsbHeadsetOutInfo.getProductName());
113         } else {
114             mHeadsetOutName.setText("");
115         }
116 
117         if (mUsbDeviceInInfo != null) {
118             mUsbDeviceInName.setText(
119                     "USB DEVICE INPUT Connected " + mUsbDeviceInInfo.getProductName());
120         } else {
121             mUsbDeviceInName.setText("");
122         }
123 
124         if (mUsbDeviceOutInfo != null) {
125             mUsbDeviceOutName.setText(
126                     "USB DEVICE OUTPUT Connected " + mUsbDeviceOutInfo.getProductName());
127         } else {
128             mUsbDeviceOutName.setText("");
129         }
130     }
131 
132     @Override
requiresReportLog()133     public boolean requiresReportLog() {
134         return true;
135     }
136 
137     @Override
getReportFileName()138     public String getReportFileName() {
139         return PassFailButtons.AUDIO_TESTS_REPORT_LOG_NAME;
140     }
141 
142     @Override
getReportSectionName()143     public final String getReportSectionName() {
144         return setTestNameSuffix(sCurrentDisplayMode, SECTION_USBDEVICENOTIFICATIONS);
145     }
146 
147     // ReportLog Schema
148     private static final String SECTION_USBDEVICENOTIFICATIONS =
149             "usb_audio_peripheral_notifications_activity";
150 
151     private static final String KEY_HEADSET_IN = "headset_in_received";
152     private static final String KEY_HEADSET_OUT = "headset_out_received";
153     private static final String KEY_DEVICE_IN = "device_in_received";
154     private static final String KEY_DEVICE_OUT = "device_out_received";
155 
156     @Override
recordTestResults()157     public void recordTestResults() {
158         CtsVerifierReportLog reportLog = getReportLog();
159 
160         reportLog.addValue(
161                 KEY_HEADSET_IN,
162                 mUsbHeadsetInReceived,
163                 ResultType.NEUTRAL,
164                 ResultUnit.NONE);
165 
166         reportLog.addValue(
167                 KEY_HEADSET_OUT,
168                 mUsbHeadsetOutReceived,
169                 ResultType.NEUTRAL,
170                 ResultUnit.NONE);
171 
172         reportLog.addValue(
173                 KEY_DEVICE_IN,
174                 mUsbDeviceInReceived,
175                 ResultType.NEUTRAL,
176                 ResultUnit.NONE);
177 
178         reportLog.addValue(
179                 KEY_DEVICE_OUT,
180                 mUsbDeviceOutReceived,
181                 ResultType.NEUTRAL,
182                 ResultUnit.NONE);
183 
184         reportLog.submit();
185     }
186 
reportPlugIntent(Intent intent)187     private void reportPlugIntent(Intent intent) {
188         // [ 7.8 .2.2/H-2-1] MUST broadcast Intent ACTION_HEADSET_PLUG with "microphone" extra
189         // set to 0 when the USB audio terminal types 0x0302 is detected.
190         // [ 7.8 .2.2/H-3-1] MUST broadcast Intent ACTION_HEADSET_PLUG with "microphone" extra
191         // set to 1 when the USB audio terminal types 0x0402 is detected, they:
192         mPlugIntentReceived = true;
193 
194         // state - 0 for unplugged, 1 for plugged.
195         // name - Headset type, human readable string
196         // microphone - 1 if headset has a microphone, 0 otherwise
197         int state = intent.getIntExtra("state", -1);
198         if (state != -1) {
199 
200             StringBuilder sb = new StringBuilder();
201             sb.append("ACTION_HEADSET_PLUG received - " + (state == 0 ? "Unplugged" : "Plugged"));
202 
203             String name = intent.getStringExtra("name");
204             if (name != null) {
205                 sb.append(" - " + name);
206             }
207 
208             int hasMic = intent.getIntExtra("microphone", 0);
209             if (hasMic == 1) {
210                 sb.append(" [mic]");
211             }
212 
213             mHeadsetPlugMessage.setText(sb.toString());
214         }
215 
216         getPassButton().setEnabled(calculatePass());
217     }
218 
219     //
220     // Test Status
221     //
calculatePass()222     private boolean calculatePass() {
223         return isReportLogOkToPass()
224                 && mUsbHeadsetInReceived && mUsbHeadsetOutReceived
225                 && mUsbDeviceInReceived && mUsbDeviceOutReceived
226                 && mPlugIntentReceived;
227     }
228 
229     //
230     // Devices
231     //
scanDevices(AudioDeviceInfo[] devices)232     private void scanDevices(AudioDeviceInfo[] devices) {
233         mUsbHeadsetInInfo = mUsbHeadsetOutInfo =
234                 mUsbDeviceInInfo = mUsbDeviceOutInfo = null;
235 
236         for (AudioDeviceInfo devInfo : devices) {
237             if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET) {
238                 if (devInfo.isSource()) {
239                     // [ 7.8 .2.2/H-4-3] MUST list a device of type AudioDeviceInfo.TYPE_USB_HEADSET
240                     // and role isSource() if the USB audio terminal type field is 0x0402.
241                     mUsbHeadsetInReceived = true;
242                     mUsbHeadsetInInfo = devInfo;
243                 } else if (devInfo.isSink()) {
244                     // [ 7.8 .2.2/H-4-2] MUST list a device of type AudioDeviceInfo.TYPE_USB_HEADSET
245                     // and role isSink() if the USB audio terminal type field is 0x0402.
246                     mUsbHeadsetOutReceived = true;
247                     mUsbHeadsetOutInfo = devInfo;
248                 }
249             } else if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE) {
250                 if (devInfo.isSource()) {
251                     // [ 7.8 .2.2/H-4-5] MUST list a device of type AudioDeviceInfo.TYPE_USB_DEVICE
252                     // and role isSource() if the USB audio terminal type field is 0x604.
253                     mUsbDeviceInReceived = true;
254                     mUsbDeviceInInfo = devInfo;
255                 } else if (devInfo.isSink()) {
256                     // [ 7.8 .2.2/H-4-4] MUST list a device of type AudioDeviceInfo.TYPE_USB_DEVICE
257                     // and role isSink() if the USB audio terminal type field is 0x603.
258                     mUsbDeviceOutReceived = true;
259                     mUsbDeviceOutInfo = devInfo;
260                 }
261             }
262 
263             if (mUsbHeadsetInInfo != null &&
264                     mUsbHeadsetOutInfo != null &&
265                     mUsbDeviceInInfo != null &&
266                     mUsbDeviceOutInfo != null) {
267                 break;
268             }
269         }
270 
271 
272         showConnectedDevices();
273         getPassButton().setEnabled(calculatePass());
274     }
275 
276     private class ConnectListener extends AudioDeviceCallback {
ConnectListener()277         /*package*/ ConnectListener() {}
278 
279         //
280         // AudioDevicesManager.OnDeviceConnectionListener
281         //
282         @Override
onAudioDevicesAdded(AudioDeviceInfo[] addedDevices)283         public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
284             Log.i(TAG, "onAudioDevicesAdded() num:" + addedDevices.length);
285 
286             scanDevices(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
287         }
288 
289         @Override
onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices)290         public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
291             Log.i(TAG, "onAudioDevicesRemoved() num:" + removedDevices.length);
292 
293             scanDevices(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
294         }
295     }
296 
297     // Intents
298     private class HeadsetPlugReceiver extends BroadcastReceiver {
299         @Override
onReceive(Context context, Intent intent)300         public void onReceive(Context context, Intent intent) {
301             reportPlugIntent(intent);
302         }
303     }
304 
305 }
306