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