summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Robert Wu <robertwu@google.com> 2022-05-18 18:35:04 +0000
committer Robert Wu <robertwu@google.com> 2022-05-19 16:01:52 +0000
commit6cd937c48870b18cbf6575a2019c21e523afbc86 (patch)
tree6707bc9da0e16b1294f8da859397a7c5881a3afa
parent96ad5ccad955dc5fc852d9cd2af9c1cfd4053bbf (diff)
USB MIDI: Use Thread.interrupt() to close threads
We start input threads before setting mIsOpen. This means that sometimes, input threads immediately exit due to a race condition. The fix here is to remove the use of mIsOpen and use a new mechanism with Thread.interrupt() and Thread.interrupted() to mark a thread to be closed. Bug: 223316278 Test: MidiScope/MidiKeyboard Test: CTS Verifier Bluetooth Tests Change-Id: I7c221504ae8a8814c1aa5598552494c80c202eb2
-rw-r--r--services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java60
1 files changed, 48 insertions, 12 deletions
diff --git a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
index dc96c66bff29..df9fc6601ac5 100644
--- a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
@@ -80,9 +80,13 @@ public final class UsbDirectMidiDevice implements Closeable {
// of cycles and not being permanently stuck.
private static final int BULK_TRANSFER_TIMEOUT_MILLISECONDS = 10;
+ // Arbitrary number for timeout when closing a thread
+ private static final int THREAD_JOIN_TIMEOUT_MILLISECONDS = 50;
+
private ArrayList<UsbDeviceConnection> mUsbDeviceConnections;
private ArrayList<ArrayList<UsbEndpoint>> mInputUsbEndpoints;
private ArrayList<ArrayList<UsbEndpoint>> mOutputUsbEndpoints;
+ private ArrayList<Thread> mThreads;
private UsbMidiBlockParser mMidiBlockParser = new UsbMidiBlockParser();
private int mDefaultMidiProtocol = MidiDeviceInfo.PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS;
@@ -273,9 +277,10 @@ public final class UsbDirectMidiDevice implements Closeable {
// to USB MIDI for each USB output.
mUsbMidiPacketConverter = new UsbMidiPacketConverter(mNumOutputs);
- mUsbDeviceConnections = new ArrayList<UsbDeviceConnection>(mUsbInterfaces.size());
- mInputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>(mUsbInterfaces.size());
- mOutputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>(mUsbInterfaces.size());
+ mUsbDeviceConnections = new ArrayList<UsbDeviceConnection>();
+ mInputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>();
+ mOutputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>();
+ mThreads = new ArrayList<Thread>();
for (int interfaceIndex = 0; interfaceIndex < mUsbInterfaces.size(); interfaceIndex++) {
ArrayList<UsbEndpoint> inputEndpoints = new ArrayList<UsbEndpoint>();
@@ -327,7 +332,7 @@ public final class UsbDirectMidiDevice implements Closeable {
mInputUsbEndpoints.get(connectionIndex).get(endpointIndex);
final int portFinal = portNumber;
- new Thread("UsbDirectMidiDevice input thread " + portFinal) {
+ Thread newThread = new Thread("UsbDirectMidiDevice input thread " + portFinal) {
@Override
public void run() {
final UsbRequest request = new UsbRequest();
@@ -335,9 +340,12 @@ public final class UsbDirectMidiDevice implements Closeable {
request.initialize(connectionFinal, endpointFinal);
byte[] inputBuffer = new byte[endpointFinal.getMaxPacketSize()];
while (true) {
+ if (Thread.currentThread().interrupted()) {
+ Log.w(TAG, "input thread interrupted");
+ break;
+ }
// Record time of event immediately after waking.
long timestamp = System.nanoTime();
- if (!mIsOpen) break;
final ByteBuffer byteBuffer = ByteBuffer.wrap(inputBuffer);
if (!request.queue(byteBuffer)) {
Log.w(TAG, "Cannot queue request");
@@ -382,8 +390,9 @@ public final class UsbDirectMidiDevice implements Closeable {
}
Log.d(TAG, "input thread exit");
}
- }.start();
-
+ };
+ newThread.start();
+ mThreads.add(newThread);
portNumber++;
}
}
@@ -402,18 +411,23 @@ public final class UsbDirectMidiDevice implements Closeable {
final int portFinal = portNumber;
final MidiEventScheduler eventSchedulerFinal = mEventSchedulers[portFinal];
- new Thread("UsbDirectMidiDevice output thread " + portFinal) {
+ Thread newThread = new Thread("UsbDirectMidiDevice output thread " + portFinal) {
@Override
public void run() {
while (true) {
+ if (Thread.currentThread().interrupted()) {
+ Log.w(TAG, "output thread interrupted");
+ break;
+ }
MidiEvent event;
try {
event = (MidiEvent) eventSchedulerFinal.waitNextEvent();
} catch (InterruptedException e) {
- // try again
- continue;
+ Log.w(TAG, "event scheduler interrupted");
+ break;
}
if (event == null) {
+ Log.w(TAG, "event is null");
break;
}
@@ -446,8 +460,9 @@ public final class UsbDirectMidiDevice implements Closeable {
}
Log.d(TAG, "output thread exit");
}
- }.start();
-
+ };
+ newThread.start();
+ mThreads.add(newThread);
portNumber++;
}
}
@@ -523,6 +538,27 @@ public final class UsbDirectMidiDevice implements Closeable {
private void closeLocked() {
Log.d(TAG, "closeLocked()");
+
+ // Send an interrupt signal to threads.
+ for (Thread thread : mThreads) {
+ if (thread != null) {
+ thread.interrupt();
+ }
+ }
+
+ // Wait for threads to actually stop.
+ for (Thread thread : mThreads) {
+ if (thread != null) {
+ try {
+ thread.join(THREAD_JOIN_TIMEOUT_MILLISECONDS);
+ } catch (InterruptedException e) {
+ Log.w(TAG, "thread join interrupted");
+ break;
+ }
+ }
+ }
+ mThreads = null;
+
for (int i = 0; i < mEventSchedulers.length; i++) {
mMidiInputPortReceivers[i].setReceiver(null);
mEventSchedulers[i].close();