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.BluetoothManager
20 import android.bluetooth.BluetoothProfile
21 import android.content.Context
22 import android.util.Log
23 import io.grpc.BindableService
24 import io.grpc.Server as GrpcServer
25 import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder
26 import java.io.Closeable
27 
28 @kotlinx.coroutines.ExperimentalCoroutinesApi
29 class Server(context: Context) {
30 
31     private val TAG = "PandoraServer"
32     private val GRPC_PORT = 8999
33 
34     private var grpcServer: GrpcServer
35     private var services: List<BindableService>
36 
37     init {
38         val bluetoothAdapter = context.getSystemService(BluetoothManager::class.java)!!.adapter
39 
40         val security = Security(context)
41 
42         // If Bluetooth is turned off, the server will try to get the available profiles,
43         // but they will be null which will cause the server to crash.
44         // Filtering the nullable profiles will allow the user to
45         // perform a factory reset to turn the Bluetooth back on.
46         services =
47             listOf(
48                 security,
49                 Host(context, security, this),
50                 L2cap(context),
51                 MediaPlayer(context),
52                 Rfcomm(context),
53                 SecurityStorage(context),
54                 Os(context),
55             ) +
56                 mapOf(
57                         BluetoothProfile.A2DP to ::A2dp,
58                         BluetoothProfile.A2DP_SINK to ::A2dpSink,
59                         BluetoothProfile.HAP_CLIENT to ::Hap,
60                         BluetoothProfile.HEARING_AID to ::Asha,
61                         BluetoothProfile.AVRCP to ::Avrcp,
62                         BluetoothProfile.GATT to ::Gatt,
63                         BluetoothProfile.HEADSET to ::Hfp,
64                         BluetoothProfile.HEADSET_CLIENT to ::HfpHandsfree,
65                         BluetoothProfile.HID_HOST to ::Hid,
66                         BluetoothProfile.PAN to ::Pan,
67                         BluetoothProfile.PBAP to ::Pbap,
68                         BluetoothProfile.OPP to ::Opp,
69                         BluetoothProfile.MAP to ::Map,
70                         BluetoothProfile.LE_AUDIO to ::LeAudio,
71                         BluetoothProfile.VOLUME_CONTROL to ::Vcp,
72                     )
<lambda>null73                     .filter { bluetoothAdapter.isEnabled }
<lambda>null74                     .filter { bluetoothAdapter.getSupportedProfiles().contains(it.key) == true }
<lambda>null75                     .map { it.value(context) }
76 
77         val grpcServerBuilder = NettyServerBuilder.forPort(GRPC_PORT)
78 
<lambda>null79         services.forEach { grpcServerBuilder.addService(it) }
80 
81         grpcServer = grpcServerBuilder.build()
82 
83         Log.d(TAG, "Starting Pandora Server")
84         grpcServer.start()
85         Log.d(TAG, "Pandora Server started at $GRPC_PORT")
86     }
87 
shutdownnull88     fun shutdown() = grpcServer.shutdown()
89 
90     fun awaitTermination() = grpcServer.awaitTermination()
91 
92     fun deinit() = services.forEach { if (it is Closeable) it.close() }
93 }
94