1 /*
2  * Copyright (C) 2018 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.app.AlertDialog;
20 import android.content.DialogInterface;
21 import android.content.pm.PackageManager;
22 import android.content.res.Resources;
23 import android.media.AudioDeviceInfo;
24 import android.media.AudioFormat;
25 import android.os.Bundle;
26 import android.util.Log;
27 import android.view.View;
28 import android.widget.CheckBox;
29 import android.widget.TextView;
30 
31 import com.android.compatibility.common.util.ResultType;
32 import com.android.compatibility.common.util.ResultUnit;
33 import com.android.cts.verifier.audio.audiolib.AudioSystemFlags;
34 import com.android.cts.verifier.CtsVerifierReportLog;
35 import com.android.cts.verifier.PassFailButtons;
36 import com.android.cts.verifier.R;
37 
38 import static com.android.cts.verifier.TestListActivity.sCurrentDisplayMode;
39 import static com.android.cts.verifier.TestListAdapter.setTestNameSuffix;
40 
41 public class ProAudioActivity
42         extends PassFailButtons.Activity
43         implements View.OnClickListener {
44     private static final String TAG = ProAudioActivity.class.getSimpleName();
45     private static final boolean DEBUG = false;
46 
47     // Flags
48     private boolean mClaimsProAudio;
49     private boolean mClaimsLowLatencyAudio;    // CDD ProAudio section C-1-1
50     private boolean mClaimsMIDI;               // CDD ProAudio section C-1-4
51     private boolean mClaimsUSBHostMode;        // CDD ProAudio section C-1-3
52     private boolean mClaimsUSBPeripheralMode;  // CDD ProAudio section C-1-3
53     private boolean mClaimsHDMI;               // CDD ProAudio section C-1-3
54 
55     AudioDeviceInfo mHDMIDeviceInfo;
56 
57     // Widgets
58     TextView mHDMISupportLbl;
59 
60     CheckBox mClaimsHDMICheckBox;
61 
62     TextView mTestStatusLbl;
63 
64     // Borrowed from PassFailButtons.java
65     private static final int INFO_DIALOG_ID = 1337;
66     private static final String INFO_DIALOG_TITLE_ID = "infoDialogTitleId";
67     private static final String INFO_DIALOG_MESSAGE_ID = "infoDialogMessageId";
68 
69     // ReportLog Schema
70     private static final String KEY_CLAIMS_PRO = "claims_pro_audio";
71     private static final String KEY_CLAIMS_LOW_LATENCY = "claims_low_latency_audio";
72     private static final String KEY_CLAIMS_MIDI = "claims_midi";
73     private static final String KEY_CLAIMS_USB_HOST = "claims_usb_host";
74     private static final String KEY_CLAIMS_USB_PERIPHERAL = "claims_usb_peripheral";
75     private static final String KEY_CLAIMS_HDMI = "claims_hdmi";
76 
ProAudioActivity()77     public ProAudioActivity() {
78         super();
79     }
80 
81     // HDMI Stuff
isHDMIValid()82     private boolean isHDMIValid() {
83         if (mHDMIDeviceInfo == null) {
84             return false;
85         }
86 
87         // MUST support output in stereo and eight channels...
88         boolean has2Chans = false;
89         boolean has8Chans = false;
90         int[] channelCounts = mHDMIDeviceInfo.getChannelCounts();
91         for (int count : channelCounts) {
92             if (count == 2) {
93                 has2Chans = true;
94             } else if (count == 8) {
95                 has8Chans = true;
96             }
97         }
98         if (!has2Chans || !has8Chans) {
99             return false;
100         }
101 
102         // at 20-bit or 24-bit depth
103         boolean hasFloatEncoding = false;
104         int[] encodings = mHDMIDeviceInfo.getEncodings();
105         for (int encoding : encodings) {
106             if (encoding == AudioFormat.ENCODING_PCM_FLOAT) {
107                 hasFloatEncoding = true;
108                 break;
109             }
110         }
111         if (!hasFloatEncoding) {
112             return false;
113         }
114 
115          // and 192 kHz
116         boolean has192K = false;
117         int[] sampleRates = mHDMIDeviceInfo.getSampleRates();
118         for (int rate : sampleRates) {
119             if (rate >= 192000) {
120                 has192K = true;
121             }
122         }
123         if (!has192K) {
124             return false;
125         }
126 
127         // without bit-depth loss or resampling (hmmmmm....).
128 
129         return true;
130     }
131 
handleDeviceConnection(AudioDeviceInfo devInfo)132     protected void handleDeviceConnection(AudioDeviceInfo devInfo) {
133         mHDMIDeviceInfo = null;
134 
135         if (devInfo.isSink() && devInfo.getType() == AudioDeviceInfo.TYPE_HDMI) {
136             mHDMIDeviceInfo = devInfo;
137         }
138 
139         if (mHDMIDeviceInfo != null) {
140             mClaimsHDMICheckBox.setChecked(true);
141             mHDMISupportLbl.setText(getResources().getString(
142                     isHDMIValid() ? R.string.pass_button_text : R.string.fail_button_text));
143         }
144         mHDMISupportLbl.setText(getResources().getString(R.string.audio_proaudio_NA));
145 
146         calculatePass();
147     }
148 
calculatePass()149     private boolean calculatePass() {
150         boolean usbOK = mClaimsUSBHostMode && mClaimsUSBPeripheralMode;
151         boolean hdmiOK = !mClaimsHDMI || isHDMIValid();
152 
153         boolean hasPassed = !mClaimsProAudio ||
154                 (mClaimsLowLatencyAudio &&
155                 mClaimsMIDI &&
156                 usbOK &&
157                 hdmiOK);
158 
159         getPassButton().setEnabled(hasPassed);
160         return hasPassed;
161     }
162 
displayTestResults()163     private void displayTestResults() {
164         boolean hasPassed = calculatePass();
165 
166         Resources strings = getResources();
167         if (hasPassed) {
168             mTestStatusLbl.setText(strings.getString(R.string.audio_proaudio_pass));
169         } else if (!mClaimsMIDI) {
170             mTestStatusLbl.setText(strings.getString(R.string.audio_proaudio_midinotreported));
171         } else if (!mClaimsUSBHostMode) {
172             mTestStatusLbl.setText(strings.getString(R.string.audio_proaudio_usbhostnotreported));
173         } else if (!mClaimsUSBPeripheralMode) {
174             mTestStatusLbl.setText(strings.getString(
175                     R.string.audio_proaudio_usbperipheralnotreported));
176         } else if (mClaimsHDMI && isHDMIValid()) {
177             mTestStatusLbl.setText(strings.getString(R.string.audio_proaudio_hdminotvalid));
178         }
179     }
180 
181     @Override
onCreate(Bundle savedInstanceState)182     protected void onCreate(Bundle savedInstanceState) {
183         setContentView(R.layout.pro_audio);
184 
185         super.onCreate(savedInstanceState);
186 
187         setPassFailButtonClickListeners();
188         setInfoResources(R.string.proaudio_test, R.string.proaudio_info, -1);
189 
190         mClaimsProAudio = AudioSystemFlags.claimsProAudio(this);
191         ((TextView)findViewById(R.id.proAudioHasProAudioLbl)).setText("" + mClaimsProAudio);
192 
193         if (!mClaimsProAudio) {
194             Bundle args = new Bundle();
195             args.putInt(INFO_DIALOG_TITLE_ID, R.string.pro_audio_latency_test);
196             args.putInt(INFO_DIALOG_MESSAGE_ID, R.string.audio_proaudio_nopa_message);
197             showDialog(INFO_DIALOG_ID, args);
198         }
199 
200         mClaimsLowLatencyAudio = AudioSystemFlags.claimsLowLatencyAudio(this);
201         ((TextView)findViewById(R.id.proAudioHasLLALbl)).setText("" + mClaimsLowLatencyAudio);
202 
203         mClaimsMIDI = AudioSystemFlags.claimsMIDI(this);
204         ((TextView)findViewById(R.id.proAudioHasMIDILbl)).setText("" + mClaimsMIDI);
205 
206         mClaimsUSBHostMode = AudioSystemFlags.claimsUSBHostMode(this);
207         ((TextView)findViewById(R.id.proAudioMidiHasUSBHostLbl)).setText("" + mClaimsUSBHostMode);
208 
209         mClaimsUSBPeripheralMode = AudioSystemFlags.claimsUSBPeripheralMode(this);
210         ((TextView)findViewById(
211                 R.id.proAudioMidiHasUSBPeripheralLbl)).setText("" + mClaimsUSBPeripheralMode);
212 
213         // HDMI
214         mHDMISupportLbl = (TextView)findViewById(R.id.proAudioHDMISupportLbl);
215         mClaimsHDMICheckBox = (CheckBox)findViewById(R.id.proAudioHasHDMICheckBox);
216         mClaimsHDMICheckBox.setOnClickListener(this);
217 
218         mTestStatusLbl = (TextView)findViewById(R.id.proAudioTestStatusLbl);
219 
220         calculatePass();
221     }
222 
223     /**
224      * Store test results in log
225      */
226     @Override
getTestId()227     public String getTestId() {
228         return setTestNameSuffix(sCurrentDisplayMode, getClass().getName());
229     }
230 
231     //
232     // PassFailButtons Overrides
233     //
234     @Override
recordTestResults()235     public void recordTestResults() {
236 
237         CtsVerifierReportLog reportLog = getReportLog();
238         reportLog.addValue(
239                 KEY_CLAIMS_PRO,
240                 mClaimsProAudio,
241                 ResultType.NEUTRAL,
242                 ResultUnit.NONE);
243 
244         reportLog.addValue(
245                 KEY_CLAIMS_LOW_LATENCY,
246                 mClaimsLowLatencyAudio,
247                 ResultType.NEUTRAL,
248                 ResultUnit.NONE);
249 
250         reportLog.addValue(
251                 KEY_CLAIMS_MIDI,
252                 mClaimsMIDI,
253                 ResultType.NEUTRAL,
254                 ResultUnit.NONE);
255 
256         reportLog.addValue(
257                 KEY_CLAIMS_USB_HOST,
258                 mClaimsUSBHostMode,
259                 ResultType.NEUTRAL,
260                 ResultUnit.NONE);
261 
262         reportLog.addValue(
263                 KEY_CLAIMS_USB_PERIPHERAL,
264                 mClaimsUSBPeripheralMode,
265                 ResultType.NEUTRAL,
266                 ResultUnit.NONE);
267 
268         reportLog.addValue(
269                 KEY_CLAIMS_HDMI,
270                 mClaimsHDMI,
271                 ResultType.NEUTRAL,
272                 ResultUnit.NONE);
273 
274         reportLog.submit();
275     }
276 
277     @Override
onClick(View view)278     public void onClick(View view) {
279         switch (view.getId()) {
280         case R.id.proAudioHasHDMICheckBox:
281             if (mClaimsHDMICheckBox.isChecked()) {
282                 AlertDialog.Builder builder =
283                         new AlertDialog.Builder(this, android.R.style.Theme_Material_Dialog_Alert);
284                 builder.setTitle(getResources().getString(R.string.proaudio_hdmi_infotitle));
285                 builder.setMessage(getResources().getString(R.string.proaudio_hdmi_message));
286                 builder.setPositiveButton(android.R.string.yes,
287                     new DialogInterface.OnClickListener() {
288                         public void onClick(DialogInterface dialog, int which) {}
289                  });
290                 builder.setIcon(android.R.drawable.ic_dialog_alert);
291                 builder.show();
292 
293                 mClaimsHDMI = true;
294                 mHDMISupportLbl.setText(getResources().getString(R.string.audio_proaudio_pending));
295             } else {
296                 mClaimsHDMI = false;
297                 mHDMISupportLbl.setText(getResources().getString(R.string.audio_proaudio_NA));
298             }
299             calculatePass();
300             break;
301         }
302     }
303 }
304