• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 HIMSA II K/S - www.himsa.com.
3  * Represented by EHIMA - www.ehima.com
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.bluetooth.tbs;
19 
20 import android.bluetooth.BluetoothLeCall;
21 import android.bluetooth.IBluetoothLeCallControl;
22 import android.bluetooth.IBluetoothLeCallControlCallback;
23 import android.content.AttributionSource;
24 import android.os.ParcelUuid;
25 import android.os.RemoteException;
26 import android.sysprop.BluetoothProperties;
27 import android.util.Log;
28 
29 import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
30 
31 import com.android.bluetooth.Utils;
32 import com.android.bluetooth.btservice.ProfileService;
33 import com.android.internal.annotations.VisibleForTesting;
34 
35 import java.util.List;
36 import java.util.UUID;
37 
38 public class TbsService extends ProfileService {
39 
40     private static final String TAG = "TbsService";
41     private static final boolean DBG = true;
42 
43     private static TbsService sTbsService;
44 
45     private final TbsGeneric mTbsGeneric = new TbsGeneric();
46 
isEnabled()47     public static boolean isEnabled() {
48         return BluetoothProperties.isProfileCcpServerEnabled().orElse(false);
49     }
50 
51     @Override
initBinder()52     protected IProfileServiceBinder initBinder() {
53         return new TbsServerBinder(this);
54     }
55 
56     @Override
create()57     protected void create() {
58         if (DBG) {
59             Log.d(TAG, "create()");
60         }
61     }
62 
63     @Override
start()64     protected boolean start() {
65 
66         if (DBG) {
67             Log.d(TAG, "start()");
68         }
69         if (sTbsService != null) {
70             throw new IllegalStateException("start() called twice");
71         }
72 
73         // Mark service as started
74         setTbsService(this);
75 
76         mTbsGeneric.init(new TbsGatt(this));
77 
78         return true;
79     }
80 
81     @Override
stop()82     protected boolean stop() {
83         if (DBG) {
84             Log.d(TAG, "stop()");
85         }
86         if (sTbsService == null) {
87             Log.w(TAG, "stop() called before start()");
88             return true;
89         }
90 
91         // Mark service as stopped
92         setTbsService(null);
93 
94         if (mTbsGeneric != null) {
95             mTbsGeneric.cleanup();
96         }
97 
98         return true;
99     }
100 
101     @Override
cleanup()102     protected void cleanup() {
103         if (DBG) {
104             Log.d(TAG, "cleanup()");
105         }
106     }
107 
108     /**
109      * Get the TbsService instance
110      *
111      * @return TbsService instance
112      */
getTbsService()113     public static synchronized TbsService getTbsService() {
114         if (sTbsService == null) {
115             Log.w(TAG, "getTbsService: service is NULL");
116             return null;
117         }
118 
119         if (!sTbsService.isAvailable()) {
120             Log.w(TAG, "getTbsService: service is not available");
121             return null;
122         }
123 
124         return sTbsService;
125     }
126 
setTbsService(TbsService instance)127     private static synchronized void setTbsService(TbsService instance) {
128         if (DBG) {
129             Log.d(TAG, "setTbsService: set to=" + instance);
130         }
131 
132         sTbsService = instance;
133     }
134 
135     /** Binder object: must be a static class or memory leak may occur */
136     @VisibleForTesting
137     static class TbsServerBinder extends IBluetoothLeCallControl.Stub implements IProfileServiceBinder {
138         private TbsService mService;
139 
getService(AttributionSource source)140         private TbsService getService(AttributionSource source) {
141             if (!Utils.checkServiceAvailable(mService, TAG)
142                     || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)
143                     || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
144                 Log.w(TAG, "TbsService call not allowed for non-active user");
145                 return null;
146             }
147 
148             if (mService != null) {
149                 if (DBG) {
150                     Log.d(TAG, "Service available");
151                 }
152 
153                 enforceBluetoothPrivilegedPermission(mService);
154                 return mService;
155             }
156 
157             return null;
158         }
159 
TbsServerBinder(TbsService service)160         TbsServerBinder(TbsService service) {
161             mService = service;
162         }
163 
164         @Override
cleanup()165         public void cleanup() {
166             mService = null;
167         }
168 
169         @Override
registerBearer(String token, IBluetoothLeCallControlCallback callback, String uci, List<String> uriSchemes, int capabilities, String providerName, int technology, AttributionSource source)170         public void registerBearer(String token, IBluetoothLeCallControlCallback callback, String uci,
171                 List<String> uriSchemes, int capabilities, String providerName, int technology,
172                 AttributionSource source) {
173             TbsService service = getService(source);
174             if (service != null) {
175                 service.registerBearer(token, callback, uci, uriSchemes, capabilities, providerName,
176                         technology);
177             } else {
178                 Log.w(TAG, "Service not active");
179             }
180         }
181 
182         @Override
unregisterBearer(String token, AttributionSource source)183         public void unregisterBearer(String token,
184                 AttributionSource source) {
185             TbsService service = getService(source);
186             if (service != null) {
187                 service.unregisterBearer(token);
188             } else {
189                 Log.w(TAG, "Service not active");
190             }
191         }
192 
193         @Override
requestResult(int ccid, int requestId, int result, AttributionSource source)194         public void requestResult(int ccid, int requestId, int result,
195                 AttributionSource source) {
196             TbsService service = getService(source);
197             if (service != null) {
198                 service.requestResult(ccid, requestId, result);
199             } else {
200                 Log.w(TAG, "Service not active");
201             }
202         }
203 
204         @Override
callAdded(int ccid, BluetoothLeCall call, AttributionSource source)205         public void callAdded(int ccid, BluetoothLeCall call,
206                 AttributionSource source) {
207             TbsService service = getService(source);
208             if (service != null) {
209                 service.callAdded(ccid, call);
210             } else {
211                 Log.w(TAG, "Service not active");
212             }
213         }
214 
215         @Override
callRemoved(int ccid, ParcelUuid callId, int reason, AttributionSource source)216         public void callRemoved(int ccid, ParcelUuid callId, int reason,
217                 AttributionSource source) {
218             TbsService service = getService(source);
219             if (service != null) {
220                 service.callRemoved(ccid, callId.getUuid(), reason);
221             } else {
222                 Log.w(TAG, "Service not active");
223             }
224         }
225 
226         @Override
callStateChanged(int ccid, ParcelUuid callId, int state, AttributionSource source)227         public void callStateChanged(int ccid, ParcelUuid callId, int state,
228                 AttributionSource source) {
229             TbsService service = getService(source);
230             if (service != null) {
231                 service.callStateChanged(ccid, callId.getUuid(), state);
232             } else {
233                 Log.w(TAG, "Service not active");
234             }
235         }
236 
237         @Override
currentCallsList(int ccid, List<BluetoothLeCall> calls, AttributionSource source)238         public void currentCallsList(int ccid, List<BluetoothLeCall> calls,
239                 AttributionSource source) {
240             TbsService service = getService(source);
241             if (service != null) {
242                 service.currentCallsList(ccid, calls);
243             } else {
244                 Log.w(TAG, "Service not active");
245             }
246         }
247 
248         @Override
networkStateChanged(int ccid, String providerName, int technology, AttributionSource source)249         public void networkStateChanged(int ccid, String providerName, int technology,
250                 AttributionSource source) {
251             TbsService service = getService(source);
252             if (service != null) {
253                 service.networkStateChanged(ccid, providerName, technology);
254             } else {
255                 Log.w(TAG, "Service not active");
256             }
257         }
258     }
259 
260     @VisibleForTesting
registerBearer(String token, IBluetoothLeCallControlCallback callback, String uci, List<String> uriSchemes, int capabilities, String providerName, int technology)261     void registerBearer(String token, IBluetoothLeCallControlCallback callback, String uci,
262             List<String> uriSchemes, int capabilities, String providerName, int technology) {
263         if (DBG) {
264             Log.d(TAG, "registerBearer: token=" + token);
265         }
266 
267         boolean success = mTbsGeneric.addBearer(token, callback, uci, uriSchemes, capabilities,
268                 providerName, technology);
269         if (success) {
270             try {
271                 callback.asBinder().linkToDeath(() -> {
272                     Log.e(TAG, token + " application died, removing...");
273                     unregisterBearer(token);
274                 }, 0);
275             } catch (RemoteException e) {
276                 e.printStackTrace();
277             }
278         }
279 
280         if (DBG) {
281             Log.d(TAG, "registerBearer: token=" + token + " success=" + success);
282         }
283     }
284 
285     @VisibleForTesting
unregisterBearer(String token)286     void unregisterBearer(String token) {
287         if (DBG) {
288             Log.d(TAG, "unregisterBearer: token=" + token);
289         }
290 
291         mTbsGeneric.removeBearer(token);
292     }
293 
294     @VisibleForTesting
requestResult(int ccid, int requestId, int result)295     public void requestResult(int ccid, int requestId, int result) {
296         if (DBG) {
297             Log.d(TAG, "requestResult: ccid=" + ccid + " requestId=" + requestId + " result="
298                     + result);
299         }
300 
301         mTbsGeneric.requestResult(ccid, requestId, result);
302     }
303 
304     @VisibleForTesting
callAdded(int ccid, BluetoothLeCall call)305     void callAdded(int ccid, BluetoothLeCall call) {
306         if (DBG) {
307             Log.d(TAG, "callAdded: ccid=" + ccid + " call=" + call);
308         }
309 
310         mTbsGeneric.callAdded(ccid, call);
311     }
312 
313     @VisibleForTesting
callRemoved(int ccid, UUID callId, int reason)314     void callRemoved(int ccid, UUID callId, int reason) {
315         if (DBG) {
316             Log.d(TAG, "callRemoved: ccid=" + ccid + " callId=" + callId + " reason=" + reason);
317         }
318 
319         mTbsGeneric.callRemoved(ccid, callId, reason);
320     }
321 
322     @VisibleForTesting
callStateChanged(int ccid, UUID callId, int state)323     void callStateChanged(int ccid, UUID callId, int state) {
324         if (DBG) {
325             Log.d(TAG, "callStateChanged: ccid=" + ccid + " callId=" + callId + " state=" + state);
326         }
327 
328         mTbsGeneric.callStateChanged(ccid, callId, state);
329     }
330 
331     @VisibleForTesting
currentCallsList(int ccid, List<BluetoothLeCall> calls)332     void currentCallsList(int ccid, List<BluetoothLeCall> calls) {
333         if (DBG) {
334             Log.d(TAG, "currentCallsList: ccid=" + ccid + " calls=" + calls);
335         }
336 
337         mTbsGeneric.currentCallsList(ccid, calls);
338     }
339 
340     @VisibleForTesting
networkStateChanged(int ccid, String providerName, int technology)341     void networkStateChanged(int ccid, String providerName, int technology) {
342         if (DBG) {
343             Log.d(TAG, "networkStateChanged: ccid=" + ccid + " providerName=" + providerName
344                     + " technology=" + technology);
345         }
346 
347         mTbsGeneric.networkStateChanged(ccid, providerName, technology);
348     }
349 
350     @Override
dump(StringBuilder sb)351     public void dump(StringBuilder sb) {
352         super.dump(sb);
353     }
354 }
355