1 /* 2 * Copyright (C) 2024 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 static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.Mockito.any; 22 import static org.mockito.Mockito.anyInt; 23 import static org.mockito.Mockito.eq; 24 import static org.mockito.Mockito.mock; 25 import static org.mockito.Mockito.timeout; 26 import static org.mockito.Mockito.verify; 27 28 import android.bluetooth.le.BluetoothLeScanner; 29 import android.bluetooth.le.ScanCallback; 30 import android.bluetooth.le.ScanFilter; 31 import android.bluetooth.le.ScanResult; 32 import android.bluetooth.le.ScanSettings; 33 import android.content.Context; 34 import android.platform.test.annotations.RequiresFlagsEnabled; 35 import android.platform.test.flag.junit.CheckFlagsRule; 36 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 37 import android.util.Log; 38 39 import androidx.test.core.app.ApplicationProvider; 40 import androidx.test.runner.AndroidJUnit4; 41 42 import com.android.bluetooth.flags.Flags; 43 import com.android.compatibility.common.util.AdoptShellPermissionsRule; 44 45 import org.junit.Ignore; 46 import org.junit.Rule; 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 50 import pandora.HostProto.AdvertiseRequest; 51 import pandora.HostProto.OwnAddressType; 52 53 import java.util.List; 54 import java.util.concurrent.CompletableFuture; 55 import java.util.concurrent.TimeUnit; 56 57 /** Test cases for {@link BluetoothGattServer}. */ 58 @RunWith(AndroidJUnit4.class) 59 public class GattServerConnectWithScanTest { 60 private static final String TAG = "GattServerConnectWithScanTest"; 61 62 private static final int TIMEOUT_SCANNING_MS = 2_000; 63 private static final int TIMEOUT_GATT_CONNECTION_MS = 2_000; 64 65 @Rule(order = 2) 66 public final AdoptShellPermissionsRule mPermissionRule = new AdoptShellPermissionsRule(); 67 68 @Rule(order = 1) 69 public final PandoraDevice mBumble = new PandoraDevice(); 70 71 @Rule(order = 0) 72 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 73 74 private final Context mContext = ApplicationProvider.getApplicationContext(); 75 private final BluetoothManager mBluetoothManager = 76 mContext.getSystemService(BluetoothManager.class); 77 private final BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter(); 78 private final BluetoothLeScanner mLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); 79 80 @Test 81 @Ignore("b/343525982: Remove hidden api's dependencies to enable the test.") serverConnectToRandomAddress_withTransportAuto()82 public void serverConnectToRandomAddress_withTransportAuto() throws Exception { 83 advertiseWithBumble(OwnAddressType.RANDOM); 84 assertThat(scanBumbleDevice(Utils.BUMBLE_RANDOM_ADDRESS)).isNotNull(); 85 86 BluetoothGattServerCallback mockGattServerCallback = 87 mock(BluetoothGattServerCallback.class); 88 BluetoothGattServer gattServer = 89 mBluetoothManager.openGattServer( 90 mContext, mockGattServerCallback, BluetoothDevice.TRANSPORT_AUTO); 91 92 assertThat(gattServer).isNotNull(); 93 94 try { 95 BluetoothDevice device = 96 mBluetoothAdapter.getRemoteLeDevice( 97 Utils.BUMBLE_RANDOM_ADDRESS, BluetoothDevice.ADDRESS_TYPE_RANDOM); 98 99 gattServer.connect(device, false); 100 verify(mockGattServerCallback, timeout(TIMEOUT_GATT_CONNECTION_MS)) 101 .onConnectionStateChange(any(), anyInt(), eq(BluetoothProfile.STATE_CONNECTED)); 102 } finally { 103 gattServer.close(); 104 } 105 } 106 107 @Test 108 @Ignore("b/343525982: Remove hidden api's dependencies to enable the test.") serverConnectToRandomAddress_withTransportLE()109 public void serverConnectToRandomAddress_withTransportLE() throws Exception { 110 advertiseWithBumble(OwnAddressType.RANDOM); 111 assertThat(scanBumbleDevice(Utils.BUMBLE_RANDOM_ADDRESS)).isNotNull(); 112 113 BluetoothGattServerCallback mockGattServerCallback = 114 mock(BluetoothGattServerCallback.class); 115 BluetoothGattServer gattServer = 116 mBluetoothManager.openGattServer( 117 mContext, mockGattServerCallback, BluetoothDevice.TRANSPORT_LE); 118 119 assertThat(gattServer).isNotNull(); 120 121 try { 122 BluetoothDevice device = 123 mBluetoothAdapter.getRemoteLeDevice( 124 Utils.BUMBLE_RANDOM_ADDRESS, BluetoothDevice.ADDRESS_TYPE_RANDOM); 125 126 gattServer.connect(device, false); 127 verify(mockGattServerCallback, timeout(TIMEOUT_GATT_CONNECTION_MS)) 128 .onConnectionStateChange(any(), anyInt(), eq(BluetoothProfile.STATE_CONNECTED)); 129 } finally { 130 gattServer.close(); 131 } 132 } 133 134 @Test 135 @RequiresFlagsEnabled(Flags.FLAG_BLE_GATT_SERVER_USE_ADDRESS_TYPE_IN_CONNECTION) 136 @Ignore("b/343749428: Remove hidden api's dependencies to enable the test.") serverConnectToPublicAddress_withTransportAuto()137 public void serverConnectToPublicAddress_withTransportAuto() throws Exception { 138 String publicAddress = mBumble.getRemoteDevice().getAddress(); 139 advertiseWithBumble(OwnAddressType.PUBLIC); 140 assertThat(scanBumbleDevice(publicAddress)).isNotNull(); 141 142 BluetoothGattServerCallback mockGattServerCallback = 143 mock(BluetoothGattServerCallback.class); 144 BluetoothGattServer gattServer = 145 mBluetoothManager.openGattServer( 146 mContext, mockGattServerCallback, BluetoothDevice.TRANSPORT_AUTO); 147 148 assertThat(gattServer).isNotNull(); 149 150 try { 151 gattServer.connect(mBumble.getRemoteDevice(), false); 152 verify(mockGattServerCallback, timeout(TIMEOUT_GATT_CONNECTION_MS)) 153 .onConnectionStateChange(any(), anyInt(), eq(BluetoothProfile.STATE_CONNECTED)); 154 } finally { 155 gattServer.close(); 156 } 157 } 158 159 @Test 160 @Ignore("b/343749428: Remove hidden api's dependencies to enable the test.") serverConnectToPublicAddress_withTransportLE()161 public void serverConnectToPublicAddress_withTransportLE() throws Exception { 162 String publicAddress = mBumble.getRemoteDevice().getAddress(); 163 advertiseWithBumble(OwnAddressType.PUBLIC); 164 assertThat(scanBumbleDevice(publicAddress)).isNotNull(); 165 166 BluetoothGattServerCallback mockGattServerCallback = 167 mock(BluetoothGattServerCallback.class); 168 BluetoothGattServer gattServer = 169 mBluetoothManager.openGattServer( 170 mContext, mockGattServerCallback, BluetoothDevice.TRANSPORT_LE); 171 172 assertThat(gattServer).isNotNull(); 173 174 try { 175 gattServer.connect(mBumble.getRemoteDevice(), false); 176 verify(mockGattServerCallback, timeout(TIMEOUT_GATT_CONNECTION_MS)) 177 .onConnectionStateChange(any(), anyInt(), eq(BluetoothProfile.STATE_CONNECTED)); 178 } finally { 179 gattServer.close(); 180 } 181 } 182 advertiseWithBumble(OwnAddressType ownAddressType)183 private void advertiseWithBumble(OwnAddressType ownAddressType) { 184 AdvertiseRequest request = 185 AdvertiseRequest.newBuilder() 186 .setLegacy(true) 187 .setConnectable(true) 188 .setOwnAddressType(ownAddressType) 189 .build(); 190 mBumble.hostBlocking().advertise(request); 191 } 192 scanBumbleDevice(String address)193 private List<ScanResult> scanBumbleDevice(String address) { 194 CompletableFuture<List<ScanResult>> future = new CompletableFuture<>(); 195 ScanSettings scanSettings = 196 new ScanSettings.Builder() 197 .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) 198 .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) 199 .build(); 200 201 ScanFilter scanFilter = new ScanFilter.Builder().setDeviceAddress(address).build(); 202 203 ScanCallback scanCallback = 204 new ScanCallback() { 205 @Override 206 public void onScanResult(int callbackType, ScanResult result) { 207 Log.d(TAG, "onScanResult: result=" + result); 208 future.complete(List.of(result)); 209 } 210 211 @Override 212 public void onScanFailed(int errorCode) { 213 Log.d(TAG, "onScanFailed: errorCode=" + errorCode); 214 future.complete(null); 215 } 216 }; 217 218 mLeScanner.startScan(List.of(scanFilter), scanSettings, scanCallback); 219 220 List<ScanResult> result = 221 future.completeOnTimeout(null, TIMEOUT_SCANNING_MS, TimeUnit.MILLISECONDS).join(); 222 223 mLeScanner.stopScan(scanCallback); 224 return result; 225 } 226 } 227