• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package android.bluetooth;
2 
3 import android.bluetooth.BluetoothAdapter;
4 import android.bluetooth.BluetoothManager;
5 import android.bluetooth.Utils;
6 import android.bluetooth.le.AdvertiseData;
7 import android.bluetooth.le.AdvertisingSet;
8 import android.bluetooth.le.AdvertisingSetCallback;
9 import android.bluetooth.le.AdvertisingSetParameters;
10 import android.bluetooth.le.BluetoothLeAdvertiser;
11 import android.util.Log;
12 
13 import androidx.core.util.Pair;
14 import androidx.test.core.app.ApplicationProvider;
15 import androidx.test.filters.SmallTest;
16 import androidx.test.platform.app.InstrumentationRegistry;
17 import androidx.test.runner.AndroidJUnit4;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import com.google.protobuf.Empty;
21 
22 import io.grpc.Context.CancellableContext;
23 import io.grpc.Deadline;
24 import io.grpc.ManagedChannel;
25 import io.grpc.okhttp.OkHttpChannelBuilder;
26 import io.grpc.stub.StreamObserver;
27 
28 import java.util.concurrent.CompletableFuture;
29 import java.util.concurrent.TimeUnit;
30 
31 import org.junit.After;
32 import org.junit.Before;
33 import org.junit.BeforeClass;
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 
37 import pandora.HostGrpc;
38 import pandora.HostProto.ScanRequest;
39 import pandora.HostProto.ScanningResponse;
40 
41 
42 /**
43  * Test cases for {@link AdvertiseManager}.
44  */
45 @RunWith(AndroidJUnit4.class)
46 public class LeAdvertisingTest {
47 
48     private static final String LOG_TAG = "LeAdvertisingTest";
49 
50     private static final int TIMEOUT_ADVERTISING_MS = 1000;
51 
52     private static ManagedChannel mChannel;
53 
54     private static HostGrpc.HostBlockingStub mHostBlockingStub;
55 
56     private static HostGrpc.HostStub mHostStub;
57 
58     @BeforeClass
setUpClass()59     public static void setUpClass() throws Exception {
60         InstrumentationRegistry.getInstrumentation().getUiAutomation()
61                 .adoptShellPermissionIdentity();
62     }
63 
64     @Before
setUp()65     public void setUp() throws Exception {
66         // FactorReset is killing the server and restart
67         // all channel created before the server restarted
68         // cannot be reused
69         ManagedChannel channel = OkHttpChannelBuilder
70               .forAddress("localhost", 7999)
71               .usePlaintext()
72               .build();
73 
74         HostGrpc.HostBlockingStub stub = HostGrpc.newBlockingStub(channel);
75         stub.factoryReset(Empty.getDefaultInstance());
76 
77         // terminate the channel
78         channel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
79 
80         // Create a new channel for all successive grpc calls
81         mChannel = OkHttpChannelBuilder
82               .forAddress("localhost", 7999)
83               .usePlaintext()
84               .build();
85 
86         mHostBlockingStub = HostGrpc.newBlockingStub(mChannel);
87         mHostStub = HostGrpc.newStub(mChannel);
88         mHostBlockingStub.withWaitForReady().readLocalAddress(Empty.getDefaultInstance());
89     }
90 
91     @After
tearDown()92     public void tearDown() throws Exception {
93         // terminate the channel
94         mChannel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
95     }
96 
97     @Test
advertisingSet()98     public void advertisingSet() throws Exception {
99         ScanningResponse response = startAdvertising()
100                                       .thenCompose(advAddressPair -> scanWithBumble(advAddressPair))
101                                       .join();
102 
103         Log.i(LOG_TAG, "scan response: " + response);
104         assertThat(response).isNotNull();
105     }
106 
startAdvertising()107     private CompletableFuture<Pair<String, Integer>> startAdvertising() {
108         CompletableFuture<Pair<String, Integer>> future =
109             new CompletableFuture<Pair<String, Integer>>();
110 
111         android.content.Context context = ApplicationProvider.getApplicationContext();
112         BluetoothManager bluetoothManager = context.getSystemService(BluetoothManager.class);
113         BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
114 
115         // Start advertising
116         BluetoothLeAdvertiser leAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
117         AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder().
118              setOwnAddressType(AdvertisingSetParameters.ADDRESS_TYPE_RANDOM).build();
119         AdvertiseData advertiseData = new AdvertiseData.Builder().build();
120         AdvertiseData scanResponse = new AdvertiseData.Builder().build();
121         AdvertisingSetCallback advertisingSetCallback = new AdvertisingSetCallback() {
122             @Override
123             public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower,
124                     int status) {
125                 Log.i(LOG_TAG, "onAdvertisingSetStarted " + " txPower:" + txPower
126                     + " status:" + status);
127                 advertisingSet.enableAdvertising(true, TIMEOUT_ADVERTISING_MS, 0);
128             }
129             @Override
130             public void onOwnAddressRead(AdvertisingSet advertisingSet, int addressType,
131                     String address) {
132                 Log.i(LOG_TAG, "onOwnAddressRead " + " addressType:" + addressType
133                     + " address:" + address);
134                 future.complete(new Pair<String, Integer>(address, addressType));
135             }
136             @Override
137             public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enabled,
138                     int status) {
139                 Log.i(LOG_TAG, "onAdvertisingEnabled " + " enabled:" + enabled
140                         + " status:" + status);
141                 advertisingSet.getOwnAddress();
142             }
143         };
144         leAdvertiser.startAdvertisingSet(parameters, advertiseData, scanResponse,
145           null, null, 0, 0, advertisingSetCallback);
146 
147         return future;
148     }
149 
scanWithBumble(Pair<String, Integer> addressPair)150     private CompletableFuture<ScanningResponse> scanWithBumble(Pair<String, Integer> addressPair) {
151         final CompletableFuture<ScanningResponse> future =
152             new CompletableFuture<ScanningResponse>();
153         CancellableContext withCancellation = io.grpc.Context.current().withCancellation();
154 
155         String address = addressPair.first;
156         int addressType = addressPair.second;
157 
158         ScanRequest request = ScanRequest.newBuilder().build();
159         StreamObserver<ScanningResponse> responseObserver = new StreamObserver<ScanningResponse>(){
160             public void onNext(ScanningResponse response) {
161                 String addr = "";
162                 if (addressType == AdvertisingSetParameters.ADDRESS_TYPE_PUBLIC) {
163                     addr = Utils.addressStringFromByteString(response.getPublic());
164                 }
165                 else {
166                     addr = Utils.addressStringFromByteString(response.getRandom());
167                 }
168                 Log.i(LOG_TAG,"scan observer: scan response address: " + addr);
169 
170                 if (addr.equals(address)) {
171                     future.complete(response);
172                 }
173             }
174 
175             @Override
176             public void onError(Throwable e) {
177                 Log.e(LOG_TAG,"scan observer: on error " + e);
178                 future.completeExceptionally(e);
179             }
180 
181             @Override
182             public void onCompleted() {
183                 Log.i(LOG_TAG,"scan observer: on completed");
184                 future.complete(null);
185             }
186         };
187 
188         Deadline initialDeadline = Deadline.after(TIMEOUT_ADVERTISING_MS, TimeUnit.MILLISECONDS);
189         withCancellation.run(() -> mHostStub.withDeadline(initialDeadline)
190             .scan(request, responseObserver));
191 
192         return future.whenComplete((input, exception) -> {
193             withCancellation.cancel(null);
194         });
195     }
196 }
197