diff options
Diffstat (limited to 'apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestDisconnectActivity.java')
-rw-r--r-- | apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestDisconnectActivity.java | 209 |
1 files changed, 181 insertions, 28 deletions
diff --git a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestDisconnectActivity.java b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestDisconnectActivity.java index 2f3c78c5..b74c3b10 100644 --- a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestDisconnectActivity.java +++ b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestDisconnectActivity.java @@ -20,9 +20,16 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.hardware.usb.UsbConstants; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbEndpoint; +import android.hardware.usb.UsbInterface; +import android.hardware.usb.UsbManager; import android.os.Bundle; +import android.util.Log; import android.view.View; import android.widget.Button; +import android.widget.CheckBox; import android.widget.TextView; import java.io.IOException; @@ -47,9 +54,14 @@ public class TestDisconnectActivity extends TestAudioActivity { private volatile boolean mTestFailed; private volatile boolean mSkipTest; private volatile int mPlugCount; + private volatile int mUsbDeviceAttachedCount; + private volatile int mPlugState; + private volatile int mPlugMicrophone; private BroadcastReceiver mPluginReceiver = new PluginBroadcastReceiver(); private Button mFailButton; private Button mSkipButton; + private CheckBox mCheckBoxInputs; + private CheckBox mCheckBoxOutputs; protected AutomatedTestRunner mAutomatedTestRunner; @@ -58,15 +70,68 @@ public class TestDisconnectActivity extends TestAudioActivity { public class PluginBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - mPlugCount++; + switch (intent.getAction()) { + case Intent.ACTION_HEADSET_PLUG: { + mPlugMicrophone = intent.getIntExtra("microphone", -1); + mPlugState = intent.getIntExtra("state", -1); + mPlugCount++; + } break; + case UsbManager.ACTION_USB_DEVICE_ATTACHED: + case UsbManager.ACTION_USB_DEVICE_DETACHED: { + UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + final boolean hasAudioPlayback = + containsAudioStreamingInterface(device, UsbConstants.USB_DIR_OUT); + final boolean hasAudioCapture = + containsAudioStreamingInterface(device, UsbConstants.USB_DIR_IN); + if (hasAudioPlayback || hasAudioCapture) { + mPlugState = + intent.getAction() == UsbManager.ACTION_USB_DEVICE_ATTACHED ? 1 : 0; + mUsbDeviceAttachedCount++; + mPlugMicrophone = hasAudioCapture ? 1 : 0; + } + } break; + default: + break; + } runOnUiThread(new Runnable() { @Override public void run() { - String message = "Intent.HEADSET_PLUG #" + mPlugCount; + String message = "HEADSET_PLUG #" + mPlugCount + + ", USB_DEVICE_DE/ATTACHED #" + mUsbDeviceAttachedCount + + ", mic = " + mPlugMicrophone + + ", state = " + mPlugState; mPlugTextView.setText(message); + log(message); } }); } + + private static final int AUDIO_STREAMING_SUB_CLASS = 2; + + /** + * Figure out if an UsbDevice contains audio input/output streaming interface or not. + * + * @param device the given UsbDevice + * @param direction the direction of the audio streaming interface + * @return true if the UsbDevice contains the audio input/output streaming interface. + */ + private boolean containsAudioStreamingInterface(UsbDevice device, int direction) { + final int interfaceCount = device.getInterfaceCount(); + for (int i = 0; i < interfaceCount; ++i) { + UsbInterface usbInterface = device.getInterface(i); + if (usbInterface.getInterfaceClass() != UsbConstants.USB_CLASS_AUDIO + && usbInterface.getInterfaceSubclass() != AUDIO_STREAMING_SUB_CLASS) { + continue; + } + final int endpointCount = usbInterface.getEndpointCount(); + for (int j = 0; j < endpointCount; ++j) { + if (usbInterface.getEndpoint(j).getDirection() == direction) { + return true; + } + } + } + return false; + } } @Override @@ -85,6 +150,9 @@ public class TestDisconnectActivity extends TestAudioActivity { mStatusTextView = (TextView) findViewById(R.id.text_status); mPlugTextView = (TextView) findViewById(R.id.text_plug_events); + mCheckBoxInputs = (CheckBox)findViewById(R.id.checkbox_disco_inputs); + mCheckBoxOutputs = (CheckBox)findViewById(R.id.checkbox_disco_outputs); + mFailButton = (Button) findViewById(R.id.button_fail); mSkipButton = (Button) findViewById(R.id.button_skip); updateFailSkipButton(false); @@ -138,6 +206,8 @@ public class TestDisconnectActivity extends TestAudioActivity { public void onResume() { super.onResume(); IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); + filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); + filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); this.registerReceiver(mPluginReceiver, filter); } @@ -185,15 +255,25 @@ public class TestDisconnectActivity extends TestAudioActivity { private String getConfigText(StreamConfiguration config) { return ((config.getDirection() == StreamConfiguration.DIRECTION_OUTPUT) ? "OUT" : "IN") + ", Perf = " + StreamConfiguration.convertPerformanceModeToText( - config.getPerformanceMode()) + config.getPerformanceMode()) + ", " + StreamConfiguration.convertSharingModeToText(config.getSharingMode()) - + ", " + config.getSampleRate(); + + ", " + config.getSampleRate() + + ", SRC = " + StreamConfiguration.convertRateConversionQualityToText(config.getRateConversionQuality()); + } + + private void log(Exception e) { + Log.e(TestAudioActivity.TAG, "Caught ", e); + mAutomatedTestRunner.log("Caught " + e); } private void log(String text) { mAutomatedTestRunner.log(text); } + private void flushLog() { + mAutomatedTestRunner.flushLog(); + } + private void appendFailedSummary(String text) { mAutomatedTestRunner.appendFailedSummary(text); } @@ -202,14 +282,36 @@ public class TestDisconnectActivity extends TestAudioActivity { int perfMode, int sharingMode, int sampleRate, + int sampleRateConversionQuality, boolean requestPlugin) throws InterruptedException { if ((getSingleTestIndex() >= 0) && (mAutomatedTestRunner.getTestCount() != getSingleTestIndex())) { mAutomatedTestRunner.incrementTestCount(); return; } + if (!isInput && !mCheckBoxOutputs.isChecked()) { + return; + } + if (isInput && !mCheckBoxInputs.isChecked()) { + return; + } + + updateFailSkipButton(true); + String actualConfigText = "none"; mSkipTest = false; + mTestFailed = false; + + // Try to synchronize with the current headset state, IN or OUT. + while (mAutomatedTestRunner.isThreadEnabled() && !mSkipTest && !mTestFailed) { + if (requestPlugin != (mPlugState == 0)) { + String message = "SYNC: " + (requestPlugin ? "UNplug" : "Plug IN") + " headset now!"; + setInstructionsText(message); + Thread.sleep(POLL_DURATION_MILLIS); + } else { + break; + } + } AudioInputTester mAudioInTester = null; AudioOutputTester mAudioOutTester = null; @@ -234,8 +336,9 @@ public class TestDisconnectActivity extends TestAudioActivity { requestedConfig.setPerformanceMode(perfMode); requestedConfig.setSharingMode(sharingMode); requestedConfig.setSampleRate(sampleRate); + if (sampleRate != 0) { - requestedConfig.setRateConversionQuality(StreamConfiguration.RATE_CONVERSION_QUALITY_MEDIUM); + requestedConfig.setRateConversionQuality(sampleRateConversionQuality); } log("========================== #" + mAutomatedTestRunner.getTestCount()); @@ -246,20 +349,24 @@ public class TestDisconnectActivity extends TestAudioActivity { Thread.sleep(SETTLING_TIME_MILLIS); if (!mAutomatedTestRunner.isThreadEnabled()) return; boolean openFailed = false; + boolean hasMicFailed = false; AudioStreamBase stream = null; try { openAudio(); log("Actual:"); actualConfigText = getConfigText(actualConfig) - + ", " + (actualConfig.isMMap() ? "MMAP" : "Legacy"); + + ", " + ((actualConfig.isMMap() ? "MMAP" : "Legacy") + + ", Dev = " + actualConfig.getDeviceId() + ); log(actualConfigText); + flushLog(); stream = (isInput) ? mAudioInTester.getCurrentAudioStream() : mAudioOutTester.getCurrentAudioStream(); } catch (IOException e) { openFailed = true; - log(e.getMessage()); + log(e); } // The test is only worth running if we got the configuration we requested. @@ -285,14 +392,13 @@ public class TestDisconnectActivity extends TestAudioActivity { } catch (IOException e) { e.printStackTrace(); valid = false; - log(e.getMessage()); + log(e); } } int oldPlugCount = mPlugCount; if (!openFailed && valid) { mTestFailed = false; - updateFailSkipButton(true); // poll until stream started while (!mTestFailed && mAutomatedTestRunner.isThreadEnabled() && !mSkipTest && stream.getState() == StreamConfiguration.STREAM_STATE_STARTING) { @@ -305,15 +411,18 @@ public class TestDisconnectActivity extends TestAudioActivity { // Wait for Java plug count to change or stream to disconnect. while (!mTestFailed && mAutomatedTestRunner.isThreadEnabled() && !mSkipTest && stream.getState() == StreamConfiguration.STREAM_STATE_STARTED) { + flushLog(); Thread.sleep(POLL_DURATION_MILLIS); if (mPlugCount > oldPlugCount) { timeoutCount = TIME_TO_FAILURE_MILLIS / POLL_DURATION_MILLIS; break; } } + // Wait for timeout or stream to disconnect. while (!mTestFailed && mAutomatedTestRunner.isThreadEnabled() && !mSkipTest && (timeoutCount > 0) && stream.getState() == StreamConfiguration.STREAM_STATE_STARTED) { + flushLog(); Thread.sleep(POLL_DURATION_MILLIS); timeoutCount--; if (timeoutCount == 0) { @@ -322,15 +431,26 @@ public class TestDisconnectActivity extends TestAudioActivity { setStatusText("Plug detected by Java.\nCounting down to Oboe failure: " + timeoutCount); } } - if (!mTestFailed) { - int error = stream.getLastErrorCallbackResult(); - if (error != StreamConfiguration.ERROR_DISCONNECTED) { - log("onEerrorCallback error = " + error - + ", expected " + StreamConfiguration.ERROR_DISCONNECTED); - mTestFailed = true; + + if (mSkipTest) { + setStatusText("Skipped"); + } else { + if (mTestFailed) { + // Check whether the peripheral has a microphone. + // Sometimes the microphones does not appear on the first HEADSET_PLUG event. + if (isInput && (mPlugMicrophone == 0)) { + hasMicFailed = true; + } + } else { + int error = stream.getLastErrorCallbackResult(); + if (error != StreamConfiguration.ERROR_DISCONNECTED) { + log("onErrorCallback error = " + error + + ", expected " + StreamConfiguration.ERROR_DISCONNECTED); + mTestFailed = true; + } } + setStatusText(mTestFailed ? "Failed" : "Passed - detected"); } - setStatusText(mTestFailed ? "Failed" : "Passed - detected"); } updateFailSkipButton(false); setInstructionsText("Wait..."); @@ -352,6 +472,9 @@ public class TestDisconnectActivity extends TestAudioActivity { boolean passed = !mTestFailed; String resultText = requestPlugin ? "plugIN" : "UNplug"; resultText += ", " + (passed ? TEXT_PASS : TEXT_FAIL); + if (hasMicFailed) { + resultText += ", Headset has no mic!"; + } log(resultText); if (!passed) { appendFailedSummary("------ #" + mAutomatedTestRunner.getTestCount() + "\n"); @@ -365,17 +488,27 @@ public class TestDisconnectActivity extends TestAudioActivity { } else { log(TEXT_SKIP); } + flushLog(); // Give hardware time to settle between tests. Thread.sleep(1000); mAutomatedTestRunner.incrementTestCount(); } private void testConfiguration(boolean isInput, int performanceMode, - int sharingMode, int sampleRate) throws InterruptedException { + int sharingMode, int sampleRate, + int sampleRateConversionQuality) throws InterruptedException { boolean requestPlugin = true; // plug IN - testConfiguration(isInput, performanceMode, sharingMode, sampleRate, requestPlugin); + testConfiguration(isInput, performanceMode, sharingMode, sampleRate, + sampleRateConversionQuality, requestPlugin); requestPlugin = false; // UNplug - testConfiguration(isInput, performanceMode, sharingMode, sampleRate, requestPlugin); + testConfiguration(isInput, performanceMode, sharingMode, sampleRate, + sampleRateConversionQuality, requestPlugin); + } + + private void testConfiguration(boolean isInput, int performanceMode, + int sharingMode, int sampleRate) throws InterruptedException { + testConfiguration(isInput, performanceMode, sharingMode, sampleRate, + StreamConfiguration.RATE_CONVERSION_QUALITY_NONE); } private void testConfiguration(boolean isInput, int performanceMode, @@ -390,25 +523,45 @@ public class TestDisconnectActivity extends TestAudioActivity { testConfiguration(true, performanceMode, sharingMode); } + private void testConfiguration(int performanceMode, + int sharingMode, int sampleRate, + int sampleRateConversionQuality) throws InterruptedException { + testConfiguration(false, performanceMode, sharingMode, sampleRate, sampleRateConversionQuality); + testConfiguration(true, performanceMode, sharingMode, sampleRate, sampleRateConversionQuality); + } + @Override public void runTest() { + + runOnUiThread(() -> keepScreenOn(true)); + mPlugCount = 0; + // Try several different configurations. try { - testConfiguration(false, StreamConfiguration.PERFORMANCE_MODE_LOW_LATENCY, - StreamConfiguration.SHARING_MODE_EXCLUSIVE, 44100); - 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); + if (NativeEngine.isMMapExclusiveSupported()){ + testConfiguration(StreamConfiguration.PERFORMANCE_MODE_LOW_LATENCY, + StreamConfiguration.SHARING_MODE_EXCLUSIVE); + } + testConfiguration(StreamConfiguration.PERFORMANCE_MODE_LOW_LATENCY, + StreamConfiguration.SHARING_MODE_SHARED); + testConfiguration(StreamConfiguration.PERFORMANCE_MODE_LOW_LATENCY, + StreamConfiguration.SHARING_MODE_SHARED, 44100, + StreamConfiguration.RATE_CONVERSION_QUALITY_NONE); + testConfiguration(StreamConfiguration.PERFORMANCE_MODE_LOW_LATENCY, + StreamConfiguration.SHARING_MODE_SHARED, 44100, + StreamConfiguration.RATE_CONVERSION_QUALITY_MEDIUM); } catch (InterruptedException e) { - log(e.getMessage()); - showErrorToast(e.getMessage()); + log("Test CANCELLED - INVALID!"); + } catch (Exception e) { + log(e); + showErrorToast("Caught " + e); } finally { - setInstructionsText("Test completed."); + setInstructionsText("Test finished."); updateFailSkipButton(false); + runOnUiThread(() -> keepScreenOn(false)); } } } |