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 // Android supports inband ringtone 196 mTbsGatt.setInbandRingtoneFlag(); 197 198 mReceiver = new Receiver(); 199 mTbsGatt.getContext().registerReceiver(mReceiver, 200 new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION)); 201 202 mIsInitialized = true; 203 return true; 204 } 205 cleanup()206 public synchronized void cleanup() { 207 if (DBG) { 208 Log.d(TAG, "cleanup"); 209 } 210 211 if (mTbsGatt != null) { 212 if (mReceiver != null) { 213 mTbsGatt.getContext().unregisterReceiver(mReceiver); 214 } 215 mTbsGatt.cleanup(); 216 mTbsGatt = null; 217 } 218 219 mIsInitialized = false; 220 } 221 isSilentModeEnabled()222 private synchronized boolean isSilentModeEnabled() { 223 return mStoredRingerMode != AudioManager.RINGER_MODE_NORMAL; 224 } 225 getBearerByToken(String token)226 private synchronized Bearer getBearerByToken(String token) { 227 for (Bearer bearer : mBearerList) { 228 if (bearer.token.equals(token)) { 229 return bearer; 230 } 231 } 232 return null; 233 } 234 getBearerByCcid(int ccid)235 private synchronized Bearer getBearerByCcid(int ccid) { 236 for (Bearer bearer : mBearerList) { 237 if (bearer.ccid == ccid) { 238 return bearer; 239 } 240 } 241 return null; 242 } 243 getBearerSupportingUri(String uri)244 private synchronized Bearer getBearerSupportingUri(String uri) { 245 for (Bearer bearer : mBearerList) { 246 for (String s : bearer.uriSchemes) { 247 if (uri.startsWith(s + ":")) { 248 return bearer; 249 } 250 } 251 } 252 return null; 253 } 254 getCallIdByIndex(int callIndex)255 private synchronized Map.Entry<UUID, Bearer> getCallIdByIndex(int callIndex) { 256 for (Bearer bearer : mBearerList) { 257 for (Map.Entry<UUID, Integer> callIdToIndex : bearer.callIdIndexMap.entrySet()) { 258 if (callIndex == callIdToIndex.getValue()) { 259 return Map.entry(callIdToIndex.getKey(), bearer); 260 } 261 } 262 } 263 return null; 264 } 265 addBearer(String token, IBluetoothLeCallControlCallback callback, String uci, List<String> uriSchemes, int capabilities, String providerName, int technology)266 public synchronized boolean addBearer(String token, IBluetoothLeCallControlCallback callback, 267 String uci, List<String> uriSchemes, int capabilities, String providerName, 268 int technology) { 269 if (DBG) { 270 Log.d(TAG, 271 "addBearer: token=" + token + " uci=" + uci + " uriSchemes=" + uriSchemes 272 + " capabilities=" + capabilities + " providerName=" + providerName 273 + " technology=" + technology); 274 } 275 if (!mIsInitialized) { 276 Log.w(TAG, "addBearer called while not initialized."); 277 return false; 278 } 279 280 if (getBearerByToken(token) != null) { 281 Log.w(TAG, "addBearer: token=" + token + " registered already"); 282 return false; 283 } 284 285 // Acquire CCID for TbsObject. The CCID is released on remove() 286 Bearer bearer = new Bearer(token, callback, uci, uriSchemes, capabilities, providerName, 287 technology, ContentControlIdKeeper.acquireCcid(new ParcelUuid(UUID.randomUUID()), 288 BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL)); 289 if (isCcidValid(bearer.ccid)) { 290 mBearerList.add(bearer); 291 292 updateUriSchemesSupported(); 293 if (mForegroundBearer == null) { 294 setForegroundBearer(bearer); 295 } 296 } else { 297 Log.e(TAG, "Failed to acquire ccid"); 298 } 299 300 if (callback != null) { 301 try { 302 Log.d(TAG, "ccid=" + bearer.ccid); 303 callback.onBearerRegistered(bearer.ccid); 304 } catch (RemoteException e) { 305 e.printStackTrace(); 306 } 307 } 308 309 return isCcidValid(bearer.ccid); 310 } 311 removeBearer(String token)312 public synchronized void removeBearer(String token) { 313 if (DBG) { 314 Log.d(TAG, "removeBearer: token=" + token); 315 } 316 317 if (!mIsInitialized) { 318 Log.w(TAG, "removeBearer called while not initialized."); 319 return; 320 } 321 322 Bearer bearer = getBearerByToken(token); 323 if (bearer == null) { 324 return; 325 } 326 327 // Remove the calls associated with this bearer 328 for (Integer callIndex : bearer.callIdIndexMap.values()) { 329 mCurrentCallsList.remove(callIndex); 330 } 331 332 if (bearer.callIdIndexMap.size() > 0) { 333 notifyCclc(); 334 } 335 336 // Release the ccid acquired 337 ContentControlIdKeeper.releaseCcid(bearer.ccid); 338 339 mBearerList.remove(bearer); 340 341 updateUriSchemesSupported(); 342 if (mForegroundBearer == bearer) { 343 setForegroundBearer(findNewForegroundBearer()); 344 } 345 } 346 checkRequestComplete(Bearer bearer, UUID callId, TbsCall tbsCall)347 private synchronized void checkRequestComplete(Bearer bearer, UUID callId, TbsCall tbsCall) { 348 // check if there's any pending request related to this call 349 Map.Entry<Integer, Request> requestEntry = null; 350 if (bearer.requestMap.size() > 0) { 351 for (Map.Entry<Integer, Request> entry : bearer.requestMap.entrySet()) { 352 if (entry.getValue().callIdList.contains(callId)) { 353 requestEntry = entry; 354 } 355 } 356 } 357 358 if (requestEntry == null) { 359 if (DBG) { 360 Log.d(TAG, "requestEntry is null"); 361 } 362 return; 363 } 364 365 int requestId = requestEntry.getKey(); 366 Request request = requestEntry.getValue(); 367 368 int result; 369 if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE) { 370 if (mCurrentCallsList.get(request.callIndex) == null) { 371 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 372 } else { 373 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 374 } 375 } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT) { 376 if (tbsCall.getState() != BluetoothLeCall.STATE_INCOMING) { 377 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 378 } else { 379 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 380 } 381 } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD) { 382 if (tbsCall.getState() == BluetoothLeCall.STATE_LOCALLY_HELD 383 || tbsCall.getState() == BluetoothLeCall.STATE_LOCALLY_AND_REMOTELY_HELD) { 384 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 385 } else { 386 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 387 } 388 } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE) { 389 if (tbsCall.getState() != BluetoothLeCall.STATE_LOCALLY_HELD 390 && tbsCall.getState() != BluetoothLeCall.STATE_LOCALLY_AND_REMOTELY_HELD) { 391 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 392 } else { 393 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 394 } 395 } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE) { 396 if (bearer.callIdIndexMap.get(request.callIdList.get(0)) != null) { 397 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 398 } else { 399 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 400 } 401 } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_JOIN) { 402 /* While joining calls, those that are not in remotely held state should go to active */ 403 if (bearer.callIdIndexMap.get(callId) == null 404 || (tbsCall.getState() != BluetoothLeCall.STATE_ACTIVE 405 && tbsCall.getState() != BluetoothLeCall.STATE_REMOTELY_HELD)) { 406 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 407 } else { 408 /* Check if all of the pending calls transit to required state */ 409 for (UUID pendingCallId : request.callIdList) { 410 Integer callIndex = bearer.callIdIndexMap.get(pendingCallId); 411 TbsCall pendingTbsCall = mCurrentCallsList.get(callIndex); 412 if (pendingTbsCall.getState() != BluetoothLeCall.STATE_ACTIVE 413 && pendingTbsCall.getState() != BluetoothLeCall.STATE_REMOTELY_HELD) { 414 /* Still waiting for more call state updates */ 415 return; 416 } 417 } 418 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 419 } 420 } else { 421 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 422 423 } 424 425 mTbsGatt.setCallControlPointResult(request.device, request.requestedOpcode, 426 request.callIndex, result); 427 428 bearer.requestMap.remove(requestId); 429 } 430 getTbsResult(int result, int requestedOpcode)431 private synchronized int getTbsResult(int result, int requestedOpcode) { 432 if (result == BluetoothLeCallControl.RESULT_ERROR_UNKNOWN_CALL_ID) { 433 return TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX; 434 } 435 436 if (result == BluetoothLeCallControl.RESULT_ERROR_INVALID_URI 437 && requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE) { 438 return TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_OUTGOING_URI; 439 } 440 441 return TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 442 } 443 requestResult(int ccid, int requestId, int result)444 public synchronized void requestResult(int ccid, int requestId, int result) { 445 if (DBG) { 446 Log.d(TAG, "requestResult: ccid=" + ccid + " requestId=" + requestId + " result=" 447 + result); 448 } 449 450 if (!mIsInitialized) { 451 Log.w(TAG, "requestResult called while not initialized."); 452 return; 453 } 454 455 Bearer bearer = getBearerByCcid(ccid); 456 if (bearer == null) { 457 Log.i(TAG, " Bearer for ccid " + ccid + " does not exist"); 458 return; 459 } 460 461 if (result == BluetoothLeCallControl.RESULT_SUCCESS) { 462 // don't send the success here, wait for state transition instead 463 return; 464 } 465 466 // check if there's any pending request related to this call 467 Request request = bearer.requestMap.remove(requestId); 468 if (request == null) { 469 // already sent response 470 return; 471 } 472 473 int tbsResult = getTbsResult(result, request.requestedOpcode); 474 mTbsGatt.setCallControlPointResult(request.device, request.requestedOpcode, 475 request.callIndex, tbsResult); 476 } 477 callAdded(int ccid, BluetoothLeCall call)478 public synchronized void callAdded(int ccid, BluetoothLeCall call) { 479 if (DBG) { 480 Log.d(TAG, "callAdded: ccid=" + ccid + " call=" + call); 481 } 482 483 if (!mIsInitialized) { 484 Log.w(TAG, "callAdded called while not initialized."); 485 return; 486 } 487 488 Bearer bearer = getBearerByCcid(ccid); 489 if (bearer == null) { 490 Log.e(TAG, "callAdded: unknown ccid=" + ccid); 491 return; 492 } 493 494 UUID callId = call.getUuid(); 495 if (bearer.callIdIndexMap.containsKey(callId)) { 496 Log.e(TAG, "callAdded: uuidId=" + callId + " already on list"); 497 return; 498 } 499 500 Integer callIndex = getFreeCallIndex(); 501 if (callIndex == null) { 502 Log.e(TAG, "callAdded: out of call indices!"); 503 return; 504 } 505 506 bearer.callIdIndexMap.put(callId, callIndex); 507 TbsCall tbsCall = TbsCall.create(call); 508 mCurrentCallsList.put(callIndex, tbsCall); 509 510 checkRequestComplete(bearer, callId, tbsCall); 511 if (tbsCall.isIncoming()) { 512 mTbsGatt.setIncomingCall(callIndex, tbsCall.getUri()); 513 } 514 515 String friendlyName = tbsCall.getFriendlyName(); 516 if (friendlyName == null) { 517 friendlyName = UNKNOWN_FRIENDLY_NAME; 518 } 519 mTbsGatt.setCallFriendlyName(callIndex, friendlyName); 520 521 notifyCclc(); 522 if (mForegroundBearer != bearer) { 523 setForegroundBearer(bearer); 524 } 525 } 526 callRemoved(int ccid, UUID callId, int reason)527 public synchronized void callRemoved(int ccid, UUID callId, int reason) { 528 if (DBG) { 529 Log.d(TAG, "callRemoved: ccid=" + ccid + "reason=" + reason); 530 } 531 532 if (!mIsInitialized) { 533 Log.w(TAG, "callRemoved called while not initialized."); 534 return; 535 } 536 537 Bearer bearer = getBearerByCcid(ccid); 538 if (bearer == null) { 539 Log.e(TAG, "callRemoved: unknown ccid=" + ccid); 540 return; 541 } 542 543 Integer callIndex = bearer.callIdIndexMap.remove(callId); 544 if (callIndex == null) { 545 Log.e(TAG, "callIndex: is null for callId" + callId); 546 return; 547 } 548 549 TbsCall tbsCall = mCurrentCallsList.remove(callIndex); 550 if (tbsCall == null) { 551 Log.e(TAG, "callRemoved: no such call"); 552 return; 553 } 554 555 checkRequestComplete(bearer, callId, tbsCall); 556 mTbsGatt.setTerminationReason(callIndex, reason); 557 notifyCclc(); 558 559 Integer incomingCallIndex = mTbsGatt.getIncomingCallIndex(); 560 if (incomingCallIndex != null && incomingCallIndex.equals(callIndex)) { 561 mTbsGatt.clearIncomingCall(); 562 // TODO: check if there's any incoming call more??? 563 } 564 565 Integer friendlyNameCallIndex = mTbsGatt.getCallFriendlyNameIndex(); 566 if (friendlyNameCallIndex != null && friendlyNameCallIndex.equals(callIndex)) { 567 mTbsGatt.clearFriendlyName(); 568 // TODO: check if there's any incoming/outgoing call more??? 569 } 570 } 571 callStateChanged(int ccid, UUID callId, int state)572 public synchronized void callStateChanged(int ccid, UUID callId, int state) { 573 if (DBG) { 574 Log.d(TAG, "callStateChanged: ccid=" + ccid + " callId=" + callId + " state=" + state); 575 } 576 577 if (!mIsInitialized) { 578 Log.w(TAG, "callStateChanged called while not initialized."); 579 return; 580 } 581 582 Bearer bearer = getBearerByCcid(ccid); 583 if (bearer == null) { 584 Log.e(TAG, "callStateChanged: unknown ccid=" + ccid); 585 return; 586 } 587 588 Integer callIndex = bearer.callIdIndexMap.get(callId); 589 if (callIndex == null) { 590 Log.e(TAG, "callStateChanged: unknown callId=" + callId); 591 return; 592 } 593 594 TbsCall tbsCall = mCurrentCallsList.get(callIndex); 595 if (tbsCall.getState() == state) { 596 return; 597 } 598 599 tbsCall.setState(state); 600 601 checkRequestComplete(bearer, callId, tbsCall); 602 notifyCclc(); 603 604 Integer incomingCallIndex = mTbsGatt.getIncomingCallIndex(); 605 if (incomingCallIndex != null && incomingCallIndex.equals(callIndex)) { 606 mTbsGatt.clearIncomingCall(); 607 // TODO: check if there's any incoming call more??? 608 } 609 } 610 currentCallsList(int ccid, List<BluetoothLeCall> calls)611 public synchronized void currentCallsList(int ccid, List<BluetoothLeCall> calls) { 612 if (DBG) { 613 Log.d(TAG, "currentCallsList: ccid=" + ccid + " callsNum=" + calls.size()); 614 } 615 616 if (!mIsInitialized) { 617 Log.w(TAG, "currentCallsList called while not initialized."); 618 return; 619 } 620 621 Bearer bearer = getBearerByCcid(ccid); 622 if (bearer == null) { 623 Log.e(TAG, "currentCallsList: unknown ccid=" + ccid); 624 return; 625 } 626 627 boolean cclc = false; 628 Map<UUID, Integer> storedCallIdList = new HashMap<>(bearer.callIdIndexMap); 629 bearer.callIdIndexMap = new HashMap<>(); 630 for (BluetoothLeCall call : calls) { 631 UUID callId = call.getUuid(); 632 Integer callIndex = storedCallIdList.get(callId); 633 if (callIndex == null) { 634 // new call 635 callIndex = getFreeCallIndex(); 636 if (callIndex == null) { 637 Log.e(TAG, "currentCallsList: out of call indices!"); 638 continue; 639 } 640 641 mCurrentCallsList.put(callIndex, TbsCall.create(call)); 642 cclc |= true; 643 } else { 644 TbsCall tbsCall = mCurrentCallsList.get(callIndex); 645 TbsCall tbsCallNew = TbsCall.create(call); 646 if (tbsCall != tbsCallNew) { 647 mCurrentCallsList.replace(callIndex, tbsCallNew); 648 cclc |= true; 649 } 650 } 651 652 bearer.callIdIndexMap.put(callId, callIndex); 653 } 654 655 for (Map.Entry<UUID, Integer> callIdToIndex : storedCallIdList.entrySet()) { 656 if (!bearer.callIdIndexMap.containsKey(callIdToIndex.getKey())) { 657 mCurrentCallsList.remove(callIdToIndex.getValue()); 658 cclc |= true; 659 } 660 } 661 662 if (cclc) { 663 notifyCclc(); 664 } 665 } 666 networkStateChanged(int ccid, String providerName, int technology)667 public synchronized void networkStateChanged(int ccid, String providerName, int technology) { 668 if (DBG) { 669 Log.d(TAG, "networkStateChanged: ccid=" + ccid + " providerName=" + providerName 670 + " technology=" + technology); 671 } 672 673 if (!mIsInitialized) { 674 Log.w(TAG, "networkStateChanged called while not initialized."); 675 return; 676 } 677 678 Bearer bearer = getBearerByCcid(ccid); 679 if (bearer == null) { 680 return; 681 } 682 683 boolean providerChanged = !bearer.providerName.equals(providerName); 684 if (providerChanged) { 685 bearer.providerName = providerName; 686 } 687 688 boolean technologyChanged = bearer.technology != technology; 689 if (technologyChanged) { 690 bearer.technology = technology; 691 } 692 693 if (bearer == mForegroundBearer) { 694 if (providerChanged) { 695 mTbsGatt.setBearerProviderName(bearer.providerName); 696 } 697 698 if (technologyChanged) { 699 mTbsGatt.setBearerTechnology(bearer.technology); 700 } 701 } 702 } 703 processOriginateCall(BluetoothDevice device, String uri)704 private synchronized int processOriginateCall(BluetoothDevice device, String uri) { 705 if (uri.startsWith("tel")) { 706 /* 707 * FIXME: For now, process telephone call originate request here, as 708 * BluetoothInCallService might be not running. The BluetoothInCallService is active 709 * when there is a call only. 710 */ 711 Log.i(TAG, "originate uri=" + uri); 712 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Uri.parse(uri)); 713 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 714 mTbsGatt.getContext().startActivity(intent); 715 mTbsGatt.setCallControlPointResult(device, TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE, 716 TbsCall.INDEX_UNASSIGNED, TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS); 717 } else { 718 UUID callId = UUID.randomUUID(); 719 int requestId = mLastRequestIdAssigned + 1; 720 Request request = new Request(device, callId, 721 TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE, TbsCall.INDEX_UNASSIGNED); 722 723 Bearer bearer = getBearerSupportingUri(uri); 724 if (bearer == null) { 725 return TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_OUTGOING_URI; 726 } 727 728 try { 729 bearer.callback.onPlaceCall(requestId, new ParcelUuid(callId), uri); 730 } catch (RemoteException e) { 731 e.printStackTrace(); 732 return TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 733 } 734 735 bearer.requestMap.put(requestId, request); 736 mLastIndexAssigned = requestId; 737 } 738 739 setActiveLeDevice(device); 740 return TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 741 } 742 743 private final TbsGatt.Callback mTbsGattCallback = new TbsGatt.Callback() { 744 745 @Override 746 public void onServiceAdded(boolean success) { 747 synchronized (TbsGeneric.this) { 748 if (DBG) { 749 Log.d(TAG, "onServiceAdded: success=" + success); 750 } 751 } 752 } 753 754 @Override 755 public void onCallControlPointRequest(BluetoothDevice device, int opcode, byte[] args) { 756 synchronized (TbsGeneric.this) { 757 if (DBG) { 758 Log.d(TAG, "onCallControlPointRequest: device=" + device + " opcode=" 759 + opcode + " argsLen=" + args.length); 760 } 761 762 if (!mIsInitialized) { 763 Log.w(TAG, "onCallControlPointRequest called while not initialized."); 764 return; 765 } 766 767 int result; 768 769 switch (opcode) { 770 case TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT: 771 case TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE: 772 case TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD: 773 case TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE: { 774 if (args.length == 0) { 775 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 776 break; 777 } 778 779 int callIndex = args[0]; 780 Map.Entry<UUID, Bearer> entry = getCallIdByIndex(callIndex); 781 if (entry == null) { 782 result = TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX; 783 break; 784 } 785 786 TbsCall call = mCurrentCallsList.get(callIndex); 787 if (!isCallStateTransitionValid(call.getState(), opcode)) { 788 result = TbsGatt.CALL_CONTROL_POINT_RESULT_STATE_MISMATCH; 789 break; 790 } 791 792 Bearer bearer = entry.getValue(); 793 UUID callId = entry.getKey(); 794 int requestId = mLastRequestIdAssigned + 1; 795 Request request = new Request(device, callId, opcode, callIndex); 796 try { 797 if (opcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT) { 798 setActiveLeDevice(device); 799 bearer.callback.onAcceptCall(requestId, new ParcelUuid(callId)); 800 } else if (opcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE) { 801 bearer.callback.onTerminateCall(requestId, new ParcelUuid(callId)); 802 } else if (opcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD) { 803 if ((bearer.capabilities 804 & BluetoothLeCallControl.CAPABILITY_HOLD_CALL) == 0) { 805 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED; 806 break; 807 } 808 bearer.callback.onHoldCall(requestId, new ParcelUuid(callId)); 809 } else { 810 if ((bearer.capabilities 811 & BluetoothLeCallControl.CAPABILITY_HOLD_CALL) == 0) { 812 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED; 813 break; 814 } 815 bearer.callback.onUnholdCall(requestId, new ParcelUuid(callId)); 816 } 817 } catch (RemoteException e) { 818 e.printStackTrace(); 819 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 820 break; 821 } 822 823 bearer.requestMap.put(requestId, request); 824 mLastRequestIdAssigned = requestId; 825 826 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 827 break; 828 } 829 830 case TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE: { 831 result = processOriginateCall(device, new String(args)); 832 break; 833 } 834 835 case TbsGatt.CALL_CONTROL_POINT_OPCODE_JOIN: { 836 // at least 2 call indices are required 837 if (args.length < 2) { 838 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 839 break; 840 } 841 842 Map.Entry<UUID, Bearer> firstEntry = null; 843 List<ParcelUuid> parcelUuids = new ArrayList<>(); 844 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 845 for (int callIndex : args) { 846 Map.Entry<UUID, Bearer> entry = getCallIdByIndex(callIndex); 847 if (entry == null) { 848 result = TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX; 849 break; 850 } 851 852 // state transition is valid, because a call in any state 853 // can requested to join 854 855 if (firstEntry == null) { 856 firstEntry = entry; 857 } 858 859 if (firstEntry.getValue() != entry.getValue()) { 860 Log.w(TAG, "Cannot join calls from different bearers!"); 861 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 862 break; 863 } 864 865 parcelUuids.add(new ParcelUuid(entry.getKey())); 866 } 867 868 if (result != TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS) { 869 break; 870 } 871 872 Bearer bearer = firstEntry.getValue(); 873 Request request = new Request(device, parcelUuids, opcode, args[0]); 874 int requestId = mLastRequestIdAssigned + 1; 875 try { 876 bearer.callback.onJoinCalls(requestId, parcelUuids); 877 } catch (RemoteException e) { 878 e.printStackTrace(); 879 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE; 880 break; 881 } 882 883 bearer.requestMap.put(requestId, request); 884 mLastIndexAssigned = requestId; 885 886 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; 887 break; 888 } 889 890 default: 891 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED; 892 break; 893 } 894 895 if (result == TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS) { 896 // return here and wait for the request completition from application 897 return; 898 } 899 900 mTbsGatt.setCallControlPointResult(device, opcode, 0, result); 901 } 902 } 903 }; 904 isCcidValid(int ccid)905 private static boolean isCcidValid(int ccid) { 906 return ccid != ContentControlIdKeeper.CCID_INVALID; 907 } 908 isCallIndexAssigned(int callIndex)909 private static boolean isCallIndexAssigned(int callIndex) { 910 return callIndex != TbsCall.INDEX_UNASSIGNED; 911 } 912 getFreeCallIndex()913 private synchronized Integer getFreeCallIndex() { 914 int callIndex = mLastIndexAssigned; 915 for (int i = TbsCall.INDEX_MIN; i <= TbsCall.INDEX_MAX; i++) { 916 callIndex = (callIndex + 1) % TbsCall.INDEX_MAX; 917 if (!isCallIndexAssigned(callIndex)) { 918 continue; 919 } 920 921 if (mCurrentCallsList.keySet().contains(callIndex)) { 922 continue; 923 } 924 925 mLastIndexAssigned = callIndex; 926 927 return callIndex; 928 } 929 930 return null; 931 } 932 getCallByStates( LinkedHashSet<Integer> states)933 private synchronized Map.Entry<Integer, TbsCall> getCallByStates( 934 LinkedHashSet<Integer> states) { 935 for (Map.Entry<Integer, TbsCall> entry : mCurrentCallsList.entrySet()) { 936 if (states.contains(entry.getValue().getState())) { 937 return entry; 938 } 939 } 940 941 return null; 942 } 943 getForegroundCall()944 private synchronized Map.Entry<Integer, TbsCall> getForegroundCall() { 945 LinkedHashSet<Integer> states = new LinkedHashSet<Integer>(); 946 Map.Entry<Integer, TbsCall> foregroundCall; 947 948 if (mCurrentCallsList.size() == 0) { 949 return null; 950 } 951 952 states.add(BluetoothLeCall.STATE_INCOMING); 953 foregroundCall = getCallByStates(states); 954 if (foregroundCall != null) { 955 return foregroundCall; 956 } 957 958 states.clear(); 959 states.add(BluetoothLeCall.STATE_DIALING); 960 states.add(BluetoothLeCall.STATE_ALERTING); 961 foregroundCall = getCallByStates(states); 962 if (foregroundCall != null) { 963 return foregroundCall; 964 } 965 966 states.clear(); 967 states.add(BluetoothLeCall.STATE_ACTIVE); 968 foregroundCall = getCallByStates(states); 969 if (foregroundCall != null) { 970 return foregroundCall; 971 } 972 973 return null; 974 } 975 findNewForegroundBearer()976 private synchronized Bearer findNewForegroundBearer() { 977 if (mBearerList.size() == 0) { 978 return null; 979 } 980 981 // the bearer that owns the foreground call 982 Map.Entry<Integer, TbsCall> foregroundCall = getForegroundCall(); 983 if (foregroundCall != null) { 984 for (Bearer bearer : mBearerList) { 985 if (bearer.callIdIndexMap.values().contains(foregroundCall.getKey())) { 986 return bearer; 987 } 988 } 989 } 990 991 // the last bearer registered 992 return mBearerList.get(mBearerList.size() - 1); 993 } 994 setForegroundBearer(Bearer bearer)995 private synchronized void setForegroundBearer(Bearer bearer) { 996 if (DBG) { 997 Log.d(TAG, "setForegroundBearer: bearer=" + bearer); 998 } 999 1000 if (bearer == null) { 1001 mTbsGatt.setBearerProviderName(DEFAULT_PROVIDER_NAME); 1002 mTbsGatt.setBearerTechnology(DEFAULT_BEARER_TECHNOLOGY); 1003 } else if (mForegroundBearer == null) { 1004 mTbsGatt.setBearerProviderName(bearer.providerName); 1005 mTbsGatt.setBearerTechnology(bearer.technology); 1006 } else { 1007 if (!bearer.providerName.equals(mForegroundBearer.providerName)) { 1008 mTbsGatt.setBearerProviderName(bearer.providerName); 1009 } 1010 1011 if (bearer.technology != mForegroundBearer.technology) { 1012 mTbsGatt.setBearerTechnology(bearer.technology); 1013 } 1014 } 1015 1016 mForegroundBearer = bearer; 1017 } 1018 isLeAudioServiceAvailable()1019 private boolean isLeAudioServiceAvailable() { 1020 if (mLeAudioService != null) { 1021 return true; 1022 } 1023 1024 mLeAudioService = mFactory.getLeAudioService(); 1025 if (mLeAudioService == null) { 1026 Log.e(TAG, "leAudioService not available"); 1027 return false; 1028 } 1029 1030 return true; 1031 } 1032 1033 @VisibleForTesting setLeAudioServiceForTesting(LeAudioService leAudioService)1034 void setLeAudioServiceForTesting(LeAudioService leAudioService) { 1035 mLeAudioService = leAudioService; 1036 } 1037 notifyCclc()1038 private synchronized void notifyCclc() { 1039 if (DBG) { 1040 Log.d(TAG, "notifyCclc"); 1041 } 1042 1043 if (isLeAudioServiceAvailable()) { 1044 if (mCurrentCallsList.size() > 0) { 1045 mLeAudioService.setInCall(true); 1046 } else { 1047 mLeAudioService.setInCall(false); 1048 } 1049 } 1050 1051 mTbsGatt.setCallState(mCurrentCallsList); 1052 mTbsGatt.setBearerListCurrentCalls(mCurrentCallsList); 1053 } 1054 updateUriSchemesSupported()1055 private synchronized void updateUriSchemesSupported() { 1056 List<String> newUriSchemes = new ArrayList<>(); 1057 for (Bearer bearer : mBearerList) { 1058 newUriSchemes.addAll(bearer.uriSchemes); 1059 } 1060 1061 // filter duplicates 1062 newUriSchemes = new ArrayList<>(new HashSet<>(newUriSchemes)); 1063 if (newUriSchemes.equals(mUriSchemes)) { 1064 return; 1065 } 1066 1067 mUriSchemes = new ArrayList<>(newUriSchemes); 1068 mTbsGatt.setBearerUriSchemesSupportedList(mUriSchemes); 1069 } 1070 setActiveLeDevice(BluetoothDevice device)1071 private void setActiveLeDevice(BluetoothDevice device) { 1072 if (device == null) { 1073 Log.w(TAG, "setActiveLeDevice: ignore null device"); 1074 return; 1075 } 1076 if (!isLeAudioServiceAvailable()) { 1077 Log.w(TAG, "mLeAudioService not available"); 1078 return; 1079 } 1080 mLeAudioService.setActiveDevice(device); 1081 } 1082 isCallStateTransitionValid(int callState, int requestedOpcode)1083 private static boolean isCallStateTransitionValid(int callState, int requestedOpcode) { 1084 switch (requestedOpcode) { 1085 case TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT: 1086 if (callState == BluetoothLeCall.STATE_INCOMING) { 1087 return true; 1088 } 1089 break; 1090 1091 case TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE: 1092 // Any call can be terminated. 1093 return true; 1094 1095 case TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD: 1096 if (callState == BluetoothLeCall.STATE_INCOMING 1097 || callState == BluetoothLeCall.STATE_ACTIVE 1098 || callState == BluetoothLeCall.STATE_REMOTELY_HELD) { 1099 return true; 1100 } 1101 break; 1102 1103 case TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE: 1104 if (callState == BluetoothLeCall.STATE_LOCALLY_HELD 1105 || callState == BluetoothLeCall.STATE_LOCALLY_AND_REMOTELY_HELD) { 1106 return true; 1107 } 1108 break; 1109 1110 default: 1111 Log.e(TAG, "unhandled opcode " + requestedOpcode); 1112 } 1113 1114 return false; 1115 } 1116 } 1117