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