Skip to content

Commit e42ec3d

Browse files
committed
IOIO: make the android usb connection more reliable
1 parent 4a37079 commit e42ec3d

File tree

4 files changed

+100
-34
lines changed

4 files changed

+100
-34
lines changed

ioio/ioio/src/main/java/ioio/smallbasic/IOIOImpl.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,16 @@ public void sync() {
6161

6262
public void waitForConnect(int latency) {
6363
IOUtil.setError(null);
64-
TimerUtil.setLatency(latency);
64+
if (latency < 0) {
65+
IOUtil.setHardReset(true);
66+
} else {
67+
IOUtil.setHardReset(false);
68+
TimerUtil.setLatency(latency);
69+
}
6570
IOService.getInstance().start();
71+
handleError();
6672
lock.invoke(IOIO::waitForConnect);
73+
handleError();
6774
}
6875

6976
public void waitForDisconnect() {

ioio/ioio/src/main/java/ioio/smallbasic/IOUtil.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package ioio.smallbasic;
22

3+
import java.util.concurrent.atomic.AtomicBoolean;
34
import java.util.concurrent.atomic.AtomicReference;
45

56
public class IOUtil {
67
private static final AtomicReference<String> ERROR = new AtomicReference<>();
8+
private static final AtomicBoolean HARD_RESET = new AtomicBoolean(false);
79

810
private IOUtil() {
911
// no access
@@ -13,11 +15,19 @@ public static String getError() {
1315
return ERROR.get();
1416
}
1517

18+
public static boolean getHardReset() {
19+
return HARD_RESET.get();
20+
}
21+
1622
public static synchronized void setError(String error) {
1723
if (error != null && ERROR.get() != null && !ERROR.get().contains(error)) {
1824
ERROR.set(error + " [" + ERROR.get() + "]");
1925
} else {
2026
ERROR.set(error);
2127
}
2228
}
29+
30+
public static void setHardReset(boolean hardReset) {
31+
HARD_RESET.set(hardReset);
32+
}
2333
}

ioio/ioio/src/main/java/ioio/smallbasic/android/AccessoryPermissionCheck.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
import android.os.Build;
1212
import android.os.Handler;
1313
import android.os.Looper;
14+
import android.widget.Toast;
1415

1516
import ioio.lib.spi.Log;
1617
import ioio.smallbasic.IOIOException;
18+
import ioio.smallbasic.IOUtil;
1719

1820
public class AccessoryPermissionCheck extends BroadcastReceiver {
1921
private static final String TAG = AccessoryPermissionCheck.class.getSimpleName();
@@ -28,7 +30,7 @@ public AccessoryPermissionCheck() {
2830
throw new IOIOException("No usb accessory found.");
2931
}
3032

31-
UsbManager usbManager = (UsbManager) IOIOLoader.getContext().getSystemService(Context.USB_SERVICE);
33+
UsbManager usbManager = UsbUtil.getUsbManager();
3234
if (!usbManager.hasPermission(accessory)) {
3335
new Handler(Looper.getMainLooper()).post(() -> {
3436
Context context = IOIOLoader.getContext();
@@ -40,8 +42,9 @@ public AccessoryPermissionCheck() {
4042
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, flags);
4143
usbManager.requestPermission(accessory, pendingIntent);
4244
});
43-
// for some reason using a latch here caused an ANR
45+
// for some reason using a latch here causes an ANR
4446
Log.d(TAG, "requesting permission");
47+
IOUtil.setHardReset(true);
4548
throw new IOIOException(PERMISSION_ERROR);
4649
}
4750
}
@@ -50,8 +53,11 @@ public AccessoryPermissionCheck() {
5053
public synchronized void onReceive(final Context context, Intent intent) {
5154
Log.d(TAG, "onReceive entered");
5255
if (ACTION_USB_PERMISSION.equals(intent.getAction())) {
56+
boolean permitted = UsbUtil.getUsbManager().hasPermission(UsbUtil.getUsbAccessory());
57+
final String message = "USB access " + (permitted ? "permitted" : "denied");
5358
final BroadcastReceiver receiver = this;
5459
new Handler(Looper.getMainLooper()).post(() -> {
60+
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
5561
context.unregisterReceiver(receiver);
5662
});
5763
}

ioio/ioio/src/main/java/ioio/smallbasic/android/UsbConnection.java

Lines changed: 74 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.io.FileDescriptor;
3636
import java.io.FileInputStream;
3737
import java.io.FileOutputStream;
38+
import java.io.IOException;
3839
import java.io.InputStream;
3940
import java.io.OutputStream;
4041

@@ -47,6 +48,8 @@
4748
class UsbConnection implements IOIOConnection {
4849
private static final String TAG = UsbConnection.class.getSimpleName();
4950
private static final int SOFT_RESET = 0x01;
51+
private static final int HARD_RESET = 0x00;
52+
private static final int MAX_RETRIES = 10;
5053
private ConnectionState state;
5154
private FixedReadBufferedInputStream inputStream;
5255
private OutputStream outputStream;
@@ -73,17 +76,8 @@ public boolean canClose() {
7376
public synchronized void disconnect() {
7477
Log.i(TAG, "disconnect entered");
7578
if (this.state != ConnectionState.DISCONNECTED) {
76-
this.state = ConnectionState.DISCONNECTED;
7779
IOUtil.setError("USB disconnected");
78-
if (fileDescriptor != null) {
79-
try {
80-
fileDescriptor.close();
81-
} catch (java.io.IOException e) {
82-
IOUtil.setError("Failed to close file descriptor");
83-
Log.e(TAG, "Failed to close file descriptor.", e);
84-
}
85-
fileDescriptor = null;
86-
}
80+
close();
8781
}
8882
Log.d(TAG, "leaving disconnect");
8983
}
@@ -103,10 +97,6 @@ public synchronized void waitForConnect() {
10397
if (state != ConnectionState.INIT) {
10498
throw new IllegalStateException("waitForConnect() may only be called once");
10599
}
106-
this.fileDescriptor = UsbUtil.getParcelFileDescriptor();
107-
if (this.fileDescriptor == null) {
108-
throw new IOIOException("Failed to obtain descriptor");
109-
}
110100
if (open()) {
111101
state = ConnectionState.CONNECTED;
112102
} else {
@@ -119,34 +109,87 @@ protected void finalize() {
119109
disconnect();
120110
}
121111

112+
private void close() {
113+
state = ConnectionState.DISCONNECTED;
114+
try {
115+
if (inputStream != null) {
116+
inputStream.close();
117+
}
118+
if (outputStream != null) {
119+
outputStream.close();
120+
}
121+
if (fileDescriptor != null) {
122+
fileDescriptor.close();
123+
}
124+
inputStream = null;
125+
outputStream = null;
126+
fileDescriptor = null;
127+
} catch (java.io.IOException e) {
128+
IOUtil.setError("Failed to close file descriptor: " + e);
129+
Log.e(TAG, "Failed to close file descriptor.", e);
130+
}
131+
}
132+
133+
private void handleResetResponse(int attempt) throws IOException {
134+
if (attempt > 0) {
135+
int response = inputStream.read();
136+
Log.e(TAG, "Response:" + response + " available:" + inputStream.available());
137+
if (response != SOFT_RESET) {
138+
// fail
139+
if (inputStream.available() == 0) {
140+
try {
141+
Thread.sleep(100);
142+
}
143+
catch (InterruptedException e) {
144+
throw new IOIOException(e);
145+
}
146+
}
147+
handleResetResponse(attempt - 1);
148+
}
149+
} else {
150+
throw new IOIOException("USB connection failure");
151+
}
152+
}
153+
122154
private boolean open() {
123155
boolean result = false;
124156
Log.i(TAG, "open() entered");
125157

126158
try {
127-
FileDescriptor fd = fileDescriptor.getFileDescriptor();
128-
inputStream = new FixedReadBufferedInputStream(new FileInputStream(fd), 1024);
129-
outputStream = new BufferedOutputStream(new FileOutputStream(fd), 1024);
130-
outputStream.write(SOFT_RESET);
131-
outputStream.flush();
132-
int response = inputStream.read();
133-
if (response == SOFT_RESET) {
134-
result = true;
135-
} else {
136-
IOUtil.setError("Unexpected response: " + response);
137-
}
159+
openStreams();
160+
resetBoard();
161+
handleResetResponse(MAX_RETRIES);
162+
result = true;
138163
} catch (java.io.IOException e) {
139164
IOUtil.setError("Failed to open streams: " + e);
140165
} finally {
141166
if (!result) {
142-
try {
143-
fileDescriptor.close();
144-
} catch (java.io.IOException e) {
145-
Log.e(TAG, "Failed to close file descriptor.", e);
146-
}
147-
fileDescriptor = null;
167+
close();
148168
}
149169
}
150170
return result;
151171
}
172+
173+
private void openStreams() {
174+
this.fileDescriptor = UsbUtil.getParcelFileDescriptor();
175+
if (this.fileDescriptor == null) {
176+
throw new IOIOException("Failed to obtain descriptor");
177+
}
178+
FileDescriptor fd = fileDescriptor.getFileDescriptor();
179+
inputStream = new FixedReadBufferedInputStream(new FileInputStream(fd), 1024);
180+
outputStream = new BufferedOutputStream(new FileOutputStream(fd), 1024);
181+
}
182+
183+
private void resetBoard() throws IOException {
184+
if (IOUtil.getHardReset()) {
185+
outputStream.write(HARD_RESET);
186+
outputStream.write('I');
187+
outputStream.write('O');
188+
outputStream.write('I');
189+
outputStream.write('O');
190+
} else {
191+
outputStream.write(SOFT_RESET);
192+
}
193+
outputStream.flush();
194+
}
152195
}

0 commit comments

Comments
 (0)