1*b7c941bbSAndroid Build Coastguard Worker /* 2*b7c941bbSAndroid Build Coastguard Worker * Copyright 2023 The Android Open Source Project 3*b7c941bbSAndroid Build Coastguard Worker * 4*b7c941bbSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*b7c941bbSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*b7c941bbSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*b7c941bbSAndroid Build Coastguard Worker * 8*b7c941bbSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*b7c941bbSAndroid Build Coastguard Worker * 10*b7c941bbSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*b7c941bbSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*b7c941bbSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*b7c941bbSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*b7c941bbSAndroid Build Coastguard Worker * limitations under the License. 15*b7c941bbSAndroid Build Coastguard Worker */ 16*b7c941bbSAndroid Build Coastguard Worker 17*b7c941bbSAndroid Build Coastguard Worker package com.google.snippet.bluetooth; 18*b7c941bbSAndroid Build Coastguard Worker 19*b7c941bbSAndroid Build Coastguard Worker import static android.bluetooth.BluetoothDevice.TRANSPORT_LE; 20*b7c941bbSAndroid Build Coastguard Worker import static android.bluetooth.BluetoothGattService.SERVICE_TYPE_PRIMARY; 21*b7c941bbSAndroid Build Coastguard Worker 22*b7c941bbSAndroid Build Coastguard Worker import static java.util.concurrent.TimeUnit.SECONDS; 23*b7c941bbSAndroid Build Coastguard Worker 24*b7c941bbSAndroid Build Coastguard Worker import android.bluetooth.BluetoothAdapter; 25*b7c941bbSAndroid Build Coastguard Worker import android.bluetooth.BluetoothDevice; 26*b7c941bbSAndroid Build Coastguard Worker import android.bluetooth.BluetoothGattServer; 27*b7c941bbSAndroid Build Coastguard Worker import android.bluetooth.BluetoothGattServerCallback; 28*b7c941bbSAndroid Build Coastguard Worker import android.bluetooth.BluetoothGattService; 29*b7c941bbSAndroid Build Coastguard Worker import android.bluetooth.BluetoothManager; 30*b7c941bbSAndroid Build Coastguard Worker import android.bluetooth.BluetoothProfile; 31*b7c941bbSAndroid Build Coastguard Worker import android.bluetooth.OobData; 32*b7c941bbSAndroid Build Coastguard Worker import android.bluetooth.le.AdvertiseData; 33*b7c941bbSAndroid Build Coastguard Worker import android.bluetooth.le.AdvertisingSetCallback; 34*b7c941bbSAndroid Build Coastguard Worker import android.bluetooth.le.AdvertisingSetParameters; 35*b7c941bbSAndroid Build Coastguard Worker import android.content.Context; 36*b7c941bbSAndroid Build Coastguard Worker import android.os.Handler; 37*b7c941bbSAndroid Build Coastguard Worker import android.os.Looper; 38*b7c941bbSAndroid Build Coastguard Worker import android.os.ParcelUuid; 39*b7c941bbSAndroid Build Coastguard Worker import android.util.Log; 40*b7c941bbSAndroid Build Coastguard Worker 41*b7c941bbSAndroid Build Coastguard Worker import java.util.List; 42*b7c941bbSAndroid Build Coastguard Worker import java.util.UUID; 43*b7c941bbSAndroid Build Coastguard Worker import java.util.concurrent.CountDownLatch; 44*b7c941bbSAndroid Build Coastguard Worker import java.util.concurrent.Executors; 45*b7c941bbSAndroid Build Coastguard Worker 46*b7c941bbSAndroid Build Coastguard Worker public final class BluetoothGattMultiDevicesServer { 47*b7c941bbSAndroid Build Coastguard Worker private static final String TAG = "BluetoothGattMultiDevicesServer"; 48*b7c941bbSAndroid Build Coastguard Worker private static final int CALLBACK_TIMEOUT_SEC = 1; 49*b7c941bbSAndroid Build Coastguard Worker 50*b7c941bbSAndroid Build Coastguard Worker private Context mContext; 51*b7c941bbSAndroid Build Coastguard Worker private BluetoothManager mBluetoothManager; 52*b7c941bbSAndroid Build Coastguard Worker private BluetoothAdapter mBluetoothAdapter; 53*b7c941bbSAndroid Build Coastguard Worker private OobData mOobData; 54*b7c941bbSAndroid Build Coastguard Worker BluetoothGattMultiDevicesServer(Context context, BluetoothManager manager)55*b7c941bbSAndroid Build Coastguard Worker public BluetoothGattMultiDevicesServer(Context context, BluetoothManager manager) { 56*b7c941bbSAndroid Build Coastguard Worker mContext = context; 57*b7c941bbSAndroid Build Coastguard Worker mBluetoothManager = manager; 58*b7c941bbSAndroid Build Coastguard Worker mBluetoothAdapter = manager.getAdapter(); 59*b7c941bbSAndroid Build Coastguard Worker } 60*b7c941bbSAndroid Build Coastguard Worker createGattServer(String uuid)61*b7c941bbSAndroid Build Coastguard Worker public BluetoothGattServer createGattServer(String uuid) { 62*b7c941bbSAndroid Build Coastguard Worker var bluetoothGattServer = 63*b7c941bbSAndroid Build Coastguard Worker mBluetoothManager.openGattServer(mContext, new BluetoothGattServerCallback() {}); 64*b7c941bbSAndroid Build Coastguard Worker var service = new BluetoothGattService(UUID.fromString(uuid), SERVICE_TYPE_PRIMARY); 65*b7c941bbSAndroid Build Coastguard Worker bluetoothGattServer.addService(service); 66*b7c941bbSAndroid Build Coastguard Worker return bluetoothGattServer; 67*b7c941bbSAndroid Build Coastguard Worker } 68*b7c941bbSAndroid Build Coastguard Worker getConnectedDevices()69*b7c941bbSAndroid Build Coastguard Worker public List<BluetoothDevice> getConnectedDevices() { 70*b7c941bbSAndroid Build Coastguard Worker return mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT_SERVER); 71*b7c941bbSAndroid Build Coastguard Worker } 72*b7c941bbSAndroid Build Coastguard Worker createAndAdvertiseServer(String uuid)73*b7c941bbSAndroid Build Coastguard Worker public void createAndAdvertiseServer(String uuid) { 74*b7c941bbSAndroid Build Coastguard Worker createGattServer(uuid); 75*b7c941bbSAndroid Build Coastguard Worker 76*b7c941bbSAndroid Build Coastguard Worker var bluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser(); 77*b7c941bbSAndroid Build Coastguard Worker var params = new AdvertisingSetParameters.Builder().setConnectable(true).build(); 78*b7c941bbSAndroid Build Coastguard Worker var data = 79*b7c941bbSAndroid Build Coastguard Worker new AdvertiseData.Builder() 80*b7c941bbSAndroid Build Coastguard Worker .addServiceUuid(new ParcelUuid(UUID.fromString(uuid))) 81*b7c941bbSAndroid Build Coastguard Worker .build(); 82*b7c941bbSAndroid Build Coastguard Worker 83*b7c941bbSAndroid Build Coastguard Worker bluetoothLeAdvertiser.startAdvertisingSet( 84*b7c941bbSAndroid Build Coastguard Worker params, data, null, null, null, new AdvertisingSetCallback() {}); 85*b7c941bbSAndroid Build Coastguard Worker } 86*b7c941bbSAndroid Build Coastguard Worker createAndAdvertiseIsolatedServer(String uuid)87*b7c941bbSAndroid Build Coastguard Worker public void createAndAdvertiseIsolatedServer(String uuid) { 88*b7c941bbSAndroid Build Coastguard Worker var gattServer = createGattServer(uuid); 89*b7c941bbSAndroid Build Coastguard Worker 90*b7c941bbSAndroid Build Coastguard Worker var bluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser(); 91*b7c941bbSAndroid Build Coastguard Worker var params = 92*b7c941bbSAndroid Build Coastguard Worker new AdvertisingSetParameters.Builder() 93*b7c941bbSAndroid Build Coastguard Worker .setConnectable(true) 94*b7c941bbSAndroid Build Coastguard Worker .setOwnAddressType( 95*b7c941bbSAndroid Build Coastguard Worker AdvertisingSetParameters.ADDRESS_TYPE_RANDOM_NON_RESOLVABLE) 96*b7c941bbSAndroid Build Coastguard Worker .build(); 97*b7c941bbSAndroid Build Coastguard Worker var data = 98*b7c941bbSAndroid Build Coastguard Worker new AdvertiseData.Builder() 99*b7c941bbSAndroid Build Coastguard Worker .addServiceUuid(new ParcelUuid(UUID.fromString(uuid))) 100*b7c941bbSAndroid Build Coastguard Worker .build(); 101*b7c941bbSAndroid Build Coastguard Worker 102*b7c941bbSAndroid Build Coastguard Worker bluetoothLeAdvertiser.startAdvertisingSet( 103*b7c941bbSAndroid Build Coastguard Worker params, 104*b7c941bbSAndroid Build Coastguard Worker data, 105*b7c941bbSAndroid Build Coastguard Worker null, 106*b7c941bbSAndroid Build Coastguard Worker null, 107*b7c941bbSAndroid Build Coastguard Worker null, 108*b7c941bbSAndroid Build Coastguard Worker 0, 109*b7c941bbSAndroid Build Coastguard Worker 0, 110*b7c941bbSAndroid Build Coastguard Worker gattServer, 111*b7c941bbSAndroid Build Coastguard Worker new AdvertisingSetCallback() {}, 112*b7c941bbSAndroid Build Coastguard Worker new Handler(Looper.getMainLooper())); 113*b7c941bbSAndroid Build Coastguard Worker } 114*b7c941bbSAndroid Build Coastguard Worker 115*b7c941bbSAndroid Build Coastguard Worker private class OobDataCallbackImpl implements BluetoothAdapter.OobDataCallback { 116*b7c941bbSAndroid Build Coastguard Worker private final CountDownLatch mCountDownLatch; 117*b7c941bbSAndroid Build Coastguard Worker OobDataCallbackImpl(CountDownLatch countDownLatch)118*b7c941bbSAndroid Build Coastguard Worker OobDataCallbackImpl(CountDownLatch countDownLatch) { 119*b7c941bbSAndroid Build Coastguard Worker mCountDownLatch = countDownLatch; 120*b7c941bbSAndroid Build Coastguard Worker } 121*b7c941bbSAndroid Build Coastguard Worker 122*b7c941bbSAndroid Build Coastguard Worker @Override onOobData(int transport, OobData oobData)123*b7c941bbSAndroid Build Coastguard Worker public void onOobData(int transport, OobData oobData) { 124*b7c941bbSAndroid Build Coastguard Worker Log.i(TAG, "OobDataCallback: onOobData: " + transport + ", " + oobData); 125*b7c941bbSAndroid Build Coastguard Worker mOobData = oobData; 126*b7c941bbSAndroid Build Coastguard Worker mCountDownLatch.countDown(); 127*b7c941bbSAndroid Build Coastguard Worker 128*b7c941bbSAndroid Build Coastguard Worker } 129*b7c941bbSAndroid Build Coastguard Worker 130*b7c941bbSAndroid Build Coastguard Worker @Override onError(int errorCode)131*b7c941bbSAndroid Build Coastguard Worker public void onError(int errorCode) { 132*b7c941bbSAndroid Build Coastguard Worker Log.i(TAG, "OobDataCallback: onError: " + errorCode); 133*b7c941bbSAndroid Build Coastguard Worker mOobData = null; 134*b7c941bbSAndroid Build Coastguard Worker mCountDownLatch.countDown(); 135*b7c941bbSAndroid Build Coastguard Worker 136*b7c941bbSAndroid Build Coastguard Worker } 137*b7c941bbSAndroid Build Coastguard Worker } 138*b7c941bbSAndroid Build Coastguard Worker generateLocalOObData()139*b7c941bbSAndroid Build Coastguard Worker public OobData generateLocalOObData() { 140*b7c941bbSAndroid Build Coastguard Worker CountDownLatch oobLatch = new CountDownLatch(1); 141*b7c941bbSAndroid Build Coastguard Worker mBluetoothAdapter.generateLocalOobData( 142*b7c941bbSAndroid Build Coastguard Worker TRANSPORT_LE, 143*b7c941bbSAndroid Build Coastguard Worker Executors.newSingleThreadExecutor(), 144*b7c941bbSAndroid Build Coastguard Worker new OobDataCallbackImpl(oobLatch)); 145*b7c941bbSAndroid Build Coastguard Worker boolean timeout; 146*b7c941bbSAndroid Build Coastguard Worker try { 147*b7c941bbSAndroid Build Coastguard Worker timeout = !oobLatch.await(CALLBACK_TIMEOUT_SEC, SECONDS); 148*b7c941bbSAndroid Build Coastguard Worker } catch (InterruptedException e) { 149*b7c941bbSAndroid Build Coastguard Worker Log.e(TAG, "", e); 150*b7c941bbSAndroid Build Coastguard Worker timeout = true; 151*b7c941bbSAndroid Build Coastguard Worker } 152*b7c941bbSAndroid Build Coastguard Worker if (timeout || mOobData == null) { 153*b7c941bbSAndroid Build Coastguard Worker Log.e(TAG, "Did not generate local oob data"); 154*b7c941bbSAndroid Build Coastguard Worker return null; 155*b7c941bbSAndroid Build Coastguard Worker } 156*b7c941bbSAndroid Build Coastguard Worker return mOobData; 157*b7c941bbSAndroid Build Coastguard Worker } 158*b7c941bbSAndroid Build Coastguard Worker 159*b7c941bbSAndroid Build Coastguard Worker } 160