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