• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 com.android.pandora
18 
19 import android.bluetooth.BluetoothAdapter
20 import android.bluetooth.BluetoothAssignedNumbers
21 import android.bluetooth.BluetoothDevice
22 import android.bluetooth.BluetoothDevice.ADDRESS_TYPE_PUBLIC
23 import android.bluetooth.BluetoothDevice.BOND_BONDED
24 import android.bluetooth.BluetoothDevice.TRANSPORT_BREDR
25 import android.bluetooth.BluetoothDevice.TRANSPORT_LE
26 import android.bluetooth.BluetoothManager
27 import android.bluetooth.BluetoothProfile
28 import android.bluetooth.BluetoothUuid
29 import android.bluetooth.le.AdvertiseCallback
30 import android.bluetooth.le.AdvertiseData
31 import android.bluetooth.le.AdvertiseSettings
32 import android.bluetooth.le.AdvertisingSetParameters
33 import android.bluetooth.le.ScanCallback
34 import android.bluetooth.le.ScanRecord
35 import android.bluetooth.le.ScanResult
36 import android.content.Context
37 import android.content.Intent
38 import android.content.IntentFilter
39 import android.net.MacAddress
40 import android.os.ParcelUuid
41 import android.util.Log
42 import com.google.protobuf.ByteString
43 import com.google.protobuf.Empty
44 import io.grpc.stub.StreamObserver
45 import java.io.Closeable
46 import java.lang.IllegalArgumentException
47 import java.nio.ByteBuffer
48 import java.time.Duration
49 import java.util.UUID
50 import kotlinx.coroutines.CoroutineScope
51 import kotlinx.coroutines.Dispatchers
52 import kotlinx.coroutines.awaitCancellation
53 import kotlinx.coroutines.cancel
54 import kotlinx.coroutines.channels.awaitClose
55 import kotlinx.coroutines.channels.trySendBlocking
56 import kotlinx.coroutines.delay
57 import kotlinx.coroutines.flow.Flow
58 import kotlinx.coroutines.flow.SharingStarted
59 import kotlinx.coroutines.flow.callbackFlow
60 import kotlinx.coroutines.flow.filter
61 import kotlinx.coroutines.flow.first
62 import kotlinx.coroutines.flow.map
63 import kotlinx.coroutines.flow.shareIn
64 import kotlinx.coroutines.launch
65 import kotlinx.coroutines.runBlocking
66 import pandora.HostGrpc.HostImplBase
67 import pandora.HostProto.*
68 
69 object ByteArrayOps {
getUShortAtnull70   public fun getUShortAt(input: ByteArray, index: Int): UShort {
71     return (((input[index + 1].toUInt() and 0xffU) shl 8) or (input[index].toUInt() and 0xffU))
72       .toUShort()
73   }
74 
getShortAtnull75   public fun getShortAt(input: ByteArray, index: Int): Short {
76     return getUShortAt(input, index).toShort()
77   }
78 
getUIntAtnull79   public fun getUIntAt(input: ByteArray, index: Int): UInt {
80     return (((input[index + 3].toUInt() and 0xffU) shl 24) or
81       ((input[index + 2].toUInt() and 0xffU) shl 16) or
82       ((input[index + 1].toUInt() and 0xffU) shl 8) or
83       (input[index].toUInt() and 0xffU))
84   }
85 
getIntAtnull86   public fun getIntAt(input: ByteArray, index: Int): Int {
87     return getUIntAt(input, index).toInt()
88   }
89 
getUInt24Atnull90   public fun getUInt24At(input: ByteArray, index: Int): UInt {
91     return (((input[index + 2].toUInt() and 0xffU) shl 16) or
92       ((input[index + 1].toUInt() and 0xffU) shl 8) or
93       (input[index].toUInt() and 0xffU))
94   }
95 
getInt24Atnull96   public fun getInt24At(input: ByteArray, index: Int): Int {
97     return getUInt24At(input, index).toInt()
98   }
99 }
100 
101 @kotlinx.coroutines.ExperimentalCoroutinesApi
102 class Host(
103   private val context: Context,
104   private val security: Security,
105   private val server: Server
106 ) : HostImplBase(), Closeable {
107   private val TAG = "PandoraHost"
108 
109   private val scope: CoroutineScope
110   private val flow: Flow<Intent>
111 
112   private val bluetoothManager = context.getSystemService(BluetoothManager::class.java)!!
113   private val bluetoothAdapter = bluetoothManager.adapter
114 
115   private var connectability = ConnectabilityMode.NOT_CONNECTABLE
116   private var discoverability = DiscoverabilityMode.NOT_DISCOVERABLE
117 
118   private val advertisers = mutableMapOf<UUID, AdvertiseCallback>()
119 
120   init {
121     scope = CoroutineScope(Dispatchers.Default)
122 
123     // Add all intent actions to be listened.
124     val intentFilter = IntentFilter()
125     intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
126     intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
127     intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)
128     intentFilter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST)
129     intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED)
130     intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED)
131     intentFilter.addAction(BluetoothDevice.ACTION_FOUND)
132 
133     // Creates a shared flow of intents that can be used in all methods in the coroutine scope.
134     // This flow is started eagerly to make sure that the broadcast receiver is registered before
135     // any function call. This flow is only cancelled when the corresponding scope is cancelled.
136     flow = intentFlow(context, intentFilter).shareIn(scope, SharingStarted.Eagerly)
137   }
138 
closenull139   override fun close() {
140     scope.cancel()
141   }
142 
rebootBluetoothnull143   private suspend fun rebootBluetooth() {
144     Log.i(TAG, "rebootBluetooth")
145 
146     val stateFlow =
147       flow
148         .filter { it.getAction() == BluetoothAdapter.ACTION_STATE_CHANGED }
149         .map { it.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR) }
150 
151     if (bluetoothAdapter.isEnabled) {
152       bluetoothAdapter.disable()
153       stateFlow.filter { it == BluetoothAdapter.STATE_OFF }.first()
154     }
155 
156     // TODO: b/234892968
157     delay(3000L)
158 
159     bluetoothAdapter.enable()
160     stateFlow.filter { it == BluetoothAdapter.STATE_ON }.first()
161   }
162 
factoryResetnull163   override fun factoryReset(request: Empty, responseObserver: StreamObserver<Empty>) {
164     grpcUnary<Empty>(scope, responseObserver, 30) {
165       Log.i(TAG, "factoryReset")
166 
167       val stateFlow =
168         flow
169           .filter { it.getAction() == BluetoothAdapter.ACTION_STATE_CHANGED }
170           .map { it.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR) }
171 
172       initiatedConnection.clear()
173       waitedAclConnection.clear()
174       waitedAclDisconnection.clear()
175 
176       bluetoothAdapter.clearBluetooth()
177 
178       stateFlow.filter { it == BluetoothAdapter.STATE_ON }.first()
179       // Delay to initialize the Bluetooth completely and to fix flakiness: b/266611263
180       delay(1000L)
181       Log.i(TAG, "Shutdown the gRPC Server")
182       server.shutdown()
183 
184       // The last expression is the return value.
185       Empty.getDefaultInstance()
186     }
187   }
188 
resetnull189   override fun reset(request: Empty, responseObserver: StreamObserver<Empty>) {
190     grpcUnary<Empty>(scope, responseObserver) {
191       Log.i(TAG, "reset")
192       initiatedConnection.clear()
193       waitedAclConnection.clear()
194       waitedAclDisconnection.clear()
195 
196       rebootBluetooth()
197 
198       Empty.getDefaultInstance()
199     }
200   }
201 
readLocalAddressnull202   override fun readLocalAddress(
203     request: Empty,
204     responseObserver: StreamObserver<ReadLocalAddressResponse>
205   ) {
206     grpcUnary<ReadLocalAddressResponse>(scope, responseObserver) {
207       Log.i(TAG, "readLocalAddress")
208       val localMacAddress = MacAddress.fromString(bluetoothAdapter.getAddress())
209       ReadLocalAddressResponse.newBuilder()
210         .setAddress(ByteString.copyFrom(localMacAddress.toByteArray()))
211         .build()
212     }
213   }
214 
waitPairingRequestIntentnull215   private suspend fun waitPairingRequestIntent(bluetoothDevice: BluetoothDevice) {
216     Log.i(TAG, "waitPairingRequestIntent: device=$bluetoothDevice")
217     var pairingVariant =
218       flow
219         .filter { it.getAction() == BluetoothDevice.ACTION_PAIRING_REQUEST }
220         .filter { it.getBluetoothDeviceExtra() == bluetoothDevice }
221         .first()
222         .getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR)
223 
224     val confirmationCases =
225       intArrayOf(
226         BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION,
227         BluetoothDevice.PAIRING_VARIANT_CONSENT,
228         BluetoothDevice.PAIRING_VARIANT_PIN,
229       )
230 
231     if (pairingVariant in confirmationCases) {
232       bluetoothDevice.setPairingConfirmation(true)
233     }
234   }
235 
waitConnectionIntentnull236   private suspend fun waitConnectionIntent(bluetoothDevice: BluetoothDevice) {
237     Log.i(TAG, "waitConnectionIntent: device=$bluetoothDevice")
238     flow
239       .filter { it.action == BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED }
240       .filter { it.getBluetoothDeviceExtra() == bluetoothDevice }
241       .map { it.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, BluetoothAdapter.ERROR) }
242       .filter { it == BluetoothAdapter.STATE_CONNECTED }
243       .first()
244   }
245 
waitBondIntentnull246   suspend fun waitBondIntent(bluetoothDevice: BluetoothDevice) {
247     // We only wait for bonding to be completed since we only need the ACL connection to be
248     // established with the peer device (on Android state connected is sent when all profiles
249     // have been connected).
250     Log.i(TAG, "waitBondIntent: device=$bluetoothDevice")
251     flow
252       .filter { it.action == BluetoothDevice.ACTION_BOND_STATE_CHANGED }
253       .filter { it.getBluetoothDeviceExtra() == bluetoothDevice }
254       .map { it.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothAdapter.ERROR) }
255       .filter { it == BOND_BONDED }
256       .first()
257   }
258 
waitIncomingAclConnectedIntentnull259   suspend fun waitIncomingAclConnectedIntent(address: String?, transport: Int): Intent {
260     return flow
261       .filter { it.action == BluetoothDevice.ACTION_ACL_CONNECTED }
262       .filter { address == null || it.getBluetoothDeviceExtra().address == address }
263       .filter { !initiatedConnection.contains(it.getBluetoothDeviceExtra()) }
264       .filter {
265         it.getIntExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.ERROR) == transport
266       }
267       .first()
268   }
269 
acceptPairingAndAwaitBondednull270   private suspend fun acceptPairingAndAwaitBonded(bluetoothDevice: BluetoothDevice) {
271     val acceptPairingJob = scope.launch { waitPairingRequestIntent(bluetoothDevice) }
272     waitBondIntent(bluetoothDevice)
273     if (acceptPairingJob.isActive) {
274       acceptPairingJob.cancel()
275     }
276   }
277 
waitConnectionnull278   override fun waitConnection(
279     request: WaitConnectionRequest,
280     responseObserver: StreamObserver<WaitConnectionResponse>
281   ) {
282     grpcUnary(scope, responseObserver) {
283       if (request.address.isEmpty())
284         throw IllegalArgumentException("Request address field must be set")
285       var bluetoothDevice = request.address.toBluetoothDevice(bluetoothAdapter)
286 
287       Log.i(TAG, "waitConnection: device=$bluetoothDevice")
288 
289       if (!bluetoothAdapter.isEnabled) {
290         throw RuntimeException("Bluetooth is not enabled, cannot waitConnection")
291       }
292 
293       if (!bluetoothDevice.isConnected() || waitedAclConnection.contains(bluetoothDevice)) {
294         bluetoothDevice =
295           waitIncomingAclConnectedIntent(bluetoothDevice.address, TRANSPORT_BREDR)
296             .getBluetoothDeviceExtra()
297       }
298 
299       waitedAclConnection.add(bluetoothDevice)
300 
301       WaitConnectionResponse.newBuilder()
302         .setConnection(bluetoothDevice.toConnection(TRANSPORT_BREDR))
303         .build()
304     }
305   }
306 
waitDisconnectionnull307   override fun waitDisconnection(
308     request: WaitDisconnectionRequest,
309     responseObserver: StreamObserver<Empty>
310   ) {
311     grpcUnary(scope, responseObserver) {
312       val bluetoothDevice = request.connection.toBluetoothDevice(bluetoothAdapter)
313       Log.i(TAG, "waitDisconnection: device=$bluetoothDevice")
314       if (!bluetoothAdapter.isEnabled) {
315         throw RuntimeException("Bluetooth is not enabled, cannot waitDisconnection")
316       }
317       if (
318         bluetoothDevice.bondState != BluetoothDevice.BOND_NONE &&
319           !waitedAclDisconnection.contains(bluetoothDevice)
320       ) {
321         flow
322           .filter { it.action == BluetoothDevice.ACTION_ACL_DISCONNECTED }
323           .filter { it.getBluetoothDeviceExtra() == bluetoothDevice }
324           .first()
325       }
326 
327       waitedAclDisconnection.add(bluetoothDevice)
328 
329       Empty.getDefaultInstance()
330     }
331   }
332 
connectnull333   override fun connect(request: ConnectRequest, responseObserver: StreamObserver<ConnectResponse>) {
334     grpcUnary(scope, responseObserver) {
335       if (request.address.isEmpty())
336         throw IllegalArgumentException("Request address field must be set")
337       val bluetoothDevice = request.address.toBluetoothDevice(bluetoothAdapter)
338 
339       Log.i(TAG, "connect: address=$bluetoothDevice")
340 
341       initiatedConnection.add(bluetoothDevice)
342       bluetoothAdapter.cancelDiscovery()
343 
344       if (!bluetoothDevice.isConnected()) {
345         if (bluetoothDevice.bondState == BOND_BONDED) {
346           // already bonded, just reconnect
347           bluetoothDevice.connect()
348           waitConnectionIntent(bluetoothDevice)
349         } else {
350           // need to bond
351           bluetoothDevice.createBond()
352           if (!security.manuallyConfirm) {
353             acceptPairingAndAwaitBonded(bluetoothDevice)
354           }
355         }
356       }
357 
358       ConnectResponse.newBuilder()
359         .setConnection(bluetoothDevice.toConnection(TRANSPORT_BREDR))
360         .build()
361     }
362   }
363 
disconnectnull364   override fun disconnect(request: DisconnectRequest, responseObserver: StreamObserver<Empty>) {
365     grpcUnary<Empty>(scope, responseObserver) {
366       val bluetoothDevice = request.connection.toBluetoothDevice(bluetoothAdapter)
367       Log.i(TAG, "disconnect: device=$bluetoothDevice")
368 
369       if (!bluetoothDevice.isConnected()) {
370         throw RuntimeException("Device is not connected, cannot disconnect")
371       }
372 
373       when (request.connection.transport) {
374         TRANSPORT_BREDR -> {
375           Log.i(TAG, "disconnect BR_EDR")
376           bluetoothDevice.disconnect()
377         }
378         TRANSPORT_LE -> {
379           Log.i(TAG, "disconnect LE")
380           val gattInstance =
381             try {
382               GattInstance.get(bluetoothDevice.address)
383             } catch (e: Exception) {
384               Log.w(TAG, "Gatt instance doesn't exist. Android might be peripheral")
385               val instance = GattInstance(bluetoothDevice, TRANSPORT_LE, context)
386               instance.waitForState(BluetoothProfile.STATE_CONNECTED)
387               instance
388             }
389           if (gattInstance.isDisconnected()) {
390             throw RuntimeException("Device is not connected, cannot disconnect")
391           }
392 
393           bluetoothDevice.disconnect()
394           gattInstance.disconnectInstance()
395         }
396         else -> {
397           throw RuntimeException("Device type UNKNOWN")
398         }
399       }
400       flow
401         .filter { it.action == BluetoothDevice.ACTION_ACL_DISCONNECTED }
402         .filter { it.getBluetoothDeviceExtra() == bluetoothDevice }
403         .first()
404 
405       Empty.getDefaultInstance()
406     }
407   }
408 
connectLEnull409   override fun connectLE(
410     request: ConnectLERequest,
411     responseObserver: StreamObserver<ConnectLEResponse>
412   ) {
413     grpcUnary<ConnectLEResponse>(scope, responseObserver) {
414       val ownAddressType = request.ownAddressType
415       if (
416         ownAddressType != OwnAddressType.RANDOM &&
417           ownAddressType != OwnAddressType.RESOLVABLE_OR_RANDOM
418       ) {
419         throw RuntimeException("connectLE: Unsupported OwnAddressType: $ownAddressType")
420       }
421       val (address, type) =
422         when (request.getAddressCase()!!) {
423           ConnectLERequest.AddressCase.PUBLIC ->
424             Pair(request.public, BluetoothDevice.ADDRESS_TYPE_PUBLIC)
425           ConnectLERequest.AddressCase.RANDOM ->
426             Pair(request.random, BluetoothDevice.ADDRESS_TYPE_RANDOM)
427           ConnectLERequest.AddressCase.PUBLIC_IDENTITY ->
428             Pair(request.publicIdentity, BluetoothDevice.ADDRESS_TYPE_PUBLIC)
429           ConnectLERequest.AddressCase.RANDOM_STATIC_IDENTITY ->
430             Pair(request.randomStaticIdentity, BluetoothDevice.ADDRESS_TYPE_RANDOM)
431           ConnectLERequest.AddressCase.ADDRESS_NOT_SET ->
432             throw IllegalArgumentException("Request address field must be set")
433         }
434       Log.i(TAG, "connectLE: $address")
435       val bluetoothDevice = scanLeDevice(address.decodeAsMacAddressToString(), type)!!
436       initiatedConnection.add(bluetoothDevice)
437       GattInstance(bluetoothDevice, TRANSPORT_LE, context)
438         .waitForState(BluetoothProfile.STATE_CONNECTED)
439       ConnectLEResponse.newBuilder()
440         .setConnection(bluetoothDevice.toConnection(TRANSPORT_LE))
441         .build()
442     }
443   }
444 
scanLeDevicenull445   private fun scanLeDevice(address: String, addressType: Int): BluetoothDevice? {
446     Log.d(TAG, "scanLeDevice")
447     var bluetoothDevice: BluetoothDevice? = null
448     runBlocking {
449       val flow = callbackFlow {
450         val leScanCallback =
451           object : ScanCallback() {
452             override fun onScanFailed(errorCode: Int) {
453               super.onScanFailed(errorCode)
454               Log.d(TAG, "onScanFailed: errorCode: $errorCode")
455               trySendBlocking(null)
456             }
457             override fun onScanResult(callbackType: Int, result: ScanResult) {
458               super.onScanResult(callbackType, result)
459               val deviceAddress = result.device.address
460               val deviceAddressType = result.device.addressType
461               if (deviceAddress == address && deviceAddressType == addressType) {
462                 Log.d(TAG, "found device address: $deviceAddress")
463                 trySendBlocking(result.device)
464               }
465             }
466           }
467         val bluetoothLeScanner = bluetoothAdapter.bluetoothLeScanner
468         bluetoothLeScanner?.startScan(leScanCallback) ?: run { trySendBlocking(null) }
469         awaitClose { bluetoothLeScanner?.stopScan(leScanCallback) }
470       }
471       bluetoothDevice = flow.first()
472     }
473     return bluetoothDevice
474   }
475 
advertisenull476   override fun advertise(
477     request: AdvertiseRequest,
478     responseObserver: StreamObserver<AdvertiseResponse>
479   ) {
480     Log.d(TAG, "advertise")
481     grpcServerStream(scope, responseObserver) {
482       callbackFlow {
483         val callback =
484           object : AdvertiseCallback() {
485             override fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
486               Log.d(TAG, "advertising started")
487             }
488             override fun onStartFailure(errorCode: Int) {
489               error("failed to start advertising: $errorCode")
490             }
491           }
492         val advertisingDataBuilder = AdvertiseData.Builder()
493         val dataTypesRequest = request.data
494 
495         if (
496           !dataTypesRequest.getIncompleteServiceClassUuids16List().isEmpty() or
497             !dataTypesRequest.getIncompleteServiceClassUuids32List().isEmpty() or
498             !dataTypesRequest.getIncompleteServiceClassUuids128List().isEmpty()
499         ) {
500           throw RuntimeException("Incomplete Service Class Uuids not supported")
501         }
502 
503         // Handle service uuids
504         for (uuid16 in dataTypesRequest.getCompleteServiceClassUuids16List()) {
505           val parcel_uuid16 = ParcelUuid.fromString("0000${uuid16}-0000-1000-8000-00805F9B34FB")
506           advertisingDataBuilder.addServiceUuid(parcel_uuid16)
507         }
508         for (uuid32 in dataTypesRequest.getCompleteServiceClassUuids32List()) {
509           val parcel_uuid32 = ParcelUuid.fromString("${uuid32}-0000-1000-8000-00805F9B34FB")
510           advertisingDataBuilder.addServiceUuid(parcel_uuid32)
511         }
512         for (uuid128 in dataTypesRequest.getCompleteServiceClassUuids128List()) {
513           advertisingDataBuilder.addServiceUuid(ParcelUuid.fromString(uuid128))
514         }
515 
516         // Handle Service solicitation uuids
517         for (uuid16 in dataTypesRequest.getServiceSolicitationUuids16List()) {
518           val parcel_uuid16 = ParcelUuid.fromString("0000${uuid16}-0000-1000-8000-00805F9B34FB")
519           advertisingDataBuilder.addServiceSolicitationUuid(parcel_uuid16)
520         }
521         for (uuid32 in dataTypesRequest.getServiceSolicitationUuids32List()) {
522           val parcel_uuid32 = ParcelUuid.fromString("${uuid32}-0000-1000-8000-00805F9B34FB")
523           advertisingDataBuilder.addServiceSolicitationUuid(parcel_uuid32)
524         }
525         for (uuid128 in dataTypesRequest.getServiceSolicitationUuids128List()) {
526           advertisingDataBuilder.addServiceSolicitationUuid(ParcelUuid.fromString(uuid128))
527         }
528 
529         // Handle service data uuids
530         for ((uuid16, data) in dataTypesRequest.getServiceDataUuid16()) {
531           val parcel_uuid16 = ParcelUuid.fromString("0000${uuid16}-0000-1000-8000-00805F9B34FB")
532           advertisingDataBuilder.addServiceData(parcel_uuid16, data.toByteArray())
533         }
534         for ((uuid32, data) in dataTypesRequest.getServiceDataUuid32()) {
535           val parcel_uuid32 = ParcelUuid.fromString("${uuid32}-0000-1000-8000-00805F9B34FB")
536           advertisingDataBuilder.addServiceData(parcel_uuid32, data.toByteArray())
537         }
538         for ((uuid128, data) in dataTypesRequest.getServiceDataUuid128()) {
539           advertisingDataBuilder.addServiceData(ParcelUuid.fromString(uuid128), data.toByteArray())
540         }
541 
542         advertisingDataBuilder
543           .setIncludeDeviceName(
544             dataTypesRequest.includeCompleteLocalName || dataTypesRequest.includeShortenedLocalName
545           )
546           .setIncludeTxPowerLevel(dataTypesRequest.includeTxPowerLevel)
547           .addManufacturerData(
548             BluetoothAssignedNumbers.GOOGLE,
549             dataTypesRequest.manufacturerSpecificData.toByteArray()
550           )
551         val advertisingData = advertisingDataBuilder.build()
552 
553         val ownAddressType =
554           when (request.ownAddressType) {
555             OwnAddressType.RESOLVABLE_OR_PUBLIC,
556             OwnAddressType.PUBLIC -> AdvertisingSetParameters.ADDRESS_TYPE_PUBLIC
557             OwnAddressType.RESOLVABLE_OR_RANDOM,
558             OwnAddressType.RANDOM -> AdvertisingSetParameters.ADDRESS_TYPE_RANDOM
559             else -> AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT
560           }
561         val advertiseSettings =
562           AdvertiseSettings.Builder()
563             .setConnectable(request.connectable)
564             .setOwnAddressType(ownAddressType)
565             .build()
566 
567         bluetoothAdapter.bluetoothLeAdvertiser.startAdvertising(
568           advertiseSettings,
569           advertisingData,
570           callback,
571         )
572 
573         if (request.connectable) {
574           while (true) {
575             Log.d(TAG, "Waiting for incoming connection")
576             val connection =
577               waitIncomingAclConnectedIntent(null, TRANSPORT_LE)
578                 .getBluetoothDeviceExtra()
579                 .toConnection(TRANSPORT_LE)
580             Log.d(TAG, "Receive connection")
581             trySendBlocking(AdvertiseResponse.newBuilder().setConnection(connection).build())
582           }
583         }
584 
585         awaitClose { bluetoothAdapter.bluetoothLeAdvertiser.stopAdvertising(callback) }
586       }
587     }
588   }
589 
590   // TODO: Handle request parameters
scannull591   override fun scan(request: ScanRequest, responseObserver: StreamObserver<ScanningResponse>) {
592     Log.d(TAG, "scan")
593     grpcServerStream(scope, responseObserver) {
594       callbackFlow {
595         val callback =
596           object : ScanCallback() {
597             override fun onScanResult(callbackType: Int, result: ScanResult) {
598               val bluetoothDevice = result.device
599               val scanRecord = result.scanRecord
600               val scanData = scanRecord.getAdvertisingDataMap()
601               val serviceData = scanRecord?.serviceData!!
602 
603               var dataTypesBuilder =
604                 DataTypes.newBuilder().setTxPowerLevel(scanRecord.getTxPowerLevel())
605 
606               scanData[ScanRecord.DATA_TYPE_LOCAL_NAME_SHORT]?.let {
607                 dataTypesBuilder.setShortenedLocalName(it.decodeToString())
608               }
609                 ?: run { dataTypesBuilder.setIncludeShortenedLocalName(false) }
610 
611               scanData[ScanRecord.DATA_TYPE_LOCAL_NAME_COMPLETE]?.let {
612                 dataTypesBuilder.setCompleteLocalName(it.decodeToString())
613               }
614                 ?: run { dataTypesBuilder.setIncludeCompleteLocalName(false) }
615 
616               scanData[ScanRecord.DATA_TYPE_ADVERTISING_INTERVAL]?.let {
617                 dataTypesBuilder.setAdvertisingInterval(ByteArrayOps.getShortAt(it, 0).toInt())
618               }
619 
620               scanData[ScanRecord.DATA_TYPE_ADVERTISING_INTERVAL_LONG]?.let {
621                 dataTypesBuilder.setAdvertisingInterval(ByteArrayOps.getIntAt(it, 0))
622               }
623 
624               scanData[ScanRecord.DATA_TYPE_APPEARANCE]?.let {
625                 dataTypesBuilder.setAppearance(ByteArrayOps.getShortAt(it, 0).toInt())
626               }
627 
628               scanData[ScanRecord.DATA_TYPE_CLASS_OF_DEVICE]?.let {
629                 dataTypesBuilder.setClassOfDevice(ByteArrayOps.getInt24At(it, 0))
630               }
631 
632               scanData[ScanRecord.DATA_TYPE_URI]?.let {
633                 dataTypesBuilder.setUri(it.decodeToString())
634               }
635 
636               scanData[ScanRecord.DATA_TYPE_LE_SUPPORTED_FEATURES]?.let {
637                 dataTypesBuilder.setLeSupportedFeatures(ByteString.copyFrom(it))
638               }
639 
640               scanData[ScanRecord.DATA_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE]?.let {
641                 dataTypesBuilder.setPeripheralConnectionIntervalMin(
642                   ByteArrayOps.getShortAt(it, 0).toInt()
643                 )
644                 dataTypesBuilder.setPeripheralConnectionIntervalMax(
645                   ByteArrayOps.getShortAt(it, 2).toInt()
646                 )
647               }
648 
649               for (serviceDataEntry in serviceData) {
650                 val parcelUuid = serviceDataEntry.key
651                 Log.d(TAG, parcelUuid.uuid.toString().uppercase())
652 
653                 // use upper case uuid as the key
654                 if (BluetoothUuid.is16BitUuid(parcelUuid)) {
655                   val uuid16 = parcelUuid.uuid.toString().substring(4, 8).uppercase()
656                   dataTypesBuilder.putServiceDataUuid16(
657                     uuid16,
658                     ByteString.copyFrom(serviceDataEntry.value)
659                   )
660                 } else if (BluetoothUuid.is32BitUuid(parcelUuid)) {
661                   val uuid32 = parcelUuid.uuid.toString().substring(0, 8).uppercase()
662                   dataTypesBuilder.putServiceDataUuid32(
663                     uuid32,
664                     ByteString.copyFrom(serviceDataEntry.value)
665                   )
666                 } else {
667                   val uuid128 = parcelUuid.uuid.toString().uppercase()
668                   dataTypesBuilder.putServiceDataUuid128(
669                     uuid128,
670                     ByteString.copyFrom(serviceDataEntry.value)
671                   )
672                 }
673               }
674 
675               for (serviceUuid in scanRecord.serviceSolicitationUuids ?: listOf<ParcelUuid>()) {
676                 Log.d(TAG, serviceUuid.uuid.toString().uppercase())
677                 if (BluetoothUuid.is16BitUuid(serviceUuid)) {
678                   val uuid16 = serviceUuid.uuid.toString().substring(4, 8).uppercase()
679                   dataTypesBuilder.addServiceSolicitationUuids16(uuid16)
680                 } else if (BluetoothUuid.is32BitUuid(serviceUuid)) {
681                   val uuid32 = serviceUuid.uuid.toString().substring(0, 8).uppercase()
682                   dataTypesBuilder.addServiceSolicitationUuids32(uuid32)
683                 } else {
684                   val uuid128 = serviceUuid.uuid.toString().uppercase()
685                   dataTypesBuilder.addServiceSolicitationUuids128(uuid128)
686                 }
687               }
688 
689               for (serviceUuid in scanRecord.serviceUuids ?: listOf<ParcelUuid>()) {
690                 Log.d(TAG, serviceUuid.uuid.toString().uppercase())
691                 if (BluetoothUuid.is16BitUuid(serviceUuid)) {
692                   val uuid16 = serviceUuid.uuid.toString().substring(4, 8).uppercase()
693                   dataTypesBuilder.addIncompleteServiceClassUuids16(uuid16)
694                 } else if (BluetoothUuid.is32BitUuid(serviceUuid)) {
695                   val uuid32 = serviceUuid.uuid.toString().substring(0, 8).uppercase()
696                   dataTypesBuilder.addIncompleteServiceClassUuids32(uuid32)
697                 } else {
698                   val uuid128 = serviceUuid.uuid.toString().uppercase()
699                   dataTypesBuilder.addIncompleteServiceClassUuids128(uuid128)
700                 }
701               }
702 
703               // Flags DataTypes CSSv10 1.3 Flags
704               val mode: DiscoverabilityMode =
705                 when (result.scanRecord.advertiseFlags and 0b11) {
706                   0b01 -> DiscoverabilityMode.DISCOVERABLE_LIMITED
707                   0b10 -> DiscoverabilityMode.DISCOVERABLE_GENERAL
708                   else -> DiscoverabilityMode.NOT_DISCOVERABLE
709                 }
710               dataTypesBuilder.setLeDiscoverabilityMode(mode)
711               var manufacturerData = ByteBuffer.allocate(512)
712               val manufacturerSpecificDatas = scanRecord.getManufacturerSpecificData()
713               for (i in 0..manufacturerSpecificDatas.size() - 1) {
714                 val id = manufacturerSpecificDatas.keyAt(i)
715                 manufacturerData
716                   .put(id.toByte())
717                   .put(id.shr(8).toByte())
718                   .put(manufacturerSpecificDatas.get(id))
719               }
720               dataTypesBuilder.setManufacturerSpecificData(
721                 ByteString.copyFrom(manufacturerData.array(), 0, manufacturerData.position())
722               )
723               val primaryPhy =
724                 when (result.getPrimaryPhy()) {
725                   BluetoothDevice.PHY_LE_1M -> PrimaryPhy.PRIMARY_1M
726                   BluetoothDevice.PHY_LE_CODED -> PrimaryPhy.PRIMARY_CODED
727                   else -> PrimaryPhy.UNRECOGNIZED
728                 }
729               val secondaryPhy =
730                 when (result.getSecondaryPhy()) {
731                   ScanResult.PHY_UNUSED -> SecondaryPhy.SECONDARY_NONE
732                   BluetoothDevice.PHY_LE_1M -> SecondaryPhy.SECONDARY_1M
733                   BluetoothDevice.PHY_LE_2M -> SecondaryPhy.SECONDARY_2M
734                   BluetoothDevice.PHY_LE_CODED -> SecondaryPhy.SECONDARY_CODED
735                   else -> SecondaryPhy.UNRECOGNIZED
736                 }
737               var scanningResponseBuilder =
738                 ScanningResponse.newBuilder()
739                   .setLegacy(result.isLegacy())
740                   .setConnectable(result.isConnectable())
741                   .setTruncated(result.getDataStatus() == ScanResult.DATA_TRUNCATED)
742                   .setSid(result.getAdvertisingSid())
743                   .setPrimaryPhy(primaryPhy)
744                   .setSecondaryPhy(secondaryPhy)
745                   .setTxPower(result.getTxPower())
746                   .setRssi(result.getRssi())
747                   .setPeriodicAdvertisingInterval(result.getPeriodicAdvertisingInterval().toFloat())
748                   .setData(dataTypesBuilder.build())
749               when (bluetoothDevice.addressType) {
750                 BluetoothDevice.ADDRESS_TYPE_PUBLIC ->
751                   scanningResponseBuilder.setPublic(bluetoothDevice.toByteString())
752                 BluetoothDevice.ADDRESS_TYPE_RANDOM ->
753                   scanningResponseBuilder.setRandom(bluetoothDevice.toByteString())
754                 else ->
755                   Log.w(TAG, "Address type UNKNOWN: ${bluetoothDevice.type} addr: $bluetoothDevice")
756               }
757               // TODO: Complete the missing field as needed, all the examples are here
758               trySendBlocking(scanningResponseBuilder.build())
759             }
760 
761             override fun onScanFailed(errorCode: Int) {
762               error("scan failed")
763             }
764           }
765         bluetoothAdapter.bluetoothLeScanner.startScan(callback)
766 
767         awaitClose { bluetoothAdapter.bluetoothLeScanner.stopScan(callback) }
768       }
769     }
770   }
771 
inquirynull772   override fun inquiry(request: Empty, responseObserver: StreamObserver<InquiryResponse>) {
773     Log.d(TAG, "Inquiry")
774     grpcServerStream(scope, responseObserver) {
775       launch {
776         try {
777           bluetoothAdapter.startDiscovery()
778           awaitCancellation()
779         } finally {
780           bluetoothAdapter.cancelDiscovery()
781         }
782       }
783       flow
784         .filter { it.action == BluetoothDevice.ACTION_FOUND }
785         .map {
786           val bluetoothDevice = it.getBluetoothDeviceExtra()
787           Log.i(TAG, "Device found: $bluetoothDevice")
788           InquiryResponse.newBuilder().setAddress(bluetoothDevice.toByteString()).build()
789         }
790     }
791   }
792 
setDiscoverabilityModenull793   override fun setDiscoverabilityMode(
794     request: SetDiscoverabilityModeRequest,
795     responseObserver: StreamObserver<Empty>
796   ) {
797     Log.d(TAG, "setDiscoverabilityMode")
798     grpcUnary(scope, responseObserver) {
799       discoverability = request.mode!!
800 
801       val scanMode =
802         when (discoverability) {
803           DiscoverabilityMode.UNRECOGNIZED -> null
804           DiscoverabilityMode.NOT_DISCOVERABLE ->
805             if (connectability == ConnectabilityMode.CONNECTABLE) {
806               BluetoothAdapter.SCAN_MODE_CONNECTABLE
807             } else {
808               BluetoothAdapter.SCAN_MODE_NONE
809             }
810           DiscoverabilityMode.DISCOVERABLE_LIMITED,
811           DiscoverabilityMode.DISCOVERABLE_GENERAL ->
812             BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE
813         }
814 
815       if (scanMode != null) {
816         bluetoothAdapter.setScanMode(scanMode)
817       }
818 
819       if (discoverability == DiscoverabilityMode.DISCOVERABLE_LIMITED) {
820         bluetoothAdapter.setDiscoverableTimeout(
821           Duration.ofSeconds(120)
822         ) // limited discoverability needs a timeout, 120s is Android default
823       }
824       Empty.getDefaultInstance()
825     }
826   }
827 
setConnectabilityModenull828   override fun setConnectabilityMode(
829     request: SetConnectabilityModeRequest,
830     responseObserver: StreamObserver<Empty>
831   ) {
832     grpcUnary(scope, responseObserver) {
833       Log.d(TAG, "setConnectabilityMode")
834       connectability = request.mode!!
835 
836       val scanMode =
837         when (connectability) {
838           ConnectabilityMode.UNRECOGNIZED -> null
839           ConnectabilityMode.NOT_CONNECTABLE -> {
840             BluetoothAdapter.SCAN_MODE_NONE
841           }
842           ConnectabilityMode.CONNECTABLE -> {
843             if (
844               discoverability == DiscoverabilityMode.DISCOVERABLE_LIMITED ||
845                 discoverability == DiscoverabilityMode.DISCOVERABLE_GENERAL
846             ) {
847               BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE
848             } else {
849               BluetoothAdapter.SCAN_MODE_CONNECTABLE
850             }
851           }
852         }
853       if (scanMode != null) {
854         bluetoothAdapter.setScanMode(scanMode)
855       }
856       Empty.getDefaultInstance()
857     }
858   }
859 }
860