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