OboeTester: guided disconnect test
Test whether Oboe disconnects a stream when a headset
is plugged in or unplugged.
Also listen for Intent.ACTION_HEADSET_PLUG.
diff --git a/apps/OboeTester/app/src/main/AndroidManifest.xml b/apps/OboeTester/app/src/main/AndroidManifest.xml
index 5911238..4591c57 100644
--- a/apps/OboeTester/app/src/main/AndroidManifest.xml
+++ b/apps/OboeTester/app/src/main/AndroidManifest.xml
@@ -86,6 +86,12 @@
android:screenOrientation="portrait">
</activity>
+ <activity
+ android:name="com.google.sample.oboe.manualtest.TestDisconnectActivity"
+ android:label="@string/title_test_disconnect"
+ android:screenOrientation="portrait">
+ </activity>
+
<service
android:name="com.google.sample.oboe.manualtest.AudioMidiTester"
android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
@@ -107,6 +113,7 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
+
</application>
</manifest>
\ No newline at end of file
diff --git a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
index ed0ee6c..cb1573a 100644
--- a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
+++ b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp
@@ -605,3 +605,28 @@
mFullDuplexGlitches->setOutputStream(oboeStream);
}
}
+
+
+// =================================================================== ActivityTestDisconnect
+void ActivityTestDisconnect::close(int32_t streamIndex) {
+ ActivityContext::close(streamIndex);
+ mSinkFloat.reset();
+}
+
+void ActivityTestDisconnect::configureForStart() {
+ mSinkFloat = std::make_unique<SinkFloat>(mChannelCount);
+ sineOscillator = std::make_unique<SineOscillator>();
+ monoToMulti = std::make_unique<MonoToMultiConverter>(mChannelCount);
+
+ oboe::AudioStream *outputStream = getOutputStream();
+ sineOscillator->setSampleRate(outputStream->getSampleRate());
+ sineOscillator->frequency.setValue(440.0);
+ sineOscillator->amplitude.setValue(AMPLITUDE_SINE);
+ sineOscillator->output.connect(&(monoToMulti->input));
+ monoToMulti->output.connect(&(mSinkFloat->input));
+ // Clear framePosition in sine oscillators.
+ mSinkFloat->pullReset();
+ audioStreamGateway.setAudioSink(mSinkFloat);
+ oboeCallbackProxy.setCallback(&audioStreamGateway);
+}
+
diff --git a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h
index f48858b..9b81f89 100644
--- a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h
+++ b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h
@@ -599,6 +599,29 @@
};
/**
+ * Test a single output stream.
+ */
+class ActivityTestDisconnect : public ActivityContext {
+public:
+ ActivityTestDisconnect() {}
+
+ virtual ~ActivityTestDisconnect() = default;
+
+ void close(int32_t streamIndex) override;
+
+ oboe::Result startStreams() override {
+ return getOutputStream()->start();
+ }
+
+ void configureForStart() override;
+
+private:
+ std::unique_ptr<SineOscillator> sineOscillator;
+ std::unique_ptr<MonoToMultiConverter> monoToMulti;
+ std::shared_ptr<flowgraph::SinkFloat> mSinkFloat;
+};
+
+/**
* Switch between various
*/
class NativeAudioContext {
@@ -635,6 +658,9 @@
case ActivityType::Glitches:
currentActivity = &mActivityGlitches;
break;
+ case ActivityType::TestDisconnect:
+ currentActivity = &mActivityTestDisconnect;
+ break;
}
}
@@ -649,6 +675,7 @@
ActivityEcho mActivityEcho;
ActivityRoundTripLatency mActivityRoundTripLatency;
ActivityGlitches mActivityGlitches;
+ ActivityTestDisconnect mActivityTestDisconnect;
private:
@@ -662,6 +689,7 @@
Echo = 4,
RoundTripLatency = 5,
Glitches = 6,
+ TestDisconnect = 7,
};
ActivityType mActivityType = ActivityType::Undefined;
diff --git a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/MainActivity.java b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/MainActivity.java
index 02ee804..6d379d5 100644
--- a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/MainActivity.java
+++ b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/MainActivity.java
@@ -189,6 +189,12 @@
startActivity(intent);
}
+ public void onLaunchTestDisconnect(View view) {
+ updateCallbackSize();
+ Intent intent = new Intent(this, TestDisconnectActivity.class);
+ startActivity(intent);
+ }
+
public void onUseCallbackClicked(View view) {
CheckBox checkBox = (CheckBox) view;
OboeAudioStream.setUseCallback(checkBox.isChecked());
diff --git a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/RecorderActivity.java b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/RecorderActivity.java
index c59b66a..cc4ed73 100644
--- a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/RecorderActivity.java
+++ b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/RecorderActivity.java
@@ -16,16 +16,10 @@
package com.google.sample.oboe.manualtest;
-import android.content.Intent;
-import android.net.Uri;
import android.os.Bundle;
-import android.os.Environment;
-import android.support.annotation.NonNull;
-import android.support.v4.content.FileProvider;
import android.view.View;
import android.widget.Button;
-import java.io.File;
import java.io.IOException;
/**
@@ -35,7 +29,7 @@
private static final int STATE_RECORDING = 5;
private static final int STATE_PLAYING = 6;
- private int mRecorderState = STATE_STOPPED;
+ private int mRecorderState = AUDIO_STATE_STOPPED;
private Button mRecordButton;
private Button mStopButton;
private Button mPlayButton;
@@ -55,7 +49,7 @@
mStopButton = (Button) findViewById(R.id.button_stop_record_play);
mPlayButton = (Button) findViewById(R.id.button_start_playback);
mShareButton = (Button) findViewById(R.id.button_share);
- mRecorderState = STATE_STOPPED;
+ mRecorderState = AUDIO_STATE_STOPPED;
mGotRecording = false;
updateButtons();
}
@@ -81,7 +75,7 @@
public void onStopRecordPlay(View view) {
stopAudio();
closeAudio();
- mRecorderState = STATE_STOPPED;
+ mRecorderState = AUDIO_STATE_STOPPED;
updateButtons();
}
@@ -92,10 +86,10 @@
}
private void updateButtons() {
- mRecordButton.setEnabled(mRecorderState == STATE_STOPPED);
- mStopButton.setEnabled(mRecorderState != STATE_STOPPED);
- mPlayButton.setEnabled(mRecorderState == STATE_STOPPED && mGotRecording);
- mShareButton.setEnabled(mRecorderState == STATE_STOPPED && mGotRecording);
+ mRecordButton.setEnabled(mRecorderState == AUDIO_STATE_STOPPED);
+ mStopButton.setEnabled(mRecorderState != AUDIO_STATE_STOPPED);
+ mPlayButton.setEnabled(mRecorderState == AUDIO_STATE_STOPPED && mGotRecording);
+ mShareButton.setEnabled(mRecorderState == AUDIO_STATE_STOPPED && mGotRecording);
}
public void startPlayback() {
diff --git a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/StreamConfiguration.java b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/StreamConfiguration.java
index 48ebd7b..7dc13fd 100644
--- a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/StreamConfiguration.java
+++ b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/StreamConfiguration.java
@@ -54,6 +54,8 @@
public static final int RATE_CONVERSION_QUALITY_HIGH = 4; // must match Oboe
public static final int RATE_CONVERSION_QUALITY_BEST = 5; // must match Oboe
+ public static final int STREAM_STATE_STARTING = 3; // must match Oboe
+ public static final int STREAM_STATE_STARTED = 4; // must match Oboe
public static final int INPUT_PRESET_GENERIC = 1; // must match Oboe
public static final int INPUT_PRESET_CAMCORDER = 5; // must match Oboe
diff --git a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/TestAudioActivity.java b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/TestAudioActivity.java
index 13adb4a..87ea804 100644
--- a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/TestAudioActivity.java
+++ b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/TestAudioActivity.java
@@ -43,11 +43,13 @@
public static final String TAG = "TestOboe";
protected static final int FADER_PROGRESS_MAX = 1000;
- public static final int STATE_OPEN = 0;
- public static final int STATE_STARTED = 1;
- public static final int STATE_PAUSED = 2;
- public static final int STATE_STOPPED = 3;
- public static final int STATE_CLOSED = 4;
+
+ public static final int AUDIO_STATE_OPEN = 0;
+ public static final int AUDIO_STATE_STARTED = 1;
+ public static final int AUDIO_STATE_PAUSED = 2;
+ public static final int AUDIO_STATE_STOPPED = 3;
+ public static final int AUDIO_STATE_CLOSED = 4;
+
public static final int COLOR_ACTIVE = 0xFFD0D0A0;
public static final int COLOR_IDLE = 0xFFD0D0D0;
@@ -60,8 +62,9 @@
public static final int ACTIVITY_ECHO = 4;
public static final int ACTIVITY_RT_LATENCY = 5;
public static final int ACTIVITY_GLITCHES = 6;
+ public static final int ACTIVITY_TEST_DISCONNECT = 7;
- private int mState = STATE_CLOSED;
+ private int mAudioState = AUDIO_STATE_CLOSED;
protected String audioManagerSampleRate;
protected int audioManagerFramesPerBurst;
protected ArrayList<StreamContext> mStreamContexts;
@@ -178,23 +181,19 @@
@Override
protected void onDestroy() {
- mState = STATE_CLOSED;
+ mAudioState = AUDIO_STATE_CLOSED;
super.onDestroy();
}
- int getState() {
- return mState;
- }
-
protected void updateEnabledWidgets() {
if (mOpenButton != null) {
- mOpenButton.setBackgroundColor(mState == STATE_OPEN ? COLOR_ACTIVE : COLOR_IDLE);
- mStartButton.setBackgroundColor(mState == STATE_STARTED ? COLOR_ACTIVE : COLOR_IDLE);
- mPauseButton.setBackgroundColor(mState == STATE_PAUSED ? COLOR_ACTIVE : COLOR_IDLE);
- mStopButton.setBackgroundColor(mState == STATE_STOPPED ? COLOR_ACTIVE : COLOR_IDLE);
- mCloseButton.setBackgroundColor(mState == STATE_CLOSED ? COLOR_ACTIVE : COLOR_IDLE);
+ mOpenButton.setBackgroundColor(mAudioState == AUDIO_STATE_OPEN ? COLOR_ACTIVE : COLOR_IDLE);
+ mStartButton.setBackgroundColor(mAudioState == AUDIO_STATE_STARTED ? COLOR_ACTIVE : COLOR_IDLE);
+ mPauseButton.setBackgroundColor(mAudioState == AUDIO_STATE_PAUSED ? COLOR_ACTIVE : COLOR_IDLE);
+ mStopButton.setBackgroundColor(mAudioState == AUDIO_STATE_STOPPED ? COLOR_ACTIVE : COLOR_IDLE);
+ mCloseButton.setBackgroundColor(mAudioState == AUDIO_STATE_CLOSED ? COLOR_ACTIVE : COLOR_IDLE);
}
- setConfigViewsEnabled(mState == STATE_CLOSED);
+ setConfigViewsEnabled(mAudioState == AUDIO_STATE_CLOSED);
}
private void setConfigViewsEnabled(boolean b) {
@@ -405,7 +404,7 @@
requestedConfig.setFramesPerBurst(audioManagerFramesPerBurst);
streamContext.tester.open();
mSampleRate = actualConfig.getSampleRate();
- mState = STATE_OPEN;
+ mAudioState = AUDIO_STATE_OPEN;
int sessionId = actualConfig.getSessionId();
if (sessionId > 0) {
setupEffects(sessionId);
@@ -432,7 +431,7 @@
configView.updateDisplay();
}
}
- mState = STATE_STARTED;
+ mAudioState = AUDIO_STATE_STARTED;
updateEnabledWidgets();
}
}
@@ -442,7 +441,7 @@
if (result < 0) {
showErrorToast("Pause failed with " + result);
} else {
- mState = STATE_PAUSED;
+ mAudioState = AUDIO_STATE_PAUSED;
updateEnabledWidgets();
}
}
@@ -452,17 +451,23 @@
if (result < 0) {
showErrorToast("Stop failed with " + result);
} else {
- mState = STATE_STOPPED;
+ mAudioState = AUDIO_STATE_STOPPED;
updateEnabledWidgets();
}
}
+ public void stopAudioQuiet() {
+ stopNative();
+ mAudioState = AUDIO_STATE_STOPPED;
+ updateEnabledWidgets();
+ }
+
public void closeAudio() {
mStreamSniffer.stopStreamSniffer();
for (StreamContext streamContext : mStreamContexts) {
streamContext.tester.close();
}
- mState = STATE_CLOSED;
+ mAudioState = AUDIO_STATE_CLOSED;
updateEnabledWidgets();
}
diff --git a/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/TestDisconnectActivity.java b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/TestDisconnectActivity.java
new file mode 100644
index 0000000..3681468
--- /dev/null
+++ b/apps/OboeTester/app/src/main/java/com/google/sample/oboe/manualtest/TestDisconnectActivity.java
@@ -0,0 +1,445 @@
+/*
+ * Copyright 2019 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.google.sample.oboe.manualtest;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.method.ScrollingMovementMethod;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import java.io.IOException;
+import java.util.Date;
+
+/**
+ * Guide the user through a series of tests plugging in and unplugging a headset.
+ * Print a summary at the end of any failures.
+ *
+ * TODO Test Input
+ */
+public class TestDisconnectActivity extends TestAudioActivity implements Runnable {
+
+ private static final String TEXT_SKIP = "SKIP";
+ private static final String TEXT_PASS = "PASS";
+ private static final String TEXT_FAIL = "FAIL !!!!";
+ public static final int POLL_DURATION_MILLIS = 50;
+ public static final int SETTLING_TIME_MILLIS = 600;
+ public static final int TIME_TO_FAILURE_MILLIS = 3000;
+
+ private TextView mAutoTextView;
+ private TextView mStatusTextView;
+ private TextView mPlugTextView;
+ private AudioOutputTester mAudioOutTester;
+
+ private Thread mAutoThread;
+ private volatile boolean mThreadEnabled;
+ private volatile boolean mTestFailed;
+ private volatile boolean mSkipTest;
+ private volatile int mPlugCount;
+ private int mTestCount;
+ private StringBuffer mFailedSummary;
+ private int mPassCount;
+ private int mFailCount;
+ private BroadcastReceiver pluginReceiver = new PluginBroadcastReceiver();
+ private Button mStartButton;
+ private Button mStopButton;
+ private Button mShareButton;
+ private Button mFailButton;
+ private Button mSkipButton;
+
+ // Receive a broadcast Intent when a headset is plugged in or unplugged.
+ // Display a count on screen.
+ public class PluginBroadcastReceiver extends BroadcastReceiver {
+ private static final String TAG = "MyBroadcastReceiver";
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mPlugCount++;
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ String message = "Intent.HEADSET_PLUG #" + mPlugCount;
+ mPlugTextView.setText(message);
+ }
+ });
+ }
+ }
+
+ @Override
+ protected void inflateActivity() {
+ setContentView(R.layout.activity_test_disconnect);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mStatusTextView = (TextView) findViewById(R.id.text_analyzer_result);
+ mPlugTextView = (TextView) findViewById(R.id.text_plug_events);
+ mAutoTextView = (TextView) findViewById(R.id.text_auto_result);
+ mAutoTextView.setMovementMethod(new ScrollingMovementMethod());
+
+ mStartButton = (Button) findViewById(R.id.button_start);
+ mStopButton = (Button) findViewById(R.id.button_stop);
+ mShareButton = (Button) findViewById(R.id.button_share);
+ mShareButton.setEnabled(false);
+ mFailButton = (Button) findViewById(R.id.button_fail);
+ mSkipButton = (Button) findViewById(R.id.button_skip);
+ updateStartStopButtons(false);
+ updateFailSkipButton(false);
+
+ mAudioOutTester = addAudioOutputTester();
+ }
+
+ private void updateStartStopButtons(boolean running) {
+ mStartButton.setEnabled(!running);
+ mStopButton.setEnabled(running);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ setActivityType(ACTIVITY_TEST_DISCONNECT);
+ }
+
+ @Override
+ boolean isOutput() {
+ return true;
+ }
+
+ @Override
+ public void setupEffects(int sessionId) {
+ }
+
+ private void updateFailSkipButton(final boolean running) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mFailButton.setEnabled(running);
+ mSkipButton.setEnabled(running);
+ }
+ });
+ }
+
+ // Write to scrollable TextView
+ private void log(final String text) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mAutoTextView.append(text);
+ mAutoTextView.append("\n");
+ }
+ });
+ }
+
+ // Write to status and command view
+ private void setStatusText(final String text) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mStatusTextView.setText(text);
+ }
+ });
+ }
+
+ private void logClear() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mAutoTextView.setText("");
+ }
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
+ this.registerReceiver(pluginReceiver, filter);
+ }
+
+ @Override
+ public void onPause() {
+ this.unregisterReceiver(pluginReceiver);
+ super.onPause();
+ }
+
+ // Only call from UI thread.
+ public void onTestFinished() {
+ updateStartStopButtons(false);
+ mShareButton.setEnabled(true);
+ }
+
+ public void startAudioTest() throws IOException {
+ openAudio();
+ startAudio();
+ }
+
+ public void stopAudioTest() {
+ stopAudioQuiet();
+ closeAudio();
+ }
+
+ public void onCancel(View view) {
+ stopAudioTest();
+ onTestFinished();
+ }
+
+ // Called on UI thread
+ public void onStopAudioTest(View view) {
+ stopAudioTest();
+ onTestFinished();
+ keepScreenOn(false);
+ }
+
+ public void onStartDisconnectTest(View view) {
+ updateStartStopButtons(true);
+ mThreadEnabled = true;
+ mAutoThread = new Thread(this);
+ mAutoThread.start();
+ }
+
+ public void onStopDisconnectTest(View view) {
+ try {
+ if (mAutoThread != null) {
+ mThreadEnabled = false;
+ mAutoThread.interrupt();
+ mAutoThread.join(100);
+ mAutoThread = null;
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void onFailTest(View view) {
+ mTestFailed = true;
+ }
+
+ public void onSkipTest(View view) {
+ mSkipTest = true;
+ }
+
+ // Share text from log via GMail, Drive or other method.
+ public void onShareResult(View view) {
+ Intent sharingIntent = new Intent(Intent.ACTION_SEND);
+ sharingIntent.setType("text/plain");
+
+ String subjectText = "OboeTester Test Disconnect result " + getTimestampString();
+ sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subjectText);
+
+ String shareBody = mAutoTextView.getText().toString();
+ sharingIntent.putExtra(Intent.EXTRA_TEXT, shareBody);
+
+ startActivity(Intent.createChooser(sharingIntent, "Share using:"));
+ }
+
+ private String getConfigText(StreamConfiguration config) {
+ return ((config.getDirection() == StreamConfiguration.DIRECTION_OUTPUT) ? "OUT" : "IN")
+ + ", Perf = " + StreamConfiguration.convertPerformanceModeToText(
+ config.getPerformanceMode())
+ + ", " + StreamConfiguration.convertSharingModeToText(config.getSharingMode());
+ }
+
+ private void testConfiguration(int perfMode,
+ int sharingMode,
+ int channelCount,
+ boolean requestPlugin) throws InterruptedException {
+ String actualConfigText = "none";
+ mSkipTest = false;
+ // Configure settings
+ StreamConfiguration requestedConfig = mAudioOutTester.requestedConfiguration;
+ StreamConfiguration actualConfig = mAudioOutTester.actualConfiguration;
+
+ requestedConfig.reset();
+ requestedConfig.setPerformanceMode(perfMode);
+ requestedConfig.setSharingMode(sharingMode);
+ requestedConfig.setChannelCount(channelCount);
+
+ log("========================== #" + mTestCount);
+ log("Requested:");
+ log(getConfigText(requestedConfig));
+
+ // Give previous stream time to close and release resources. Avoid race conditions.
+ Thread.sleep(SETTLING_TIME_MILLIS);
+ if (!mThreadEnabled) return;
+ boolean openFailed = false;
+ try {
+ startAudioTest(); // this will fill in actualConfig
+ log("Actual:");
+ actualConfigText = getConfigText(actualConfig)
+ + ", path = " + (actualConfig.isMMap() ? "MMAP" : "Legacy");
+ log(actualConfigText);
+ // Set output size to a level that will avoid glitches.
+ AudioStreamBase stream = mAudioOutTester.getCurrentAudioStream();
+ int sizeFrames = stream.getBufferCapacityInFrames() / 2;
+ stream.setBufferSizeInFrames(sizeFrames);
+ } catch (IOException e) {
+ openFailed = true;
+ log(e.getMessage());
+ }
+
+ // The test is only worth running if we got the configuration we requested.
+ boolean valid = true;
+ if (!openFailed) {
+ if(actualConfig.getSharingMode() != sharingMode) {
+ log("did not get requested sharing mode");
+ valid = false;
+ }
+ if (actualConfig.getPerformanceMode() != perfMode) {
+ log("did not get requested performance mode");
+ valid = false;
+ }
+ if (actualConfig.getNativeApi() == StreamConfiguration.NATIVE_API_OPENSLES) {
+ log("OpenSL ES does not support automatic disconnect");
+ valid = false;
+ }
+ }
+
+ int oldPlugCount = mPlugCount;
+ if (!openFailed && valid) {
+ mTestFailed = false;
+ updateFailSkipButton(true);
+ // poll for stream disconnected
+ while (!mTestFailed && mThreadEnabled && !mSkipTest &&
+ mAudioOutTester.getCurrentAudioStream().getState() == StreamConfiguration.STREAM_STATE_STARTING) {
+ Thread.sleep(POLL_DURATION_MILLIS);
+ }
+ String message = (requestPlugin ? "Plug IN" : "UNplug") + " headset now!";
+ setStatusText(message);
+ int timeoutCount = 0;
+ while (!mTestFailed && mThreadEnabled && !mSkipTest &&
+ mAudioOutTester.getCurrentAudioStream().getState() == StreamConfiguration.STREAM_STATE_STARTED) {
+ Thread.sleep(POLL_DURATION_MILLIS);
+ if (mPlugCount > oldPlugCount) {
+ timeoutCount = TIME_TO_FAILURE_MILLIS / POLL_DURATION_MILLIS;
+ break;
+ }
+ }
+ while (!mTestFailed && mThreadEnabled && !mSkipTest && (timeoutCount > 0) &&
+ mAudioOutTester.getCurrentAudioStream().getState() == StreamConfiguration.STREAM_STATE_STARTED) {
+ Thread.sleep(POLL_DURATION_MILLIS);
+ timeoutCount--;
+ if (timeoutCount == 0) {
+ mTestFailed = true;
+ } else {
+ setStatusText("Plug detected by Java.\nCounting down to Oboe failure: " + timeoutCount);
+ }
+ }
+ setStatusText(mTestFailed ? "Failed" : "Passed - detected");
+ }
+ updateFailSkipButton(false);
+
+ if (!openFailed) {
+ stopAudioTest();
+ }
+
+ if (mSkipTest) valid = false;
+
+ if (valid) {
+ if (openFailed) {
+ mFailedSummary.append("------ #" + mTestCount);
+ mFailedSummary.append("\n");
+ mFailedSummary.append(getConfigText(requestedConfig));
+ mFailedSummary.append("\n");
+ mFailedSummary.append("Open failed!\n");
+ mFailCount++;
+ } else {
+ log("Result:");
+ boolean passed = !mTestFailed;
+ String resultText = requestPlugin ? "plugIN" : "UNplug";
+ resultText += ", " + (passed ? TEXT_PASS : TEXT_FAIL);
+ log(resultText);
+ if (!passed) {
+ mFailedSummary.append("------ #" + mTestCount);
+ mFailedSummary.append("\n");
+ mFailedSummary.append(" ");
+ mFailedSummary.append(actualConfigText);
+ mFailedSummary.append("\n");
+ mFailedSummary.append(" ");
+ mFailedSummary.append(resultText);
+ mFailedSummary.append("\n");
+ mFailCount++;
+ } else {
+ mPassCount++;
+ }
+ }
+ } else {
+ log(TEXT_SKIP);
+ }
+ // Give hardware time to settle between tests.
+ Thread.sleep(1000);
+ mTestCount++;
+ }
+
+ private void testConfiguration(int performanceMode,
+ int sharingMode) throws InterruptedException {
+ int channelCount = 2;
+ boolean requestPlugin = true; // plug IN
+ testConfiguration(performanceMode, sharingMode, channelCount, requestPlugin);
+ requestPlugin = false; // UNplug
+ testConfiguration(performanceMode, sharingMode, channelCount, requestPlugin);
+ }
+
+ @Override
+ public void run() {
+ mPlugCount = 0;
+ logClear();
+ log("=== STARTED at " + new Date());
+ log(Build.MANUFACTURER + " " + Build.PRODUCT);
+ log(Build.DISPLAY);
+ mFailedSummary = new StringBuffer();
+ mTestCount = 0;
+ mPassCount = 0;
+ mFailCount = 0;
+ // Try several different configurations.
+ try {
+ testConfiguration(StreamConfiguration.PERFORMANCE_MODE_LOW_LATENCY,
+ StreamConfiguration.SHARING_MODE_EXCLUSIVE);
+ testConfiguration(StreamConfiguration.PERFORMANCE_MODE_LOW_LATENCY,
+ StreamConfiguration.SHARING_MODE_SHARED);
+ testConfiguration(StreamConfiguration.PERFORMANCE_MODE_NONE,
+ StreamConfiguration.SHARING_MODE_SHARED);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } finally {
+ stopAudioTest();
+ setStatusText("Finished. See summary below.");
+ log("\n==== SUMMARY ========");
+ if (mFailCount > 0) {
+ log(mPassCount + " passed. " + mFailCount + " failed.");
+ log("These tests FAILED:");
+ log(mFailedSummary.toString());
+ } else {
+ log("All tests PASSED.");
+ }
+ log("== FINISHED at " + new Date());
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ onTestFinished();
+ }
+ });
+ updateFailSkipButton(false);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/apps/OboeTester/app/src/main/res/layout/activity_main.xml b/apps/OboeTester/app/src/main/res/layout/activity_main.xml
index 39f07ff..bd8d407 100644
--- a/apps/OboeTester/app/src/main/res/layout/activity_main.xml
+++ b/apps/OboeTester/app/src/main/res/layout/activity_main.xml
@@ -73,6 +73,13 @@
android:onClick="onLaunchAutoGlitchTest"
android:text="Auto Glitch Test" />
+ <Button
+ android:id="@+id/button_test_disconnect"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:onClick="onLaunchTestDisconnect"
+ android:text="Test Disconnect" />
+
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/apps/OboeTester/app/src/main/res/layout/activity_test_disconnect.xml b/apps/OboeTester/app/src/main/res/layout/activity_test_disconnect.xml
new file mode 100644
index 0000000..ffcdf13
--- /dev/null
+++ b/apps/OboeTester/app/src/main/res/layout/activity_test_disconnect.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ tools:context="com.google.sample.oboe.manualtest.TestDisconnectActivity">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/button_start"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:onClick="onStartDisconnectTest"
+ android:text="@string/startAudio" />
+
+ <Button
+ android:id="@+id/button_stop"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:onClick="onStopDisconnectTest"
+ android:text="@string/stopAudio" />
+
+ <Button
+ android:id="@+id/button_share"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:onClick="onShareResult"
+ android:text="@string/share" />
+ </LinearLayout>
+
+
+ <TextView
+ android:id="@+id/text_analyzer_result"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:lines="4"
+ android:text="@string/test_disconnect_instructions"
+ android:textSize="18sp"
+ android:textStyle="bold"
+ />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/button_fail"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:onClick="onFailTest"
+ android:text="@string/failTest" />
+
+ <Button
+ android:id="@+id/button_skip"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:onClick="onSkipTest"
+ android:text="@string/skipTest" />
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/text_plug_events"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:lines="1"
+ android:text="plug #"
+ android:textSize="18sp"
+ android:textStyle="bold"
+ />
+
+ <TextView
+ android:id="@+id/text_auto_result"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scrollbars = "vertical"
+ android:gravity="bottom"
+ android:text="@string/log_of_glitch_tests"
+ />
+
+</LinearLayout>
diff --git a/apps/OboeTester/app/src/main/res/values/strings.xml b/apps/OboeTester/app/src/main/res/values/strings.xml
index d9fac72..f6b61ec 100644
--- a/apps/OboeTester/app/src/main/res/values/strings.xml
+++ b/apps/OboeTester/app/src/main/res/values/strings.xml
@@ -131,6 +131,10 @@
<string name="src_prompt">SRC:</string>
<string name="average">Average</string>
+ <string name="failTest">Fail</string>
+ <string name="skipTest">Skip</string>
+ <string name="title_test_disconnect">Test Disconnect</string>
+ <string name="test_disconnect_instructions">Unplug all headsets then click [START]</string>
<string-array name="conversion_qualities">
<item>None</item>
<item>Fastest</item>