• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.shadows;
2 
3 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
4 import static android.os.Build.VERSION_CODES.KITKAT;
5 import static android.os.Build.VERSION_CODES.LOLLIPOP;
6 import static android.os.Build.VERSION_CODES.O;
7 
8 import android.hardware.usb.UsbDeviceConnection;
9 import android.hardware.usb.UsbEndpoint;
10 import android.hardware.usb.UsbInterface;
11 import android.hardware.usb.UsbRequest;
12 import java.io.FilterInputStream;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.io.PipedInputStream;
16 import java.io.PipedOutputStream;
17 import java.util.concurrent.TimeoutException;
18 import org.robolectric.annotation.Implementation;
19 import org.robolectric.annotation.Implements;
20 
21 /** Robolectric implementation of {@link android.hardware.usb.UsbDeviceConnection}. */
22 @Implements(value = UsbDeviceConnection.class)
23 public class ShadowUsbDeviceConnection {
24 
25   private PipedInputStream outgoingDataPipedInputStream;
26   private PipedOutputStream outgoingDataPipedOutputStream;
27 
28   private DataListener dataListener;
29 
30   @Implementation
claimInterface(UsbInterface intf, boolean force)31   protected boolean claimInterface(UsbInterface intf, boolean force) {
32     try {
33       this.outgoingDataPipedInputStream = new PipedInputStream();
34       this.outgoingDataPipedOutputStream = new PipedOutputStream(outgoingDataPipedInputStream);
35     } catch (IOException e) {
36       return false;
37     }
38 
39     return true;
40   }
41 
42   @Implementation
releaseInterface(UsbInterface intf)43   protected boolean releaseInterface(UsbInterface intf) {
44     try {
45       outgoingDataPipedInputStream.close();
46     } catch (IOException e) {
47       // ignored
48     }
49     try {
50       outgoingDataPipedOutputStream.close();
51     } catch (IOException e) {
52       // ignored
53     }
54 
55     return true;
56   }
57 
58   /**
59    * No-op on Robolectrict. The real implementation would return false on Robolectric and make it
60    * impossible to test callers that expect a successful result. Always returns {@code true}.
61    */
62   @Implementation(minSdk = LOLLIPOP)
setInterface(UsbInterface intf)63   protected boolean setInterface(UsbInterface intf) {
64     return true;
65   }
66 
67   @Implementation(minSdk = KITKAT)
controlTransfer( int requestType, int request, int value, int index, byte[] buffer, int length, int timeout)68   protected int controlTransfer(
69       int requestType, int request, int value, int index, byte[] buffer, int length, int timeout) {
70     return length;
71   }
72 
73   @Implementation(minSdk = KITKAT)
controlTransfer( int requestType, int request, int value, int index, byte[] buffer, int offset, int length, int timeout)74   protected int controlTransfer(
75       int requestType,
76       int request,
77       int value,
78       int index,
79       byte[] buffer,
80       int offset,
81       int length,
82       int timeout) {
83     return length;
84   }
85 
86   @Implementation
requestWait()87   protected UsbRequest requestWait() {
88     if (dataListener == null) {
89       throw new IllegalStateException("No UsbRequest initialized for this UsbDeviceConnection");
90     }
91 
92     return dataListener.getUsbRequest();
93   }
94 
95   @Implementation(minSdk = O)
requestWait(long timeout)96   protected UsbRequest requestWait(long timeout) throws TimeoutException {
97     return requestWait();
98   }
99 
100   @Implementation(minSdk = JELLY_BEAN_MR2)
bulkTransfer( UsbEndpoint endpoint, byte[] buffer, int offset, int length, int timeout)101   protected int bulkTransfer(
102       UsbEndpoint endpoint, byte[] buffer, int offset, int length, int timeout) {
103     try {
104       outgoingDataPipedOutputStream.write(buffer, offset, length);
105       return length;
106     } catch (IOException e) {
107       return -1;
108     }
109   }
110 
111   @Implementation
bulkTransfer(UsbEndpoint endpoint, byte[] buffer, int length, int timeout)112   protected int bulkTransfer(UsbEndpoint endpoint, byte[] buffer, int length, int timeout) {
113     try {
114       outgoingDataPipedOutputStream.write(buffer, /* off= */ 0, length);
115       return length;
116     } catch (IOException e) {
117       return -1;
118     }
119   }
120 
121   /**
122    * Fills the buffer with data that was written by UsbDeviceConnection#bulkTransfer.
123    *
124    * @deprecated prefer {@link #getOutgoingDataStream()}, which allows callers to know how much data
125    *     has been read and when the {@link UsbDeviceConnection} closes.
126    */
127   @Deprecated
readOutgoingData(byte[] buffer)128   public void readOutgoingData(byte[] buffer) throws IOException {
129     getOutgoingDataStream().read(buffer);
130   }
131 
132   /**
133    * Provides an {@link InputStream} that allows reading data written by
134    * UsbDeviceConnection#bulkTransfer. Closing this stream has no effect. It is effectively closed
135    * during {@link UsbDeviceConnection#releaseInterface(UsbInterface)}.
136    */
getOutgoingDataStream()137   public InputStream getOutgoingDataStream() {
138     return new FilterInputStream(outgoingDataPipedInputStream) {
139       @Override
140       public void close() throws IOException {
141         // Override close() to prevent clients from closing the piped stream and causing unexpected
142         // side-effects if further writes happen.
143       }
144     };
145   }
146 
147   /** Passes data that can then be read by an initialized UsbRequest#queue(ByteBuffer). */
writeIncomingData(byte[] data)148   public void writeIncomingData(byte[] data) {
149     if (dataListener == null) {
150       throw new IllegalStateException("No UsbRequest initialized for this UsbDeviceConnection");
151     }
152 
153     dataListener.onDataReceived(data);
154   }
155 
registerDataListener(DataListener dataListener)156   void registerDataListener(DataListener dataListener) {
157     this.dataListener = dataListener;
158   }
159 
unregisterDataListener()160   void unregisterDataListener() {
161     this.dataListener = null;
162   }
163 
164   interface DataListener {
onDataReceived(byte[] data)165     void onDataReceived(byte[] data);
166 
getUsbRequest()167     UsbRequest getUsbRequest();
168   }
169 }
170