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.BluetoothDevice; 21 import android.bluetooth.BluetoothLeCall; 22 import android.bluetooth.BluetoothProfile; 23 import android.bluetooth.IBluetoothLeCallControl; 24 import android.bluetooth.IBluetoothLeCallControlCallback; 25 import android.content.AttributionSource; 26 import android.os.ParcelUuid; 27 import android.os.RemoteException; 28 import android.sysprop.BluetoothProperties; 29 import android.util.Log; 30 31 import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission; 32 33 import com.android.bluetooth.Utils; 34 import com.android.bluetooth.btservice.ProfileService; 35 import com.android.bluetooth.le_audio.LeAudioService; 36 import com.android.internal.annotations.VisibleForTesting; 37 38 import java.util.HashMap; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.UUID; 42 43 public class TbsService extends ProfileService { 44 45 private static final String TAG = "TbsService"; 46 private static final boolean DBG = true; 47 48 private static TbsService sTbsService; 49 private Map<BluetoothDevice, Integer> mDeviceAuthorizations = new HashMap<>(); 50 51 private final TbsGeneric mTbsGeneric = new TbsGeneric(); 52 isEnabled()53 public static boolean isEnabled() { 54 return BluetoothProperties.isProfileCcpServerEnabled().orElse(false); 55 } 56 57 @Override initBinder()58 protected IProfileServiceBinder initBinder() { 59 return new TbsServerBinder(this); 60 } 61 62 @Override create()63 protected void create() { 64 if (DBG) { 65 Log.d(TAG, "create()"); 66 } 67 } 68 69 @Override start()70 protected boolean start() { 71 72 if (DBG) { 73 Log.d(TAG, "start()"); 74 } 75 if (sTbsService != null) { 76 throw new IllegalStateException("start() called twice"); 77 } 78 79 // Mark service as started 80 setTbsService(this); 81 82 mTbsGeneric.init(new TbsGatt(this)); 83 84 return true; 85 } 86 87 @Override stop()88 protected boolean stop() { 89 if (DBG) { 90 Log.d(TAG, "stop()"); 91 } 92 if (sTbsService == null) { 93 Log.w(TAG, "stop() called before start()"); 94 return true; 95 } 96 97 // Mark service as stopped 98 setTbsService(null); 99 100 if (mTbsGeneric != null) { 101 mTbsGeneric.cleanup(); 102 } 103 104 return true; 105 } 106 107 @Override cleanup()108 protected void cleanup() { 109 if (DBG) { 110 Log.d(TAG, "cleanup()"); 111 } 112 mDeviceAuthorizations.clear(); 113 } 114 115 /** 116 * Get the TbsService instance 117 * 118 * @return TbsService instance 119 */ getTbsService()120 public static synchronized TbsService getTbsService() { 121 if (sTbsService == null) { 122 Log.w(TAG, "getTbsService: service is NULL"); 123 return null; 124 } 125 126 if (!sTbsService.isAvailable()) { 127 Log.w(TAG, "getTbsService: service is not available"); 128 return null; 129 } 130 131 return sTbsService; 132 } 133 setTbsService(TbsService instance)134 private static synchronized void setTbsService(TbsService instance) { 135 if (DBG) { 136 Log.d(TAG, "setTbsService: set to=" + instance); 137 } 138 139 sTbsService = instance; 140 } 141 onDeviceUnauthorized(BluetoothDevice device)142 public void onDeviceUnauthorized(BluetoothDevice device) { 143 if (Utils.isPtsTestMode()) { 144 Log.d(TAG, "PTS test: setDeviceAuthorized"); 145 setDeviceAuthorized(device, true); 146 return; 147 } 148 Log.w(TAG, "onDeviceUnauthorized - authorization notification not implemented yet "); 149 setDeviceAuthorized(device, false); 150 } 151 152 /** 153 * Sets device authorization for TBS. 154 * 155 * @param device device that would be authorized 156 * @param isAuthorized boolean value of authorization permission 157 * @hide 158 */ setDeviceAuthorized(BluetoothDevice device, boolean isAuthorized)159 public void setDeviceAuthorized(BluetoothDevice device, boolean isAuthorized) { 160 Log.i(TAG, "setDeviceAuthorized(): device: " + device + ", isAuthorized: " + isAuthorized); 161 int authorization = isAuthorized ? BluetoothDevice.ACCESS_ALLOWED 162 : BluetoothDevice.ACCESS_REJECTED; 163 mDeviceAuthorizations.put(device, authorization); 164 165 if (mTbsGeneric != null) { 166 mTbsGeneric.onDeviceAuthorizationSet(device); 167 } 168 } 169 170 /** 171 * Returns authorization value for given device. 172 * 173 * @param device device that would be authorized 174 * @return authorization value for device 175 * 176 * Possible authorization values: 177 * {@link BluetoothDevice.ACCESS_UNKNOWN}, 178 * {@link BluetoothDevice.ACCESS_ALLOWED} 179 * @hide 180 */ getDeviceAuthorization(BluetoothDevice device)181 public int getDeviceAuthorization(BluetoothDevice device) { 182 /* Telephony Bearer Service is allowed for 183 * 1. in PTS mode 184 * 2. authorized devices 185 * 3. Any LeAudio devices which are allowed to connect 186 */ 187 int authorization = mDeviceAuthorizations.getOrDefault(device, Utils.isPtsTestMode() 188 ? BluetoothDevice.ACCESS_ALLOWED : BluetoothDevice.ACCESS_UNKNOWN); 189 if (authorization != BluetoothDevice.ACCESS_UNKNOWN) { 190 return authorization; 191 } 192 193 LeAudioService leAudioService = LeAudioService.getLeAudioService(); 194 if (leAudioService == null) { 195 Log.e(TAG, "TBS access not permited. LeAudioService not available"); 196 return BluetoothDevice.ACCESS_UNKNOWN; 197 } 198 199 if (leAudioService.getConnectionPolicy(device) 200 > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 201 if (DBG) { 202 Log.d(TAG, "TBS authorization allowed based on supported LeAudio service"); 203 } 204 setDeviceAuthorized(device, true); 205 return BluetoothDevice.ACCESS_ALLOWED; 206 } 207 208 Log.e(TAG, "TBS access not permited"); 209 return BluetoothDevice.ACCESS_UNKNOWN; 210 } 211 212 /** 213 * Set inband ringtone for the device. 214 * When set, notification will be sent to given device. 215 * 216 * @param device device for which inband ringtone has been set 217 */ setInbandRingtoneSupport(BluetoothDevice device)218 public void setInbandRingtoneSupport(BluetoothDevice device) { 219 if (mTbsGeneric == null) { 220 Log.i(TAG, "setInbandRingtoneSupport, mTbsGeneric not available"); 221 return; 222 } 223 mTbsGeneric.setInbandRingtoneSupport(device); 224 } 225 226 /** 227 * Clear inband ringtone for the device. 228 * When set, notification will be sent to given device. 229 * 230 * @param device device for which inband ringtone has been clear 231 */ clearInbandRingtoneSupport(BluetoothDevice device)232 public void clearInbandRingtoneSupport(BluetoothDevice device) { 233 if (mTbsGeneric == null) { 234 Log.i(TAG, "clearInbandRingtoneSupport, mTbsGeneric not available"); 235 return; 236 } 237 mTbsGeneric.clearInbandRingtoneSupport(device); 238 } 239 240 241 /** Binder object: must be a static class or memory leak may occur */ 242 @VisibleForTesting 243 static class TbsServerBinder extends IBluetoothLeCallControl.Stub implements IProfileServiceBinder { 244 private TbsService mService; 245 getService(AttributionSource source)246 private TbsService getService(AttributionSource source) { 247 if (!Utils.checkServiceAvailable(mService, TAG) 248 || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG) 249 || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) { 250 Log.w(TAG, "TbsService call not allowed for non-active user"); 251 return null; 252 } 253 254 if (mService != null) { 255 if (DBG) { 256 Log.d(TAG, "Service available"); 257 } 258 259 enforceBluetoothPrivilegedPermission(mService); 260 return mService; 261 } 262 263 return null; 264 } 265 TbsServerBinder(TbsService service)266 TbsServerBinder(TbsService service) { 267 mService = service; 268 } 269 270 @Override cleanup()271 public void cleanup() { 272 mService = null; 273 } 274 275 @Override registerBearer(String token, IBluetoothLeCallControlCallback callback, String uci, List<String> uriSchemes, int capabilities, String providerName, int technology, AttributionSource source)276 public void registerBearer(String token, IBluetoothLeCallControlCallback callback, String uci, 277 List<String> uriSchemes, int capabilities, String providerName, int technology, 278 AttributionSource source) { 279 TbsService service = getService(source); 280 if (service != null) { 281 service.registerBearer(token, callback, uci, uriSchemes, capabilities, providerName, 282 technology); 283 } else { 284 Log.w(TAG, "Service not active"); 285 } 286 } 287 288 @Override unregisterBearer(String token, AttributionSource source)289 public void unregisterBearer(String token, 290 AttributionSource source) { 291 TbsService service = getService(source); 292 if (service != null) { 293 service.unregisterBearer(token); 294 } else { 295 Log.w(TAG, "Service not active"); 296 } 297 } 298 299 @Override requestResult(int ccid, int requestId, int result, AttributionSource source)300 public void requestResult(int ccid, int requestId, int result, 301 AttributionSource source) { 302 TbsService service = getService(source); 303 if (service != null) { 304 service.requestResult(ccid, requestId, result); 305 } else { 306 Log.w(TAG, "Service not active"); 307 } 308 } 309 310 @Override callAdded(int ccid, BluetoothLeCall call, AttributionSource source)311 public void callAdded(int ccid, BluetoothLeCall call, 312 AttributionSource source) { 313 TbsService service = getService(source); 314 if (service != null) { 315 service.callAdded(ccid, call); 316 } else { 317 Log.w(TAG, "Service not active"); 318 } 319 } 320 321 @Override callRemoved(int ccid, ParcelUuid callId, int reason, AttributionSource source)322 public void callRemoved(int ccid, ParcelUuid callId, int reason, 323 AttributionSource source) { 324 TbsService service = getService(source); 325 if (service != null) { 326 service.callRemoved(ccid, callId.getUuid(), reason); 327 } else { 328 Log.w(TAG, "Service not active"); 329 } 330 } 331 332 @Override callStateChanged(int ccid, ParcelUuid callId, int state, AttributionSource source)333 public void callStateChanged(int ccid, ParcelUuid callId, int state, 334 AttributionSource source) { 335 TbsService service = getService(source); 336 if (service != null) { 337 service.callStateChanged(ccid, callId.getUuid(), state); 338 } else { 339 Log.w(TAG, "Service not active"); 340 } 341 } 342 343 @Override currentCallsList(int ccid, List<BluetoothLeCall> calls, AttributionSource source)344 public void currentCallsList(int ccid, List<BluetoothLeCall> calls, 345 AttributionSource source) { 346 TbsService service = getService(source); 347 if (service != null) { 348 service.currentCallsList(ccid, calls); 349 } else { 350 Log.w(TAG, "Service not active"); 351 } 352 } 353 354 @Override networkStateChanged(int ccid, String providerName, int technology, AttributionSource source)355 public void networkStateChanged(int ccid, String providerName, int technology, 356 AttributionSource source) { 357 TbsService service = getService(source); 358 if (service != null) { 359 service.networkStateChanged(ccid, providerName, technology); 360 } else { 361 Log.w(TAG, "Service not active"); 362 } 363 } 364 } 365 366 @VisibleForTesting registerBearer(String token, IBluetoothLeCallControlCallback callback, String uci, List<String> uriSchemes, int capabilities, String providerName, int technology)367 void registerBearer(String token, IBluetoothLeCallControlCallback callback, String uci, 368 List<String> uriSchemes, int capabilities, String providerName, int technology) { 369 if (DBG) { 370 Log.d(TAG, "registerBearer: token=" + token); 371 } 372 373 boolean success = mTbsGeneric.addBearer(token, callback, uci, uriSchemes, capabilities, 374 providerName, technology); 375 if (success) { 376 try { 377 callback.asBinder().linkToDeath(() -> { 378 Log.e(TAG, token + " application died, removing..."); 379 unregisterBearer(token); 380 }, 0); 381 } catch (RemoteException e) { 382 e.printStackTrace(); 383 } 384 } 385 386 if (DBG) { 387 Log.d(TAG, "registerBearer: token=" + token + " success=" + success); 388 } 389 } 390 391 @VisibleForTesting unregisterBearer(String token)392 void unregisterBearer(String token) { 393 if (DBG) { 394 Log.d(TAG, "unregisterBearer: token=" + token); 395 } 396 397 mTbsGeneric.removeBearer(token); 398 } 399 400 @VisibleForTesting requestResult(int ccid, int requestId, int result)401 public void requestResult(int ccid, int requestId, int result) { 402 if (DBG) { 403 Log.d(TAG, "requestResult: ccid=" + ccid + " requestId=" + requestId + " result=" 404 + result); 405 } 406 407 mTbsGeneric.requestResult(ccid, requestId, result); 408 } 409 410 @VisibleForTesting callAdded(int ccid, BluetoothLeCall call)411 void callAdded(int ccid, BluetoothLeCall call) { 412 if (DBG) { 413 Log.d(TAG, "callAdded: ccid=" + ccid + " call=" + call); 414 } 415 416 mTbsGeneric.callAdded(ccid, call); 417 } 418 419 @VisibleForTesting callRemoved(int ccid, UUID callId, int reason)420 void callRemoved(int ccid, UUID callId, int reason) { 421 if (DBG) { 422 Log.d(TAG, "callRemoved: ccid=" + ccid + " callId=" + callId + " reason=" + reason); 423 } 424 425 mTbsGeneric.callRemoved(ccid, callId, reason); 426 } 427 428 @VisibleForTesting callStateChanged(int ccid, UUID callId, int state)429 void callStateChanged(int ccid, UUID callId, int state) { 430 if (DBG) { 431 Log.d(TAG, "callStateChanged: ccid=" + ccid + " callId=" + callId + " state=" + state); 432 } 433 434 mTbsGeneric.callStateChanged(ccid, callId, state); 435 } 436 437 @VisibleForTesting currentCallsList(int ccid, List<BluetoothLeCall> calls)438 void currentCallsList(int ccid, List<BluetoothLeCall> calls) { 439 if (DBG) { 440 Log.d(TAG, "currentCallsList: ccid=" + ccid + " calls=" + calls); 441 } 442 443 mTbsGeneric.currentCallsList(ccid, calls); 444 } 445 446 @VisibleForTesting networkStateChanged(int ccid, String providerName, int technology)447 void networkStateChanged(int ccid, String providerName, int technology) { 448 if (DBG) { 449 Log.d(TAG, "networkStateChanged: ccid=" + ccid + " providerName=" + providerName 450 + " technology=" + technology); 451 } 452 453 mTbsGeneric.networkStateChanged(ccid, providerName, technology); 454 } 455 456 @Override dump(StringBuilder sb)457 public void dump(StringBuilder sb) { 458 super.dump(sb); 459 sb.append("TbsService instance:\n"); 460 461 mTbsGeneric.dump(sb); 462 463 for (Map.Entry<BluetoothDevice, Integer> entry : mDeviceAuthorizations.entrySet()) { 464 String accessString; 465 if (entry.getValue() == BluetoothDevice.ACCESS_REJECTED) { 466 accessString = "ACCESS_REJECTED"; 467 } else if (entry.getValue() == BluetoothDevice.ACCESS_ALLOWED) { 468 accessString = "ACCESS_ALLOWED"; 469 } else { 470 accessString = "ACCESS_UNKNOWN"; 471 } 472 sb.append("\n\tDevice: " + entry.getKey() + ", access: " + accessString); 473 } 474 } 475 } 476