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