• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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