1 /* 2 * Copyright (C) 2023 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.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.os.Bundle; 24 import android.util.Log; 25 import android.view.View; 26 import android.widget.Button; 27 import android.widget.TextView; 28 29 import com.android.cts.verifier.PassFailButtons; 30 import com.android.cts.verifier.R; 31 32 // MegaAudio 33 import org.hyphonate.megaaudio.common.BuilderBase; 34 import org.hyphonate.megaaudio.common.Globals; 35 import org.hyphonate.megaaudio.common.StreamBase; 36 import org.hyphonate.megaaudio.common.StreamState; 37 import org.hyphonate.megaaudio.player.AudioSourceProvider; 38 import org.hyphonate.megaaudio.player.OboePlayer; 39 import org.hyphonate.megaaudio.player.PlayerBuilder; 40 import org.hyphonate.megaaudio.player.sources.SilenceAudioSourceProvider; 41 import org.hyphonate.megaaudio.recorder.AudioSinkProvider; 42 import org.hyphonate.megaaudio.recorder.OboeRecorder; 43 import org.hyphonate.megaaudio.recorder.RecorderBuilder; 44 import org.hyphonate.megaaudio.recorder.sinks.NopAudioSinkProvider; 45 46 import java.util.ArrayList; 47 48 // @CddTest(requirement = "7.8.2.1/C-1-1,C-1-2,C-1-3,C-1-4,C-2-1") 49 50 /** 51 * CTS Verifier Test module for AAudio Stream Disconnect events 52 */ 53 public class AudioDisconnectActivity 54 extends PassFailButtons.Activity 55 implements View.OnClickListener { 56 private static final String TAG = AudioDisconnectActivity.class.getSimpleName(); 57 private static final boolean DEBUG = false; 58 59 private BroadcastReceiver mPluginReceiver = new PluginBroadcastReceiver(); 60 61 // MegaAudio 62 private OboePlayer mPlayer; 63 private OboeRecorder mRecorder; 64 private StreamBase mStream; 65 66 private int mNumExchangeFrames; 67 private int mSystemSampleRate; 68 69 // UI 70 private TextView mHasPortQueryText; 71 private Button mHasAnalogPortYesBtn; 72 private Button mHasAnalogPortNoBtn; 73 74 private Button mStartBtn; 75 private Button mStopBtn; 76 77 private TextView mUserPromptTx; 78 private TextView mDebugMessageTx; 79 private TextView mResultsTx; 80 81 // Test State 82 private boolean mHasHeadset; 83 private boolean mIsAudioRunning; 84 private volatile int mPlugCount; 85 86 static { StreamBase.loadMegaAudioLibrary()87 StreamBase.loadMegaAudioLibrary(); 88 } 89 90 // Lowlatency/not lowlatency 91 // MMAP/not MMAP (legacy i.e. audioflinger). 92 // Shared/Exclusive (not legacy) 93 class TestConfiguration { 94 static final int IO_INPUT = 0; 95 static final int IO_OUTPUT = 1; 96 int mDirection; 97 int mSampleRate; 98 int mNumChannels; 99 100 static final int OPTION_NONE = 0x00000000; 101 static final int OPTION_LOWLATENCY = 0x00000001; 102 static final int OPTION_EXCLUSIVE = 0x00000002; 103 static final int OPTION_MMAP = 0x00000004; 104 int mOptions; 105 106 static final int RESULT_NOTTESTED = -1; 107 static final int RESULT_TIMEOUT = -2; 108 static final int RESULT_SKIPPED = -3; 109 static final int RESULT_DETECTED = 0; // i.e. the disconnect notification was received 110 111 int mInsertPlugResult; 112 int mInsertDisconnectResult; 113 int mRemovalPlugResult; 114 int mRemovalDisconnectResult; 115 TestConfiguration(int direction, int sampleRate, int numChannels, int options)116 TestConfiguration(int direction, int sampleRate, int numChannels, int options) { 117 mDirection = direction; 118 119 mSampleRate = sampleRate; 120 mNumChannels = numChannels; 121 122 mOptions = options; 123 124 mInsertPlugResult = RESULT_NOTTESTED; 125 mInsertDisconnectResult = RESULT_NOTTESTED; 126 mRemovalPlugResult = RESULT_NOTTESTED; 127 mRemovalDisconnectResult = RESULT_NOTTESTED; 128 } 129 isLowLatency()130 boolean isLowLatency() { 131 return (mOptions & OPTION_LOWLATENCY) != 0; 132 } 133 isExclusive()134 boolean isExclusive() { 135 return (mOptions & OPTION_EXCLUSIVE) != 0; 136 } 137 isMMap()138 boolean isMMap() { 139 return (mOptions & OPTION_MMAP) != 0; 140 } 141 resultToString(int resultCode)142 static String resultToString(int resultCode) { 143 switch (resultCode) { 144 case RESULT_NOTTESTED: 145 return "NT"; 146 147 case RESULT_TIMEOUT: 148 return "TO"; 149 150 case RESULT_SKIPPED: 151 return "SK"; 152 153 case RESULT_DETECTED: 154 return "OK"; 155 156 default: 157 return "??"; 158 } 159 } 160 toString()161 public String toString() { 162 StringBuilder sb = new StringBuilder(); 163 164 sb.append("-----------\n"); 165 sb.append("" + (mDirection == TestConfiguration.IO_INPUT ? "IN" : "OUT") 166 + " " + mSampleRate + " " + mNumChannels 167 + (isLowLatency() ? " LOW" : "") 168 + (isExclusive() ? " EX" : "") 169 + (isMMap() ? " MMAP" : "") 170 + "\n"); 171 sb.append("insert:" + resultToString(mInsertPlugResult) 172 + " result:" + resultToString(mInsertDisconnectResult)); 173 sb.append(" remove:" + resultToString(mRemovalPlugResult) 174 + " result:" + resultToString(mInsertDisconnectResult) + "\n"); 175 176 return sb.toString(); 177 } 178 isPass()179 boolean isPass() { 180 return (mInsertPlugResult == RESULT_DETECTED 181 || mInsertPlugResult == RESULT_SKIPPED) 182 && (mInsertDisconnectResult == RESULT_DETECTED 183 || mInsertDisconnectResult == RESULT_SKIPPED) 184 && (mRemovalPlugResult == RESULT_DETECTED 185 || mRemovalPlugResult == RESULT_SKIPPED) 186 && (mRemovalDisconnectResult == RESULT_DETECTED 187 || mRemovalDisconnectResult == RESULT_SKIPPED); 188 } 189 setSkipped()190 void setSkipped() { 191 mInsertPlugResult = RESULT_SKIPPED; 192 mInsertDisconnectResult = RESULT_SKIPPED; 193 mRemovalPlugResult = RESULT_SKIPPED; 194 mRemovalDisconnectResult = RESULT_SKIPPED; 195 } 196 } 197 198 private ArrayList<TestConfiguration> mTestConfigs = new ArrayList<TestConfiguration>(); 199 setTestConfigs()200 void setTestConfigs() { 201 // This logic will cover the four main data paths. 202 // if (isMMapSupported) { 203 // LOWLATENCY + MMAP + EXCLUSIVE 204 // LOWLATENCY + MMAP // shared 205 // } 206 // LOWLATENCY // legacy 207 // NONE 208 209 210 // Player 211 // mTestConfigs.add(new TestConfiguration(true, false, 41000, 2)); 212 mTestConfigs.add(new TestConfiguration(TestConfiguration.IO_OUTPUT, 213 mSystemSampleRate, 2, 214 TestConfiguration.OPTION_LOWLATENCY)); 215 mTestConfigs.add(new TestConfiguration(TestConfiguration.IO_OUTPUT, 216 mSystemSampleRate, 2, 217 TestConfiguration.OPTION_LOWLATENCY 218 | TestConfiguration.OPTION_MMAP)); 219 mTestConfigs.add(new TestConfiguration(TestConfiguration.IO_OUTPUT, 220 mSystemSampleRate, 2, 221 TestConfiguration.OPTION_LOWLATENCY 222 | TestConfiguration.OPTION_MMAP 223 | TestConfiguration.OPTION_EXCLUSIVE)); 224 mTestConfigs.add(new TestConfiguration(TestConfiguration.IO_OUTPUT, 225 mSystemSampleRate, 2, 226 TestConfiguration.OPTION_NONE)); 227 228 // Recorder 229 mTestConfigs.add(new TestConfiguration(TestConfiguration.IO_INPUT, 230 mSystemSampleRate, 1, 231 TestConfiguration.OPTION_LOWLATENCY)); 232 mTestConfigs.add(new TestConfiguration(TestConfiguration.IO_INPUT, 233 mSystemSampleRate, 1, 234 TestConfiguration.OPTION_LOWLATENCY 235 | TestConfiguration.OPTION_MMAP)); 236 mTestConfigs.add(new TestConfiguration(TestConfiguration.IO_INPUT, 237 mSystemSampleRate, 1, 238 TestConfiguration.OPTION_LOWLATENCY 239 | TestConfiguration.OPTION_MMAP 240 | TestConfiguration.OPTION_EXCLUSIVE)); 241 mTestConfigs.add(new TestConfiguration(TestConfiguration.IO_INPUT, 242 mSystemSampleRate, 1, 243 TestConfiguration.OPTION_NONE)); 244 } 245 resetTestConfigs()246 void resetTestConfigs() { 247 for (TestConfiguration testConfig : mTestConfigs) { 248 testConfig.mInsertPlugResult = TestConfiguration.RESULT_NOTTESTED; 249 testConfig.mInsertDisconnectResult = TestConfiguration.RESULT_NOTTESTED; 250 testConfig.mRemovalPlugResult = TestConfiguration.RESULT_NOTTESTED; 251 testConfig.mRemovalDisconnectResult = TestConfiguration.RESULT_NOTTESTED; 252 } 253 } 254 setTextMessage(TextView textView, String message)255 void setTextMessage(TextView textView, String message) { 256 runOnUiThread(new Runnable() { 257 @Override 258 public void run() { 259 textView.setText(message); 260 } 261 }); 262 } 263 264 class Tester implements Runnable { runTest()265 private void runTest() { 266 boolean abortTest = false; 267 int timeoutCount; 268 mPlugCount = 0; 269 270 for (int testConfigIndex = 0; 271 testConfigIndex < mTestConfigs.size(); 272 testConfigIndex++) { 273 TestConfiguration testConfig = mTestConfigs.get(testConfigIndex); 274 275 if (testConfig.isMMap() || testConfig.isExclusive()) { 276 if (!Globals.isMMapSupported()) { 277 testConfig.setSkipped(); 278 continue; 279 } 280 } 281 startAudio(testConfig); 282 283 // Wait for stream to start... 284 setTextMessage(mUserPromptTx, "Waiting for stream to start."); 285 try { 286 int oldPlugCount; 287 int error; 288 289 // 290 // Wait for Stream to start 291 // 292 timeoutCount = TIME_TO_FAILURE_MILLIS / POLL_DURATION_MILLIS; 293 while (!abortTest && timeoutCount-- > 0 294 && mStream.getStreamState() != StreamState.STARTED) { 295 setTextMessage(mDebugMessageTx, "Waiting for stream to start. state:" 296 + mStream.getStreamState() + " count:" + timeoutCount); 297 Thread.sleep(POLL_DURATION_MILLIS); 298 } 299 if (timeoutCount <= 0) { 300 setTextMessage(mUserPromptTx, "TIMEOUT waiting for stream to start"); 301 abortTest = true; 302 break; 303 } 304 305 // 306 // Prompt for headset connect 307 // 308 setTextMessage(mUserPromptTx, "Insert headset now!"); 309 310 // Wait for plug count to change 311 oldPlugCount = mPlugCount; 312 timeoutCount = TIME_TO_FAILURE_MILLIS / POLL_DURATION_MILLIS; 313 while (!abortTest && timeoutCount-- > 0 && mPlugCount == oldPlugCount) { 314 setTextMessage(mDebugMessageTx, "Waiting for plug event " 315 + mPlugCount + ":" + oldPlugCount + " count: " + timeoutCount); 316 Thread.sleep(POLL_DURATION_MILLIS); 317 } 318 if (timeoutCount <= 0) { 319 setTextMessage(mUserPromptTx, "TIMEOUT waiting for plug event"); 320 testConfig.mInsertPlugResult = TestConfiguration.RESULT_TIMEOUT; 321 abortTest = true; 322 break; 323 } 324 325 testConfig.mInsertPlugResult = TestConfiguration.RESULT_DETECTED; 326 327 // Wait for stream to disconnect. 328 timeoutCount = TIME_TO_FAILURE_MILLIS / POLL_DURATION_MILLIS; 329 while (!abortTest && (timeoutCount > 0) 330 && mStream.getStreamState() == StreamState.STARTED) { 331 setTextMessage(mDebugMessageTx, "state:" + mStream.getStreamState() 332 + " count:" + timeoutCount); 333 Thread.sleep(POLL_DURATION_MILLIS); 334 timeoutCount--; 335 } 336 if (timeoutCount <= 0) { 337 setTextMessage(mUserPromptTx, "TIMEOUT waiting for disconnect"); 338 testConfig.mInsertPlugResult = TestConfiguration.RESULT_TIMEOUT; 339 abortTest = true; 340 break; 341 } 342 343 error = mStream.getLastErrorCallbackResult(); 344 if (error != OboePlayer.ERROR_DISCONNECTED) { 345 // Need to address this 346 abortTest = true; 347 } 348 testConfig.mInsertDisconnectResult = TestConfiguration.RESULT_DETECTED; 349 350 // need to restart the stream 351 restartAudio(testConfig); 352 353 // 354 // Prompt for headset Remove 355 // 356 setTextMessage(mUserPromptTx, "Remove headset now!"); 357 358 // Wait for plug count to change 359 oldPlugCount = mPlugCount; 360 timeoutCount = TIME_TO_FAILURE_MILLIS / POLL_DURATION_MILLIS; 361 while (!abortTest && timeoutCount-- > 0 && mPlugCount == oldPlugCount) { 362 setTextMessage(mDebugMessageTx, "Waiting for plug event " 363 + mPlugCount + ":" + oldPlugCount + " count: " + timeoutCount); 364 Thread.sleep(POLL_DURATION_MILLIS); 365 } 366 if (timeoutCount <= 0) { 367 setTextMessage(mUserPromptTx, "TIMEOUT waiting for plug event"); 368 testConfig.mRemovalPlugResult = TestConfiguration.RESULT_TIMEOUT; 369 abortTest = true; 370 break; 371 } 372 373 testConfig.mRemovalPlugResult = TestConfiguration.RESULT_DETECTED; 374 375 // Wait for stream to disconnect. 376 timeoutCount = TIME_TO_FAILURE_MILLIS / POLL_DURATION_MILLIS; 377 while (!abortTest && (timeoutCount > 0) 378 && mStream.getStreamState() == StreamState.STARTED) { 379 setTextMessage(mDebugMessageTx, "state:" + mStream.getStreamState() 380 + " count:" + timeoutCount); 381 Thread.sleep(POLL_DURATION_MILLIS); 382 timeoutCount--; 383 } 384 if (timeoutCount <= 0) { 385 setTextMessage(mUserPromptTx, "TIMEOUT waiting for disconnect"); 386 testConfig.mRemovalDisconnectResult = TestConfiguration.RESULT_TIMEOUT; 387 abortTest = true; 388 break; 389 } 390 391 error = mStream.getLastErrorCallbackResult(); 392 if (error != OboePlayer.ERROR_DISCONNECTED) { 393 // Need to address this 394 } 395 testConfig.mRemovalDisconnectResult = TestConfiguration.RESULT_DETECTED; 396 397 } catch (InterruptedException ex) { 398 Log.e(TAG, "InterruptedException: " + ex); 399 abortTest = true; 400 } 401 runOnUiThread(new Runnable() { 402 @Override 403 public void run() { 404 showTestResults(); 405 } 406 }); 407 stopAudio(); 408 } // while (true) 409 410 endTest(); 411 } 412 run()413 public void run() { 414 runTest(); 415 } 416 } 417 418 // 419 // Test Process 420 // startTest()421 void startTest() { 422 resetTestConfigs(); 423 424 enableTestButtons(false, true); 425 426 (new Thread(new Tester())).start(); 427 } 428 endTest()429 void endTest() { 430 showTestResults(); 431 runOnUiThread(new Runnable() { 432 @Override 433 public void run() { 434 mDebugMessageTx.setText(""); 435 enableTestButtons(true, false); 436 boolean passed = calcTestPass(); 437 getPassButton().setEnabled(passed); 438 String passStr = getResources().getString( 439 passed ? R.string.audio_general_teststatus_pass 440 : R.string.audio_general_teststatus_fail); 441 mUserPromptTx.setText(passStr); 442 } 443 }); 444 } 445 showTestResults()446 void showTestResults() { 447 StringBuilder sb = new StringBuilder(); 448 449 for (TestConfiguration testConfig : mTestConfigs) { 450 if (testConfig.mInsertPlugResult != TestConfiguration.RESULT_NOTTESTED 451 || testConfig.mRemovalPlugResult != TestConfiguration.RESULT_NOTTESTED) { 452 sb.append(testConfig.toString()); 453 } 454 } 455 456 runOnUiThread(new Runnable() { 457 @Override 458 public void run() { 459 mResultsTx.setText(sb.toString()); 460 } 461 }); 462 } 463 calcTestPass()464 boolean calcTestPass() { 465 for (TestConfiguration testConfig : mTestConfigs) { 466 if (!testConfig.isPass()) { 467 return false; 468 } 469 } 470 return true; 471 } 472 AudioDisconnectActivity()473 public AudioDisconnectActivity() { 474 super(); 475 } 476 477 // Test Phases 478 private static final int TESTPHASE_NONE = -1; 479 private static final int TESTPHASE_WAITFORSTART = 0; 480 private static final int TESTPHASE_WAITFORCONNECT = 1; 481 private int mTestPhase = TESTPHASE_NONE; 482 483 // Test Parameters 484 public static final int POLL_DURATION_MILLIS = 50; 485 public static final int TIME_TO_FAILURE_MILLIS = 10000; 486 487 @Override onCreate(Bundle savedInstanceState)488 protected void onCreate(Bundle savedInstanceState) { 489 setContentView(R.layout.audio_disconnect_activity); 490 super.onCreate(savedInstanceState); 491 492 setInfoResources(R.string.audio_disconnect_test, R.string.audio_disconnect_info, -1); 493 494 // Analog Port? 495 mHasPortQueryText = (TextView) findViewById(R.id.analog_headset_query); 496 mHasAnalogPortYesBtn = (Button) findViewById(R.id.headset_analog_port_yes); 497 mHasAnalogPortYesBtn.setOnClickListener(this); 498 mHasAnalogPortNoBtn = (Button) findViewById(R.id.headset_analog_port_no); 499 mHasAnalogPortNoBtn.setOnClickListener(this); 500 501 (mStartBtn = (Button) findViewById(R.id.connection_start_btn)).setOnClickListener(this); 502 (mStopBtn = (Button) findViewById(R.id.connection_stop_btn)).setOnClickListener(this); 503 504 mUserPromptTx = (TextView) findViewById(R.id.user_prompt_tx); 505 mDebugMessageTx = (TextView) findViewById(R.id.debug_message_tx); 506 mResultsTx = (TextView) findViewById(R.id.results_tx); 507 508 setPassFailButtonClickListeners(); 509 getPassButton().setEnabled(false); 510 511 StreamBase.setup(this); 512 mSystemSampleRate = StreamBase.getSystemSampleRate(); 513 mNumExchangeFrames = StreamBase.getNumBurstFrames(BuilderBase.TYPE_NONE); 514 515 setTestConfigs(); 516 517 enableTestButtons(false, false); 518 } 519 520 @Override onResume()521 public void onResume() { 522 super.onResume(); 523 IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); 524 this.registerReceiver(mPluginReceiver, filter); 525 } 526 527 @Override onPause()528 public void onPause() { 529 this.unregisterReceiver(mPluginReceiver); 530 super.onPause(); 531 } 532 533 // 534 // PassFailButtons Overrides 535 // startAudio(TestConfiguration config)536 private boolean startAudio(TestConfiguration config) { 537 Log.i(TAG, "startAudio()..."); 538 if (mIsAudioRunning) { 539 stopAudio(); 540 } 541 542 boolean wasMMapEnabled = Globals.isMMapEnabled(); 543 Globals.setMMapEnabled(config.isMMap()); 544 if (config.mDirection == TestConfiguration.IO_OUTPUT) { 545 AudioSourceProvider sourceProvider = new SilenceAudioSourceProvider(); 546 try { 547 PlayerBuilder playerBuilder = new PlayerBuilder(); 548 playerBuilder.setPerformanceMode(config.isLowLatency() 549 ? BuilderBase.PERFORMANCE_MODE_LOWLATENCY 550 : BuilderBase.PERFORMANCE_MODE_NONE); 551 playerBuilder.setSharingMode(config.isExclusive() 552 ? BuilderBase.SHARING_MODE_EXCLUSIVE 553 : BuilderBase.SHARING_MODE_SHARED); 554 playerBuilder.setChannelCount(config.mNumChannels); 555 playerBuilder.setSampleRate(config.mSampleRate); 556 playerBuilder.setSourceProvider(sourceProvider); 557 playerBuilder.setPlayerType(BuilderBase.TYPE_OBOE); 558 mPlayer = (OboePlayer) playerBuilder.build(); 559 mPlayer.startStream(); 560 mIsAudioRunning = true; 561 mStream = mPlayer; 562 } catch (PlayerBuilder.BadStateException badStateException) { 563 Log.e(TAG, "BadStateException: " + badStateException); 564 mIsAudioRunning = false; 565 } 566 } else { 567 AudioSinkProvider sinkProvider = new NopAudioSinkProvider(); 568 try { 569 RecorderBuilder recorderBuilder = new RecorderBuilder(); 570 recorderBuilder.setRecorderType(BuilderBase.TYPE_OBOE); 571 recorderBuilder.setAudioSinkProvider(sinkProvider); 572 recorderBuilder.setChannelCount(config.mNumChannels); 573 recorderBuilder.setSampleRate(config.mSampleRate); 574 recorderBuilder.setChannelCount(config.mNumChannels); 575 recorderBuilder.setNumExchangeFrames(mNumExchangeFrames); 576 recorderBuilder.setPerformanceMode(config.isLowLatency() 577 ? BuilderBase.PERFORMANCE_MODE_LOWLATENCY 578 : BuilderBase.PERFORMANCE_MODE_NONE); 579 recorderBuilder.setSharingMode(config.isExclusive() 580 ? BuilderBase.SHARING_MODE_EXCLUSIVE 581 : BuilderBase.SHARING_MODE_SHARED); 582 mRecorder = (OboeRecorder) recorderBuilder.build(); 583 mRecorder.startStream(); 584 mIsAudioRunning = true; 585 mStream = mRecorder; 586 } catch (RecorderBuilder.BadStateException badStateException) { 587 Log.e(TAG, "BadStateException: " + badStateException); 588 mIsAudioRunning = false; 589 } 590 } 591 Globals.setMMapEnabled(wasMMapEnabled); 592 593 Log.i(TAG, " mIsAudioRunning: " + mIsAudioRunning); 594 return mIsAudioRunning; 595 } 596 restartAudio(TestConfiguration config)597 private boolean restartAudio(TestConfiguration config) { 598 mIsAudioRunning = false; 599 return startAudio(config); 600 } 601 stopAudio()602 private void stopAudio() { 603 if (!mIsAudioRunning) { 604 return; 605 } 606 607 if (mPlayer != null) { 608 mPlayer.stopStream(); 609 mPlayer.teardownStream(); 610 } 611 612 if (mRecorder != null) { 613 mRecorder.stopStream(); 614 mRecorder.teardownStream(); 615 } 616 617 mIsAudioRunning = false; 618 } 619 enableTestButtons(boolean start, boolean stop)620 void enableTestButtons(boolean start, boolean stop) { 621 mStartBtn.setEnabled(start); 622 mStopBtn.setEnabled(stop); 623 } 624 hideHasHeadsetUI()625 void hideHasHeadsetUI() { 626 mHasPortQueryText.setText(getResources().getString( 627 R.string.analog_headset_port_detected)); 628 mHasAnalogPortYesBtn.setVisibility(View.GONE); 629 mHasAnalogPortNoBtn.setVisibility(View.GONE); 630 } 631 632 // 633 // View.OnClickHandler 634 // 635 @Override onClick(View view)636 public void onClick(View view) { 637 int id = view.getId(); 638 if (id == R.id.headset_analog_port_yes) { 639 enableTestButtons(true, false); 640 } else if (id == R.id.headset_analog_port_no) { 641 String passStr = getResources().getString( 642 R.string.audio_general_teststatus_pass); 643 mUserPromptTx.setText(passStr); 644 getPassButton().setEnabled(true); 645 enableTestButtons(false, false); 646 } else if (id == R.id.connection_start_btn) { 647 mResultsTx.setText(""); 648 startTest(); 649 } else if (id == R.id.connection_stop_btn) { 650 stopAudio(); 651 } 652 } 653 654 /** 655 * Receive a broadcast Intent when a headset is plugged in or unplugged. 656 * Display a count on screen. 657 */ 658 public class PluginBroadcastReceiver extends BroadcastReceiver { 659 @Override onReceive(Context context, Intent intent)660 public void onReceive(Context context, Intent intent) { 661 if (!mHasHeadset) { 662 mHasHeadset = true; 663 hideHasHeadsetUI(); 664 enableTestButtons(true, false); 665 } 666 mPlugCount++; 667 } 668 } 669 } 670