1 /* 2 * Copyright (C) 2015 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.media.AudioDeviceCallback; 21 import android.media.AudioDeviceInfo; 22 import android.media.AudioManager; 23 import android.media.MediaRecorder; 24 import android.os.Bundle; 25 import android.os.Handler; 26 import android.os.Message; 27 import android.util.Log; 28 import android.view.View; 29 import android.view.View.OnClickListener; 30 import android.view.ViewGroup; 31 import android.widget.Button; 32 import android.widget.ProgressBar; 33 import android.widget.SeekBar; 34 import android.widget.TextView; 35 36 import com.android.compatibility.common.util.ResultType; 37 import com.android.compatibility.common.util.ResultUnit; 38 import com.android.cts.verifier.audio.audiolib.AudioSystemFlags; 39 import com.android.cts.verifier.audio.audiolib.StatUtils; 40 import com.android.cts.verifier.audio.audiolib.AudioUtils; 41 import com.android.cts.verifier.CtsVerifierReportLog; 42 import com.android.cts.verifier.PassFailButtons; 43 import com.android.cts.verifier.R; 44 45 import static com.android.cts.verifier.TestListActivity.sCurrentDisplayMode; 46 import static com.android.cts.verifier.TestListAdapter.setTestNameSuffix; 47 48 /** 49 * CtsVerifier Audio Loopback Latency Test 50 */ 51 public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity { 52 private static final String TAG = "AudioLoopbackLatencyActivity"; 53 54 // JNI load 55 static { 56 try { 57 System.loadLibrary("audioloopback_jni"); 58 } catch (UnsatisfiedLinkError e) { 59 Log.e(TAG, "Error loading Audio Loopback JNI library"); 60 Log.e(TAG, "e: " + e); 61 e.printStackTrace(); 62 } 63 64 /* TODO: gracefully fail/notify if the library can't be loaded */ 65 } 66 protected AudioManager mAudioManager; 67 68 // UI 69 TextView mInputDeviceTxt; 70 TextView mOutputDeviceTxt; 71 72 TextView mAudioLevelText; 73 SeekBar mAudioLevelSeekbar; 74 75 TextView mTestPathTxt; 76 77 TextView mResultText; 78 ProgressBar mProgressBar; 79 int mMaxLevel; 80 81 OnBtnClickListener mBtnClickListener = new OnBtnClickListener(); 82 protected Button mTestButton; 83 84 String mYesString; 85 String mNoString; 86 87 // These flags determine the maximum allowed latency 88 private boolean mClaimsProAudio; 89 private boolean mClaimsOutput; 90 private boolean mClaimsInput; 91 92 // Useful info 93 private boolean mSupportsMMAP = AudioUtils.isMMapSupported(); 94 private boolean mSupportsMMAPExclusive = AudioUtils.isMMapExclusiveSupported(); 95 96 // Peripheral(s) 97 boolean mIsPeripheralAttached; // CDD ProAudio section C-1-3 98 AudioDeviceInfo mOutputDevInfo; 99 AudioDeviceInfo mInputDevInfo; 100 101 protected static final int TESTPERIPHERAL_INVALID = -1; 102 protected static final int TESTPERIPHERAL_NONE = 0; 103 protected static final int TESTPERIPHERAL_ANALOG_JACK = 1; 104 protected static final int TESTPERIPHERAL_USB = 2; 105 protected static final int TESTPERIPHERAL_DEVICE = 3; // device speaker + mic 106 protected int mTestPeripheral = TESTPERIPHERAL_NONE; 107 108 // Loopback Logic 109 NativeAnalyzerThread mNativeAnalyzerThread = null; 110 111 protected static final int NUM_TEST_PHASES = 5; 112 protected int mTestPhase = 0; 113 114 protected double[] mLatencyMillis = new double[NUM_TEST_PHASES]; 115 protected double[] mConfidence = new double[NUM_TEST_PHASES]; 116 117 protected double mMeanLatencyMillis; 118 protected double mMeanAbsoluteDeviation; 119 protected double mMeanConfidence; 120 121 protected static final double CONFIDENCE_THRESHOLD = 0.6; 122 // impossibly low latencies (indicating something in the test went wrong). 123 protected static final float EPSILON = 1.0f; 124 protected static final double PROAUDIO_RECOMMENDED_LATENCY_MS = 20.0; 125 protected static final double PROAUDIO_RECOMMENDED_USB_LATENCY_MS = 25.0; 126 protected static final double PROAUDIO_MUST_LATENCY_MS = 20.0; 127 protected static final double BASIC_RECOMMENDED_LATENCY_MS = 50.0; 128 protected static final double BASIC_MUST_LATENCY_MS = 800.0; 129 protected double mMustLatency; 130 protected double mRecommendedLatency; 131 132 // The audio stream callback threads should stop and close 133 // in less than a few hundred msec. This is a generous timeout value. 134 private static final int STOP_TEST_TIMEOUT_MSEC = 2 * 1000; 135 136 // 137 // Common UI Handling 138 // connectLoopbackUI()139 private void connectLoopbackUI() { 140 // Connected Device 141 mInputDeviceTxt = ((TextView)findViewById(R.id.audioLoopbackInputLbl)); 142 mOutputDeviceTxt = ((TextView)findViewById(R.id.audioLoopbackOutputLbl)); 143 144 mAudioLevelText = (TextView)findViewById(R.id.audio_loopback_level_text); 145 mAudioLevelSeekbar = (SeekBar)findViewById(R.id.audio_loopback_level_seekbar); 146 mMaxLevel = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); 147 mAudioLevelSeekbar.setMax(mMaxLevel); 148 mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (int)(0.7 * mMaxLevel), 0); 149 refreshLevel(); 150 151 mAudioLevelSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 152 @Override 153 public void onStopTrackingTouch(SeekBar seekBar) {} 154 155 @Override 156 public void onStartTrackingTouch(SeekBar seekBar) {} 157 158 @Override 159 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 160 mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 161 progress, 0); 162 Log.i(TAG,"Level set to: " + progress); 163 refreshLevel(); 164 } 165 }); 166 167 mResultText = (TextView)findViewById(R.id.audio_loopback_results_text); 168 mProgressBar = (ProgressBar)findViewById(R.id.audio_loopback_progress_bar); 169 showWait(false); 170 } 171 172 // 173 // Peripheral Connection Logic 174 // scanPeripheralList(AudioDeviceInfo[] devices)175 protected void scanPeripheralList(AudioDeviceInfo[] devices) { 176 // CDD Section C-1-3: USB port, host-mode support 177 178 // Can't just use the first record because then we will only get 179 // Source OR sink, not both even on devices that are both. 180 mOutputDevInfo = null; 181 mInputDevInfo = null; 182 183 // Any valid peripherals 184 // Do we leave in the Headset test to support a USB-Dongle? 185 for (AudioDeviceInfo devInfo : devices) { 186 if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE || // USB Peripheral 187 devInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET || // USB dongle+LBPlug 188 devInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET || // Loopback Plug? 189 devInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) { // Aux-cable loopback? 190 if (devInfo.isSink()) { 191 mOutputDevInfo = devInfo; 192 } 193 if (devInfo.isSource()) { 194 mInputDevInfo = devInfo; 195 } 196 } else { 197 handleDeviceConnection(devInfo); 198 } 199 } 200 201 // need BOTH input and output to test 202 mIsPeripheralAttached = mOutputDevInfo != null && mInputDevInfo != null; 203 calculateTestPeripheral(); 204 showConnectedAudioPeripheral(); 205 calculateLatencyThresholds(); 206 displayLatencyThresholds(); 207 } 208 handleDeviceConnection(AudioDeviceInfo deviceInfo)209 protected void handleDeviceConnection(AudioDeviceInfo deviceInfo) { 210 // NOP 211 } 212 213 private class ConnectListener extends AudioDeviceCallback { ConnectListener()214 /*package*/ ConnectListener() {} 215 216 // 217 // AudioDevicesManager.OnDeviceConnectionListener 218 // 219 @Override onAudioDevicesAdded(AudioDeviceInfo[] addedDevices)220 public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) { 221 scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL)); 222 } 223 224 @Override onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices)225 public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) { 226 scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL)); 227 } 228 } 229 calculateTestPeripheral()230 private void calculateTestPeripheral() { 231 if (!mIsPeripheralAttached) { 232 if ((mOutputDevInfo != null && mInputDevInfo == null) 233 || (mOutputDevInfo == null && mInputDevInfo != null)) { 234 mTestPeripheral = TESTPERIPHERAL_INVALID; 235 } else { 236 mTestPeripheral = TESTPERIPHERAL_DEVICE; 237 } 238 } else if (!areIODevicesOnePeripheral()) { 239 mTestPeripheral = TESTPERIPHERAL_INVALID; 240 } else if (mInputDevInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE || 241 mInputDevInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET) { 242 mTestPeripheral = TESTPERIPHERAL_USB; 243 } else if (mInputDevInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET || 244 mInputDevInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) { 245 mTestPeripheral = TESTPERIPHERAL_ANALOG_JACK; 246 } else { 247 // Huh? 248 Log.e(TAG, "No valid peripheral found!?"); 249 mTestPeripheral = TESTPERIPHERAL_NONE; 250 } 251 } 252 isPeripheralValidForTest()253 private boolean isPeripheralValidForTest() { 254 return mTestPeripheral == TESTPERIPHERAL_ANALOG_JACK 255 || mTestPeripheral == TESTPERIPHERAL_USB; 256 } 257 showConnectedAudioPeripheral()258 private void showConnectedAudioPeripheral() { 259 mInputDeviceTxt.setText( 260 mInputDevInfo != null ? mInputDevInfo.getProductName().toString() 261 : "Not connected"); 262 mOutputDeviceTxt.setText( 263 mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString() 264 : "Not connected"); 265 266 String pathName; 267 switch (mTestPeripheral) { 268 case TESTPERIPHERAL_INVALID: 269 pathName = "Invalid Test Peripheral"; 270 break; 271 272 case TESTPERIPHERAL_ANALOG_JACK: 273 pathName = "Headset Jack"; 274 break; 275 276 case TESTPERIPHERAL_USB: 277 pathName = "USB"; 278 break; 279 280 case TESTPERIPHERAL_DEVICE: 281 pathName = "Device Speaker + Microphone"; 282 break; 283 284 case TESTPERIPHERAL_NONE: 285 default: 286 pathName = "Error. Unknown Test Path"; 287 break; 288 } 289 mTestPathTxt.setText(pathName); 290 mTestButton.setEnabled( 291 mTestPeripheral != TESTPERIPHERAL_INVALID && mTestPeripheral != TESTPERIPHERAL_NONE); 292 293 } 294 areIODevicesOnePeripheral()295 private boolean areIODevicesOnePeripheral() { 296 if (mOutputDevInfo == null || mInputDevInfo == null) { 297 return false; 298 } 299 300 return mOutputDevInfo.getProductName().toString().equals( 301 mInputDevInfo.getProductName().toString()); 302 } 303 calculateLatencyThresholds()304 private void calculateLatencyThresholds() { 305 switch (mTestPeripheral) { 306 case TESTPERIPHERAL_ANALOG_JACK: 307 mRecommendedLatency = mClaimsProAudio 308 ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS; 309 mMustLatency = mClaimsProAudio 310 ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_MUST_LATENCY_MS; 311 break; 312 313 case TESTPERIPHERAL_USB: 314 mRecommendedLatency = mClaimsProAudio 315 ? PROAUDIO_RECOMMENDED_USB_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS; 316 mMustLatency = mClaimsProAudio 317 ? PROAUDIO_RECOMMENDED_USB_LATENCY_MS : BASIC_MUST_LATENCY_MS; 318 break; 319 320 case TESTPERIPHERAL_DEVICE: 321 // This isn't a valid case so we won't pass it, but it can be run 322 mRecommendedLatency = mClaimsProAudio 323 ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS; 324 mMustLatency = mClaimsProAudio 325 ? PROAUDIO_RECOMMENDED_LATENCY_MS :BASIC_MUST_LATENCY_MS; 326 break; 327 328 case TESTPERIPHERAL_NONE: 329 default: 330 mRecommendedLatency = BASIC_RECOMMENDED_LATENCY_MS; 331 mMustLatency = BASIC_MUST_LATENCY_MS; 332 break; 333 } 334 } 335 displayLatencyThresholds()336 private void displayLatencyThresholds() { 337 if (isPeripheralValidForTest()) { 338 ((TextView) findViewById(R.id.audio_loopback_must_latency)).setText("" + mMustLatency); 339 ((TextView) findViewById(R.id.audio_loopback_recommended_latency)).setText( 340 "" + mRecommendedLatency); 341 } else { 342 String naStr = getResources().getString(R.string.audio_proaudio_NA); 343 ((TextView) findViewById(R.id.audio_loopback_must_latency)).setText(naStr); 344 ((TextView) findViewById(R.id.audio_loopback_recommended_latency)).setText(naStr); 345 } 346 } 347 348 /** 349 * refresh Audio Level seekbar and text 350 */ refreshLevel()351 private void refreshLevel() { 352 int currentLevel = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 353 mAudioLevelSeekbar.setProgress(currentLevel); 354 355 String levelText = String.format("%s: %d/%d", 356 getResources().getString(R.string.audio_loopback_level_text), 357 currentLevel, mMaxLevel); 358 mAudioLevelText.setText(levelText); 359 } 360 361 // 362 // show active progress bar 363 // showWait(boolean show)364 protected void showWait(boolean show) { 365 mProgressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE); 366 } 367 368 // 369 // Common logging 370 // 371 // Schema 372 private static final String KEY_LATENCY = "latency"; 373 private static final String KEY_CONFIDENCE = "confidence"; 374 private static final String KEY_SAMPLE_RATE = "sample_rate"; 375 private static final String KEY_IS_PRO_AUDIO = "is_pro_audio"; 376 private static final String KEY_IS_LOW_LATENCY = "is_low_latency"; 377 private static final String KEY_IS_PERIPHERAL_ATTACHED = "is_peripheral_attached"; 378 private static final String KEY_INPUT_PERIPHERAL_NAME = "input_peripheral"; 379 private static final String KEY_OUTPUT_PERIPHERAL_NAME = "output_peripheral"; 380 private static final String KEY_TEST_PERIPHERAL = "test_peripheral"; 381 private static final String KEY_TEST_MMAP = "supports_mmap"; 382 private static final String KEY_TEST_MMAPEXCLUSIVE = "supports_mmap_exclusive"; 383 private static final String KEY_LEVEL = "level"; 384 private static final String KEY_BUFFER_SIZE = "buffer_size_in_frames"; 385 386 @Override getTestId()387 public String getTestId() { 388 return setTestNameSuffix(sCurrentDisplayMode, getClass().getName()); 389 } 390 391 @Override getReportFileName()392 public String getReportFileName() { return PassFailButtons.AUDIO_TESTS_REPORT_LOG_NAME; } 393 394 @Override getReportSectionName()395 public final String getReportSectionName() { 396 return setTestNameSuffix(sCurrentDisplayMode, "audio_loopback_latency_activity"); 397 } 398 399 // 400 // Subclasses should call this explicitly. SubClasses should call submit() after their logs 401 // 402 @Override recordTestResults()403 public void recordTestResults() { 404 if (mNativeAnalyzerThread == null) { 405 return; // no results to report 406 } 407 408 CtsVerifierReportLog reportLog = getReportLog(); 409 reportLog.addValue( 410 KEY_LATENCY, 411 mMeanLatencyMillis, 412 ResultType.LOWER_BETTER, 413 ResultUnit.MS); 414 415 reportLog.addValue( 416 KEY_CONFIDENCE, 417 mMeanConfidence, 418 ResultType.HIGHER_BETTER, 419 ResultUnit.NONE); 420 421 reportLog.addValue( 422 KEY_SAMPLE_RATE, 423 mNativeAnalyzerThread.getSampleRate(), 424 ResultType.NEUTRAL, 425 ResultUnit.NONE); 426 427 reportLog.addValue( 428 KEY_IS_LOW_LATENCY, 429 mNativeAnalyzerThread.isLowLatencyStream(), 430 ResultType.NEUTRAL, 431 ResultUnit.NONE); 432 433 reportLog.addValue( 434 KEY_IS_PERIPHERAL_ATTACHED, 435 mIsPeripheralAttached, 436 ResultType.NEUTRAL, 437 ResultUnit.NONE); 438 439 reportLog.addValue( 440 KEY_IS_PRO_AUDIO, 441 mClaimsProAudio, 442 ResultType.NEUTRAL, 443 ResultUnit.NONE); 444 445 reportLog.addValue( 446 KEY_TEST_PERIPHERAL, 447 mTestPeripheral, 448 ResultType.NEUTRAL, 449 ResultUnit.NONE); 450 451 reportLog.addValue( 452 KEY_TEST_MMAP, 453 mSupportsMMAP, 454 ResultType.NEUTRAL, 455 ResultUnit.NONE); 456 457 reportLog.addValue( 458 KEY_TEST_MMAPEXCLUSIVE , 459 mSupportsMMAPExclusive, 460 ResultType.NEUTRAL, 461 ResultUnit.NONE); 462 463 if (mIsPeripheralAttached) { 464 reportLog.addValue( 465 KEY_INPUT_PERIPHERAL_NAME, 466 mInputDevInfo != null ? mInputDevInfo.getProductName().toString() : "None", 467 ResultType.NEUTRAL, 468 ResultUnit.NONE); 469 470 reportLog.addValue( 471 KEY_OUTPUT_PERIPHERAL_NAME, 472 mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString() : "None", 473 ResultType.NEUTRAL, 474 ResultUnit.NONE); 475 } 476 477 int audioLevel = mAudioLevelSeekbar.getProgress(); 478 reportLog.addValue( 479 KEY_LEVEL, 480 audioLevel, 481 ResultType.NEUTRAL, 482 ResultUnit.NONE); 483 484 reportLog.submit(); 485 } 486 startAudioTest(Handler messageHandler)487 private void startAudioTest(Handler messageHandler) { 488 getPassButton().setEnabled(false); 489 490 mTestPhase = 0; 491 java.util.Arrays.fill(mLatencyMillis, 0.0); 492 java.util.Arrays.fill(mConfidence, 0.0); 493 494 mNativeAnalyzerThread = new NativeAnalyzerThread(this); 495 if (mNativeAnalyzerThread != null) { 496 mNativeAnalyzerThread.setMessageHandler(messageHandler); 497 // This value matches AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 498 mNativeAnalyzerThread.setInputPreset(MediaRecorder.AudioSource.VOICE_RECOGNITION); 499 startTestPhase(); 500 } else { 501 Log.e(TAG, "Couldn't allocate native analyzer thread"); 502 mResultText.setText(getResources().getString(R.string.audio_loopback_failure)); 503 } 504 } 505 startTestPhase()506 private void startTestPhase() { 507 if (mNativeAnalyzerThread != null) { 508 mNativeAnalyzerThread.startTest(); 509 510 // what is this for? 511 try { 512 Thread.sleep(200); 513 } catch (InterruptedException e) { 514 e.printStackTrace(); 515 } 516 } 517 } 518 handleTestCompletion()519 private void handleTestCompletion() { 520 mMeanLatencyMillis = StatUtils.calculateMean(mLatencyMillis); 521 mMeanAbsoluteDeviation = 522 StatUtils.calculateMeanAbsoluteDeviation(mMeanLatencyMillis, mLatencyMillis); 523 mMeanConfidence = StatUtils.calculateMean(mConfidence); 524 525 boolean pass = isPeripheralValidForTest() 526 && mMeanConfidence >= CONFIDENCE_THRESHOLD 527 && mMeanLatencyMillis > EPSILON 528 && mMeanLatencyMillis < mMustLatency; 529 530 getPassButton().setEnabled(pass); 531 532 String result; 533 if (mMeanConfidence < CONFIDENCE_THRESHOLD) { 534 result = String.format( 535 "Test Finished\nInsufficient Confidence (%.2f < %.2f). No Results.", 536 mMeanConfidence, CONFIDENCE_THRESHOLD); 537 } else { 538 result = String.format( 539 "Test Finished - %s\nMean Latency:%.2f ms (required:%.2f)\n" + 540 "Mean Absolute Deviation: %.2f\n" + 541 " Confidence: %.2f\n" + 542 " Low Latency Path: %s", 543 (pass ? "PASS" : "FAIL"), 544 mMeanLatencyMillis, 545 mMustLatency, 546 mMeanAbsoluteDeviation, 547 mMeanConfidence, 548 mNativeAnalyzerThread.isLowLatencyStream() ? mYesString : mNoString); 549 } 550 551 // Make sure the test thread is finished. It should already be done. 552 if (mNativeAnalyzerThread != null) { 553 try { 554 mNativeAnalyzerThread.stopTest(STOP_TEST_TIMEOUT_MSEC); 555 } catch (InterruptedException e) { 556 e.printStackTrace(); 557 } 558 } 559 mResultText.setText(result); 560 561 recordTestResults(); 562 563 showWait(false); 564 mTestButton.setEnabled(true); 565 } 566 567 private void handleTestPhaseCompletion() { 568 if (mNativeAnalyzerThread != null && mTestPhase < NUM_TEST_PHASES) { 569 mLatencyMillis[mTestPhase] = mNativeAnalyzerThread.getLatencyMillis(); 570 mConfidence[mTestPhase] = mNativeAnalyzerThread.getConfidence(); 571 572 String result = String.format( 573 "Test %d Finished\nLatency: %.2f ms\nConfidence: %.2f\n", 574 mTestPhase, 575 mLatencyMillis[mTestPhase], 576 mConfidence[mTestPhase]); 577 578 mResultText.setText(result); 579 try { 580 mNativeAnalyzerThread.stopTest(STOP_TEST_TIMEOUT_MSEC); 581 // Thread.sleep(/*STOP_TEST_TIMEOUT_MSEC*/500); 582 } catch (InterruptedException e) { 583 e.printStackTrace(); 584 } 585 586 mTestPhase++; 587 if (mTestPhase >= NUM_TEST_PHASES) { 588 handleTestCompletion(); 589 } else { 590 startTestPhase(); 591 } 592 } 593 } 594 595 /** 596 * handler for messages from audio thread 597 */ 598 private Handler mMessageHandler = new Handler() { 599 public void handleMessage(Message msg) { 600 super.handleMessage(msg); 601 switch(msg.what) { 602 case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_STARTED: 603 Log.v(TAG,"got message native rec started!!"); 604 showWait(true); 605 mResultText.setText(String.format("[phase: %d] - Test Running...", 606 (mTestPhase + 1))); 607 break; 608 case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_OPEN_ERROR: 609 Log.v(TAG,"got message native rec can't start!!"); 610 mResultText.setText("Test Error opening streams."); 611 handleTestCompletion(); 612 break; 613 case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR: 614 Log.v(TAG,"got message native rec can't start!!"); 615 mResultText.setText("Test Error while recording."); 616 handleTestCompletion(); 617 break; 618 case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE_ERRORS: 619 mResultText.setText("Test FAILED due to errors."); 620 handleTestCompletion(); 621 break; 622 case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING: 623 Log.i(TAG, "NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING"); 624 mResultText.setText(String.format("[phase: %d] - Analyzing ...", 625 mTestPhase + 1)); 626 break; 627 case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE: 628 Log.i(TAG, "NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE"); 629 handleTestPhaseCompletion(); 630 break; 631 default: 632 break; 633 } 634 } 635 }; 636 637 @Override 638 protected void onCreate(Bundle savedInstanceState) { 639 super.onCreate(savedInstanceState); 640 641 setContentView(R.layout.audio_loopback_latency_activity); 642 643 setPassFailButtonClickListeners(); 644 getPassButton().setEnabled(false); 645 setInfoResources(R.string.audio_loopback_latency_test, R.string.audio_loopback_info, -1); 646 647 mClaimsOutput = AudioSystemFlags.claimsOutput(this); 648 mClaimsInput = AudioSystemFlags.claimsInput(this); 649 mClaimsProAudio = AudioSystemFlags.claimsProAudio(this); 650 651 mYesString = getResources().getString(R.string.audio_general_yes); 652 mNoString = getResources().getString(R.string.audio_general_no); 653 654 // Pro Audio 655 ((TextView)findViewById(R.id.audio_loopback_pro_audio)).setText( 656 "" + (mClaimsProAudio ? mYesString : mNoString)); 657 658 // MMAP 659 ((TextView)findViewById(R.id.audio_loopback_mmap)).setText( 660 "" + (mSupportsMMAP ? mYesString : mNoString)); 661 ((TextView)findViewById(R.id.audio_loopback_mmap_exclusive)).setText( 662 "" + (mSupportsMMAPExclusive ? mYesString : mNoString)); 663 664 // Low Latency 665 ((TextView)findViewById(R.id.audio_loopback_low_latency)).setText( 666 "" + (AudioSystemFlags.claimsLowLatencyAudio(this) ? mYesString : mNoString)); 667 668 mTestPathTxt = ((TextView)findViewById(R.id.audio_loopback_testpath)); 669 670 mTestButton = (Button)findViewById(R.id.audio_loopback_test_btn); 671 mTestButton.setOnClickListener(mBtnClickListener); 672 673 mAudioManager = getSystemService(AudioManager.class); 674 675 mAudioManager.registerAudioDeviceCallback(new ConnectListener(), new Handler()); 676 677 connectLoopbackUI(); 678 679 calculateLatencyThresholds(); 680 displayLatencyThresholds(); 681 } 682 683 private class OnBtnClickListener implements OnClickListener { 684 @Override 685 public void onClick(View v) { 686 switch (v.getId()) { 687 case R.id.audio_loopback_test_btn: 688 Log.i(TAG, "audio loopback test"); 689 startAudioTest(mMessageHandler); 690 break; 691 } 692 } 693 } 694 } 695