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.BluetoothLeAudio; 22 import android.bluetooth.BluetoothLeCall; 23 import android.bluetooth.BluetoothLeCallControl; 24 import android.bluetooth.IBluetoothLeCallControlCallback; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.media.AudioManager; 30 import android.net.Uri; 31 import android.os.ParcelUuid; 32 import android.os.RemoteException; 33 import android.util.Log; 34 35 import com.android.bluetooth.btservice.ServiceFactory; 36 import com.android.bluetooth.le_audio.ContentControlIdKeeper; 37 import com.android.bluetooth.le_audio.LeAudioService; 38 import com.android.internal.annotations.VisibleForTesting; 39 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.HashMap; 43 import java.util.HashSet; 44 import java.util.LinkedHashSet; 45 import java.util.List; 46 import java.util.Map; 47 import java.util.TreeMap; 48 import java.util.UUID; 49 50 /** Container class to store TBS instances */ 51 public class TbsGeneric { 52 53 private static final String TAG = "TbsGeneric"; 54 private static final boolean DBG = true; 55 56 private static final String UCI = "GTBS"; 57 private static final String DEFAULT_PROVIDER_NAME = "none"; 58 /* Use GSM as default technology value. It is used only 59 * when bearer is not registered. It will be updated on the phone call 60 */ 61 private static final int DEFAULT_BEARER_TECHNOLOGY = 62 BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_GSM; 63 private static final String UNKNOWN_FRIENDLY_NAME = "unknown"; 64 65 /** Class representing the pending request sent to the application */ 66 private class Request { 67 BluetoothDevice device; 68 List<UUID> callIdList; 69 int requestedOpcode; 70 int callIndex; 71 Request(BluetoothDevice device, UUID callId, int requestedOpcode, int callIndex)72 public Request(BluetoothDevice device, UUID callId, int requestedOpcode, int callIndex) { 73 this.device = device; 74 this.callIdList = Arrays.asList(callId); 75 this.requestedOpcode = requestedOpcode; 76 this.callIndex = callIndex; 77 } 78 Request(BluetoothDevice device, List<ParcelUuid> callIds, int requestedOpcode, int callIndex)79 public Request(BluetoothDevice device, List<ParcelUuid> callIds, int requestedOpcode, 80 int callIndex) { 81 this.device = device; 82 this.callIdList = new ArrayList<>(); 83 for (ParcelUuid callId : callIds) { 84 this.callIdList.add(callId.getUuid()); 85 } 86 this.requestedOpcode = requestedOpcode; 87 this.callIndex = callIndex; 88 } 89 } 90 91 /* Application-registered TBS instance */ 92 private class Bearer { 93 final String token; 94 final IBluetoothLeCallControlCallback callback; 95 final String uci; 96 List<String> uriSchemes; 97 final int capabilities; 98 final int ccid; 99 String providerName; 100 int technology; 101 Map<UUID, Integer> callIdIndexMap = new HashMap<>(); 102 Map<Integer, Request> requestMap = new HashMap<>(); 103 Bearer(String token, IBluetoothLeCallControlCallback callback, String uci, List<String> uriSchemes, int capabilities, String providerName, int technology, int ccid)104 public Bearer(String token, IBluetoothLeCallControlCallback callback, String uci, 105 List<String> uriSchemes, int capabilities, String providerName, int technology, 106 int ccid) { 107 this.token = token; 108 this.callback = callback; 109 this.uci = uci; 110 this.uriSchemes = uriSchemes; 111 this.capabilities = capabilities; 112 this.providerName = providerName; 113 this.technology = technology; 114 this.ccid = ccid; 115 } 116 } 117 118 private boolean mIsInitialized = false; 119 private TbsGatt mTbsGatt = null; 120 private List<Bearer> mBearerList = new ArrayList<>(); 121 private int mLastIndexAssigned = TbsCall.INDEX_UNASSIGNED; 122 private Map<Integer, TbsCall> mCurrentCallsList = new TreeMap<>(); 123 private Bearer mForegroundBearer = null; 124 private int mLastRequestIdAssigned = 0; 125 private List<String> mUriSchemes = new ArrayList<>(Arrays.asList("tel")); 126 private Receiver mReceiver = null; 127 private int mStoredRingerMode = -1; 128 private final ServiceFactory mFactory = new ServiceFactory(); 129 private LeAudioService mLeAudioService; 130 131 private final class Receiver extends BroadcastReceiver { 132 @Override onReceive(Context context, Intent intent)133 public void onReceive(Context context, Intent intent) { 134 synchronized (TbsGeneric.this) { 135 if (!mIsInitialized) { 136 Log.w(TAG, "onReceive called while not initialized."); 137 return; 138 } 139 140 final String action = intent.getAction(); 141 if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) { 142 int ringerMode = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1); 143 144 if (ringerMode < 0 || ringerMode == mStoredRingerMode) return; 145 146 mStoredRingerMode = ringerMode; 147 148 if (isSilentModeEnabled()) { 149 mTbsGatt.setSilentModeFlag(); 150 } else { 151 mTbsGatt.clearSilentModeFlag(); 152 } 153 } 154 } 155 } 156 }; 157 init(TbsGatt tbsGatt)158 public synchronized boolean init(TbsGatt tbsGatt) { 159 if (DBG) { 160 Log.d(TAG, "init"); 161 } 162 mTbsGatt = tbsGatt; 163 164 int ccid = ContentControlIdKeeper.acquireCcid(new ParcelUuid(TbsGatt.UUID_GTBS), 165 BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL); 166 if (!isCcidValid(ccid)) { 167 Log.e(TAG, " CCID is not valid"); 168 cleanup(); 169 return false; 170 } 171 172 if (!mTbsGatt.init(ccid, UCI, mUriSchemes, true, true, DEFAULT_PROVIDER_NAME, 173 DEFAULT_BEARER_TECHNOLOGY, mTbsGattCallback)) { 174 Log.e(TAG, " TbsGatt init failed"); 175 cleanup(); 176 return false; 177 } 178 179 AudioManager audioManager = mTbsGatt.getContext().getSystemService(AudioManager.class); 180 if (audioManager == null) { 181 Log.w(TAG, " AudioManager is not available"); 182 cleanup(); 183 return false; 184 } 185 186 // read initial value of ringer mode 187 mStoredRingerMode = audioManager.getRingerMode(); 188 189 if (isSilentModeEnabled()) { 190 mTbsGatt.setSilentModeFlag(); 191 } else { 192 mTbsGatt.clearSilentModeFlag(); 193 } 194 195 mReceiver = new Receiver(); 196 IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION); 197 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 198 mTbsGatt.getContext().registerReceiver(mReceiver, filter); 199 200 mIsInitialized = true; 201 return true; 202 } 203 cleanup()204 public synchronized void cleanup() { 205 if (DBG) { 206 Log.d(TAG, "cleanup"); 207 } 208 209 if (mTbsGatt != null) { 210 if (mReceiver != null) { 211 mTbsGatt.getContext().unregisterReceiver(mReceiver); 212 } 213 mTbsGatt.cleanup(); 214 mTbsGatt = null; 215 } 216 217 mIsInitialized = false; 218 } 219 220 /** 221 * Inform TBS GATT instance about authorization change for device. 222 * 223 * @param device device for which authorization is changed 224 */ onDeviceAuthorizationSet(BluetoothDevice device)225 public void onDeviceAuthorizationSet(BluetoothDevice device) { 226 // Notify TBS GATT service instance in case of pending operations 227 if (mTbsGatt != null) { 228 mTbsGatt.onDeviceAuthorizationSet(device); 229 } 230 } 231 232 /** 233 * Set inband ringtone for the device. 234 * When set, notification will be sent to given device. 235 * 236 * @param device device for which inband ringtone has been set 237 */ setInbandRingtoneSupport(BluetoothDevice device)238 public synchronized void setInbandRingtoneSupport(BluetoothDevice device) { 239 if (mTbsGatt == null) { 240 Log.w(TAG, "setInbandRingtoneSupport, mTbsGatt is null"); 241 return; 242 } 243 mTbsGatt.setInbandRingtoneFlag(device); 244 } 245 246 /** 247 * Clear inband ringtone for the device. 248 * When set, notification will be sent to given device. 249 * 250 * @param device device for which inband ringtone has been cleared 251 */ clearInbandRingtoneSupport(BluetoothDevice device)252 public synchronized void clearInbandRingtoneSupport(BluetoothDevice device) { 253 if (mTbsGatt == null) { 254 Log.w(TAG, "setInbandRingtoneSupport, mTbsGatt is null"); 255 return; 256 } 257 mTbsGatt.clearInbandRingtoneFlag(device); 258 } 259 isSilentModeEnabled()260 private synchronized boolean isSilentModeEnabled() { 261 return mStoredRingerMode != AudioManager.RINGER_MODE_NORMAL; 262 } 263 getBearerByToken(String token)264 private synchronized Bearer getBearerByToken(String token) { 265 for (Bearer bearer : mBearerList) { 266 if (bearer.token.equals(token)) { 267 return bearer; 268 } 269 } 270 return null; 271 } 272 getBearerByCcid(int ccid)273 private synchronized Bearer getBearerByCcid(int ccid) { 274 for (Bearer bearer : mBearerList) { 275 if (bearer.ccid == ccid) { 276 return bearer; 277 } 278 } 279 return null; 280 } 281 getBearerSupportingUri(String uri)282 private synchronized Bearer getBearerSupportingUri(String uri) { 283 for (Bearer bearer : mBearerList) { 284 for (String s : bearer.uriSchemes) { 285 if (uri.startsWith(s + ":")) { 286 return bearer; 287 } 288 } 289 } 290 return null; 291 } 292 getCallIdByIndex(int callIndex)293 private synchronized Map.Entry<UUID, Bearer> getCallIdByIndex(int callIndex) { 294 for (Bearer bearer : mBearerList) { 295 for (Map.Entry<UUID, Integer> callIdToIndex : bearer.callIdIndexMap.entrySet()) { 296 if (callIndex == callIdToIndex.getValue()) { 297 return Map.entry(callIdToIndex.getKey(), bearer); 298 } 299 } 300 } 301 return null; 302 } 303 addBearer(String token, IBluetoothLeCallControlCallback callback, String uci, List<String> uriSchemes, int capabilities, String providerName, int technology)304 public synchronized boolean addBearer(String token, IBluetoothLeCallControlCallback callback, 305 String uci, List<String> uriSchemes, int capabilities, String providerName, 306 int technology) { 307 if (DBG) { 308 Log.d(TAG, 309 "addBearer: token=" + token + " uci=" + uci + " uriSchemes=" + uriSchemes 310 + " capabilities=" + capabilities + " providerName=" + providerName 311 + " technology=" + technology); 312 } 313 if (!mIsInitialized) { 314 Log.w(TAG, "addBearer called while not initialized."); 315 return false; 316 } 317 318 if (getBearerByToken(token) != null) { 319 Log.w(TAG, "addBearer: token=" + token + " registered already"); 320 return false; 321 } 322 323 // Acquire CCID for TbsObject. The CCID is released on remove() 324 Bearer bearer = new Bearer(token, callback, uci, uriSchemes, capabilities, providerName, 325 technology, ContentControlIdKeeper.acquireCcid(new ParcelUuid(UUID.randomUUID()), 326 BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL)); 327 if (isCcidValid(bearer.ccid)) { 328 mBearerList.add(bearer); 329 330 updateUriSchemesSupported(); 331 if (mForegroundBearer == null) { 332 setForegroundBearer(bearer); 333 } 334 } else { 335 Log.e(TAG, "Failed to acquire ccid"); 336 } 337 338 if (callback != null) { 339 try { 340 Log.d(TAG, "ccid=" + bearer.ccid); 341 callback.onBearerRegistered(bearer.ccid); 342 } catch (RemoteException e) { 343 e.printStackTrace(); 344 } 345 } 346 347 return isCcidValid(bearer.ccid); 348 } 349 removeBearer(String token)350 public synchronized void removeBearer(String token) { 351 if (DBG) { 352 Log.d(TAG, "removeBearer: token=" + token); 353 } 354 355 if (!mIsInitialized) { 356 Log.w(TAG, "removeBearer called while not initialized."); 357 return; 358 } 359 360 Bearer bearer = getBearerByToken(token); 361 if (bearer == null) { 362 return; 363 } 364 365 // Remove the calls associated with this bearer 366 for (Integer callIndex : bearer.callIdIndexMap.values()) { 367 mCurrentCallsList.remove(callIndex); 368 } 369 370 if (bearer.callIdIndexMap.size() > 0) { 371 notifyCclc(); 372 } 373 374 // Release the ccid acquired 375 ContentControlIdKeeper.releaseCcid(bearer.ccid); 376 377 mBearerList.remove(bearer); 378 379 updateUriSchemesSupported(); 380 if (mForegroundBearer == bearer) { 381 setForegroundBearer(findNewForegroundBearer()); 382 } 383 } 384 checkRequestComplete(Bearer bearer, UUID callId, TbsCall tbsCall)385 private synchronized void checkRequestComplete(Bearer bearer, UUID callId, TbsCall tbsCall) { 386 // check if there's any pending request related to this call 387 Map.Entry<Integer, Request> requestEntry = null; 388 if (bearer.requestMap.size() > 0) { 389 for (Map.Entry<Integer, Request> entry : bearer.requestMap.entrySet()) { 390 if (entry.getValue().callIdList.contains(callId)) { 391 requestEntry = entry; 392 } 393 } 394 } 395 396 if (requestEntry == null) { 397 if (DBG) { 398 Log.d(TAG, "requestEntry is null"); 399 } 400 return; 401 } 402 403 int requestId = requestEntry.getKey(); 404 Request request = requestEntry.getValue(); 405 406 int result; 407 if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE) { 408 if (mCurrentCallsList.get(request.callIndex) == null) { 409 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 410 } else { 411 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 412 } 413 } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT) { 414 if (tbsCall.getState() != BluetoothLeCall.STATE_INCOMING) { 415 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 416 } else { 417 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 418 } 419 } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD) { 420 if (tbsCall.getState() == BluetoothLeCall.STATE_LOCALLY_HELD 421 || tbsCall.getState() == BluetoothLeCall.STATE_LOCALLY_AND_REMOTELY_HELD) { 422 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 423 } else { 424 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 425 } 426 } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE) { 427 if (tbsCall.getState() != BluetoothLeCall.STATE_LOCALLY_HELD 428 && tbsCall.getState() != BluetoothLeCall.STATE_LOCALLY_AND_REMOTELY_HELD) { 429 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 430 } else { 431 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 432 } 433 } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE) { 434 if (bearer.callIdIndexMap.get(request.callIdList.get(0)) != null) { 435 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 436 } else { 437 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 438 } 439 } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_JOIN) { 440 /* While joining calls, those that are not in remotely held state should go to active */ 441 if (bearer.callIdIndexMap.get(callId) == null 442 || (tbsCall.getState() != BluetoothLeCall.STATE_ACTIVE 443 && tbsCall.getState() != BluetoothLeCall.STATE_REMOTELY_HELD)) { 444 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 445 } else { 446 /* Check if all of the pending calls transit to required state */ 447 for (UUID pendingCallId : request.callIdList) { 448 Integer callIndex = bearer.callIdIndexMap.get(pendingCallId); 449 TbsCall pendingTbsCall = mCurrentCallsList.get(callIndex); 450 if (pendingTbsCall.getState() != BluetoothLeCall.STATE_ACTIVE 451 && pendingTbsCall.getState() != BluetoothLeCall.STATE_REMOTELY_HELD) { 452 /* Still waiting for more call state updates */ 453 return; 454 } 455 } 456 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 457 } 458 } else { 459 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 460 461 } 462 463 mTbsGatt.setCallControlPointResult(request.device, request.requestedOpcode, 464 request.callIndex, result); 465 466 bearer.requestMap.remove(requestId); 467 } 468 getTbsResult(int result, int requestedOpcode)469 private synchronized int getTbsResult(int result, int requestedOpcode) { 470 if (result == BluetoothLeCallControl.RESULT_ERROR_UNKNOWN_CALL_ID) { 471 return TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX; 472 } 473 474 if (result == BluetoothLeCallControl.RESULT_ERROR_INVALID_URI 475 && requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE) { 476 return TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_OUTGOING_URI; 477 } 478 479 return TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 480 } 481 requestResult(int ccid, int requestId, int result)482 public synchronized void requestResult(int ccid, int requestId, int result) { 483 if (DBG) { 484 Log.d(TAG, "requestResult: ccid=" + ccid + " requestId=" + requestId + " result=" 485 + result); 486 } 487 488 if (!mIsInitialized) { 489 Log.w(TAG, "requestResult called while not initialized."); 490 return; 491 } 492 493 Bearer bearer = getBearerByCcid(ccid); 494 if (bearer == null) { 495 Log.i(TAG, " Bearer for ccid " + ccid + " does not exist"); 496 return; 497 } 498 499 if (result == BluetoothLeCallControl.RESULT_SUCCESS) { 500 // don't send the success here, wait for state transition instead 501 return; 502 } 503 504 // check if there's any pending request related to this call 505 Request request = bearer.requestMap.remove(requestId); 506 if (request == null) { 507 // already sent response 508 return; 509 } 510 511 int tbsResult = getTbsResult(result, request.requestedOpcode); 512 mTbsGatt.setCallControlPointResult(request.device, request.requestedOpcode, 513 request.callIndex, tbsResult); 514 } 515 callAdded(int ccid, BluetoothLeCall call)516 public synchronized void callAdded(int ccid, BluetoothLeCall call) { 517 if (DBG) { 518 Log.d(TAG, "callAdded: ccid=" + ccid + " call=" + call); 519 } 520 521 if (!mIsInitialized) { 522 Log.w(TAG, "callAdded called while not initialized."); 523 return; 524 } 525 526 Bearer bearer = getBearerByCcid(ccid); 527 if (bearer == null) { 528 Log.e(TAG, "callAdded: unknown ccid=" + ccid); 529 return; 530 } 531 532 UUID callId = call.getUuid(); 533 if (bearer.callIdIndexMap.containsKey(callId)) { 534 Log.e(TAG, "callAdded: uuidId=" + callId + " already on list"); 535 return; 536 } 537 538 Integer callIndex = getFreeCallIndex(); 539 if (callIndex == null) { 540 Log.e(TAG, "callAdded: out of call indices!"); 541 return; 542 } 543 544 bearer.callIdIndexMap.put(callId, callIndex); 545 TbsCall tbsCall = TbsCall.create(call); 546 mCurrentCallsList.put(callIndex, tbsCall); 547 548 checkRequestComplete(bearer, callId, tbsCall); 549 if (tbsCall.isIncoming()) { 550 mTbsGatt.setIncomingCall(callIndex, tbsCall.getUri()); 551 } 552 553 String friendlyName = tbsCall.getFriendlyName(); 554 if (friendlyName == null) { 555 friendlyName = UNKNOWN_FRIENDLY_NAME; 556 } 557 mTbsGatt.setCallFriendlyName(callIndex, friendlyName); 558 559 notifyCclc(); 560 if (mForegroundBearer != bearer) { 561 setForegroundBearer(bearer); 562 } 563 } 564 callRemoved(int ccid, UUID callId, int reason)565 public synchronized void callRemoved(int ccid, UUID callId, int reason) { 566 if (DBG) { 567 Log.d(TAG, "callRemoved: ccid=" + ccid + "reason=" + reason); 568 } 569 570 if (!mIsInitialized) { 571 Log.w(TAG, "callRemoved called while not initialized."); 572 return; 573 } 574 575 Bearer bearer = getBearerByCcid(ccid); 576 if (bearer == null) { 577 Log.e(TAG, "callRemoved: unknown ccid=" + ccid); 578 return; 579 } 580 581 Integer callIndex = bearer.callIdIndexMap.remove(callId); 582 if (callIndex == null) { 583 Log.e(TAG, "callIndex: is null for callId" + callId); 584 return; 585 } 586 587 TbsCall tbsCall = mCurrentCallsList.remove(callIndex); 588 if (tbsCall == null) { 589 Log.e(TAG, "callRemoved: no such call"); 590 return; 591 } 592 593 checkRequestComplete(bearer, callId, tbsCall); 594 mTbsGatt.setTerminationReason(callIndex, reason); 595 notifyCclc(); 596 597 Integer incomingCallIndex = mTbsGatt.getIncomingCallIndex(); 598 if (incomingCallIndex != null && incomingCallIndex.equals(callIndex)) { 599 mTbsGatt.clearIncomingCall(); 600 // TODO: check if there's any incoming call more??? 601 } 602 603 Integer friendlyNameCallIndex = mTbsGatt.getCallFriendlyNameIndex(); 604 if (friendlyNameCallIndex != null && friendlyNameCallIndex.equals(callIndex)) { 605 mTbsGatt.clearFriendlyName(); 606 // TODO: check if there's any incoming/outgoing call more??? 607 } 608 } 609 callStateChanged(int ccid, UUID callId, int state)610 public synchronized void callStateChanged(int ccid, UUID callId, int state) { 611 if (DBG) { 612 Log.d(TAG, "callStateChanged: ccid=" + ccid + " callId=" + callId + " state=" + state); 613 } 614 615 if (!mIsInitialized) { 616 Log.w(TAG, "callStateChanged called while not initialized."); 617 return; 618 } 619 620 Bearer bearer = getBearerByCcid(ccid); 621 if (bearer == null) { 622 Log.e(TAG, "callStateChanged: unknown ccid=" + ccid); 623 return; 624 } 625 626 Integer callIndex = bearer.callIdIndexMap.get(callId); 627 if (callIndex == null) { 628 Log.e(TAG, "callStateChanged: unknown callId=" + callId); 629 return; 630 } 631 632 TbsCall tbsCall = mCurrentCallsList.get(callIndex); 633 if (tbsCall.getState() == state) { 634 return; 635 } 636 637 tbsCall.setState(state); 638 639 checkRequestComplete(bearer, callId, tbsCall); 640 notifyCclc(); 641 642 Integer incomingCallIndex = mTbsGatt.getIncomingCallIndex(); 643 if (incomingCallIndex != null && incomingCallIndex.equals(callIndex)) { 644 mTbsGatt.clearIncomingCall(); 645 // TODO: check if there's any incoming call more??? 646 } 647 } 648 currentCallsList(int ccid, List<BluetoothLeCall> calls)649 public synchronized void currentCallsList(int ccid, List<BluetoothLeCall> calls) { 650 if (DBG) { 651 Log.d(TAG, "currentCallsList: ccid=" + ccid + " callsNum=" + calls.size()); 652 } 653 654 if (!mIsInitialized) { 655 Log.w(TAG, "currentCallsList called while not initialized."); 656 return; 657 } 658 659 Bearer bearer = getBearerByCcid(ccid); 660 if (bearer == null) { 661 Log.e(TAG, "currentCallsList: unknown ccid=" + ccid); 662 return; 663 } 664 665 boolean cclc = false; 666 Map<UUID, Integer> storedCallIdList = new HashMap<>(bearer.callIdIndexMap); 667 bearer.callIdIndexMap = new HashMap<>(); 668 for (BluetoothLeCall call : calls) { 669 UUID callId = call.getUuid(); 670 Integer callIndex = storedCallIdList.get(callId); 671 if (callIndex == null) { 672 // new call 673 callIndex = getFreeCallIndex(); 674 if (callIndex == null) { 675 Log.e(TAG, "currentCallsList: out of call indices!"); 676 continue; 677 } 678 679 mCurrentCallsList.put(callIndex, TbsCall.create(call)); 680 cclc |= true; 681 } else { 682 TbsCall tbsCall = mCurrentCallsList.get(callIndex); 683 TbsCall tbsCallNew = TbsCall.create(call); 684 if (tbsCall != tbsCallNew) { 685 mCurrentCallsList.replace(callIndex, tbsCallNew); 686 cclc |= true; 687 } 688 } 689 690 bearer.callIdIndexMap.put(callId, callIndex); 691 } 692 693 for (Map.Entry<UUID, Integer> callIdToIndex : storedCallIdList.entrySet()) { 694 if (!bearer.callIdIndexMap.containsKey(callIdToIndex.getKey())) { 695 mCurrentCallsList.remove(callIdToIndex.getValue()); 696 cclc |= true; 697 } 698 } 699 700 if (cclc) { 701 notifyCclc(); 702 } 703 } 704 networkStateChanged(int ccid, String providerName, int technology)705 public synchronized void networkStateChanged(int ccid, String providerName, int technology) { 706 if (DBG) { 707 Log.d(TAG, "networkStateChanged: ccid=" + ccid + " providerName=" + providerName 708 + " technology=" + technology); 709 } 710 711 if (!mIsInitialized) { 712 Log.w(TAG, "networkStateChanged called while not initialized."); 713 return; 714 } 715 716 Bearer bearer = getBearerByCcid(ccid); 717 if (bearer == null) { 718 return; 719 } 720 721 boolean providerChanged = !bearer.providerName.equals(providerName); 722 if (providerChanged) { 723 bearer.providerName = providerName; 724 } 725 726 boolean technologyChanged = bearer.technology != technology; 727 if (technologyChanged) { 728 bearer.technology = technology; 729 } 730 731 if (bearer == mForegroundBearer) { 732 if (providerChanged) { 733 mTbsGatt.setBearerProviderName(bearer.providerName); 734 } 735 736 if (technologyChanged) { 737 mTbsGatt.setBearerTechnology(bearer.technology); 738 } 739 } 740 } 741 processOriginateCall(BluetoothDevice device, String uri)742 private synchronized int processOriginateCall(BluetoothDevice device, String uri) { 743 if (uri.startsWith("tel")) { 744 /* 745 * FIXME: For now, process telephone call originate request here, as 746 * BluetoothInCallService might be not running. The BluetoothInCallService is active 747 * when there is a call only. 748 */ 749 Log.i(TAG, "originate uri=" + uri); 750 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Uri.parse(uri)); 751 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 752 mTbsGatt.getContext().startActivity(intent); 753 mTbsGatt.setCallControlPointResult(device, TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE, 754 TbsCall.INDEX_UNASSIGNED, TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS); 755 } else { 756 UUID callId = UUID.randomUUID(); 757 int requestId = mLastRequestIdAssigned + 1; 758 Request request = new Request(device, callId, 759 TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE, TbsCall.INDEX_UNASSIGNED); 760 761 Bearer bearer = getBearerSupportingUri(uri); 762 if (bearer == null) { 763 return TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_OUTGOING_URI; 764 } 765 766 try { 767 bearer.callback.onPlaceCall(requestId, new ParcelUuid(callId), uri); 768 } catch (RemoteException e) { 769 e.printStackTrace(); 770 return TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 771 } 772 773 bearer.requestMap.put(requestId, request); 774 mLastIndexAssigned = requestId; 775 } 776 777 setActiveLeDevice(device); 778 return TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 779 } 780 781 private final TbsGatt.Callback mTbsGattCallback = new TbsGatt.Callback() { 782 783 @Override 784 public void onServiceAdded(boolean success) { 785 synchronized (TbsGeneric.this) { 786 if (DBG) { 787 Log.d(TAG, "onServiceAdded: success=" + success); 788 } 789 } 790 } 791 792 @Override 793 public boolean isInbandRingtoneEnabled(BluetoothDevice device) { 794 if (!isLeAudioServiceAvailable()) { 795 Log.i(TAG, "LeAudio service not available"); 796 return false; 797 } 798 int groupId = mLeAudioService.getGroupId(device); 799 return mLeAudioService.isInbandRingtoneEnabled(groupId); 800 } 801 802 @Override 803 public void onCallControlPointRequest(BluetoothDevice device, int opcode, byte[] args) { 804 synchronized (TbsGeneric.this) { 805 if (DBG) { 806 Log.d(TAG, "onCallControlPointRequest: device=" + device + " opcode=" 807 + opcode + " argsLen=" + args.length); 808 } 809 810 if (!mIsInitialized) { 811 Log.w(TAG, "onCallControlPointRequest called while not initialized."); 812 return; 813 } 814 815 int result; 816 817 switch (opcode) { 818 case TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT: 819 case TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE: 820 case TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD: 821 case TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE: { 822 if (args.length == 0) { 823 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 824 break; 825 } 826 827 int callIndex = args[0]; 828 Map.Entry<UUID, Bearer> entry = getCallIdByIndex(callIndex); 829 if (entry == null) { 830 result = TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX; 831 break; 832 } 833 834 TbsCall call = mCurrentCallsList.get(callIndex); 835 if (!isCallStateTransitionValid(call.getState(), opcode)) { 836 result = TbsGatt.CALL_CONTROL_POINT_RESULT_STATE_MISMATCH; 837 break; 838 } 839 840 Bearer bearer = entry.getValue(); 841 UUID callId = entry.getKey(); 842 int requestId = mLastRequestIdAssigned + 1; 843 Request request = new Request(device, callId, opcode, callIndex); 844 try { 845 if (opcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT) { 846 setActiveLeDevice(device); 847 bearer.callback.onAcceptCall(requestId, new ParcelUuid(callId)); 848 } else if (opcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE) { 849 bearer.callback.onTerminateCall(requestId, new ParcelUuid(callId)); 850 } else if (opcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD) { 851 if ((bearer.capabilities 852 & BluetoothLeCallControl.CAPABILITY_HOLD_CALL) == 0) { 853 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED; 854 break; 855 } 856 bearer.callback.onHoldCall(requestId, new ParcelUuid(callId)); 857 } else { 858 if ((bearer.capabilities 859 & BluetoothLeCallControl.CAPABILITY_HOLD_CALL) == 0) { 860 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED; 861 break; 862 } 863 bearer.callback.onUnholdCall(requestId, new ParcelUuid(callId)); 864 } 865 } catch (RemoteException e) { 866 e.printStackTrace(); 867 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 868 break; 869 } 870 871 bearer.requestMap.put(requestId, request); 872 mLastRequestIdAssigned = requestId; 873 874 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 875 break; 876 } 877 878 case TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE: { 879 result = processOriginateCall(device, new String(args)); 880 break; 881 } 882 883 case TbsGatt.CALL_CONTROL_POINT_OPCODE_JOIN: { 884 // at least 2 call indices are required 885 if (args.length < 2) { 886 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 887 break; 888 } 889 890 Map.Entry<UUID, Bearer> firstEntry = null; 891 List<ParcelUuid> parcelUuids = new ArrayList<>(); 892 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 893 for (int callIndex : args) { 894 Map.Entry<UUID, Bearer> entry = getCallIdByIndex(callIndex); 895 if (entry == null) { 896 result = TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX; 897 break; 898 } 899 900 // state transition is valid, because a call in any state 901 // can requested to join 902 903 if (firstEntry == null) { 904 firstEntry = entry; 905 } 906 907 if (firstEntry.getValue() != entry.getValue()) { 908 Log.w(TAG, "Cannot join calls from different bearers!"); 909 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 910 break; 911 } 912 913 parcelUuids.add(new ParcelUuid(entry.getKey())); 914 } 915 916 if (result != TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS) { 917 break; 918 } 919 920 Bearer bearer = firstEntry.getValue(); 921 Request request = new Request(device, parcelUuids, opcode, args[0]); 922 int requestId = mLastRequestIdAssigned + 1; 923 try { 924 bearer.callback.onJoinCalls(requestId, parcelUuids); 925 } catch (RemoteException e) { 926 e.printStackTrace(); 927 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 928 break; 929 } 930 931 bearer.requestMap.put(requestId, request); 932 mLastIndexAssigned = requestId; 933 934 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 935 break; 936 } 937 938 default: 939 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED; 940 break; 941 } 942 943 if (result == TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS) { 944 // return here and wait for the request completition from application 945 return; 946 } 947 948 mTbsGatt.setCallControlPointResult(device, opcode, 0, result); 949 } 950 } 951 }; 952 isCcidValid(int ccid)953 private static boolean isCcidValid(int ccid) { 954 return ccid != ContentControlIdKeeper.CCID_INVALID; 955 } 956 isCallIndexAssigned(int callIndex)957 private static boolean isCallIndexAssigned(int callIndex) { 958 return callIndex != TbsCall.INDEX_UNASSIGNED; 959 } 960 getFreeCallIndex()961 private synchronized Integer getFreeCallIndex() { 962 int callIndex = mLastIndexAssigned; 963 for (int i = TbsCall.INDEX_MIN; i <= TbsCall.INDEX_MAX; i++) { 964 callIndex = (callIndex + 1) % TbsCall.INDEX_MAX; 965 if (!isCallIndexAssigned(callIndex)) { 966 continue; 967 } 968 969 if (mCurrentCallsList.keySet().contains(callIndex)) { 970 continue; 971 } 972 973 mLastIndexAssigned = callIndex; 974 975 return callIndex; 976 } 977 978 return null; 979 } 980 getCallByStates( LinkedHashSet<Integer> states)981 private synchronized Map.Entry<Integer, TbsCall> getCallByStates( 982 LinkedHashSet<Integer> states) { 983 for (Map.Entry<Integer, TbsCall> entry : mCurrentCallsList.entrySet()) { 984 if (states.contains(entry.getValue().getState())) { 985 return entry; 986 } 987 } 988 989 return null; 990 } 991 getForegroundCall()992 private synchronized Map.Entry<Integer, TbsCall> getForegroundCall() { 993 LinkedHashSet<Integer> states = new LinkedHashSet<Integer>(); 994 Map.Entry<Integer, TbsCall> foregroundCall; 995 996 if (mCurrentCallsList.size() == 0) { 997 return null; 998 } 999 1000 states.add(BluetoothLeCall.STATE_INCOMING); 1001 foregroundCall = getCallByStates(states); 1002 if (foregroundCall != null) { 1003 return foregroundCall; 1004 } 1005 1006 states.clear(); 1007 states.add(BluetoothLeCall.STATE_DIALING); 1008 states.add(BluetoothLeCall.STATE_ALERTING); 1009 foregroundCall = getCallByStates(states); 1010 if (foregroundCall != null) { 1011 return foregroundCall; 1012 } 1013 1014 states.clear(); 1015 states.add(BluetoothLeCall.STATE_ACTIVE); 1016 foregroundCall = getCallByStates(states); 1017 if (foregroundCall != null) { 1018 return foregroundCall; 1019 } 1020 1021 return null; 1022 } 1023 findNewForegroundBearer()1024 private synchronized Bearer findNewForegroundBearer() { 1025 if (mBearerList.size() == 0) { 1026 return null; 1027 } 1028 1029 // the bearer that owns the foreground call 1030 Map.Entry<Integer, TbsCall> foregroundCall = getForegroundCall(); 1031 if (foregroundCall != null) { 1032 for (Bearer bearer : mBearerList) { 1033 if (bearer.callIdIndexMap.values().contains(foregroundCall.getKey())) { 1034 return bearer; 1035 } 1036 } 1037 } 1038 1039 // the last bearer registered 1040 return mBearerList.get(mBearerList.size() - 1); 1041 } 1042 setForegroundBearer(Bearer bearer)1043 private synchronized void setForegroundBearer(Bearer bearer) { 1044 if (DBG) { 1045 Log.d(TAG, "setForegroundBearer: bearer=" + bearer); 1046 } 1047 1048 if (bearer == null) { 1049 mTbsGatt.setBearerProviderName(DEFAULT_PROVIDER_NAME); 1050 mTbsGatt.setBearerTechnology(DEFAULT_BEARER_TECHNOLOGY); 1051 } else if (mForegroundBearer == null) { 1052 mTbsGatt.setBearerProviderName(bearer.providerName); 1053 mTbsGatt.setBearerTechnology(bearer.technology); 1054 } else { 1055 if (!bearer.providerName.equals(mForegroundBearer.providerName)) { 1056 mTbsGatt.setBearerProviderName(bearer.providerName); 1057 } 1058 1059 if (bearer.technology != mForegroundBearer.technology) { 1060 mTbsGatt.setBearerTechnology(bearer.technology); 1061 } 1062 } 1063 1064 mForegroundBearer = bearer; 1065 } 1066 isLeAudioServiceAvailable()1067 private boolean isLeAudioServiceAvailable() { 1068 if (mLeAudioService != null) { 1069 return true; 1070 } 1071 1072 mLeAudioService = mFactory.getLeAudioService(); 1073 if (mLeAudioService == null) { 1074 Log.e(TAG, "leAudioService not available"); 1075 return false; 1076 } 1077 1078 return true; 1079 } 1080 1081 @VisibleForTesting setLeAudioServiceForTesting(LeAudioService leAudioService)1082 void setLeAudioServiceForTesting(LeAudioService leAudioService) { 1083 mLeAudioService = leAudioService; 1084 } 1085 notifyCclc()1086 private synchronized void notifyCclc() { 1087 if (DBG) { 1088 Log.d(TAG, "notifyCclc"); 1089 } 1090 1091 if (isLeAudioServiceAvailable()) { 1092 if (mCurrentCallsList.size() > 0) { 1093 mLeAudioService.setInCall(true); 1094 } else { 1095 mLeAudioService.setInCall(false); 1096 } 1097 } 1098 1099 mTbsGatt.setCallState(mCurrentCallsList); 1100 mTbsGatt.setBearerListCurrentCalls(mCurrentCallsList); 1101 } 1102 updateUriSchemesSupported()1103 private synchronized void updateUriSchemesSupported() { 1104 List<String> newUriSchemes = new ArrayList<>(); 1105 for (Bearer bearer : mBearerList) { 1106 newUriSchemes.addAll(bearer.uriSchemes); 1107 } 1108 1109 // filter duplicates 1110 newUriSchemes = new ArrayList<>(new HashSet<>(newUriSchemes)); 1111 if (newUriSchemes.equals(mUriSchemes)) { 1112 return; 1113 } 1114 1115 mUriSchemes = new ArrayList<>(newUriSchemes); 1116 mTbsGatt.setBearerUriSchemesSupportedList(mUriSchemes); 1117 } 1118 setActiveLeDevice(BluetoothDevice device)1119 private void setActiveLeDevice(BluetoothDevice device) { 1120 if (device == null) { 1121 Log.w(TAG, "setActiveLeDevice: ignore null device"); 1122 return; 1123 } 1124 if (!isLeAudioServiceAvailable()) { 1125 Log.w(TAG, "mLeAudioService not available"); 1126 return; 1127 } 1128 mLeAudioService.setActiveDevice(device); 1129 } 1130 isCallStateTransitionValid(int callState, int requestedOpcode)1131 private static boolean isCallStateTransitionValid(int callState, int requestedOpcode) { 1132 switch (requestedOpcode) { 1133 case TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT: 1134 if (callState == BluetoothLeCall.STATE_INCOMING) { 1135 return true; 1136 } 1137 break; 1138 1139 case TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE: 1140 // Any call can be terminated. 1141 return true; 1142 1143 case TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD: 1144 if (callState == BluetoothLeCall.STATE_INCOMING 1145 || callState == BluetoothLeCall.STATE_ACTIVE 1146 || callState == BluetoothLeCall.STATE_REMOTELY_HELD) { 1147 return true; 1148 } 1149 break; 1150 1151 case TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE: 1152 if (callState == BluetoothLeCall.STATE_LOCALLY_HELD 1153 || callState == BluetoothLeCall.STATE_LOCALLY_AND_REMOTELY_HELD) { 1154 return true; 1155 } 1156 break; 1157 1158 default: 1159 Log.e(TAG, "unhandled opcode " + requestedOpcode); 1160 } 1161 1162 return false; 1163 } 1164 1165 /** 1166 * Dump status of TBS service along with related objects 1167 * 1168 * @param sb string builder object that TBS module will be appending 1169 */ dump(StringBuilder sb)1170 public void dump(StringBuilder sb) { 1171 sb.append("\tRinger Mode: " + mStoredRingerMode); 1172 1173 sb.append("\n\tCurrent call list:"); 1174 for (TbsCall call : mCurrentCallsList.values()) { 1175 sb.append("\n\t\tFriendly name: " + call.getSafeFriendlyName()); 1176 sb.append("\n\t\t\tState: " + TbsCall.stateToString(call.getState())); 1177 sb.append("\n\t\t\tURI: " + call.getSafeUri()); 1178 sb.append("\n\t\t\tFlags: " + TbsCall.flagsToString(call.getFlags())); 1179 } 1180 1181 mTbsGatt.dump(sb); 1182 } 1183 } 1184