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.ManagedChannel; 26 import io.grpc.Status; 27 import io.grpc.StatusRuntimeException; 28 import io.grpc.okhttp.OkHttpChannelBuilder; 29 30 import org.junit.rules.ExternalResource; 31 32 import pandora.DckGrpc; 33 import pandora.GATTGrpc; 34 import pandora.HostGrpc; 35 import pandora.HostProto; 36 import pandora.RFCOMMGrpc; 37 import pandora.SecurityGrpc; 38 39 import java.util.concurrent.TimeUnit; 40 41 public final class PandoraDevice extends ExternalResource { 42 private static final String TAG = PandoraDevice.class.getSimpleName(); 43 private final String mNetworkAddress; 44 private String mPublicBluetoothAddress; 45 private final int mPort; 46 private ManagedChannel mChannel; 47 PandoraDevice(String networkAddress, int port)48 public PandoraDevice(String networkAddress, int port) { 49 mNetworkAddress = networkAddress; 50 mPort = port; 51 } 52 PandoraDevice()53 public PandoraDevice() { 54 this("localhost", 7999); 55 } 56 57 @Override before()58 protected void before() { 59 Log.i(TAG, "factoryReset"); 60 // FactoryReset is killing the server and restarting all channels created before the server 61 // restarted that cannot be reused 62 ManagedChannel channel = 63 OkHttpChannelBuilder.forAddress(mNetworkAddress, mPort).usePlaintext().build(); 64 HostGrpc.HostBlockingStub stub = HostGrpc.newBlockingStub(channel); 65 try { 66 stub.factoryReset(Empty.getDefaultInstance()); 67 } catch (StatusRuntimeException e) { 68 if (e.getStatus().getCode() == Status.Code.UNAVAILABLE) { 69 // Server is shutting down, the call might be canceled with an UNAVAILABLE status 70 // because the stream is closed. 71 } else { 72 throw e; 73 } 74 } 75 try { 76 // terminate the channel 77 channel.shutdown().awaitTermination(1, TimeUnit.SECONDS); 78 } catch (InterruptedException e) { 79 throw new RuntimeException(e); 80 } 81 mChannel = OkHttpChannelBuilder.forAddress(mNetworkAddress, mPort).usePlaintext().build(); 82 stub = HostGrpc.newBlockingStub(mChannel); 83 HostProto.ReadLocalAddressResponse readLocalAddressResponse = 84 stub.withWaitForReady().readLocalAddress(Empty.getDefaultInstance()); 85 mPublicBluetoothAddress = 86 Utils.addressStringFromByteString(readLocalAddressResponse.getAddress()); 87 } 88 89 @Override after()90 protected void after() { 91 Log.i(TAG, "shutdown"); 92 try { 93 // terminate the channel 94 mChannel.shutdown().awaitTermination(1, TimeUnit.SECONDS); 95 mChannel = null; 96 } catch (InterruptedException e) { 97 throw new RuntimeException(e); 98 } 99 } 100 101 /** 102 * @return bumble as a remote device 103 */ getRemoteDevice()104 public BluetoothDevice getRemoteDevice() { 105 return ApplicationProvider.getApplicationContext() 106 .getSystemService(BluetoothManager.class) 107 .getAdapter() 108 .getRemoteDevice(mPublicBluetoothAddress); 109 } 110 111 /** Get Pandora Host service */ host()112 public HostGrpc.HostStub host() { 113 return HostGrpc.newStub(mChannel); 114 } 115 116 /** Get Pandora Host service */ hostBlocking()117 public HostGrpc.HostBlockingStub hostBlocking() { 118 return HostGrpc.newBlockingStub(mChannel); 119 } 120 121 /** Get Pandora Dck service */ dck()122 public DckGrpc.DckStub dck() { 123 return DckGrpc.newStub(mChannel); 124 } 125 126 /** Get Pandora Dck blocking service */ dckBlocking()127 public DckGrpc.DckBlockingStub dckBlocking() { 128 return DckGrpc.newBlockingStub(mChannel); 129 } 130 131 /** Get Pandora Security service */ security()132 public SecurityGrpc.SecurityStub security() { 133 return SecurityGrpc.newStub(mChannel); 134 } 135 136 /** Get Pandora GATT service */ gatt()137 public GATTGrpc.GATTStub gatt() { 138 return GATTGrpc.newStub(mChannel); 139 } 140 141 /** Get Pandora GATT blocking service */ gattBlocking()142 public GATTGrpc.GATTBlockingStub gattBlocking() { 143 return GATTGrpc.newBlockingStub(mChannel); 144 } 145 146 /** Get Pandora RFCOMM service */ rfcomm()147 public RFCOMMGrpc.RFCOMMStub rfcomm() { 148 return RFCOMMGrpc.newStub(mChannel); 149 } 150 151 /** Get Pandora RFCOMM blocking service */ rfcommBlocking()152 public RFCOMMGrpc.RFCOMMBlockingStub rfcommBlocking() { 153 return RFCOMMGrpc.newBlockingStub(mChannel); 154 } 155 } 156