• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.bluetooth;
18 
19 import android.util.Log;
20 
21 import androidx.test.core.app.ApplicationProvider;
22 
23 import com.google.protobuf.Empty;
24 
25 import io.grpc.Context;
26 import io.grpc.ManagedChannel;
27 import io.grpc.Status;
28 import io.grpc.StatusRuntimeException;
29 import io.grpc.okhttp.OkHttpChannelBuilder;
30 
31 import org.junit.rules.ExternalResource;
32 
33 import pandora.BumbleConfigGrpc;
34 import pandora.DckGrpc;
35 import pandora.GATTGrpc;
36 import pandora.HIDGrpc;
37 import pandora.HostGrpc;
38 import pandora.HostProto;
39 import pandora.HostProto.AdvertiseRequest;
40 import pandora.HostProto.OwnAddressType;
41 import pandora.OOBGrpc;
42 import pandora.OppGrpc;
43 import pandora.RFCOMMGrpc;
44 import pandora.SecurityGrpc;
45 import pandora.l2cap.L2CAPGrpc;
46 
47 import java.util.UUID;
48 import java.util.concurrent.TimeUnit;
49 
50 public final class PandoraDevice extends ExternalResource {
51     private static final String TAG = PandoraDevice.class.getSimpleName();
52 
53     private final String mNetworkAddress;
54     private String mPublicBluetoothAddress;
55     private final int mPort;
56     private ManagedChannel mChannel;
57 
PandoraDevice(String networkAddress, int port)58     public PandoraDevice(String networkAddress, int port) {
59         mNetworkAddress = networkAddress;
60         mPort = port;
61     }
62 
PandoraDevice()63     public PandoraDevice() {
64         this("localhost", 7999);
65     }
66 
67     @Override
before()68     protected void before() {
69         Log.i(TAG, "factoryReset");
70         // FactoryReset is killing the server and restarting all channels created before the server
71         // restarted that cannot be reused
72         ManagedChannel channel =
73                 OkHttpChannelBuilder.forAddress(mNetworkAddress, mPort).usePlaintext().build();
74         HostGrpc.HostBlockingStub stub =
75                 HostGrpc.newBlockingStub(channel).withDeadlineAfter(10000, TimeUnit.MILLISECONDS);
76         try {
77             stub.factoryReset(Empty.getDefaultInstance());
78         } catch (StatusRuntimeException e) {
79             if (e.getStatus().getCode() == Status.Code.UNAVAILABLE) {
80                 // Server is shutting down, the call might be canceled with an UNAVAILABLE status
81                 // because the stream is closed.
82             } else {
83                 throw e;
84             }
85         }
86         try {
87             // terminate the channel
88             channel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
89         } catch (InterruptedException e) {
90             throw new RuntimeException(e);
91         }
92         mChannel = OkHttpChannelBuilder.forAddress(mNetworkAddress, mPort).usePlaintext().build();
93         stub = HostGrpc.newBlockingStub(mChannel);
94         HostProto.ReadLocalAddressResponse readLocalAddressResponse =
95                 stub.withWaitForReady().readLocalAddress(Empty.getDefaultInstance());
96         mPublicBluetoothAddress =
97                 Utils.addressStringFromByteString(readLocalAddressResponse.getAddress());
98     }
99 
100     @Override
after()101     protected void after() {
102         Log.i(TAG, "shutdown");
103         try {
104             // terminate the channel
105             mChannel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
106             mChannel = null;
107         } catch (InterruptedException e) {
108             throw new RuntimeException(e);
109         }
110     }
111 
112     /**
113      * @return bumble as a remote device
114      */
getRemoteDevice()115     public BluetoothDevice getRemoteDevice() {
116         return ApplicationProvider.getApplicationContext()
117                 .getSystemService(BluetoothManager.class)
118                 .getAdapter()
119                 .getRemoteDevice(mPublicBluetoothAddress);
120     }
121 
122     /**
123      * Start advertising with Random address type
124      *
125      * @return Context.CancellableContext
126      */
advertise()127     public Context.CancellableContext advertise() {
128         return advertise(OwnAddressType.RANDOM, null, true, true);
129     }
130 
131     /**
132      * Start advertising.
133      *
134      * @return a Context.CancellableContext to cancel the advertising
135      */
advertise(OwnAddressType ownAddressType)136     public Context.CancellableContext advertise(OwnAddressType ownAddressType) {
137         return advertise(ownAddressType, null, true, true);
138     }
139 
140     /**
141      * Start advertising.
142      *
143      * @return a Context.CancellableContext to cancel the advertising
144      */
advertise( OwnAddressType ownAddressType, UUID serviceUuid, boolean legacy, boolean connectable)145     public Context.CancellableContext advertise(
146             OwnAddressType ownAddressType, UUID serviceUuid, boolean legacy, boolean connectable) {
147         AdvertiseRequest.Builder requestBuilder =
148                 AdvertiseRequest.newBuilder()
149                         .setLegacy(legacy)
150                         .setConnectable(connectable)
151                         .setOwnAddressType(ownAddressType);
152 
153         if (serviceUuid != null) {
154             requestBuilder.setData(
155                     HostProto.DataTypes.newBuilder()
156                             .addCompleteServiceClassUuids128(serviceUuid.toString())
157                             .build());
158         }
159 
160         Context.CancellableContext cancellableContext = Context.current().withCancellation();
161         cancellableContext.run(
162                 new Runnable() {
163                     public void run() {
164                         hostBlocking().advertise(requestBuilder.build());
165                     }
166                 });
167 
168         return cancellableContext;
169     }
170 
171     /** Get Pandora Host service */
host()172     public HostGrpc.HostStub host() {
173         return HostGrpc.newStub(mChannel);
174     }
175 
176     /** Get Pandora Host service */
hostBlocking()177     public HostGrpc.HostBlockingStub hostBlocking() {
178         return HostGrpc.newBlockingStub(mChannel);
179     }
180 
181     /** Get Pandora BumbleConfig service */
bumbleConfig()182     public BumbleConfigGrpc.BumbleConfigStub bumbleConfig() {
183         return BumbleConfigGrpc.newStub(mChannel);
184     }
185 
186     /** Get Pandora BumbleConfig service */
bumbleConfigBlocking()187     public BumbleConfigGrpc.BumbleConfigBlockingStub bumbleConfigBlocking() {
188         return BumbleConfigGrpc.newBlockingStub(mChannel);
189     }
190 
191     /** Get Pandora HID service */
hid()192     public HIDGrpc.HIDStub hid() {
193         return HIDGrpc.newStub(mChannel);
194     }
195 
196     /** Get Pandora HID blocking service */
hidBlocking()197     public HIDGrpc.HIDBlockingStub hidBlocking() {
198         return HIDGrpc.newBlockingStub(mChannel);
199     }
200 
201     /** Get Pandora Dck service */
dck()202     public DckGrpc.DckStub dck() {
203         return DckGrpc.newStub(mChannel);
204     }
205 
206     /** Get Pandora Dck blocking service */
dckBlocking()207     public DckGrpc.DckBlockingStub dckBlocking() {
208         return DckGrpc.newBlockingStub(mChannel);
209     }
210 
211     /** Get Pandora Security service */
security()212     public SecurityGrpc.SecurityStub security() {
213         return SecurityGrpc.newStub(mChannel);
214     }
215 
216     /** Get Pandora OOB blocking service */
oobBlocking()217     public OOBGrpc.OOBBlockingStub oobBlocking() {
218         return OOBGrpc.newBlockingStub(mChannel);
219     }
220 
221     /** Get Pandora GATT service */
gatt()222     public GATTGrpc.GATTStub gatt() {
223         return GATTGrpc.newStub(mChannel);
224     }
225 
226     /** Get Pandora GATT blocking service */
gattBlocking()227     public GATTGrpc.GATTBlockingStub gattBlocking() {
228         return GATTGrpc.newBlockingStub(mChannel);
229     }
230 
231     /** Get Pandora RFCOMM service */
rfcomm()232     public RFCOMMGrpc.RFCOMMStub rfcomm() {
233         return RFCOMMGrpc.newStub(mChannel);
234     }
235 
236     /** Get Pandora RFCOMM blocking service */
rfcommBlocking()237     public RFCOMMGrpc.RFCOMMBlockingStub rfcommBlocking() {
238         return RFCOMMGrpc.newBlockingStub(mChannel);
239     }
240 
241     /** Get Pandora L2CAP service */
l2cap()242     public L2CAPGrpc.L2CAPStub l2cap() {
243         return L2CAPGrpc.newStub(mChannel);
244     }
245 
246     /** Get Pandora L2CAP blocking service */
l2capBlocking()247     public L2CAPGrpc.L2CAPBlockingStub l2capBlocking() {
248         return L2CAPGrpc.newBlockingStub(mChannel);
249     }
250 
251     /** Get Pandora OPP service */
opp()252     public OppGrpc.OppStub opp() {
253         return OppGrpc.newStub(mChannel);
254     }
255 
256     /** Get Pandora OPP blocking service */
oppBlocking()257     public OppGrpc.OppBlockingStub oppBlocking() {
258         return OppGrpc.newBlockingStub(mChannel);
259     }
260 }
261