1 package org.robolectric.shadows; 2 3 import android.annotation.SuppressLint; 4 import android.bluetooth.BluetoothDevice; 5 import android.bluetooth.BluetoothServerSocket; 6 import android.bluetooth.BluetoothSocket; 7 import android.os.ParcelUuid; 8 import java.io.IOException; 9 import java.util.concurrent.BlockingQueue; 10 import java.util.concurrent.LinkedBlockingQueue; 11 import java.util.concurrent.TimeUnit; 12 import org.robolectric.annotation.Implementation; 13 import org.robolectric.annotation.Implements; 14 import org.robolectric.shadow.api.Shadow; 15 import org.robolectric.util.ReflectionHelpers; 16 17 @Implements(value = BluetoothServerSocket.class) 18 public class ShadowBluetoothServerSocket { 19 private final BlockingQueue<BluetoothSocket> sockets = new LinkedBlockingQueue<>(); 20 private boolean closed; 21 22 @SuppressLint("PrivateApi") newInstance( int type, boolean auth, boolean encrypt, ParcelUuid uuid)23 public static BluetoothServerSocket newInstance( 24 int type, boolean auth, boolean encrypt, ParcelUuid uuid) { 25 return Shadow.newInstance( 26 BluetoothServerSocket.class, 27 new Class<?>[] {Integer.TYPE, Boolean.TYPE, Boolean.TYPE, ParcelUuid.class}, 28 new Object[] {type, auth, encrypt, uuid}); 29 } 30 31 // Port ranges are valid from 1 to MAX_RFCOMM_CHANNEL. getPort(ParcelUuid uuid)32 private static int getPort(ParcelUuid uuid) { 33 return Math.abs(uuid.hashCode() % BluetoothSocket.MAX_RFCOMM_CHANNEL) + 1; 34 } 35 36 /** 37 * May block the current thread and wait until {@link BluetoothDevice} is offered via 38 * {@link #deviceConnected(BluetoothDevice)} method or timeout occurred. 39 * 40 * @return socket of the connected bluetooth device 41 * @throws IOException if socket has been closed, thread interrupted while waiting or timeout has 42 * occurred. 43 */ 44 @Implementation accept(int timeout)45 protected BluetoothSocket accept(int timeout) throws IOException { 46 if (closed) { 47 throw new IOException("Socket closed"); 48 } 49 50 BluetoothSocket socket; 51 try { 52 socket = timeout == -1 53 ? sockets.take() : sockets.poll(timeout, TimeUnit.MILLISECONDS); 54 } catch (InterruptedException e) { 55 throw new IOException(e); 56 } 57 58 if (socket == null) { 59 throw new IOException("Timeout occurred"); 60 } 61 socket.connect(); 62 return socket; 63 } 64 65 @Implementation close()66 protected void close() throws IOException { 67 closed = true; 68 } 69 70 /** Creates {@link BluetoothSocket} for the given device and makes this socket available 71 * immediately in the {@link #accept(int)} method. */ deviceConnected(BluetoothDevice device)72 public BluetoothSocket deviceConnected(BluetoothDevice device) { 73 BluetoothSocket socket = Shadow.newInstanceOf(BluetoothSocket.class); 74 ReflectionHelpers.setField(socket, "mDevice", device); 75 sockets.offer(socket); 76 return socket; 77 } 78 } 79