• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 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.bluetooth.le_audio;
18 
19 import android.bluetooth.BluetoothDevice;
20 import android.bluetooth.BluetoothGatt;
21 import android.bluetooth.BluetoothGattCharacteristic;
22 import android.bluetooth.BluetoothGattServer;
23 import android.bluetooth.BluetoothGattServerCallback;
24 import android.bluetooth.BluetoothGattService;
25 import android.bluetooth.BluetoothManager;
26 import android.bluetooth.BluetoothProfile;
27 import android.bluetooth.BluetoothUuid;
28 import android.content.Context;
29 import android.util.Log;
30 
31 import com.android.internal.annotations.VisibleForTesting;
32 
33 import java.util.Arrays;
34 import java.util.List;
35 import java.util.UUID;
36 
37 /**
38  * A GATT server for Telephony and Media Audio Profile (TMAP)
39  */
40 @VisibleForTesting
41 public class LeAudioTmapGattServer {
42     private static final boolean DBG = true;
43     private static final String TAG = "LeAudioTmapGattServer";
44 
45     /* Telephony and Media Audio Profile Role Characteristic UUID */
46     @VisibleForTesting
47     public static final UUID UUID_TMAP_ROLE =
48             UUID.fromString("00002B51-0000-1000-8000-00805f9b34fb");
49 
50     /* TMAP Role: Call Gateway */
51     public static final int TMAP_ROLE_FLAG_CG = 1;
52     /* TMAP Role: Call Terminal */
53     public static final int TMAP_ROLE_FLAG_CT = 1 << 1;
54     /* TMAP Role: Unicast Media Sender */
55     public static final int TMAP_ROLE_FLAG_UMS = 1 << 2;
56     /* TMAP Role: Unicast Media Receiver */
57     public static final int TMAP_ROLE_FLAG_UMR = 1 << 3;
58     /* TMAP Role: Broadcast Media Sender */
59     public static final int TMAP_ROLE_FLAG_BMS = 1 << 4;
60     /* TMAP Role: Broadcast Media Receiver */
61     public static final int TMAP_ROLE_FLAG_BMR = 1 << 5;
62 
63     private final BluetoothGattServerProxy mBluetoothGattServer;
64 
LeAudioTmapGattServer(BluetoothGattServerProxy gattServer)65     /*package*/ LeAudioTmapGattServer(BluetoothGattServerProxy gattServer) {
66         mBluetoothGattServer = gattServer;
67     }
68 
69     /**
70      * Init TMAP server
71      * @param roleMask bit mask of supported roles.
72      */
73     @VisibleForTesting
start(int roleMask)74     public void start(int roleMask) {
75         if (DBG) {
76             Log.d(TAG, "start(roleMask:" + roleMask + ")");
77         }
78 
79         if (!mBluetoothGattServer.open(mBluetoothGattServerCallback)) {
80             throw new IllegalStateException("Could not open Gatt server");
81         }
82 
83         BluetoothGattService service =
84                 new BluetoothGattService(BluetoothUuid.TMAP.getUuid(),
85                 BluetoothGattService.SERVICE_TYPE_PRIMARY);
86 
87         BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(
88                 UUID_TMAP_ROLE,
89                 BluetoothGattCharacteristic.PROPERTY_READ,
90                 BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);
91 
92         characteristic.setValue(roleMask, BluetoothGattCharacteristic.FORMAT_UINT16, 0);
93         service.addCharacteristic(characteristic);
94 
95         if (!mBluetoothGattServer.addService(service)) {
96             throw new IllegalStateException("Failed to add service for TMAP");
97         }
98     }
99 
100     /**
101      * Stop TMAP server
102      */
103     @VisibleForTesting
stop()104     public void stop() {
105         if (DBG) {
106             Log.d(TAG, "stop()");
107         }
108         if (mBluetoothGattServer == null) {
109             Log.w(TAG, "mBluetoothGattServer should not be null when stop() is called");
110             return;
111         }
112         mBluetoothGattServer.close();
113     }
114 
115     /**
116      * Callback to handle incoming requests to the GATT server.
117      * All read/write requests for characteristics and descriptors are handled here.
118      */
119     private final BluetoothGattServerCallback mBluetoothGattServerCallback =
120             new BluetoothGattServerCallback() {
121         @Override
122         public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset,
123                                                 BluetoothGattCharacteristic characteristic) {
124             byte[] value = characteristic.getValue();
125             if (DBG) {
126                 Log.d(TAG, "value " + Arrays.toString(value));
127             }
128             if (value != null) {
129                 Log.e(TAG, "value null");
130                 value = Arrays.copyOfRange(value, offset, value.length);
131             }
132             mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS,
133                     offset, value);
134         }
135     };
136 
137      /**
138      * A proxy class that facilitates testing.
139      *
140      * This is necessary due to the "final" attribute of the BluetoothGattServer class.
141      */
142     public static class BluetoothGattServerProxy {
143         private final Context mContext;
144         private final BluetoothManager mBluetoothManager;
145 
146         private BluetoothGattServer mBluetoothGattServer;
147 
148          /**
149           * Create a new GATT server proxy object
150           * @param context context to use
151           */
BluetoothGattServerProxy(Context context)152         public BluetoothGattServerProxy(Context context) {
153             mContext = context;
154             mBluetoothManager = context.getSystemService(BluetoothManager.class);
155         }
156 
157          /**
158           * Open with GATT server callback
159           * @param callback callback to invoke
160           * @return true on success
161           */
open(BluetoothGattServerCallback callback)162         public boolean open(BluetoothGattServerCallback callback) {
163             mBluetoothGattServer = mBluetoothManager.openGattServer(mContext, callback);
164             return mBluetoothGattServer != null;
165         }
166 
167          /**
168           * Close the GATT server, should be called as soon as the server is not needed
169           */
close()170         public void close() {
171             if (mBluetoothGattServer == null) {
172                 Log.w(TAG, "BluetoothGattServerProxy.close() called without open()");
173                 return;
174             }
175             mBluetoothGattServer.close();
176             mBluetoothGattServer = null;
177         }
178 
179          /**
180           * Add a GATT service
181           * @param service added service
182           * @return true on success
183           */
addService(BluetoothGattService service)184         public boolean addService(BluetoothGattService service) {
185             return mBluetoothGattServer.addService(service);
186         }
187 
188          /**
189           * Send GATT response to remote
190           * @param device remote device
191           * @param requestId request id
192           * @param status status of response
193           * @param offset offset of the value
194           * @param value value content
195           * @return true on success
196           */
sendResponse( BluetoothDevice device, int requestId, int status, int offset, byte[] value)197         public boolean sendResponse(
198                 BluetoothDevice device, int requestId, int status, int offset, byte[] value) {
199             return mBluetoothGattServer.sendResponse(device, requestId, status, offset, value);
200         }
201 
202          /**
203           * Gatt a list of devices connected to this GATT server
204           * @return list of connected devices at this moment
205           */
getConnectedDevices()206         public List<BluetoothDevice> getConnectedDevices() {
207             return mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT_SERVER);
208         }
209     }
210 }
211