Merge "Revert "Camera: Collect MPC test results in CTS and ITS"" am: de559c6929
Original change: https://android-review.googlesource.com/c/platform/cts/+/1959731
Change-Id: Icf0b76a4b7a93302c092b27729b21a51cbc68729
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
index b321c60..4bd642d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
@@ -70,10 +70,7 @@
private static final String INFO_DIALOG_MESSAGE_ID = "infoDialogMessageId";
// ReportLog file for CTS-Verifier. The "stream" name gets mapped to the test class name.
- public static final String GENERAL_TESTS_REPORT_LOG_NAME = "CtsVerifierGeneralTestCases";
- public static final String AUDIO_TESTS_REPORT_LOG_NAME = "CtsVerifierAudioTestCases";
-
- private static final String SECTION_UNDEFINED = "undefined_section_name";
+ private static final String REPORT_LOG_NAME = "CTS-Verifier-Log";
// Interface mostly for making documentation and refactoring easier...
public interface PassFailActivity {
@@ -115,16 +112,10 @@
void setTestResultAndFinish(boolean passed);
/**
- * @return The name of the file to store the (suite of) ReportLog information.
+ * @return A unique name (derived from the test class name) to serve as a section
+ * header in the CtsVerifierReportLog file.
*/
- public String getReportFileName();
-
- /**
- * @return A unique name to serve as a section header in the CtsVerifierReportLog file.
- * Tests need to conform to the underscore_delineated_name standard for use with
- * the protobuff/json ReportLog parsing in Google3
- */
- public String getReportSectionName();
+ String getReportSectionName();
/**
* Test subclasses can override this to record their CtsVerifierReportLogs.
@@ -147,7 +138,7 @@
private final TestResultHistoryCollection mHistoryCollection;
public Activity() {
- this.mReportLog = new CtsVerifierReportLog(getReportFileName(), getReportSectionName());
+ this.mReportLog = new CtsVerifierReportLog(REPORT_LOG_NAME, getReportSectionName());
this.mHistoryCollection = new TestResultHistoryCollection();
}
@@ -211,15 +202,9 @@
return mReportLog;
}
- /**
- * @return The name of the file to store the (suite of) ReportLog information.
- */
@Override
- public String getReportFileName() { return GENERAL_TESTS_REPORT_LOG_NAME; }
-
- @Override
- public String getReportSectionName() {
- return setTestNameSuffix(sCurrentDisplayMode, SECTION_UNDEFINED);
+ public final String getReportSectionName() {
+ return setTestNameSuffix(sCurrentDisplayMode, getClass().getName());
}
@Override
@@ -255,7 +240,7 @@
private final TestResultHistoryCollection mHistoryCollection;
public ListActivity() {
- this.mReportLog = new CtsVerifierReportLog(getReportFileName(), getReportSectionName());
+ this.mReportLog = new CtsVerifierReportLog(REPORT_LOG_NAME, getReportSectionName());
this.mHistoryCollection = new TestResultHistoryCollection();
}
@@ -301,15 +286,9 @@
return mReportLog;
}
- /**
- * @return The name of the file to store the (suite of) ReportLog information.
- */
@Override
- public String getReportFileName() { return GENERAL_TESTS_REPORT_LOG_NAME; }
-
- @Override
- public String getReportSectionName() {
- return setTestNameSuffix(sCurrentDisplayMode, SECTION_UNDEFINED);
+ public final String getReportSectionName() {
+ return setTestNameSuffix(sCurrentDisplayMode, getClass().getName());
}
@Override
@@ -347,7 +326,9 @@
public TestListActivity() {
// TODO(b/186555602): temporary hack^H^H^H^H workaround to fix crash
// This DOES NOT in fact fix that bug.
- this.mReportLog = new CtsVerifierReportLog(getReportFileName(), getReportSectionName());
+ // if (true) this.mReportLog = new CtsVerifierReportLog(REPORT_LOG_NAME, "42"); else
+
+ this.mReportLog = new CtsVerifierReportLog(REPORT_LOG_NAME, getReportSectionName());
}
@Override
@@ -392,18 +373,11 @@
return mReportLog;
}
- /**
- * @return The name of the file to store the (suite of) ReportLog information.
- */
@Override
- public String getReportFileName() { return GENERAL_TESTS_REPORT_LOG_NAME; }
-
- @Override
- public String getReportSectionName() {
- return setTestNameSuffix(sCurrentDisplayMode, SECTION_UNDEFINED);
+ public final String getReportSectionName() {
+ return setTestNameSuffix(sCurrentDisplayMode, getClass().getName());
}
-
/**
* Get existing test history to aggregate.
*/
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java b/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java
index 495c3ef..fe314c4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java
@@ -23,7 +23,6 @@
import android.os.Environment;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
-import android.util.Log;
import com.android.compatibility.common.util.FileUtil;
import com.android.compatibility.common.util.IInvocationResult;
@@ -50,8 +49,6 @@
* Background task to generate a report and save it to external storage.
*/
class ReportExporter extends AsyncTask<Void, Void, String> {
- private static final String TAG = ReportExporter.class.getSimpleName();
- private static final boolean DEBUG = true;
public static final String REPORT_DIRECTORY = "VerifierReports";
public static final String LOGS_DIRECTORY = "ReportLogFiles";
@@ -79,15 +76,11 @@
// so that they will get ZIPped into the transmitted file.
//
private void copyReportFiles(File tempDir) {
- if (DEBUG) {
- Log.d(TAG, "copyReportFiles(" + tempDir.getAbsolutePath() + ")");
- }
-
File externalStorageDirectory = Environment.getExternalStorageDirectory();
File reportLogFolder =
new File(Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator
- + LOGS_DIRECTORY);
+ + REPORT_DIRECTORY);
File[] reportLogFiles = reportLogFolder.listFiles();
// if no ReportLog files have been created (i.e. the folder doesn't exist)
@@ -163,9 +156,6 @@
}
private void saveReportOnInternalStorage(File reportZipFile) {
- if (DEBUG) {
- Log.d(TAG, "---- saveReportOnInternalStorage(" + reportZipFile.getAbsolutePath() + ")");
- }
try {
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
reportZipFile, ParcelFileDescriptor.MODE_READ_ONLY);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java
index 6233f2c..4ce2a12 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java
@@ -400,8 +400,6 @@
}
}
- reportHeadsetPort(mHeadsetDeviceInfo != null);
-
showConnectedDevice();
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackBaseActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackBaseActivity.java
new file mode 100644
index 0000000..78e34d3
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackBaseActivity.java
@@ -0,0 +1,686 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.audio;
+
+import android.app.AlertDialog;
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.MediaRecorder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+import com.android.cts.verifier.audio.audiolib.AudioSystemFlags;
+import com.android.cts.verifier.audio.audiolib.StatUtils;
+import com.android.cts.verifier.audio.audiolib.AudioUtils;
+import com.android.cts.verifier.CtsVerifierReportLog;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import static com.android.cts.verifier.TestListActivity.sCurrentDisplayMode;
+import static com.android.cts.verifier.TestListAdapter.setTestNameSuffix;
+
+/**
+ * Base class for testing activitiees that require audio loopback hardware..
+ */
+public class AudioLoopbackBaseActivity extends PassFailButtons.Activity {
+ private static final String TAG = "AudioLoopbackBaseActivity";
+
+ // JNI load
+ static {
+ try {
+ System.loadLibrary("audioloopback_jni");
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(TAG, "Error loading Audio Loopback JNI library");
+ Log.e(TAG, "e: " + e);
+ e.printStackTrace();
+ }
+
+ /* TODO: gracefully fail/notify if the library can't be loaded */
+ }
+ protected AudioManager mAudioManager;
+
+ // UI
+ TextView mInputDeviceTxt;
+ TextView mOutputDeviceTxt;
+
+ TextView mAudioLevelText;
+ SeekBar mAudioLevelSeekbar;
+
+ TextView mTestPathTxt;
+
+ TextView mResultText;
+ ProgressBar mProgressBar;
+ int mMaxLevel;
+
+ OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+ protected Button mTestButton;
+
+ String mYesString;
+ String mNoString;
+
+ // These flags determine the maximum allowed latency
+ private boolean mClaimsProAudio;
+ private boolean mClaimsOutput;
+ private boolean mClaimsInput;
+
+ // Useful info
+ private boolean mSupportsMMAP = AudioUtils.isMMapSupported();
+ private boolean mSupportsMMAPExclusive = AudioUtils.isMMapExclusiveSupported();
+
+ // Peripheral(s)
+ boolean mIsPeripheralAttached; // CDD ProAudio section C-1-3
+ AudioDeviceInfo mOutputDevInfo;
+ AudioDeviceInfo mInputDevInfo;
+
+ protected static final int TESTPERIPHERAL_INVALID = -1;
+ protected static final int TESTPERIPHERAL_NONE = 0;
+ protected static final int TESTPERIPHERAL_ANALOG_JACK = 1;
+ protected static final int TESTPERIPHERAL_USB = 2;
+ protected static final int TESTPERIPHERAL_DEVICE = 3; // device speaker + mic
+ protected int mTestPeripheral = TESTPERIPHERAL_NONE;
+
+ // Loopback Logic
+ NativeAnalyzerThread mNativeAnalyzerThread = null;
+
+ protected static final int NUM_TEST_PHASES = 5;
+ protected int mTestPhase = 0;
+
+ protected double[] mLatencyMillis = new double[NUM_TEST_PHASES];
+ protected double[] mConfidence = new double[NUM_TEST_PHASES];
+
+ protected double mMeanLatencyMillis;
+ protected double mMeanAbsoluteDeviation;
+ protected double mMeanConfidence;
+
+ protected static final double CONFIDENCE_THRESHOLD = 0.6;
+ // impossibly low latencies (indicating something in the test went wrong).
+ protected static final float EPSILON = 1.0f;
+ protected static final double PROAUDIO_RECOMMENDED_LATENCY_MS = 20.0;
+ protected static final double PROAUDIO_RECOMMENDED_USB_LATENCY_MS = 25.0;
+ protected static final double PROAUDIO_MUST_LATENCY_MS = 20.0;
+ protected static final double BASIC_RECOMMENDED_LATENCY_MS = 50.0;
+ protected static final double BASIC_MUST_LATENCY_MS = 800.0;
+ protected double mMustLatency;
+ protected double mRecommendedLatency;
+
+ // The audio stream callback threads should stop and close
+ // in less than a few hundred msec. This is a generous timeout value.
+ private static final int STOP_TEST_TIMEOUT_MSEC = 2 * 1000;
+
+ //
+ // Common UI Handling
+ //
+ private void connectLoopbackUI() {
+ // Connected Device
+ mInputDeviceTxt = ((TextView)findViewById(R.id.audioLoopbackInputLbl));
+ mOutputDeviceTxt = ((TextView)findViewById(R.id.audioLoopbackOutputLbl));
+
+ mAudioLevelText = (TextView)findViewById(R.id.audio_loopback_level_text);
+ mAudioLevelSeekbar = (SeekBar)findViewById(R.id.audio_loopback_level_seekbar);
+ mMaxLevel = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ mAudioLevelSeekbar.setMax(mMaxLevel);
+ mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (int)(0.7 * mMaxLevel), 0);
+ refreshLevel();
+
+ mAudioLevelSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
+ progress, 0);
+ Log.i(TAG,"Level set to: " + progress);
+ refreshLevel();
+ }
+ });
+
+ mResultText = (TextView)findViewById(R.id.audio_loopback_results_text);
+ mProgressBar = (ProgressBar)findViewById(R.id.audio_loopback_progress_bar);
+ showWait(false);
+ }
+
+ //
+ // Peripheral Connection Logic
+ //
+ protected void scanPeripheralList(AudioDeviceInfo[] devices) {
+ // CDD Section C-1-3: USB port, host-mode support
+
+ // Can't just use the first record because then we will only get
+ // Source OR sink, not both even on devices that are both.
+ mOutputDevInfo = null;
+ mInputDevInfo = null;
+
+ // Any valid peripherals
+ // Do we leave in the Headset test to support a USB-Dongle?
+ for (AudioDeviceInfo devInfo : devices) {
+ if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE || // USB Peripheral
+ devInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET || // USB dongle+LBPlug
+ devInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET || // Loopback Plug?
+ devInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) { // Aux-cable loopback?
+ if (devInfo.isSink()) {
+ mOutputDevInfo = devInfo;
+ }
+ if (devInfo.isSource()) {
+ mInputDevInfo = devInfo;
+ }
+ } else {
+ handleDeviceConnection(devInfo);
+ }
+ }
+
+ // need BOTH input and output to test
+ mIsPeripheralAttached = mOutputDevInfo != null && mInputDevInfo != null;
+ calculateTestPeripheral();
+ showConnectedAudioPeripheral();
+ calculateLatencyThresholds();
+ displayLatencyThresholds();
+ }
+
+ protected void handleDeviceConnection(AudioDeviceInfo deviceInfo) {
+ // NOP
+ }
+
+ private class ConnectListener extends AudioDeviceCallback {
+ /*package*/ ConnectListener() {}
+
+ //
+ // AudioDevicesManager.OnDeviceConnectionListener
+ //
+ @Override
+ public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+ scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
+ }
+
+ @Override
+ public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+ scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
+ }
+ }
+
+ private void calculateTestPeripheral() {
+ if (!mIsPeripheralAttached) {
+ if ((mOutputDevInfo != null && mInputDevInfo == null)
+ || (mOutputDevInfo == null && mInputDevInfo != null)) {
+ mTestPeripheral = TESTPERIPHERAL_INVALID;
+ } else {
+ mTestPeripheral = TESTPERIPHERAL_DEVICE;
+ }
+ } else if (!areIODevicesOnePeripheral()) {
+ mTestPeripheral = TESTPERIPHERAL_INVALID;
+ } else if (mInputDevInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE ||
+ mInputDevInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET) {
+ mTestPeripheral = TESTPERIPHERAL_USB;
+ } else if (mInputDevInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET ||
+ mInputDevInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) {
+ mTestPeripheral = TESTPERIPHERAL_ANALOG_JACK;
+ } else {
+ // Huh?
+ Log.e(TAG, "No valid peripheral found!?");
+ mTestPeripheral = TESTPERIPHERAL_NONE;
+ }
+ }
+
+ private boolean isPeripheralValidForTest() {
+ return mTestPeripheral == TESTPERIPHERAL_ANALOG_JACK
+ || mTestPeripheral == TESTPERIPHERAL_USB;
+ }
+
+ private void showConnectedAudioPeripheral() {
+ mInputDeviceTxt.setText(
+ mInputDevInfo != null ? mInputDevInfo.getProductName().toString()
+ : "Not connected");
+ mOutputDeviceTxt.setText(
+ mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString()
+ : "Not connected");
+
+ String pathName;
+ switch (mTestPeripheral) {
+ case TESTPERIPHERAL_INVALID:
+ pathName = "Invalid Test Peripheral";
+ break;
+
+ case TESTPERIPHERAL_ANALOG_JACK:
+ pathName = "Headset Jack";
+ break;
+
+ case TESTPERIPHERAL_USB:
+ pathName = "USB";
+ break;
+
+ case TESTPERIPHERAL_DEVICE:
+ pathName = "Device Speaker + Microphone";
+ break;
+
+ case TESTPERIPHERAL_NONE:
+ default:
+ pathName = "Error. Unknown Test Path";
+ break;
+ }
+ mTestPathTxt.setText(pathName);
+ mTestButton.setEnabled(
+ mTestPeripheral != TESTPERIPHERAL_INVALID && mTestPeripheral != TESTPERIPHERAL_NONE);
+
+ }
+
+ private boolean areIODevicesOnePeripheral() {
+ if (mOutputDevInfo == null || mInputDevInfo == null) {
+ return false;
+ }
+
+ return mOutputDevInfo.getProductName().toString().equals(
+ mInputDevInfo.getProductName().toString());
+ }
+
+ private void calculateLatencyThresholds() {
+ switch (mTestPeripheral) {
+ case TESTPERIPHERAL_ANALOG_JACK:
+ mRecommendedLatency = mClaimsProAudio
+ ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
+ mMustLatency = mClaimsProAudio
+ ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_MUST_LATENCY_MS;
+ break;
+
+ case TESTPERIPHERAL_USB:
+ mRecommendedLatency = mClaimsProAudio
+ ? PROAUDIO_RECOMMENDED_USB_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
+ mMustLatency = mClaimsProAudio
+ ? PROAUDIO_RECOMMENDED_USB_LATENCY_MS : BASIC_MUST_LATENCY_MS;
+ break;
+
+ case TESTPERIPHERAL_DEVICE:
+ // This isn't a valid case so we won't pass it, but it can be run
+ mRecommendedLatency = mClaimsProAudio
+ ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
+ mMustLatency = mClaimsProAudio
+ ? PROAUDIO_RECOMMENDED_LATENCY_MS :BASIC_MUST_LATENCY_MS;
+ break;
+
+ case TESTPERIPHERAL_NONE:
+ default:
+ mRecommendedLatency = BASIC_RECOMMENDED_LATENCY_MS;
+ mMustLatency = BASIC_MUST_LATENCY_MS;
+ break;
+ }
+ }
+
+ private void displayLatencyThresholds() {
+ if (isPeripheralValidForTest()) {
+ ((TextView) findViewById(R.id.audio_loopback_must_latency)).setText("" + mMustLatency);
+ ((TextView) findViewById(R.id.audio_loopback_recommended_latency)).setText(
+ "" + mRecommendedLatency);
+ } else {
+ String naStr = getResources().getString(R.string.audio_proaudio_NA);
+ ((TextView) findViewById(R.id.audio_loopback_must_latency)).setText(naStr);
+ ((TextView) findViewById(R.id.audio_loopback_recommended_latency)).setText(naStr);
+ }
+ }
+
+ /**
+ * refresh Audio Level seekbar and text
+ */
+ private void refreshLevel() {
+ int currentLevel = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
+ mAudioLevelSeekbar.setProgress(currentLevel);
+
+ String levelText = String.format("%s: %d/%d",
+ getResources().getString(R.string.audio_loopback_level_text),
+ currentLevel, mMaxLevel);
+ mAudioLevelText.setText(levelText);
+ }
+
+ //
+ // show active progress bar
+ //
+ protected void showWait(boolean show) {
+ mProgressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ //
+ // Common loging
+ //
+ // Schema
+ private static final String KEY_LATENCY = "latency";
+ private static final String KEY_CONFIDENCE = "confidence";
+ private static final String KEY_SAMPLE_RATE = "sample_rate";
+ private static final String KEY_IS_PRO_AUDIO = "is_pro_audio";
+ private static final String KEY_IS_LOW_LATENCY = "is_low_latency";
+ private static final String KEY_IS_PERIPHERAL_ATTACHED = "is_peripheral_attached";
+ private static final String KEY_INPUT_PERIPHERAL_NAME = "input_peripheral";
+ private static final String KEY_OUTPUT_PERIPHERAL_NAME = "output_peripheral";
+ private static final String KEY_TEST_PERIPHERAL = "test_peripheral";
+ private static final String KEY_TEST_MMAP = "supports_mmap";
+ private static final String KEY_TEST_MMAPEXCLUSIVE = "supports_mmap_exclusive";
+ private static final String KEY_LEVEL = "level";
+ private static final String KEY_BUFFER_SIZE = "buffer_size_in_frames";
+
+ @Override
+ public String getTestId() {
+ return setTestNameSuffix(sCurrentDisplayMode, getClass().getName());
+ }
+
+ //
+ // Subclasses should call this explicitly. SubClasses should call submit() after their logs
+ //
+ @Override
+ public void recordTestResults() {
+ if (mNativeAnalyzerThread == null) {
+ return; // no results to report
+ }
+
+ CtsVerifierReportLog reportLog = getReportLog();
+ reportLog.addValue(
+ KEY_LATENCY,
+ mMeanLatencyMillis,
+ ResultType.LOWER_BETTER,
+ ResultUnit.MS);
+
+ reportLog.addValue(
+ KEY_CONFIDENCE,
+ mMeanConfidence,
+ ResultType.HIGHER_BETTER,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_SAMPLE_RATE,
+ mNativeAnalyzerThread.getSampleRate(),
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_IS_LOW_LATENCY,
+ mNativeAnalyzerThread.isLowLatencyStream(),
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_IS_PERIPHERAL_ATTACHED,
+ mIsPeripheralAttached,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_IS_PRO_AUDIO,
+ mClaimsProAudio,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_TEST_PERIPHERAL,
+ mTestPeripheral,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_TEST_MMAP,
+ mSupportsMMAP,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_TEST_MMAPEXCLUSIVE ,
+ mSupportsMMAPExclusive,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ if (mIsPeripheralAttached) {
+ reportLog.addValue(
+ KEY_INPUT_PERIPHERAL_NAME,
+ mInputDevInfo != null ? mInputDevInfo.getProductName().toString() : "None",
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_OUTPUT_PERIPHERAL_NAME,
+ mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString() : "None",
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ }
+
+ int audioLevel = mAudioLevelSeekbar.getProgress();
+ reportLog.addValue(
+ KEY_LEVEL,
+ audioLevel,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.submit();
+ }
+
+ private void startAudioTest(Handler messageHandler) {
+ getPassButton().setEnabled(false);
+
+ mTestPhase = 0;
+ java.util.Arrays.fill(mLatencyMillis, 0.0);
+ java.util.Arrays.fill(mConfidence, 0.0);
+
+ mNativeAnalyzerThread = new NativeAnalyzerThread(this);
+ if (mNativeAnalyzerThread != null) {
+ mNativeAnalyzerThread.setMessageHandler(messageHandler);
+ // This value matches AAUDIO_INPUT_PRESET_VOICE_RECOGNITION
+ mNativeAnalyzerThread.setInputPreset(MediaRecorder.AudioSource.VOICE_RECOGNITION);
+ startTestPhase();
+ } else {
+ Log.e(TAG, "Couldn't allocate native analyzer thread");
+ mResultText.setText(getResources().getString(R.string.audio_loopback_failure));
+ }
+ }
+
+ private void startTestPhase() {
+ if (mNativeAnalyzerThread != null) {
+ mNativeAnalyzerThread.startTest();
+
+ // what is this for?
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void handleTestCompletion() {
+ mMeanLatencyMillis = StatUtils.calculateMean(mLatencyMillis);
+ mMeanAbsoluteDeviation =
+ StatUtils.calculateMeanAbsoluteDeviation(mMeanLatencyMillis, mLatencyMillis);
+ mMeanConfidence = StatUtils.calculateMean(mConfidence);
+
+ boolean pass = isPeripheralValidForTest()
+ && mMeanConfidence >= CONFIDENCE_THRESHOLD
+ && mMeanLatencyMillis > EPSILON
+ && mMeanLatencyMillis < mMustLatency;
+
+ getPassButton().setEnabled(pass);
+
+ String result;
+ if (mMeanConfidence < CONFIDENCE_THRESHOLD) {
+ result = String.format(
+ "Test Finished\nInsufficient Confidence (%.2f < %.2f). No Results.",
+ mMeanConfidence, CONFIDENCE_THRESHOLD);
+ } else {
+ result = String.format(
+ "Test Finished - %s\nMean Latency:%.2f ms (required:%.2f)\n" +
+ "Mean Absolute Deviation: %.2f\n" +
+ " Confidence: %.2f\n" +
+ " Low Latency Path: %s",
+ (pass ? "PASS" : "FAIL"),
+ mMeanLatencyMillis,
+ mMustLatency,
+ mMeanAbsoluteDeviation,
+ mMeanConfidence,
+ mNativeAnalyzerThread.isLowLatencyStream() ? mYesString : mNoString);
+ }
+
+ // Make sure the test thread is finished. It should already be done.
+ if (mNativeAnalyzerThread != null) {
+ try {
+ mNativeAnalyzerThread.stopTest(STOP_TEST_TIMEOUT_MSEC);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ mResultText.setText(result);
+
+ recordTestResults();
+
+ showWait(false);
+ mTestButton.setEnabled(true);
+ }
+
+ private void handleTestPhaseCompletion() {
+ if (mNativeAnalyzerThread != null && mTestPhase < NUM_TEST_PHASES) {
+ mLatencyMillis[mTestPhase] = mNativeAnalyzerThread.getLatencyMillis();
+ mConfidence[mTestPhase] = mNativeAnalyzerThread.getConfidence();
+
+ String result = String.format(
+ "Test %d Finished\nLatency: %.2f ms\nConfidence: %.2f\n",
+ mTestPhase,
+ mLatencyMillis[mTestPhase],
+ mConfidence[mTestPhase]);
+
+ mResultText.setText(result);
+ try {
+ mNativeAnalyzerThread.stopTest(STOP_TEST_TIMEOUT_MSEC);
+ // Thread.sleep(/*STOP_TEST_TIMEOUT_MSEC*/500);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ mTestPhase++;
+ if (mTestPhase >= NUM_TEST_PHASES) {
+ handleTestCompletion();
+ } else {
+ startTestPhase();
+ }
+ }
+ }
+
+ /**
+ * handler for messages from audio thread
+ */
+ private Handler mMessageHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ switch(msg.what) {
+ case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_STARTED:
+ Log.v(TAG,"got message native rec started!!");
+ showWait(true);
+ mResultText.setText(String.format("[phase: %d] - Test Running...",
+ (mTestPhase + 1)));
+ break;
+ case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_OPEN_ERROR:
+ Log.v(TAG,"got message native rec can't start!!");
+ mResultText.setText("Test Error opening streams.");
+ handleTestCompletion();
+ break;
+ case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR:
+ Log.v(TAG,"got message native rec can't start!!");
+ mResultText.setText("Test Error while recording.");
+ handleTestCompletion();
+ break;
+ case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE_ERRORS:
+ mResultText.setText("Test FAILED due to errors.");
+ handleTestCompletion();
+ break;
+ case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING:
+ Log.i(TAG, "NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING");
+ mResultText.setText(String.format("[phase: %d] - Analyzing ...",
+ mTestPhase + 1));
+ break;
+ case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE:
+ Log.i(TAG, "NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE");
+ handleTestPhaseCompletion();
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.audio_loopback_latency_activity);
+
+ setPassFailButtonClickListeners();
+ getPassButton().setEnabled(false);
+ setInfoResources(R.string.audio_loopback_latency_test, R.string.audio_loopback_info, -1);
+
+ mClaimsOutput = AudioSystemFlags.claimsOutput(this);
+ mClaimsInput = AudioSystemFlags.claimsInput(this);
+ mClaimsProAudio = AudioSystemFlags.claimsProAudio(this);
+
+ mYesString = getResources().getString(R.string.audio_general_yes);
+ mNoString = getResources().getString(R.string.audio_general_no);
+
+ // Pro Audio
+ ((TextView)findViewById(R.id.audio_loopback_pro_audio)).setText(
+ "" + (mClaimsProAudio ? mYesString : mNoString));
+
+ // MMAP
+ ((TextView)findViewById(R.id.audio_loopback_mmap)).setText(
+ "" + (mSupportsMMAP ? mYesString : mNoString));
+ ((TextView)findViewById(R.id.audio_loopback_mmap_exclusive)).setText(
+ "" + (mSupportsMMAPExclusive ? mYesString : mNoString));
+
+ // Low Latency
+ ((TextView)findViewById(R.id.audio_loopback_low_latency)).setText(
+ "" + (AudioSystemFlags.claimsLowLatencyAudio(this) ? mYesString : mNoString));
+
+ mTestPathTxt = ((TextView)findViewById(R.id.audio_loopback_testpath));
+
+ mTestButton = (Button)findViewById(R.id.audio_loopback_test_btn);
+ mTestButton.setOnClickListener(mBtnClickListener);
+
+ mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);
+
+ mAudioManager.registerAudioDeviceCallback(new ConnectListener(), new Handler());
+
+ connectLoopbackUI();
+
+ calculateLatencyThresholds();
+ displayLatencyThresholds();
+ }
+
+ private class OnBtnClickListener implements OnClickListener {
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.audio_loopback_test_btn:
+ Log.i(TAG, "audio loopback test");
+ startAudioTest(mMessageHandler);
+ break;
+ }
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java
index 8ee3446..9490091 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,679 +16,9 @@
package com.android.cts.verifier.audio;
-import android.app.AlertDialog;
-import android.media.AudioDeviceCallback;
-import android.media.AudioDeviceInfo;
-import android.media.AudioManager;
-import android.media.MediaRecorder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.ProgressBar;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-import com.android.cts.verifier.audio.audiolib.AudioSystemFlags;
-import com.android.cts.verifier.audio.audiolib.StatUtils;
-import com.android.cts.verifier.audio.audiolib.AudioUtils;
-import com.android.cts.verifier.CtsVerifierReportLog;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import static com.android.cts.verifier.TestListActivity.sCurrentDisplayMode;
-import static com.android.cts.verifier.TestListAdapter.setTestNameSuffix;
-
/**
- * CtsVerifier Audio Loopback Latency Test
+ * Tests Audio Device roundtrip latency by using a loopback plug.
*/
-public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity {
- private static final String TAG = "AudioLoopbackLatencyActivity";
-
- // JNI load
- static {
- try {
- System.loadLibrary("audioloopback_jni");
- } catch (UnsatisfiedLinkError e) {
- Log.e(TAG, "Error loading Audio Loopback JNI library");
- Log.e(TAG, "e: " + e);
- e.printStackTrace();
- }
-
- /* TODO: gracefully fail/notify if the library can't be loaded */
- }
- protected AudioManager mAudioManager;
-
- // UI
- TextView mInputDeviceTxt;
- TextView mOutputDeviceTxt;
-
- TextView mAudioLevelText;
- SeekBar mAudioLevelSeekbar;
-
- TextView mTestPathTxt;
-
- TextView mResultText;
- ProgressBar mProgressBar;
- int mMaxLevel;
-
- OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
- protected Button mTestButton;
-
- String mYesString;
- String mNoString;
-
- // These flags determine the maximum allowed latency
- private boolean mClaimsProAudio;
- private boolean mClaimsOutput;
- private boolean mClaimsInput;
-
- // Useful info
- private boolean mSupportsMMAP = AudioUtils.isMMapSupported();
- private boolean mSupportsMMAPExclusive = AudioUtils.isMMapExclusiveSupported();
-
- // Peripheral(s)
- boolean mIsPeripheralAttached; // CDD ProAudio section C-1-3
- AudioDeviceInfo mOutputDevInfo;
- AudioDeviceInfo mInputDevInfo;
-
- protected static final int TESTPERIPHERAL_INVALID = -1;
- protected static final int TESTPERIPHERAL_NONE = 0;
- protected static final int TESTPERIPHERAL_ANALOG_JACK = 1;
- protected static final int TESTPERIPHERAL_USB = 2;
- protected static final int TESTPERIPHERAL_DEVICE = 3; // device speaker + mic
- protected int mTestPeripheral = TESTPERIPHERAL_NONE;
-
- // Loopback Logic
- NativeAnalyzerThread mNativeAnalyzerThread = null;
-
- protected static final int NUM_TEST_PHASES = 5;
- protected int mTestPhase = 0;
-
- protected double[] mLatencyMillis = new double[NUM_TEST_PHASES];
- protected double[] mConfidence = new double[NUM_TEST_PHASES];
-
- protected double mMeanLatencyMillis;
- protected double mMeanAbsoluteDeviation;
- protected double mMeanConfidence;
-
- protected static final double CONFIDENCE_THRESHOLD = 0.6;
- // impossibly low latencies (indicating something in the test went wrong).
- protected static final float EPSILON = 1.0f;
- protected static final double PROAUDIO_RECOMMENDED_LATENCY_MS = 20.0;
- protected static final double PROAUDIO_RECOMMENDED_USB_LATENCY_MS = 25.0;
- protected static final double PROAUDIO_MUST_LATENCY_MS = 20.0;
- protected static final double BASIC_RECOMMENDED_LATENCY_MS = 50.0;
- protected static final double BASIC_MUST_LATENCY_MS = 800.0;
- protected double mMustLatency;
- protected double mRecommendedLatency;
-
- // The audio stream callback threads should stop and close
- // in less than a few hundred msec. This is a generous timeout value.
- private static final int STOP_TEST_TIMEOUT_MSEC = 2 * 1000;
-
- //
- // Common UI Handling
- //
- private void connectLoopbackUI() {
- // Connected Device
- mInputDeviceTxt = ((TextView)findViewById(R.id.audioLoopbackInputLbl));
- mOutputDeviceTxt = ((TextView)findViewById(R.id.audioLoopbackOutputLbl));
-
- mAudioLevelText = (TextView)findViewById(R.id.audio_loopback_level_text);
- mAudioLevelSeekbar = (SeekBar)findViewById(R.id.audio_loopback_level_seekbar);
- mMaxLevel = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- mAudioLevelSeekbar.setMax(mMaxLevel);
- mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (int)(0.7 * mMaxLevel), 0);
- refreshLevel();
-
- mAudioLevelSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {}
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {}
-
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
- progress, 0);
- Log.i(TAG,"Level set to: " + progress);
- refreshLevel();
- }
- });
-
- mResultText = (TextView)findViewById(R.id.audio_loopback_results_text);
- mProgressBar = (ProgressBar)findViewById(R.id.audio_loopback_progress_bar);
- showWait(false);
- }
-
- //
- // Peripheral Connection Logic
- //
- protected void scanPeripheralList(AudioDeviceInfo[] devices) {
- // CDD Section C-1-3: USB port, host-mode support
-
- // Can't just use the first record because then we will only get
- // Source OR sink, not both even on devices that are both.
- mOutputDevInfo = null;
- mInputDevInfo = null;
-
- // Any valid peripherals
- // Do we leave in the Headset test to support a USB-Dongle?
- for (AudioDeviceInfo devInfo : devices) {
- if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE || // USB Peripheral
- devInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET || // USB dongle+LBPlug
- devInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET || // Loopback Plug?
- devInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) { // Aux-cable loopback?
- if (devInfo.isSink()) {
- mOutputDevInfo = devInfo;
- }
- if (devInfo.isSource()) {
- mInputDevInfo = devInfo;
- }
- } else {
- handleDeviceConnection(devInfo);
- }
- }
-
- // need BOTH input and output to test
- mIsPeripheralAttached = mOutputDevInfo != null && mInputDevInfo != null;
- calculateTestPeripheral();
- showConnectedAudioPeripheral();
- calculateLatencyThresholds();
- displayLatencyThresholds();
- }
-
- protected void handleDeviceConnection(AudioDeviceInfo deviceInfo) {
- // NOP
- }
-
- private class ConnectListener extends AudioDeviceCallback {
- /*package*/ ConnectListener() {}
-
- //
- // AudioDevicesManager.OnDeviceConnectionListener
- //
- @Override
- public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
- scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
- }
-
- @Override
- public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
- scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
- }
- }
-
- private void calculateTestPeripheral() {
- if (!mIsPeripheralAttached) {
- if ((mOutputDevInfo != null && mInputDevInfo == null)
- || (mOutputDevInfo == null && mInputDevInfo != null)) {
- mTestPeripheral = TESTPERIPHERAL_INVALID;
- } else {
- mTestPeripheral = TESTPERIPHERAL_DEVICE;
- }
- } else if (!areIODevicesOnePeripheral()) {
- mTestPeripheral = TESTPERIPHERAL_INVALID;
- } else if (mInputDevInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE ||
- mInputDevInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET) {
- mTestPeripheral = TESTPERIPHERAL_USB;
- } else if (mInputDevInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET ||
- mInputDevInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) {
- mTestPeripheral = TESTPERIPHERAL_ANALOG_JACK;
- } else {
- // Huh?
- Log.e(TAG, "No valid peripheral found!?");
- mTestPeripheral = TESTPERIPHERAL_NONE;
- }
- }
-
- private boolean isPeripheralValidForTest() {
- return mTestPeripheral == TESTPERIPHERAL_ANALOG_JACK
- || mTestPeripheral == TESTPERIPHERAL_USB;
- }
-
- private void showConnectedAudioPeripheral() {
- mInputDeviceTxt.setText(
- mInputDevInfo != null ? mInputDevInfo.getProductName().toString()
- : "Not connected");
- mOutputDeviceTxt.setText(
- mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString()
- : "Not connected");
-
- String pathName;
- switch (mTestPeripheral) {
- case TESTPERIPHERAL_INVALID:
- pathName = "Invalid Test Peripheral";
- break;
-
- case TESTPERIPHERAL_ANALOG_JACK:
- pathName = "Headset Jack";
- break;
-
- case TESTPERIPHERAL_USB:
- pathName = "USB";
- break;
-
- case TESTPERIPHERAL_DEVICE:
- pathName = "Device Speaker + Microphone";
- break;
-
- case TESTPERIPHERAL_NONE:
- default:
- pathName = "Error. Unknown Test Path";
- break;
- }
- mTestPathTxt.setText(pathName);
- mTestButton.setEnabled(
- mTestPeripheral != TESTPERIPHERAL_INVALID && mTestPeripheral != TESTPERIPHERAL_NONE);
-
- }
-
- private boolean areIODevicesOnePeripheral() {
- if (mOutputDevInfo == null || mInputDevInfo == null) {
- return false;
- }
-
- return mOutputDevInfo.getProductName().toString().equals(
- mInputDevInfo.getProductName().toString());
- }
-
- private void calculateLatencyThresholds() {
- switch (mTestPeripheral) {
- case TESTPERIPHERAL_ANALOG_JACK:
- mRecommendedLatency = mClaimsProAudio
- ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
- mMustLatency = mClaimsProAudio
- ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_MUST_LATENCY_MS;
- break;
-
- case TESTPERIPHERAL_USB:
- mRecommendedLatency = mClaimsProAudio
- ? PROAUDIO_RECOMMENDED_USB_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
- mMustLatency = mClaimsProAudio
- ? PROAUDIO_RECOMMENDED_USB_LATENCY_MS : BASIC_MUST_LATENCY_MS;
- break;
-
- case TESTPERIPHERAL_DEVICE:
- // This isn't a valid case so we won't pass it, but it can be run
- mRecommendedLatency = mClaimsProAudio
- ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
- mMustLatency = mClaimsProAudio
- ? PROAUDIO_RECOMMENDED_LATENCY_MS :BASIC_MUST_LATENCY_MS;
- break;
-
- case TESTPERIPHERAL_NONE:
- default:
- mRecommendedLatency = BASIC_RECOMMENDED_LATENCY_MS;
- mMustLatency = BASIC_MUST_LATENCY_MS;
- break;
- }
- }
-
- private void displayLatencyThresholds() {
- if (isPeripheralValidForTest()) {
- ((TextView) findViewById(R.id.audio_loopback_must_latency)).setText("" + mMustLatency);
- ((TextView) findViewById(R.id.audio_loopback_recommended_latency)).setText(
- "" + mRecommendedLatency);
- } else {
- String naStr = getResources().getString(R.string.audio_proaudio_NA);
- ((TextView) findViewById(R.id.audio_loopback_must_latency)).setText(naStr);
- ((TextView) findViewById(R.id.audio_loopback_recommended_latency)).setText(naStr);
- }
- }
-
- /**
- * refresh Audio Level seekbar and text
- */
- private void refreshLevel() {
- int currentLevel = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
- mAudioLevelSeekbar.setProgress(currentLevel);
-
- String levelText = String.format("%s: %d/%d",
- getResources().getString(R.string.audio_loopback_level_text),
- currentLevel, mMaxLevel);
- mAudioLevelText.setText(levelText);
- }
-
- //
- // show active progress bar
- //
- protected void showWait(boolean show) {
- mProgressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
- }
-
- //
- // Common logging
- //
- // Schema
- private static final String KEY_LATENCY = "latency";
- private static final String KEY_CONFIDENCE = "confidence";
- private static final String KEY_SAMPLE_RATE = "sample_rate";
- private static final String KEY_IS_PRO_AUDIO = "is_pro_audio";
- private static final String KEY_IS_LOW_LATENCY = "is_low_latency";
- private static final String KEY_IS_PERIPHERAL_ATTACHED = "is_peripheral_attached";
- private static final String KEY_INPUT_PERIPHERAL_NAME = "input_peripheral";
- private static final String KEY_OUTPUT_PERIPHERAL_NAME = "output_peripheral";
- private static final String KEY_TEST_PERIPHERAL = "test_peripheral";
- private static final String KEY_TEST_MMAP = "supports_mmap";
- private static final String KEY_TEST_MMAPEXCLUSIVE = "supports_mmap_exclusive";
- private static final String KEY_LEVEL = "level";
- private static final String KEY_BUFFER_SIZE = "buffer_size_in_frames";
-
- @Override
- public String getTestId() {
- return setTestNameSuffix(sCurrentDisplayMode, getClass().getName());
- }
-
- @Override
- public String getReportFileName() { return PassFailButtons.AUDIO_TESTS_REPORT_LOG_NAME; }
-
- @Override
- public final String getReportSectionName() {
- return setTestNameSuffix(sCurrentDisplayMode, "audio_loopback_latency_activity");
- }
-
- //
- // Subclasses should call this explicitly. SubClasses should call submit() after their logs
- //
- @Override
- public void recordTestResults() {
- if (mNativeAnalyzerThread == null) {
- return; // no results to report
- }
-
- CtsVerifierReportLog reportLog = getReportLog();
- reportLog.addValue(
- KEY_LATENCY,
- mMeanLatencyMillis,
- ResultType.LOWER_BETTER,
- ResultUnit.MS);
-
- reportLog.addValue(
- KEY_CONFIDENCE,
- mMeanConfidence,
- ResultType.HIGHER_BETTER,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_SAMPLE_RATE,
- mNativeAnalyzerThread.getSampleRate(),
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_IS_LOW_LATENCY,
- mNativeAnalyzerThread.isLowLatencyStream(),
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_IS_PERIPHERAL_ATTACHED,
- mIsPeripheralAttached,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_IS_PRO_AUDIO,
- mClaimsProAudio,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_TEST_PERIPHERAL,
- mTestPeripheral,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_TEST_MMAP,
- mSupportsMMAP,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_TEST_MMAPEXCLUSIVE ,
- mSupportsMMAPExclusive,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- if (mIsPeripheralAttached) {
- reportLog.addValue(
- KEY_INPUT_PERIPHERAL_NAME,
- mInputDevInfo != null ? mInputDevInfo.getProductName().toString() : "None",
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_OUTPUT_PERIPHERAL_NAME,
- mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString() : "None",
- ResultType.NEUTRAL,
- ResultUnit.NONE);
- }
-
- int audioLevel = mAudioLevelSeekbar.getProgress();
- reportLog.addValue(
- KEY_LEVEL,
- audioLevel,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.submit();
- }
-
- private void startAudioTest(Handler messageHandler) {
- getPassButton().setEnabled(false);
-
- mTestPhase = 0;
- java.util.Arrays.fill(mLatencyMillis, 0.0);
- java.util.Arrays.fill(mConfidence, 0.0);
-
- mNativeAnalyzerThread = new NativeAnalyzerThread(this);
- if (mNativeAnalyzerThread != null) {
- mNativeAnalyzerThread.setMessageHandler(messageHandler);
- // This value matches AAUDIO_INPUT_PRESET_VOICE_RECOGNITION
- mNativeAnalyzerThread.setInputPreset(MediaRecorder.AudioSource.VOICE_RECOGNITION);
- startTestPhase();
- } else {
- Log.e(TAG, "Couldn't allocate native analyzer thread");
- mResultText.setText(getResources().getString(R.string.audio_loopback_failure));
- }
- }
-
- private void startTestPhase() {
- if (mNativeAnalyzerThread != null) {
- mNativeAnalyzerThread.startTest();
-
- // what is this for?
- try {
- Thread.sleep(200);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
-
- private void handleTestCompletion() {
- mMeanLatencyMillis = StatUtils.calculateMean(mLatencyMillis);
- mMeanAbsoluteDeviation =
- StatUtils.calculateMeanAbsoluteDeviation(mMeanLatencyMillis, mLatencyMillis);
- mMeanConfidence = StatUtils.calculateMean(mConfidence);
-
- boolean pass = isPeripheralValidForTest()
- && mMeanConfidence >= CONFIDENCE_THRESHOLD
- && mMeanLatencyMillis > EPSILON
- && mMeanLatencyMillis < mMustLatency;
-
- getPassButton().setEnabled(pass);
-
- String result;
- if (mMeanConfidence < CONFIDENCE_THRESHOLD) {
- result = String.format(
- "Test Finished\nInsufficient Confidence (%.2f < %.2f). No Results.",
- mMeanConfidence, CONFIDENCE_THRESHOLD);
- } else {
- result = String.format(
- "Test Finished - %s\nMean Latency:%.2f ms (required:%.2f)\n" +
- "Mean Absolute Deviation: %.2f\n" +
- " Confidence: %.2f\n" +
- " Low Latency Path: %s",
- (pass ? "PASS" : "FAIL"),
- mMeanLatencyMillis,
- mMustLatency,
- mMeanAbsoluteDeviation,
- mMeanConfidence,
- mNativeAnalyzerThread.isLowLatencyStream() ? mYesString : mNoString);
- }
-
- // Make sure the test thread is finished. It should already be done.
- if (mNativeAnalyzerThread != null) {
- try {
- mNativeAnalyzerThread.stopTest(STOP_TEST_TIMEOUT_MSEC);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- mResultText.setText(result);
-
- recordTestResults();
-
- showWait(false);
- mTestButton.setEnabled(true);
- }
-
- private void handleTestPhaseCompletion() {
- if (mNativeAnalyzerThread != null && mTestPhase < NUM_TEST_PHASES) {
- mLatencyMillis[mTestPhase] = mNativeAnalyzerThread.getLatencyMillis();
- mConfidence[mTestPhase] = mNativeAnalyzerThread.getConfidence();
-
- String result = String.format(
- "Test %d Finished\nLatency: %.2f ms\nConfidence: %.2f\n",
- mTestPhase,
- mLatencyMillis[mTestPhase],
- mConfidence[mTestPhase]);
-
- mResultText.setText(result);
- try {
- mNativeAnalyzerThread.stopTest(STOP_TEST_TIMEOUT_MSEC);
- // Thread.sleep(/*STOP_TEST_TIMEOUT_MSEC*/500);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- mTestPhase++;
- if (mTestPhase >= NUM_TEST_PHASES) {
- handleTestCompletion();
- } else {
- startTestPhase();
- }
- }
- }
-
- /**
- * handler for messages from audio thread
- */
- private Handler mMessageHandler = new Handler() {
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- switch(msg.what) {
- case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_STARTED:
- Log.v(TAG,"got message native rec started!!");
- showWait(true);
- mResultText.setText(String.format("[phase: %d] - Test Running...",
- (mTestPhase + 1)));
- break;
- case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_OPEN_ERROR:
- Log.v(TAG,"got message native rec can't start!!");
- mResultText.setText("Test Error opening streams.");
- handleTestCompletion();
- break;
- case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR:
- Log.v(TAG,"got message native rec can't start!!");
- mResultText.setText("Test Error while recording.");
- handleTestCompletion();
- break;
- case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE_ERRORS:
- mResultText.setText("Test FAILED due to errors.");
- handleTestCompletion();
- break;
- case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING:
- Log.i(TAG, "NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING");
- mResultText.setText(String.format("[phase: %d] - Analyzing ...",
- mTestPhase + 1));
- break;
- case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE:
- Log.i(TAG, "NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE");
- handleTestPhaseCompletion();
- break;
- default:
- break;
- }
- }
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.audio_loopback_latency_activity);
-
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- setInfoResources(R.string.audio_loopback_latency_test, R.string.audio_loopback_info, -1);
-
- mClaimsOutput = AudioSystemFlags.claimsOutput(this);
- mClaimsInput = AudioSystemFlags.claimsInput(this);
- mClaimsProAudio = AudioSystemFlags.claimsProAudio(this);
-
- mYesString = getResources().getString(R.string.audio_general_yes);
- mNoString = getResources().getString(R.string.audio_general_no);
-
- // Pro Audio
- ((TextView)findViewById(R.id.audio_loopback_pro_audio)).setText(
- "" + (mClaimsProAudio ? mYesString : mNoString));
-
- // MMAP
- ((TextView)findViewById(R.id.audio_loopback_mmap)).setText(
- "" + (mSupportsMMAP ? mYesString : mNoString));
- ((TextView)findViewById(R.id.audio_loopback_mmap_exclusive)).setText(
- "" + (mSupportsMMAPExclusive ? mYesString : mNoString));
-
- // Low Latency
- ((TextView)findViewById(R.id.audio_loopback_low_latency)).setText(
- "" + (AudioSystemFlags.claimsLowLatencyAudio(this) ? mYesString : mNoString));
-
- mTestPathTxt = ((TextView)findViewById(R.id.audio_loopback_testpath));
-
- mTestButton = (Button)findViewById(R.id.audio_loopback_test_btn);
- mTestButton.setOnClickListener(mBtnClickListener);
-
- mAudioManager = getSystemService(AudioManager.class);
-
- mAudioManager.registerAudioDeviceCallback(new ConnectListener(), new Handler());
-
- connectLoopbackUI();
-
- calculateLatencyThresholds();
- displayLatencyThresholds();
- }
-
- private class OnBtnClickListener implements OnClickListener {
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.audio_loopback_test_btn:
- Log.i(TAG, "audio loopback test");
- startAudioTest(mMessageHandler);
- break;
- }
- }
- }
+public class AudioLoopbackLatencyActivity extends AudioLoopbackBaseActivity {
+ private static final String TAG = AudioLoopbackLatencyActivity.class.getSimpleName();
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
index 9804cd3..d32749f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
@@ -67,7 +67,6 @@
private static final String INFO_DIALOG_MESSAGE_ID = "infoDialogMessageId";
// ReportLog Schema
- private static final String SECTION_PRO_AUDIO_ACTIVITY = "pro_audio_activity";
private static final String KEY_CLAIMS_PRO = "claims_pro_audio";
private static final String KEY_CLAIMS_LOW_LATENCY = "claims_low_latency_audio";
private static final String KEY_CLAIMS_MIDI = "claims_midi";
@@ -233,14 +232,6 @@
// PassFailButtons Overrides
//
@Override
- public String getReportFileName() { return PassFailButtons.AUDIO_TESTS_REPORT_LOG_NAME; }
-
- @Override
- public final String getReportSectionName() {
- return setTestNameSuffix(sCurrentDisplayMode, SECTION_PRO_AUDIO_ACTIVITY);
- }
-
- @Override
public void recordTestResults() {
CtsVerifierReportLog reportLog = getReportLog();