2929
3030package ioio .smallbasic .android ;
3131
32- import android .app .PendingIntent ;
3332import android .content .Context ;
3433import android .hardware .usb .UsbAccessory ;
3534import android .hardware .usb .UsbManager ;
36- import android .os .ParcelFileDescriptor ;
3735
38- import java .io .BufferedOutputStream ;
39- import java .io .FileDescriptor ;
40- import java .io .FileInputStream ;
41- import java .io .FileOutputStream ;
42- import java .io .IOException ;
43- import java .io .InputStream ;
44- import java .io .OutputStream ;
4536import java .util .Collection ;
4637
4738import ioio .lib .api .IOIOConnection ;
48- import ioio .lib .api .exception .ConnectionLostException ;
49- import ioio .lib .impl .FixedReadBufferedInputStream ;
5039import ioio .lib .spi .IOIOConnectionBootstrap ;
5140import ioio .lib .spi .IOIOConnectionFactory ;
5241import ioio .lib .spi .Log ;
5342import ioio .lib .spi .NoRuntimeSupportException ;
5443
55- public class AccessoryConnectionBootstrap //extends BroadcastReceiver
56- implements IOIOConnectionBootstrap , IOIOConnectionFactory {
44+ public class AccessoryConnectionBootstrap implements IOIOConnectionBootstrap , IOIOConnectionFactory {
5745 private static final String TAG = AccessoryConnectionBootstrap .class .getSimpleName ();
58- private static final String ACTION_USB_PERMISSION = "ioio.lib.accessory.action.USB_PERMISSION" ;
59- private static final String EXTRA_PERMISSION_GRANTED = UsbManager .EXTRA_PERMISSION_GRANTED ;
60-
61- private final Context activity ;
62- private final UsbManager usbManager ;
63- private boolean shouldTryOpen = false ;
64- private PendingIntent pendingIntent ;
65- private ParcelFileDescriptor fileDescriptor ;
66- private InputStream inputStream ;
67- private OutputStream outputStream ;
68-
69- private enum InstanceState {
70- INIT , CONNECTED , DEAD
71- }
7246
7347 public AccessoryConnectionBootstrap () throws NoRuntimeSupportException {
7448 Log .d (TAG , "creating AccessoryConnectionBootstrap" );
75- this .activity = IOIOLoader .getContext ();
76- this .usbManager = (UsbManager ) activity .getSystemService (Context .USB_SERVICE );
77- // registerReceiver();
7849 }
7950
8051 @ Override
8152 public IOIOConnection createConnection () {
82- return new Connection ();
53+ Log .i (TAG , "createConnection" );
54+ Context activity = IOIOLoader .getContext ();
55+ UsbManager usbManager = (UsbManager ) activity .getSystemService (Context .USB_SERVICE );
56+ UsbAccessory accessory = usbManager .getAccessoryList ()[0 ];
57+ return new BluetoothConnection (getUsbManager ().openAccessory (accessory ));
8358 }
8459
8560 @ Override
@@ -94,231 +69,11 @@ public void getFactories(Collection<IOIOConnectionFactory> result) {
9469
9570 @ Override
9671 public String getType () {
97- return Connection .class .getCanonicalName ();
98- }
99-
100- // //@Override
101- // public void onDestroy() {
102- // activity.unregisterReceiver(this);
103- // }
104-
105- // @Override
106- // public synchronized void onReceive(Context context, Intent intent) {
107- // final String action = intent.getAction();
108- // if (ACTION_USB_PERMISSION.equals(action)) {
109- // pendingIntent = null;
110- // if (intent.getBooleanExtra(EXTRA_PERMISSION_GRANTED, false)) {
111- // notifyAll();
112- // } else {
113- // Log.e(TAG, "Permission denied");
114- // }
115- // }
116- // }
117-
118- //@Override
119- public synchronized void open () {
120- notifyAll ();
72+ return BluetoothConnection .class .getCanonicalName ();
12173 }
12274
123- //@Override
124- public synchronized void reopen () {
125- notifyAll ();
126- }
127-
128- private synchronized void disconnect () {
129- // This should abort any current open attempt.
130- Log .d (TAG , "private disconnect" );
131- shouldTryOpen = false ;
132- notifyAll ();
133-
134- // And this should kill any ongoing connections.
135- if (fileDescriptor != null ) {
136- try {
137- fileDescriptor .close ();
138- } catch (IOException e ) {
139- Log .e (TAG , "Failed to close file descriptor." , e );
140- }
141- fileDescriptor = null ;
142- }
143-
144- if (pendingIntent != null ) {
145- pendingIntent .cancel ();
146- pendingIntent = null ;
147- }
148- Log .d (TAG , "leaving private disconnect" );
149- }
150-
151- private void forceWait () {
152- try {
153- wait ();
154- } catch (InterruptedException e ) {
155- Log .e (TAG , "Do not interrupt me!" );
156- }
157- }
158-
159- // @TargetApi(Build.VERSION_CODES.TIRAMISU)
160- // private void registerReceiver() {
161- // IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
162- // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
163- // activity.registerReceiver(this, filter, Context.RECEIVER_NOT_EXPORTED);
164- // }
165- // }
166-
167- private boolean tryOpen () {
168- // Find the accessory.
169- UsbAccessory [] accessories = usbManager .getAccessoryList ();
170- UsbAccessory accessory = (accessories == null ? null : accessories [0 ]);
171-
172- if (accessory == null ) {
173- Log .v (TAG , "No accessory found." );
174- return false ;
175- }
176-
177- // Check for permission to access the accessory.
178- if (!usbManager .hasPermission (accessory )) {
179- // if (pendingIntent == null) {
180- // Log.v(TAG, "Requesting permission.");
181- // pendingIntent = PendingIntent.getBroadcast(activity, 0, new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE);
182- // usbManager.requestPermission(accessory, pendingIntent);
183- // }
184- return false ;
185- }
186-
187- boolean success = false ;
188-
189- // From this point on, if anything goes wrong, we're responsible for canceling the intent.
190- try {
191- // Obtain a file descriptor.
192- fileDescriptor = usbManager .openAccessory (accessory );
193- if (fileDescriptor == null ) {
194- Log .v (TAG , "Failed to open file descriptor." );
195- return false ;
196- }
197-
198- // From this point on, if anything goes wrong, we're responsible for closing the file
199- // descriptor.
200- try {
201- FileDescriptor fd = fileDescriptor .getFileDescriptor ();
202- // Apparently, some Android devices (e.g. Nexus 5) only support read operations of
203- // multiples of the endpoint buffer size. So there you have it!
204- inputStream = new FixedReadBufferedInputStream (new FileInputStream (fd ), 1024 );
205- outputStream = new BufferedOutputStream (new FileOutputStream (fd ), 1024 );
206-
207- // Soft-open the connection
208- outputStream .write (0x00 );
209- outputStream .flush ();
210-
211- // We're going to block now. We're counting on the IOIO to
212- // write back a byte, or otherwise we're locked until
213- // physical disconnection. This is a known OpenAccessory
214- // bug:
215- // http://code.google.com/p/android/issues/detail?id=20545
216- while (inputStream .read () != 1 ) {
217- trySleep ();
218- }
219-
220- success = true ;
221- return true ;
222- } catch (IOException e ) {
223- Log .v (TAG , "Failed to open streams" , e );
224- return false ;
225- } finally {
226- if (!success ) {
227- try {
228- fileDescriptor .close ();
229- } catch (IOException e ) {
230- Log .e (TAG , "Failed to close file descriptor." , e );
231- }
232- fileDescriptor = null ;
233- }
234- }
235- } finally {
236- if (!success && pendingIntent != null ) {
237- pendingIntent .cancel ();
238- pendingIntent = null ;
239- }
240- }
241- }
242-
243- private void trySleep () {
244- synchronized (AccessoryConnectionBootstrap .this ) {
245- try {
246- AccessoryConnectionBootstrap .this .wait (1000 );
247- } catch (InterruptedException e ) {
248- Log .e (TAG , e .toString ());
249- }
250- }
251- }
252-
253- private synchronized void waitForConnect (Connection connection ) throws ConnectionLostException {
254- // In order to simplify the connection process in face of many different sequences of events
255- // that might occur, we collapsed the entire sequence into one non-blocking method,
256- // tryOpen(), which tries the entire process from the beginning, undoes everything if
257- // something along the way fails and always returns immediately.
258- // This method, simply calls tryOpen() in a loop until it succeeds or until we're no longer
259- // interested. Between attempts, it waits until "something interesting" has happened, which
260- // may be permission granted, the client telling us to try again (via reopen()) or stop
261- // trying, etc.
262- shouldTryOpen = true ;
263- while (shouldTryOpen ) {
264- if (tryOpen ()) {
265- // Success!
266- return ;
267- }
268- forceWait ();
269- }
270- throw new ConnectionLostException ();
271- }
272-
273- private class Connection implements IOIOConnection {
274- private InstanceState instanceState_ = InstanceState .INIT ;
275-
276- @ Override
277- public boolean canClose () {
278- return false ;
279- }
280-
281- @ Override
282- public void disconnect () {
283- Log .d (TAG , "disconnect" );
284- synchronized (AccessoryConnectionBootstrap .this ) {
285- if (instanceState_ != InstanceState .DEAD ) {
286- AccessoryConnectionBootstrap .this .disconnect ();
287- instanceState_ = InstanceState .DEAD ;
288- }
289- }
290- }
291-
292- @ Override
293- public InputStream getInputStream () {
294- return inputStream ;
295- }
296-
297- @ Override
298- public OutputStream getOutputStream () {
299- return outputStream ;
300- }
301-
302- @ Override
303- public void waitForConnect () throws ConnectionLostException {
304- synchronized (AccessoryConnectionBootstrap .this ) {
305- if (instanceState_ != InstanceState .INIT ) {
306- throw new IllegalStateException ("waitForConnect() may only be called once" );
307- }
308-
309- try {
310- AccessoryConnectionBootstrap .this .waitForConnect (this );
311- instanceState_ = InstanceState .CONNECTED ;
312- } catch (ConnectionLostException e ) {
313- instanceState_ = InstanceState .DEAD ;
314- throw e ;
315- }
316- }
317- }
318-
319- @ Override
320- protected void finalize () {
321- disconnect ();
322- }
75+ private UsbManager getUsbManager () {
76+ Context activity = IOIOLoader .getContext ();
77+ return (UsbManager ) activity .getSystemService (Context .USB_SERVICE );
32378 }
32479}
0 commit comments