1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.telecom; 18 19 import android.annotation.SdkConstant; 20 import android.app.Service; 21 import android.content.ComponentName; 22 import android.content.Intent; 23 import android.net.Uri; 24 import android.os.Bundle; 25 import android.os.Handler; 26 import android.os.IBinder; 27 import android.os.Looper; 28 import android.os.Message; 29 import android.os.ParcelFileDescriptor; 30 import android.os.RemoteException; 31 import android.telecom.Logging.Session; 32 33 import com.android.internal.os.SomeArgs; 34 import com.android.internal.telecom.IConnectionService; 35 import com.android.internal.telecom.IConnectionServiceAdapter; 36 import com.android.internal.telecom.RemoteServiceCallback; 37 38 import java.util.ArrayList; 39 import java.util.Collection; 40 import java.util.Collections; 41 import java.util.List; 42 import java.util.Map; 43 import java.util.UUID; 44 import java.util.concurrent.ConcurrentHashMap; 45 46 /** 47 * An abstract service that should be implemented by any apps which either: 48 * <ol> 49 * <li>Can make phone calls (VoIP or otherwise) and want those calls to be integrated into the 50 * built-in phone app. Referred to as a <b>system managed</b> {@link ConnectionService}.</li> 51 * <li>Are a standalone calling app and don't want their calls to be integrated into the 52 * built-in phone app. Referred to as a <b>self managed</b> {@link ConnectionService}.</li> 53 * </ol> 54 * Once implemented, the {@link ConnectionService} needs to take the following steps so that Telecom 55 * will bind to it: 56 * <p> 57 * 1. <i>Registration in AndroidManifest.xml</i> 58 * <br/> 59 * <pre> 60 * <service android:name="com.example.package.MyConnectionService" 61 * android:label="@string/some_label_for_my_connection_service" 62 * android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"> 63 * <intent-filter> 64 * <action android:name="android.telecom.ConnectionService" /> 65 * </intent-filter> 66 * </service> 67 * </pre> 68 * <p> 69 * 2. <i> Registration of {@link PhoneAccount} with {@link TelecomManager}.</i> 70 * <br/> 71 * See {@link PhoneAccount} and {@link TelecomManager#registerPhoneAccount} for more information. 72 * <p> 73 * System managed {@link ConnectionService}s must be enabled by the user in the phone app settings 74 * before Telecom will bind to them. Self-manged {@link ConnectionService}s must be granted the 75 * appropriate permission before Telecom will bind to them. 76 * <p> 77 * Once registered and enabled by the user in the phone app settings or granted permission, telecom 78 * will bind to a {@link ConnectionService} implementation when it wants that 79 * {@link ConnectionService} to place a call or the service has indicated that is has an incoming 80 * call through {@link TelecomManager#addNewIncomingCall}. The {@link ConnectionService} can then 81 * expect a call to {@link #onCreateIncomingConnection} or {@link #onCreateOutgoingConnection} 82 * wherein it should provide a new instance of a {@link Connection} object. It is through this 83 * {@link Connection} object that telecom receives state updates and the {@link ConnectionService} 84 * receives call-commands such as answer, reject, hold and disconnect. 85 * <p> 86 * When there are no more live calls, telecom will unbind from the {@link ConnectionService}. 87 */ 88 public abstract class ConnectionService extends Service { 89 /** 90 * The {@link Intent} that must be declared as handled by the service. 91 */ 92 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) 93 public static final String SERVICE_INTERFACE = "android.telecom.ConnectionService"; 94 95 /** 96 * Boolean extra used by Telecom to inform a {@link ConnectionService} that the purpose of it 97 * being asked to create a new outgoing {@link Connection} is to perform a handover of an 98 * ongoing call on the device from another {@link PhoneAccount}/{@link ConnectionService}. Will 99 * be specified in the {@link ConnectionRequest#getExtras()} passed by Telecom when 100 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)} is called. 101 * <p> 102 * When your {@link ConnectionService} receives this extra, it should communicate the fact that 103 * this is a handover to the other device's matching {@link ConnectionService}. That 104 * {@link ConnectionService} will continue the handover using 105 * {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, Bundle)}, specifying 106 * {@link TelecomManager#EXTRA_IS_HANDOVER}. Telecom will match the phone numbers of the 107 * handover call on the other device with ongoing calls for {@link ConnectionService}s which 108 * support {@link PhoneAccount#EXTRA_SUPPORTS_HANDOVER_FROM}. 109 * @hide 110 */ 111 public static final String EXTRA_IS_HANDOVER = TelecomManager.EXTRA_IS_HANDOVER; 112 113 // Flag controlling whether PII is emitted into the logs 114 private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG); 115 116 // Session Definitions 117 private static final String SESSION_HANDLER = "H."; 118 private static final String SESSION_ADD_CS_ADAPTER = "CS.aCSA"; 119 private static final String SESSION_REMOVE_CS_ADAPTER = "CS.rCSA"; 120 private static final String SESSION_CREATE_CONN = "CS.crCo"; 121 private static final String SESSION_CREATE_CONN_COMPLETE = "CS.crCoC"; 122 private static final String SESSION_CREATE_CONN_FAILED = "CS.crCoF"; 123 private static final String SESSION_ABORT = "CS.ab"; 124 private static final String SESSION_ANSWER = "CS.an"; 125 private static final String SESSION_ANSWER_VIDEO = "CS.anV"; 126 private static final String SESSION_DEFLECT = "CS.def"; 127 private static final String SESSION_REJECT = "CS.r"; 128 private static final String SESSION_REJECT_MESSAGE = "CS.rWM"; 129 private static final String SESSION_SILENCE = "CS.s"; 130 private static final String SESSION_DISCONNECT = "CS.d"; 131 private static final String SESSION_HOLD = "CS.h"; 132 private static final String SESSION_UNHOLD = "CS.u"; 133 private static final String SESSION_CALL_AUDIO_SC = "CS.cASC"; 134 private static final String SESSION_PLAY_DTMF = "CS.pDT"; 135 private static final String SESSION_STOP_DTMF = "CS.sDT"; 136 private static final String SESSION_CONFERENCE = "CS.c"; 137 private static final String SESSION_SPLIT_CONFERENCE = "CS.sFC"; 138 private static final String SESSION_MERGE_CONFERENCE = "CS.mC"; 139 private static final String SESSION_SWAP_CONFERENCE = "CS.sC"; 140 private static final String SESSION_POST_DIAL_CONT = "CS.oPDC"; 141 private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC"; 142 private static final String SESSION_SEND_CALL_EVENT = "CS.sCE"; 143 private static final String SESSION_HANDOVER_COMPLETE = "CS.hC"; 144 private static final String SESSION_EXTRAS_CHANGED = "CS.oEC"; 145 private static final String SESSION_START_RTT = "CS.+RTT"; 146 private static final String SESSION_UPDATE_RTT_PIPES = "CS.uRTT"; 147 private static final String SESSION_STOP_RTT = "CS.-RTT"; 148 private static final String SESSION_RTT_UPGRADE_RESPONSE = "CS.rTRUR"; 149 private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL"; 150 private static final String SESSION_CONNECTION_SERVICE_FOCUS_GAINED = "CS.cSFG"; 151 private static final String SESSION_HANDOVER_FAILED = "CS.haF"; 152 153 private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1; 154 private static final int MSG_CREATE_CONNECTION = 2; 155 private static final int MSG_ABORT = 3; 156 private static final int MSG_ANSWER = 4; 157 private static final int MSG_REJECT = 5; 158 private static final int MSG_DISCONNECT = 6; 159 private static final int MSG_HOLD = 7; 160 private static final int MSG_UNHOLD = 8; 161 private static final int MSG_ON_CALL_AUDIO_STATE_CHANGED = 9; 162 private static final int MSG_PLAY_DTMF_TONE = 10; 163 private static final int MSG_STOP_DTMF_TONE = 11; 164 private static final int MSG_CONFERENCE = 12; 165 private static final int MSG_SPLIT_FROM_CONFERENCE = 13; 166 private static final int MSG_ON_POST_DIAL_CONTINUE = 14; 167 private static final int MSG_REMOVE_CONNECTION_SERVICE_ADAPTER = 16; 168 private static final int MSG_ANSWER_VIDEO = 17; 169 private static final int MSG_MERGE_CONFERENCE = 18; 170 private static final int MSG_SWAP_CONFERENCE = 19; 171 private static final int MSG_REJECT_WITH_MESSAGE = 20; 172 private static final int MSG_SILENCE = 21; 173 private static final int MSG_PULL_EXTERNAL_CALL = 22; 174 private static final int MSG_SEND_CALL_EVENT = 23; 175 private static final int MSG_ON_EXTRAS_CHANGED = 24; 176 private static final int MSG_CREATE_CONNECTION_FAILED = 25; 177 private static final int MSG_ON_START_RTT = 26; 178 private static final int MSG_ON_STOP_RTT = 27; 179 private static final int MSG_RTT_UPGRADE_RESPONSE = 28; 180 private static final int MSG_CREATE_CONNECTION_COMPLETE = 29; 181 private static final int MSG_CONNECTION_SERVICE_FOCUS_LOST = 30; 182 private static final int MSG_CONNECTION_SERVICE_FOCUS_GAINED = 31; 183 private static final int MSG_HANDOVER_FAILED = 32; 184 private static final int MSG_HANDOVER_COMPLETE = 33; 185 private static final int MSG_DEFLECT = 34; 186 187 private static Connection sNullConnection; 188 189 private final Map<String, Connection> mConnectionById = new ConcurrentHashMap<>(); 190 private final Map<Connection, String> mIdByConnection = new ConcurrentHashMap<>(); 191 private final Map<String, Conference> mConferenceById = new ConcurrentHashMap<>(); 192 private final Map<Conference, String> mIdByConference = new ConcurrentHashMap<>(); 193 private final RemoteConnectionManager mRemoteConnectionManager = 194 new RemoteConnectionManager(this); 195 private final List<Runnable> mPreInitializationConnectionRequests = new ArrayList<>(); 196 private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter(); 197 198 private boolean mAreAccountsInitialized = false; 199 private Conference sNullConference; 200 private Object mIdSyncRoot = new Object(); 201 private int mId = 0; 202 203 private final IBinder mBinder = new IConnectionService.Stub() { 204 @Override 205 public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter, 206 Session.Info sessionInfo) { 207 Log.startSession(sessionInfo, SESSION_ADD_CS_ADAPTER); 208 try { 209 SomeArgs args = SomeArgs.obtain(); 210 args.arg1 = adapter; 211 args.arg2 = Log.createSubsession(); 212 mHandler.obtainMessage(MSG_ADD_CONNECTION_SERVICE_ADAPTER, args).sendToTarget(); 213 } finally { 214 Log.endSession(); 215 } 216 } 217 218 public void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter, 219 Session.Info sessionInfo) { 220 Log.startSession(sessionInfo, SESSION_REMOVE_CS_ADAPTER); 221 try { 222 SomeArgs args = SomeArgs.obtain(); 223 args.arg1 = adapter; 224 args.arg2 = Log.createSubsession(); 225 mHandler.obtainMessage(MSG_REMOVE_CONNECTION_SERVICE_ADAPTER, args).sendToTarget(); 226 } finally { 227 Log.endSession(); 228 } 229 } 230 231 @Override 232 public void createConnection( 233 PhoneAccountHandle connectionManagerPhoneAccount, 234 String id, 235 ConnectionRequest request, 236 boolean isIncoming, 237 boolean isUnknown, 238 Session.Info sessionInfo) { 239 Log.startSession(sessionInfo, SESSION_CREATE_CONN); 240 try { 241 SomeArgs args = SomeArgs.obtain(); 242 args.arg1 = connectionManagerPhoneAccount; 243 args.arg2 = id; 244 args.arg3 = request; 245 args.arg4 = Log.createSubsession(); 246 args.argi1 = isIncoming ? 1 : 0; 247 args.argi2 = isUnknown ? 1 : 0; 248 mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget(); 249 } finally { 250 Log.endSession(); 251 } 252 } 253 254 @Override 255 public void createConnectionComplete(String id, Session.Info sessionInfo) { 256 Log.startSession(sessionInfo, SESSION_CREATE_CONN_COMPLETE); 257 try { 258 SomeArgs args = SomeArgs.obtain(); 259 args.arg1 = id; 260 args.arg2 = Log.createSubsession(); 261 mHandler.obtainMessage(MSG_CREATE_CONNECTION_COMPLETE, args).sendToTarget(); 262 } finally { 263 Log.endSession(); 264 } 265 } 266 267 @Override 268 public void createConnectionFailed( 269 PhoneAccountHandle connectionManagerPhoneAccount, 270 String callId, 271 ConnectionRequest request, 272 boolean isIncoming, 273 Session.Info sessionInfo) { 274 Log.startSession(sessionInfo, SESSION_CREATE_CONN_FAILED); 275 try { 276 SomeArgs args = SomeArgs.obtain(); 277 args.arg1 = callId; 278 args.arg2 = request; 279 args.arg3 = Log.createSubsession(); 280 args.arg4 = connectionManagerPhoneAccount; 281 args.argi1 = isIncoming ? 1 : 0; 282 mHandler.obtainMessage(MSG_CREATE_CONNECTION_FAILED, args).sendToTarget(); 283 } finally { 284 Log.endSession(); 285 } 286 } 287 288 @Override 289 public void handoverFailed(String callId, ConnectionRequest request, int reason, 290 Session.Info sessionInfo) { 291 Log.startSession(sessionInfo, SESSION_HANDOVER_FAILED); 292 try { 293 SomeArgs args = SomeArgs.obtain(); 294 args.arg1 = callId; 295 args.arg2 = request; 296 args.arg3 = Log.createSubsession(); 297 args.arg4 = reason; 298 mHandler.obtainMessage(MSG_HANDOVER_FAILED, args).sendToTarget(); 299 } finally { 300 Log.endSession(); 301 } 302 } 303 304 @Override 305 public void handoverComplete(String callId, Session.Info sessionInfo) { 306 Log.startSession(sessionInfo, SESSION_HANDOVER_COMPLETE); 307 try { 308 SomeArgs args = SomeArgs.obtain(); 309 args.arg1 = callId; 310 args.arg2 = Log.createSubsession(); 311 mHandler.obtainMessage(MSG_HANDOVER_COMPLETE, args).sendToTarget(); 312 } finally { 313 Log.endSession(); 314 } 315 } 316 317 @Override 318 public void abort(String callId, Session.Info sessionInfo) { 319 Log.startSession(sessionInfo, SESSION_ABORT); 320 try { 321 SomeArgs args = SomeArgs.obtain(); 322 args.arg1 = callId; 323 args.arg2 = Log.createSubsession(); 324 mHandler.obtainMessage(MSG_ABORT, args).sendToTarget(); 325 } finally { 326 Log.endSession(); 327 } 328 } 329 330 @Override 331 public void answerVideo(String callId, int videoState, Session.Info sessionInfo) { 332 Log.startSession(sessionInfo, SESSION_ANSWER_VIDEO); 333 try { 334 SomeArgs args = SomeArgs.obtain(); 335 args.arg1 = callId; 336 args.arg2 = Log.createSubsession(); 337 args.argi1 = videoState; 338 mHandler.obtainMessage(MSG_ANSWER_VIDEO, args).sendToTarget(); 339 } finally { 340 Log.endSession(); 341 } 342 } 343 344 @Override 345 public void answer(String callId, Session.Info sessionInfo) { 346 Log.startSession(sessionInfo, SESSION_ANSWER); 347 try { 348 SomeArgs args = SomeArgs.obtain(); 349 args.arg1 = callId; 350 args.arg2 = Log.createSubsession(); 351 mHandler.obtainMessage(MSG_ANSWER, args).sendToTarget(); 352 } finally { 353 Log.endSession(); 354 } 355 } 356 357 @Override 358 public void deflect(String callId, Uri address, Session.Info sessionInfo) { 359 Log.startSession(sessionInfo, SESSION_DEFLECT); 360 try { 361 SomeArgs args = SomeArgs.obtain(); 362 args.arg1 = callId; 363 args.arg2 = address; 364 args.arg3 = Log.createSubsession(); 365 mHandler.obtainMessage(MSG_DEFLECT, args).sendToTarget(); 366 } finally { 367 Log.endSession(); 368 } 369 } 370 371 @Override 372 public void reject(String callId, Session.Info sessionInfo) { 373 Log.startSession(sessionInfo, SESSION_REJECT); 374 try { 375 SomeArgs args = SomeArgs.obtain(); 376 args.arg1 = callId; 377 args.arg2 = Log.createSubsession(); 378 mHandler.obtainMessage(MSG_REJECT, args).sendToTarget(); 379 } finally { 380 Log.endSession(); 381 } 382 } 383 384 @Override 385 public void rejectWithMessage(String callId, String message, Session.Info sessionInfo) { 386 Log.startSession(sessionInfo, SESSION_REJECT_MESSAGE); 387 try { 388 SomeArgs args = SomeArgs.obtain(); 389 args.arg1 = callId; 390 args.arg2 = message; 391 args.arg3 = Log.createSubsession(); 392 mHandler.obtainMessage(MSG_REJECT_WITH_MESSAGE, args).sendToTarget(); 393 } finally { 394 Log.endSession(); 395 } 396 } 397 398 @Override 399 public void silence(String callId, Session.Info sessionInfo) { 400 Log.startSession(sessionInfo, SESSION_SILENCE); 401 try { 402 SomeArgs args = SomeArgs.obtain(); 403 args.arg1 = callId; 404 args.arg2 = Log.createSubsession(); 405 mHandler.obtainMessage(MSG_SILENCE, args).sendToTarget(); 406 } finally { 407 Log.endSession(); 408 } 409 } 410 411 @Override 412 public void disconnect(String callId, Session.Info sessionInfo) { 413 Log.startSession(sessionInfo, SESSION_DISCONNECT); 414 try { 415 SomeArgs args = SomeArgs.obtain(); 416 args.arg1 = callId; 417 args.arg2 = Log.createSubsession(); 418 mHandler.obtainMessage(MSG_DISCONNECT, args).sendToTarget(); 419 } finally { 420 Log.endSession(); 421 } 422 } 423 424 @Override 425 public void hold(String callId, Session.Info sessionInfo) { 426 Log.startSession(sessionInfo, SESSION_HOLD); 427 try { 428 SomeArgs args = SomeArgs.obtain(); 429 args.arg1 = callId; 430 args.arg2 = Log.createSubsession(); 431 mHandler.obtainMessage(MSG_HOLD, args).sendToTarget(); 432 } finally { 433 Log.endSession(); 434 } 435 } 436 437 @Override 438 public void unhold(String callId, Session.Info sessionInfo) { 439 Log.startSession(sessionInfo, SESSION_UNHOLD); 440 try { 441 SomeArgs args = SomeArgs.obtain(); 442 args.arg1 = callId; 443 args.arg2 = Log.createSubsession(); 444 mHandler.obtainMessage(MSG_UNHOLD, args).sendToTarget(); 445 } finally { 446 Log.endSession(); 447 } 448 } 449 450 @Override 451 public void onCallAudioStateChanged(String callId, CallAudioState callAudioState, 452 Session.Info sessionInfo) { 453 Log.startSession(sessionInfo, SESSION_CALL_AUDIO_SC); 454 try { 455 SomeArgs args = SomeArgs.obtain(); 456 args.arg1 = callId; 457 args.arg2 = callAudioState; 458 args.arg3 = Log.createSubsession(); 459 mHandler.obtainMessage(MSG_ON_CALL_AUDIO_STATE_CHANGED, args).sendToTarget(); 460 } finally { 461 Log.endSession(); 462 } 463 } 464 465 @Override 466 public void playDtmfTone(String callId, char digit, Session.Info sessionInfo) { 467 Log.startSession(sessionInfo, SESSION_PLAY_DTMF); 468 try { 469 SomeArgs args = SomeArgs.obtain(); 470 args.arg1 = digit; 471 args.arg2 = callId; 472 args.arg3 = Log.createSubsession(); 473 mHandler.obtainMessage(MSG_PLAY_DTMF_TONE, args).sendToTarget(); 474 } finally { 475 Log.endSession(); 476 } 477 } 478 479 @Override 480 public void stopDtmfTone(String callId, Session.Info sessionInfo) { 481 Log.startSession(sessionInfo, SESSION_STOP_DTMF); 482 try { 483 SomeArgs args = SomeArgs.obtain(); 484 args.arg1 = callId; 485 args.arg2 = Log.createSubsession(); 486 mHandler.obtainMessage(MSG_STOP_DTMF_TONE, args).sendToTarget(); 487 } finally { 488 Log.endSession(); 489 } 490 } 491 492 @Override 493 public void conference(String callId1, String callId2, Session.Info sessionInfo) { 494 Log.startSession(sessionInfo, SESSION_CONFERENCE); 495 try { 496 SomeArgs args = SomeArgs.obtain(); 497 args.arg1 = callId1; 498 args.arg2 = callId2; 499 args.arg3 = Log.createSubsession(); 500 mHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget(); 501 } finally { 502 Log.endSession(); 503 } 504 } 505 506 @Override 507 public void splitFromConference(String callId, Session.Info sessionInfo) { 508 Log.startSession(sessionInfo, SESSION_SPLIT_CONFERENCE); 509 try { 510 SomeArgs args = SomeArgs.obtain(); 511 args.arg1 = callId; 512 args.arg2 = Log.createSubsession(); 513 mHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, args).sendToTarget(); 514 } finally { 515 Log.endSession(); 516 } 517 } 518 519 @Override 520 public void mergeConference(String callId, Session.Info sessionInfo) { 521 Log.startSession(sessionInfo, SESSION_MERGE_CONFERENCE); 522 try { 523 SomeArgs args = SomeArgs.obtain(); 524 args.arg1 = callId; 525 args.arg2 = Log.createSubsession(); 526 mHandler.obtainMessage(MSG_MERGE_CONFERENCE, args).sendToTarget(); 527 } finally { 528 Log.endSession(); 529 } 530 } 531 532 @Override 533 public void swapConference(String callId, Session.Info sessionInfo) { 534 Log.startSession(sessionInfo, SESSION_SWAP_CONFERENCE); 535 try { 536 SomeArgs args = SomeArgs.obtain(); 537 args.arg1 = callId; 538 args.arg2 = Log.createSubsession(); 539 mHandler.obtainMessage(MSG_SWAP_CONFERENCE, args).sendToTarget(); 540 } finally { 541 Log.endSession(); 542 } 543 } 544 545 @Override 546 public void onPostDialContinue(String callId, boolean proceed, Session.Info sessionInfo) { 547 Log.startSession(sessionInfo, SESSION_POST_DIAL_CONT); 548 try { 549 SomeArgs args = SomeArgs.obtain(); 550 args.arg1 = callId; 551 args.arg2 = Log.createSubsession(); 552 args.argi1 = proceed ? 1 : 0; 553 mHandler.obtainMessage(MSG_ON_POST_DIAL_CONTINUE, args).sendToTarget(); 554 } finally { 555 Log.endSession(); 556 } 557 } 558 559 @Override 560 public void pullExternalCall(String callId, Session.Info sessionInfo) { 561 Log.startSession(sessionInfo, SESSION_PULL_EXTERNAL_CALL); 562 try { 563 SomeArgs args = SomeArgs.obtain(); 564 args.arg1 = callId; 565 args.arg2 = Log.createSubsession(); 566 mHandler.obtainMessage(MSG_PULL_EXTERNAL_CALL, args).sendToTarget(); 567 } finally { 568 Log.endSession(); 569 } 570 } 571 572 @Override 573 public void sendCallEvent(String callId, String event, Bundle extras, 574 Session.Info sessionInfo) { 575 Log.startSession(sessionInfo, SESSION_SEND_CALL_EVENT); 576 try { 577 SomeArgs args = SomeArgs.obtain(); 578 args.arg1 = callId; 579 args.arg2 = event; 580 args.arg3 = extras; 581 args.arg4 = Log.createSubsession(); 582 mHandler.obtainMessage(MSG_SEND_CALL_EVENT, args).sendToTarget(); 583 } finally { 584 Log.endSession(); 585 } 586 } 587 588 @Override 589 public void onExtrasChanged(String callId, Bundle extras, Session.Info sessionInfo) { 590 Log.startSession(sessionInfo, SESSION_EXTRAS_CHANGED); 591 try { 592 SomeArgs args = SomeArgs.obtain(); 593 args.arg1 = callId; 594 args.arg2 = extras; 595 args.arg3 = Log.createSubsession(); 596 mHandler.obtainMessage(MSG_ON_EXTRAS_CHANGED, args).sendToTarget(); 597 } finally { 598 Log.endSession(); 599 } 600 } 601 602 @Override 603 public void startRtt(String callId, ParcelFileDescriptor fromInCall, 604 ParcelFileDescriptor toInCall, Session.Info sessionInfo) throws RemoteException { 605 Log.startSession(sessionInfo, SESSION_START_RTT); 606 try { 607 SomeArgs args = SomeArgs.obtain(); 608 args.arg1 = callId; 609 args.arg2 = new Connection.RttTextStream(toInCall, fromInCall); 610 args.arg3 = Log.createSubsession(); 611 mHandler.obtainMessage(MSG_ON_START_RTT, args).sendToTarget(); 612 } finally { 613 Log.endSession(); 614 } 615 } 616 617 @Override 618 public void stopRtt(String callId, Session.Info sessionInfo) throws RemoteException { 619 Log.startSession(sessionInfo, SESSION_STOP_RTT); 620 try { 621 SomeArgs args = SomeArgs.obtain(); 622 args.arg1 = callId; 623 args.arg2 = Log.createSubsession(); 624 mHandler.obtainMessage(MSG_ON_STOP_RTT, args).sendToTarget(); 625 } finally { 626 Log.endSession(); 627 } 628 } 629 630 @Override 631 public void respondToRttUpgradeRequest(String callId, ParcelFileDescriptor fromInCall, 632 ParcelFileDescriptor toInCall, Session.Info sessionInfo) throws RemoteException { 633 Log.startSession(sessionInfo, SESSION_RTT_UPGRADE_RESPONSE); 634 try { 635 SomeArgs args = SomeArgs.obtain(); 636 args.arg1 = callId; 637 if (toInCall == null || fromInCall == null) { 638 args.arg2 = null; 639 } else { 640 args.arg2 = new Connection.RttTextStream(toInCall, fromInCall); 641 } 642 args.arg3 = Log.createSubsession(); 643 mHandler.obtainMessage(MSG_RTT_UPGRADE_RESPONSE, args).sendToTarget(); 644 } finally { 645 Log.endSession(); 646 } 647 } 648 649 @Override 650 public void connectionServiceFocusLost(Session.Info sessionInfo) throws RemoteException { 651 Log.startSession(sessionInfo, SESSION_CONNECTION_SERVICE_FOCUS_LOST); 652 try { 653 mHandler.obtainMessage(MSG_CONNECTION_SERVICE_FOCUS_LOST).sendToTarget(); 654 } finally { 655 Log.endSession(); 656 } 657 } 658 659 @Override 660 public void connectionServiceFocusGained(Session.Info sessionInfo) throws RemoteException { 661 Log.startSession(sessionInfo, SESSION_CONNECTION_SERVICE_FOCUS_GAINED); 662 try { 663 mHandler.obtainMessage(MSG_CONNECTION_SERVICE_FOCUS_GAINED).sendToTarget(); 664 } finally { 665 Log.endSession(); 666 } 667 } 668 }; 669 670 private final Handler mHandler = new Handler(Looper.getMainLooper()) { 671 @Override 672 public void handleMessage(Message msg) { 673 switch (msg.what) { 674 case MSG_ADD_CONNECTION_SERVICE_ADAPTER: { 675 SomeArgs args = (SomeArgs) msg.obj; 676 try { 677 IConnectionServiceAdapter adapter = (IConnectionServiceAdapter) args.arg1; 678 Log.continueSession((Session) args.arg2, 679 SESSION_HANDLER + SESSION_ADD_CS_ADAPTER); 680 mAdapter.addAdapter(adapter); 681 onAdapterAttached(); 682 } finally { 683 args.recycle(); 684 Log.endSession(); 685 } 686 break; 687 } 688 case MSG_REMOVE_CONNECTION_SERVICE_ADAPTER: { 689 SomeArgs args = (SomeArgs) msg.obj; 690 try { 691 Log.continueSession((Session) args.arg2, 692 SESSION_HANDLER + SESSION_REMOVE_CS_ADAPTER); 693 mAdapter.removeAdapter((IConnectionServiceAdapter) args.arg1); 694 } finally { 695 args.recycle(); 696 Log.endSession(); 697 } 698 break; 699 } 700 case MSG_CREATE_CONNECTION: { 701 SomeArgs args = (SomeArgs) msg.obj; 702 Log.continueSession((Session) args.arg4, SESSION_HANDLER + SESSION_CREATE_CONN); 703 try { 704 final PhoneAccountHandle connectionManagerPhoneAccount = 705 (PhoneAccountHandle) args.arg1; 706 final String id = (String) args.arg2; 707 final ConnectionRequest request = (ConnectionRequest) args.arg3; 708 final boolean isIncoming = args.argi1 == 1; 709 final boolean isUnknown = args.argi2 == 1; 710 if (!mAreAccountsInitialized) { 711 Log.d(this, "Enqueueing pre-init request %s", id); 712 mPreInitializationConnectionRequests.add( 713 new android.telecom.Logging.Runnable( 714 SESSION_HANDLER + SESSION_CREATE_CONN + ".pICR", 715 null /*lock*/) { 716 @Override 717 public void loggedRun() { 718 createConnection( 719 connectionManagerPhoneAccount, 720 id, 721 request, 722 isIncoming, 723 isUnknown); 724 } 725 }.prepare()); 726 } else { 727 createConnection( 728 connectionManagerPhoneAccount, 729 id, 730 request, 731 isIncoming, 732 isUnknown); 733 } 734 } finally { 735 args.recycle(); 736 Log.endSession(); 737 } 738 break; 739 } 740 case MSG_CREATE_CONNECTION_COMPLETE: { 741 SomeArgs args = (SomeArgs) msg.obj; 742 Log.continueSession((Session) args.arg2, 743 SESSION_HANDLER + SESSION_CREATE_CONN_COMPLETE); 744 try { 745 final String id = (String) args.arg1; 746 if (!mAreAccountsInitialized) { 747 Log.d(this, "Enqueueing pre-init request %s", id); 748 mPreInitializationConnectionRequests.add( 749 new android.telecom.Logging.Runnable( 750 SESSION_HANDLER + SESSION_CREATE_CONN_COMPLETE 751 + ".pICR", 752 null /*lock*/) { 753 @Override 754 public void loggedRun() { 755 notifyCreateConnectionComplete(id); 756 } 757 }.prepare()); 758 } else { 759 notifyCreateConnectionComplete(id); 760 } 761 } finally { 762 args.recycle(); 763 Log.endSession(); 764 } 765 break; 766 } 767 case MSG_CREATE_CONNECTION_FAILED: { 768 SomeArgs args = (SomeArgs) msg.obj; 769 Log.continueSession((Session) args.arg3, SESSION_HANDLER + 770 SESSION_CREATE_CONN_FAILED); 771 try { 772 final String id = (String) args.arg1; 773 final ConnectionRequest request = (ConnectionRequest) args.arg2; 774 final boolean isIncoming = args.argi1 == 1; 775 final PhoneAccountHandle connectionMgrPhoneAccount = 776 (PhoneAccountHandle) args.arg4; 777 if (!mAreAccountsInitialized) { 778 Log.d(this, "Enqueueing pre-init request %s", id); 779 mPreInitializationConnectionRequests.add( 780 new android.telecom.Logging.Runnable( 781 SESSION_HANDLER + SESSION_CREATE_CONN_FAILED + ".pICR", 782 null /*lock*/) { 783 @Override 784 public void loggedRun() { 785 createConnectionFailed(connectionMgrPhoneAccount, id, 786 request, isIncoming); 787 } 788 }.prepare()); 789 } else { 790 Log.i(this, "createConnectionFailed %s", id); 791 createConnectionFailed(connectionMgrPhoneAccount, id, request, 792 isIncoming); 793 } 794 } finally { 795 args.recycle(); 796 Log.endSession(); 797 } 798 break; 799 } 800 case MSG_HANDOVER_FAILED: { 801 SomeArgs args = (SomeArgs) msg.obj; 802 Log.continueSession((Session) args.arg3, SESSION_HANDLER + 803 SESSION_HANDOVER_FAILED); 804 try { 805 final String id = (String) args.arg1; 806 final ConnectionRequest request = (ConnectionRequest) args.arg2; 807 final int reason = (int) args.arg4; 808 if (!mAreAccountsInitialized) { 809 Log.d(this, "Enqueueing pre-init request %s", id); 810 mPreInitializationConnectionRequests.add( 811 new android.telecom.Logging.Runnable( 812 SESSION_HANDLER 813 + SESSION_HANDOVER_FAILED + ".pICR", 814 null /*lock*/) { 815 @Override 816 public void loggedRun() { 817 handoverFailed(id, request, reason); 818 } 819 }.prepare()); 820 } else { 821 Log.i(this, "createConnectionFailed %s", id); 822 handoverFailed(id, request, reason); 823 } 824 } finally { 825 args.recycle(); 826 Log.endSession(); 827 } 828 break; 829 } 830 case MSG_ABORT: { 831 SomeArgs args = (SomeArgs) msg.obj; 832 Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_ABORT); 833 try { 834 abort((String) args.arg1); 835 } finally { 836 args.recycle(); 837 Log.endSession(); 838 } 839 break; 840 } 841 case MSG_ANSWER: { 842 SomeArgs args = (SomeArgs) msg.obj; 843 Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_ANSWER); 844 try { 845 answer((String) args.arg1); 846 } finally { 847 args.recycle(); 848 Log.endSession(); 849 } 850 break; 851 } 852 case MSG_ANSWER_VIDEO: { 853 SomeArgs args = (SomeArgs) msg.obj; 854 Log.continueSession((Session) args.arg2, 855 SESSION_HANDLER + SESSION_ANSWER_VIDEO); 856 try { 857 String callId = (String) args.arg1; 858 int videoState = args.argi1; 859 answerVideo(callId, videoState); 860 } finally { 861 args.recycle(); 862 Log.endSession(); 863 } 864 break; 865 } 866 case MSG_DEFLECT: { 867 SomeArgs args = (SomeArgs) msg.obj; 868 Log.continueSession((Session) args.arg3, SESSION_HANDLER + SESSION_DEFLECT); 869 try { 870 deflect((String) args.arg1, (Uri) args.arg2); 871 } finally { 872 args.recycle(); 873 Log.endSession(); 874 } 875 break; 876 } 877 case MSG_REJECT: { 878 SomeArgs args = (SomeArgs) msg.obj; 879 Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT); 880 try { 881 reject((String) args.arg1); 882 } finally { 883 args.recycle(); 884 Log.endSession(); 885 } 886 break; 887 } 888 case MSG_REJECT_WITH_MESSAGE: { 889 SomeArgs args = (SomeArgs) msg.obj; 890 Log.continueSession((Session) args.arg3, 891 SESSION_HANDLER + SESSION_REJECT_MESSAGE); 892 try { 893 reject((String) args.arg1, (String) args.arg2); 894 } finally { 895 args.recycle(); 896 Log.endSession(); 897 } 898 break; 899 } 900 case MSG_DISCONNECT: { 901 SomeArgs args = (SomeArgs) msg.obj; 902 Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_DISCONNECT); 903 try { 904 disconnect((String) args.arg1); 905 } finally { 906 args.recycle(); 907 Log.endSession(); 908 } 909 break; 910 } 911 case MSG_SILENCE: { 912 SomeArgs args = (SomeArgs) msg.obj; 913 Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_SILENCE); 914 try { 915 silence((String) args.arg1); 916 } finally { 917 args.recycle(); 918 Log.endSession(); 919 } 920 break; 921 } 922 case MSG_HOLD: { 923 SomeArgs args = (SomeArgs) msg.obj; 924 Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT); 925 try { 926 hold((String) args.arg1); 927 } finally { 928 args.recycle(); 929 Log.endSession(); 930 } 931 break; 932 } 933 case MSG_UNHOLD: { 934 SomeArgs args = (SomeArgs) msg.obj; 935 Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_UNHOLD); 936 try { 937 unhold((String) args.arg1); 938 } finally { 939 args.recycle(); 940 Log.endSession(); 941 } 942 break; 943 } 944 case MSG_ON_CALL_AUDIO_STATE_CHANGED: { 945 SomeArgs args = (SomeArgs) msg.obj; 946 Log.continueSession((Session) args.arg3, 947 SESSION_HANDLER + SESSION_CALL_AUDIO_SC); 948 try { 949 String callId = (String) args.arg1; 950 CallAudioState audioState = (CallAudioState) args.arg2; 951 onCallAudioStateChanged(callId, new CallAudioState(audioState)); 952 } finally { 953 args.recycle(); 954 Log.endSession(); 955 } 956 break; 957 } 958 case MSG_PLAY_DTMF_TONE: { 959 SomeArgs args = (SomeArgs) msg.obj; 960 try { 961 Log.continueSession((Session) args.arg3, 962 SESSION_HANDLER + SESSION_PLAY_DTMF); 963 playDtmfTone((String) args.arg2, (char) args.arg1); 964 } finally { 965 args.recycle(); 966 Log.endSession(); 967 } 968 break; 969 } 970 case MSG_STOP_DTMF_TONE: { 971 SomeArgs args = (SomeArgs) msg.obj; 972 try { 973 Log.continueSession((Session) args.arg2, 974 SESSION_HANDLER + SESSION_STOP_DTMF); 975 stopDtmfTone((String) args.arg1); 976 } finally { 977 args.recycle(); 978 Log.endSession(); 979 } 980 break; 981 } 982 case MSG_CONFERENCE: { 983 SomeArgs args = (SomeArgs) msg.obj; 984 try { 985 Log.continueSession((Session) args.arg3, 986 SESSION_HANDLER + SESSION_CONFERENCE); 987 String callId1 = (String) args.arg1; 988 String callId2 = (String) args.arg2; 989 conference(callId1, callId2); 990 } finally { 991 args.recycle(); 992 Log.endSession(); 993 } 994 break; 995 } 996 case MSG_SPLIT_FROM_CONFERENCE: { 997 SomeArgs args = (SomeArgs) msg.obj; 998 try { 999 Log.continueSession((Session) args.arg2, 1000 SESSION_HANDLER + SESSION_SPLIT_CONFERENCE); 1001 splitFromConference((String) args.arg1); 1002 } finally { 1003 args.recycle(); 1004 Log.endSession(); 1005 } 1006 break; 1007 } 1008 case MSG_MERGE_CONFERENCE: { 1009 SomeArgs args = (SomeArgs) msg.obj; 1010 try { 1011 Log.continueSession((Session) args.arg2, 1012 SESSION_HANDLER + SESSION_MERGE_CONFERENCE); 1013 mergeConference((String) args.arg1); 1014 } finally { 1015 args.recycle(); 1016 Log.endSession(); 1017 } 1018 break; 1019 } 1020 case MSG_SWAP_CONFERENCE: { 1021 SomeArgs args = (SomeArgs) msg.obj; 1022 try { 1023 Log.continueSession((Session) args.arg2, 1024 SESSION_HANDLER + SESSION_SWAP_CONFERENCE); 1025 swapConference((String) args.arg1); 1026 } finally { 1027 args.recycle(); 1028 Log.endSession(); 1029 } 1030 break; 1031 } 1032 case MSG_ON_POST_DIAL_CONTINUE: { 1033 SomeArgs args = (SomeArgs) msg.obj; 1034 try { 1035 Log.continueSession((Session) args.arg2, 1036 SESSION_HANDLER + SESSION_POST_DIAL_CONT); 1037 String callId = (String) args.arg1; 1038 boolean proceed = (args.argi1 == 1); 1039 onPostDialContinue(callId, proceed); 1040 } finally { 1041 args.recycle(); 1042 Log.endSession(); 1043 } 1044 break; 1045 } 1046 case MSG_PULL_EXTERNAL_CALL: { 1047 SomeArgs args = (SomeArgs) msg.obj; 1048 try { 1049 Log.continueSession((Session) args.arg2, 1050 SESSION_HANDLER + SESSION_PULL_EXTERNAL_CALL); 1051 pullExternalCall((String) args.arg1); 1052 } finally { 1053 args.recycle(); 1054 Log.endSession(); 1055 } 1056 break; 1057 } 1058 case MSG_SEND_CALL_EVENT: { 1059 SomeArgs args = (SomeArgs) msg.obj; 1060 try { 1061 Log.continueSession((Session) args.arg4, 1062 SESSION_HANDLER + SESSION_SEND_CALL_EVENT); 1063 String callId = (String) args.arg1; 1064 String event = (String) args.arg2; 1065 Bundle extras = (Bundle) args.arg3; 1066 sendCallEvent(callId, event, extras); 1067 } finally { 1068 args.recycle(); 1069 Log.endSession(); 1070 } 1071 break; 1072 } 1073 case MSG_HANDOVER_COMPLETE: { 1074 SomeArgs args = (SomeArgs) msg.obj; 1075 try { 1076 Log.continueSession((Session) args.arg2, 1077 SESSION_HANDLER + SESSION_HANDOVER_COMPLETE); 1078 String callId = (String) args.arg1; 1079 notifyHandoverComplete(callId); 1080 } finally { 1081 args.recycle(); 1082 Log.endSession(); 1083 } 1084 break; 1085 } 1086 case MSG_ON_EXTRAS_CHANGED: { 1087 SomeArgs args = (SomeArgs) msg.obj; 1088 try { 1089 Log.continueSession((Session) args.arg3, 1090 SESSION_HANDLER + SESSION_EXTRAS_CHANGED); 1091 String callId = (String) args.arg1; 1092 Bundle extras = (Bundle) args.arg2; 1093 handleExtrasChanged(callId, extras); 1094 } finally { 1095 args.recycle(); 1096 Log.endSession(); 1097 } 1098 break; 1099 } 1100 case MSG_ON_START_RTT: { 1101 SomeArgs args = (SomeArgs) msg.obj; 1102 try { 1103 Log.continueSession((Session) args.arg3, 1104 SESSION_HANDLER + SESSION_START_RTT); 1105 String callId = (String) args.arg1; 1106 Connection.RttTextStream rttTextStream = 1107 (Connection.RttTextStream) args.arg2; 1108 startRtt(callId, rttTextStream); 1109 } finally { 1110 args.recycle(); 1111 Log.endSession(); 1112 } 1113 break; 1114 } 1115 case MSG_ON_STOP_RTT: { 1116 SomeArgs args = (SomeArgs) msg.obj; 1117 try { 1118 Log.continueSession((Session) args.arg2, 1119 SESSION_HANDLER + SESSION_STOP_RTT); 1120 String callId = (String) args.arg1; 1121 stopRtt(callId); 1122 } finally { 1123 args.recycle(); 1124 Log.endSession(); 1125 } 1126 break; 1127 } 1128 case MSG_RTT_UPGRADE_RESPONSE: { 1129 SomeArgs args = (SomeArgs) msg.obj; 1130 try { 1131 Log.continueSession((Session) args.arg3, 1132 SESSION_HANDLER + SESSION_RTT_UPGRADE_RESPONSE); 1133 String callId = (String) args.arg1; 1134 Connection.RttTextStream rttTextStream = 1135 (Connection.RttTextStream) args.arg2; 1136 handleRttUpgradeResponse(callId, rttTextStream); 1137 } finally { 1138 args.recycle(); 1139 Log.endSession(); 1140 } 1141 break; 1142 } 1143 case MSG_CONNECTION_SERVICE_FOCUS_GAINED: 1144 onConnectionServiceFocusGained(); 1145 break; 1146 case MSG_CONNECTION_SERVICE_FOCUS_LOST: 1147 onConnectionServiceFocusLost(); 1148 break; 1149 default: 1150 break; 1151 } 1152 } 1153 }; 1154 1155 private final Conference.Listener mConferenceListener = new Conference.Listener() { 1156 @Override 1157 public void onStateChanged(Conference conference, int oldState, int newState) { 1158 String id = mIdByConference.get(conference); 1159 switch (newState) { 1160 case Connection.STATE_ACTIVE: 1161 mAdapter.setActive(id); 1162 break; 1163 case Connection.STATE_HOLDING: 1164 mAdapter.setOnHold(id); 1165 break; 1166 case Connection.STATE_DISCONNECTED: 1167 // handled by onDisconnected 1168 break; 1169 } 1170 } 1171 1172 @Override 1173 public void onDisconnected(Conference conference, DisconnectCause disconnectCause) { 1174 String id = mIdByConference.get(conference); 1175 mAdapter.setDisconnected(id, disconnectCause); 1176 } 1177 1178 @Override 1179 public void onConnectionAdded(Conference conference, Connection connection) { 1180 } 1181 1182 @Override 1183 public void onConnectionRemoved(Conference conference, Connection connection) { 1184 } 1185 1186 @Override 1187 public void onConferenceableConnectionsChanged( 1188 Conference conference, List<Connection> conferenceableConnections) { 1189 mAdapter.setConferenceableConnections( 1190 mIdByConference.get(conference), 1191 createConnectionIdList(conferenceableConnections)); 1192 } 1193 1194 @Override 1195 public void onDestroyed(Conference conference) { 1196 removeConference(conference); 1197 } 1198 1199 @Override 1200 public void onConnectionCapabilitiesChanged( 1201 Conference conference, 1202 int connectionCapabilities) { 1203 String id = mIdByConference.get(conference); 1204 Log.d(this, "call capabilities: conference: %s", 1205 Connection.capabilitiesToString(connectionCapabilities)); 1206 mAdapter.setConnectionCapabilities(id, connectionCapabilities); 1207 } 1208 1209 @Override 1210 public void onConnectionPropertiesChanged( 1211 Conference conference, 1212 int connectionProperties) { 1213 String id = mIdByConference.get(conference); 1214 Log.d(this, "call capabilities: conference: %s", 1215 Connection.propertiesToString(connectionProperties)); 1216 mAdapter.setConnectionProperties(id, connectionProperties); 1217 } 1218 1219 @Override 1220 public void onVideoStateChanged(Conference c, int videoState) { 1221 String id = mIdByConference.get(c); 1222 Log.d(this, "onVideoStateChanged set video state %d", videoState); 1223 mAdapter.setVideoState(id, videoState); 1224 } 1225 1226 @Override 1227 public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) { 1228 String id = mIdByConference.get(c); 1229 Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c, 1230 videoProvider); 1231 mAdapter.setVideoProvider(id, videoProvider); 1232 } 1233 1234 @Override 1235 public void onStatusHintsChanged(Conference conference, StatusHints statusHints) { 1236 String id = mIdByConference.get(conference); 1237 if (id != null) { 1238 mAdapter.setStatusHints(id, statusHints); 1239 } 1240 } 1241 1242 @Override 1243 public void onExtrasChanged(Conference c, Bundle extras) { 1244 String id = mIdByConference.get(c); 1245 if (id != null) { 1246 mAdapter.putExtras(id, extras); 1247 } 1248 } 1249 1250 @Override 1251 public void onExtrasRemoved(Conference c, List<String> keys) { 1252 String id = mIdByConference.get(c); 1253 if (id != null) { 1254 mAdapter.removeExtras(id, keys); 1255 } 1256 } 1257 }; 1258 1259 private final Connection.Listener mConnectionListener = new Connection.Listener() { 1260 @Override 1261 public void onStateChanged(Connection c, int state) { 1262 String id = mIdByConnection.get(c); 1263 Log.d(this, "Adapter set state %s %s", id, Connection.stateToString(state)); 1264 switch (state) { 1265 case Connection.STATE_ACTIVE: 1266 mAdapter.setActive(id); 1267 break; 1268 case Connection.STATE_DIALING: 1269 mAdapter.setDialing(id); 1270 break; 1271 case Connection.STATE_PULLING_CALL: 1272 mAdapter.setPulling(id); 1273 break; 1274 case Connection.STATE_DISCONNECTED: 1275 // Handled in onDisconnected() 1276 break; 1277 case Connection.STATE_HOLDING: 1278 mAdapter.setOnHold(id); 1279 break; 1280 case Connection.STATE_NEW: 1281 // Nothing to tell Telecom 1282 break; 1283 case Connection.STATE_RINGING: 1284 mAdapter.setRinging(id); 1285 break; 1286 } 1287 } 1288 1289 @Override 1290 public void onDisconnected(Connection c, DisconnectCause disconnectCause) { 1291 String id = mIdByConnection.get(c); 1292 Log.d(this, "Adapter set disconnected %s", disconnectCause); 1293 mAdapter.setDisconnected(id, disconnectCause); 1294 } 1295 1296 @Override 1297 public void onVideoStateChanged(Connection c, int videoState) { 1298 String id = mIdByConnection.get(c); 1299 Log.d(this, "Adapter set video state %d", videoState); 1300 mAdapter.setVideoState(id, videoState); 1301 } 1302 1303 @Override 1304 public void onAddressChanged(Connection c, Uri address, int presentation) { 1305 String id = mIdByConnection.get(c); 1306 mAdapter.setAddress(id, address, presentation); 1307 } 1308 1309 @Override 1310 public void onCallerDisplayNameChanged( 1311 Connection c, String callerDisplayName, int presentation) { 1312 String id = mIdByConnection.get(c); 1313 mAdapter.setCallerDisplayName(id, callerDisplayName, presentation); 1314 } 1315 1316 @Override 1317 public void onDestroyed(Connection c) { 1318 removeConnection(c); 1319 } 1320 1321 @Override 1322 public void onPostDialWait(Connection c, String remaining) { 1323 String id = mIdByConnection.get(c); 1324 Log.d(this, "Adapter onPostDialWait %s, %s", c, remaining); 1325 mAdapter.onPostDialWait(id, remaining); 1326 } 1327 1328 @Override 1329 public void onPostDialChar(Connection c, char nextChar) { 1330 String id = mIdByConnection.get(c); 1331 Log.d(this, "Adapter onPostDialChar %s, %s", c, nextChar); 1332 mAdapter.onPostDialChar(id, nextChar); 1333 } 1334 1335 @Override 1336 public void onRingbackRequested(Connection c, boolean ringback) { 1337 String id = mIdByConnection.get(c); 1338 Log.d(this, "Adapter onRingback %b", ringback); 1339 mAdapter.setRingbackRequested(id, ringback); 1340 } 1341 1342 @Override 1343 public void onConnectionCapabilitiesChanged(Connection c, int capabilities) { 1344 String id = mIdByConnection.get(c); 1345 Log.d(this, "capabilities: parcelableconnection: %s", 1346 Connection.capabilitiesToString(capabilities)); 1347 mAdapter.setConnectionCapabilities(id, capabilities); 1348 } 1349 1350 @Override 1351 public void onConnectionPropertiesChanged(Connection c, int properties) { 1352 String id = mIdByConnection.get(c); 1353 Log.d(this, "properties: parcelableconnection: %s", 1354 Connection.propertiesToString(properties)); 1355 mAdapter.setConnectionProperties(id, properties); 1356 } 1357 1358 @Override 1359 public void onVideoProviderChanged(Connection c, Connection.VideoProvider videoProvider) { 1360 String id = mIdByConnection.get(c); 1361 Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c, 1362 videoProvider); 1363 mAdapter.setVideoProvider(id, videoProvider); 1364 } 1365 1366 @Override 1367 public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) { 1368 String id = mIdByConnection.get(c); 1369 mAdapter.setIsVoipAudioMode(id, isVoip); 1370 } 1371 1372 @Override 1373 public void onStatusHintsChanged(Connection c, StatusHints statusHints) { 1374 String id = mIdByConnection.get(c); 1375 mAdapter.setStatusHints(id, statusHints); 1376 } 1377 1378 @Override 1379 public void onConferenceablesChanged( 1380 Connection connection, List<Conferenceable> conferenceables) { 1381 mAdapter.setConferenceableConnections( 1382 mIdByConnection.get(connection), 1383 createIdList(conferenceables)); 1384 } 1385 1386 @Override 1387 public void onConferenceChanged(Connection connection, Conference conference) { 1388 String id = mIdByConnection.get(connection); 1389 if (id != null) { 1390 String conferenceId = null; 1391 if (conference != null) { 1392 conferenceId = mIdByConference.get(conference); 1393 } 1394 mAdapter.setIsConferenced(id, conferenceId); 1395 } 1396 } 1397 1398 @Override 1399 public void onConferenceMergeFailed(Connection connection) { 1400 String id = mIdByConnection.get(connection); 1401 if (id != null) { 1402 mAdapter.onConferenceMergeFailed(id); 1403 } 1404 } 1405 1406 @Override 1407 public void onExtrasChanged(Connection c, Bundle extras) { 1408 String id = mIdByConnection.get(c); 1409 if (id != null) { 1410 mAdapter.putExtras(id, extras); 1411 } 1412 } 1413 1414 @Override 1415 public void onExtrasRemoved(Connection c, List<String> keys) { 1416 String id = mIdByConnection.get(c); 1417 if (id != null) { 1418 mAdapter.removeExtras(id, keys); 1419 } 1420 } 1421 1422 @Override 1423 public void onConnectionEvent(Connection connection, String event, Bundle extras) { 1424 String id = mIdByConnection.get(connection); 1425 if (id != null) { 1426 mAdapter.onConnectionEvent(id, event, extras); 1427 } 1428 } 1429 1430 @Override 1431 public void onAudioRouteChanged(Connection c, int audioRoute, String bluetoothAddress) { 1432 String id = mIdByConnection.get(c); 1433 if (id != null) { 1434 mAdapter.setAudioRoute(id, audioRoute, bluetoothAddress); 1435 } 1436 } 1437 1438 @Override 1439 public void onRttInitiationSuccess(Connection c) { 1440 String id = mIdByConnection.get(c); 1441 if (id != null) { 1442 mAdapter.onRttInitiationSuccess(id); 1443 } 1444 } 1445 1446 @Override 1447 public void onRttInitiationFailure(Connection c, int reason) { 1448 String id = mIdByConnection.get(c); 1449 if (id != null) { 1450 mAdapter.onRttInitiationFailure(id, reason); 1451 } 1452 } 1453 1454 @Override 1455 public void onRttSessionRemotelyTerminated(Connection c) { 1456 String id = mIdByConnection.get(c); 1457 if (id != null) { 1458 mAdapter.onRttSessionRemotelyTerminated(id); 1459 } 1460 } 1461 1462 @Override 1463 public void onRemoteRttRequest(Connection c) { 1464 String id = mIdByConnection.get(c); 1465 if (id != null) { 1466 mAdapter.onRemoteRttRequest(id); 1467 } 1468 } 1469 1470 @Override 1471 public void onPhoneAccountChanged(Connection c, PhoneAccountHandle pHandle) { 1472 String id = mIdByConnection.get(c); 1473 if (id != null) { 1474 mAdapter.onPhoneAccountChanged(id, pHandle); 1475 } 1476 } 1477 }; 1478 1479 /** {@inheritDoc} */ 1480 @Override 1481 public final IBinder onBind(Intent intent) { 1482 return mBinder; 1483 } 1484 1485 /** {@inheritDoc} */ 1486 @Override 1487 public boolean onUnbind(Intent intent) { 1488 endAllConnections(); 1489 return super.onUnbind(intent); 1490 } 1491 1492 /** 1493 * This can be used by telecom to either create a new outgoing call or attach to an existing 1494 * incoming call. In either case, telecom will cycle through a set of services and call 1495 * createConnection util a connection service cancels the process or completes it successfully. 1496 */ 1497 private void createConnection( 1498 final PhoneAccountHandle callManagerAccount, 1499 final String callId, 1500 final ConnectionRequest request, 1501 boolean isIncoming, 1502 boolean isUnknown) { 1503 boolean isLegacyHandover = request.getExtras() != null && 1504 request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER, false); 1505 boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean( 1506 TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false); 1507 Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " + 1508 "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b", 1509 callManagerAccount, callId, request, isIncoming, isUnknown, isLegacyHandover, 1510 isHandover); 1511 1512 Connection connection = null; 1513 if (isHandover) { 1514 PhoneAccountHandle fromPhoneAccountHandle = request.getExtras() != null 1515 ? (PhoneAccountHandle) request.getExtras().getParcelable( 1516 TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT) : null; 1517 if (!isIncoming) { 1518 connection = onCreateOutgoingHandoverConnection(fromPhoneAccountHandle, request); 1519 } else { 1520 connection = onCreateIncomingHandoverConnection(fromPhoneAccountHandle, request); 1521 } 1522 } else { 1523 connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request) 1524 : isIncoming ? onCreateIncomingConnection(callManagerAccount, request) 1525 : onCreateOutgoingConnection(callManagerAccount, request); 1526 } 1527 Log.d(this, "createConnection, connection: %s", connection); 1528 if (connection == null) { 1529 Log.i(this, "createConnection, implementation returned null connection."); 1530 connection = Connection.createFailedConnection( 1531 new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION")); 1532 } 1533 1534 connection.setTelecomCallId(callId); 1535 if (connection.getState() != Connection.STATE_DISCONNECTED) { 1536 addConnection(request.getAccountHandle(), callId, connection); 1537 } 1538 1539 Uri address = connection.getAddress(); 1540 String number = address == null ? "null" : address.getSchemeSpecificPart(); 1541 Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s, properties: %s", 1542 Connection.toLogSafePhoneNumber(number), 1543 Connection.stateToString(connection.getState()), 1544 Connection.capabilitiesToString(connection.getConnectionCapabilities()), 1545 Connection.propertiesToString(connection.getConnectionProperties())); 1546 1547 Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId); 1548 mAdapter.handleCreateConnectionComplete( 1549 callId, 1550 request, 1551 new ParcelableConnection( 1552 request.getAccountHandle(), 1553 connection.getState(), 1554 connection.getConnectionCapabilities(), 1555 connection.getConnectionProperties(), 1556 connection.getSupportedAudioRoutes(), 1557 connection.getAddress(), 1558 connection.getAddressPresentation(), 1559 connection.getCallerDisplayName(), 1560 connection.getCallerDisplayNamePresentation(), 1561 connection.getVideoProvider() == null ? 1562 null : connection.getVideoProvider().getInterface(), 1563 connection.getVideoState(), 1564 connection.isRingbackRequested(), 1565 connection.getAudioModeIsVoip(), 1566 connection.getConnectTimeMillis(), 1567 connection.getConnectElapsedTimeMillis(), 1568 connection.getStatusHints(), 1569 connection.getDisconnectCause(), 1570 createIdList(connection.getConferenceables()), 1571 connection.getExtras())); 1572 1573 if (isIncoming && request.shouldShowIncomingCallUi() && 1574 (connection.getConnectionProperties() & Connection.PROPERTY_SELF_MANAGED) == 1575 Connection.PROPERTY_SELF_MANAGED) { 1576 // Tell ConnectionService to show its incoming call UX. 1577 connection.onShowIncomingCallUi(); 1578 } 1579 if (isUnknown) { 1580 triggerConferenceRecalculate(); 1581 } 1582 } 1583 1584 private void createConnectionFailed(final PhoneAccountHandle callManagerAccount, 1585 final String callId, final ConnectionRequest request, 1586 boolean isIncoming) { 1587 1588 Log.i(this, "createConnectionFailed %s", callId); 1589 if (isIncoming) { 1590 onCreateIncomingConnectionFailed(callManagerAccount, request); 1591 } else { 1592 onCreateOutgoingConnectionFailed(callManagerAccount, request); 1593 } 1594 } 1595 1596 private void handoverFailed(final String callId, final ConnectionRequest request, 1597 int reason) { 1598 1599 Log.i(this, "handoverFailed %s", callId); 1600 onHandoverFailed(request, reason); 1601 } 1602 1603 /** 1604 * Called by Telecom when the creation of a new Connection has completed and it is now added 1605 * to Telecom. 1606 * @param callId The ID of the connection. 1607 */ 1608 private void notifyCreateConnectionComplete(final String callId) { 1609 Log.i(this, "notifyCreateConnectionComplete %s", callId); 1610 if (callId == null) { 1611 // This could happen if the connection fails quickly and is removed from the 1612 // ConnectionService before Telecom sends the create connection complete callback. 1613 Log.w(this, "notifyCreateConnectionComplete: callId is null."); 1614 return; 1615 } 1616 onCreateConnectionComplete(findConnectionForAction(callId, 1617 "notifyCreateConnectionComplete")); 1618 } 1619 1620 private void abort(String callId) { 1621 Log.d(this, "abort %s", callId); 1622 findConnectionForAction(callId, "abort").onAbort(); 1623 } 1624 1625 private void answerVideo(String callId, int videoState) { 1626 Log.d(this, "answerVideo %s", callId); 1627 findConnectionForAction(callId, "answer").onAnswer(videoState); 1628 } 1629 1630 private void answer(String callId) { 1631 Log.d(this, "answer %s", callId); 1632 findConnectionForAction(callId, "answer").onAnswer(); 1633 } 1634 1635 private void deflect(String callId, Uri address) { 1636 Log.d(this, "deflect %s", callId); 1637 findConnectionForAction(callId, "deflect").onDeflect(address); 1638 } 1639 1640 private void reject(String callId) { 1641 Log.d(this, "reject %s", callId); 1642 findConnectionForAction(callId, "reject").onReject(); 1643 } 1644 1645 private void reject(String callId, String rejectWithMessage) { 1646 Log.d(this, "reject %s with message", callId); 1647 findConnectionForAction(callId, "reject").onReject(rejectWithMessage); 1648 } 1649 1650 private void silence(String callId) { 1651 Log.d(this, "silence %s", callId); 1652 findConnectionForAction(callId, "silence").onSilence(); 1653 } 1654 1655 private void disconnect(String callId) { 1656 Log.d(this, "disconnect %s", callId); 1657 if (mConnectionById.containsKey(callId)) { 1658 findConnectionForAction(callId, "disconnect").onDisconnect(); 1659 } else { 1660 findConferenceForAction(callId, "disconnect").onDisconnect(); 1661 } 1662 } 1663 1664 private void hold(String callId) { 1665 Log.d(this, "hold %s", callId); 1666 if (mConnectionById.containsKey(callId)) { 1667 findConnectionForAction(callId, "hold").onHold(); 1668 } else { 1669 findConferenceForAction(callId, "hold").onHold(); 1670 } 1671 } 1672 1673 private void unhold(String callId) { 1674 Log.d(this, "unhold %s", callId); 1675 if (mConnectionById.containsKey(callId)) { 1676 findConnectionForAction(callId, "unhold").onUnhold(); 1677 } else { 1678 findConferenceForAction(callId, "unhold").onUnhold(); 1679 } 1680 } 1681 1682 private void onCallAudioStateChanged(String callId, CallAudioState callAudioState) { 1683 Log.d(this, "onAudioStateChanged %s %s", callId, callAudioState); 1684 if (mConnectionById.containsKey(callId)) { 1685 findConnectionForAction(callId, "onCallAudioStateChanged").setCallAudioState( 1686 callAudioState); 1687 } else { 1688 findConferenceForAction(callId, "onCallAudioStateChanged").setCallAudioState( 1689 callAudioState); 1690 } 1691 } 1692 1693 private void playDtmfTone(String callId, char digit) { 1694 Log.d(this, "playDtmfTone %s %c", callId, digit); 1695 if (mConnectionById.containsKey(callId)) { 1696 findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit); 1697 } else { 1698 findConferenceForAction(callId, "playDtmfTone").onPlayDtmfTone(digit); 1699 } 1700 } 1701 1702 private void stopDtmfTone(String callId) { 1703 Log.d(this, "stopDtmfTone %s", callId); 1704 if (mConnectionById.containsKey(callId)) { 1705 findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone(); 1706 } else { 1707 findConferenceForAction(callId, "stopDtmfTone").onStopDtmfTone(); 1708 } 1709 } 1710 1711 private void conference(String callId1, String callId2) { 1712 Log.d(this, "conference %s, %s", callId1, callId2); 1713 1714 // Attempt to get second connection or conference. 1715 Connection connection2 = findConnectionForAction(callId2, "conference"); 1716 Conference conference2 = getNullConference(); 1717 if (connection2 == getNullConnection()) { 1718 conference2 = findConferenceForAction(callId2, "conference"); 1719 if (conference2 == getNullConference()) { 1720 Log.w(this, "Connection2 or Conference2 missing in conference request %s.", 1721 callId2); 1722 return; 1723 } 1724 } 1725 1726 // Attempt to get first connection or conference and perform merge. 1727 Connection connection1 = findConnectionForAction(callId1, "conference"); 1728 if (connection1 == getNullConnection()) { 1729 Conference conference1 = findConferenceForAction(callId1, "addConnection"); 1730 if (conference1 == getNullConference()) { 1731 Log.w(this, 1732 "Connection1 or Conference1 missing in conference request %s.", 1733 callId1); 1734 } else { 1735 // Call 1 is a conference. 1736 if (connection2 != getNullConnection()) { 1737 // Call 2 is a connection so merge via call 1 (conference). 1738 conference1.onMerge(connection2); 1739 } else { 1740 // Call 2 is ALSO a conference; this should never happen. 1741 Log.wtf(this, "There can only be one conference and an attempt was made to " + 1742 "merge two conferences."); 1743 return; 1744 } 1745 } 1746 } else { 1747 // Call 1 is a connection. 1748 if (conference2 != getNullConference()) { 1749 // Call 2 is a conference, so merge via call 2. 1750 conference2.onMerge(connection1); 1751 } else { 1752 // Call 2 is a connection, so merge together. 1753 onConference(connection1, connection2); 1754 } 1755 } 1756 } 1757 1758 private void splitFromConference(String callId) { 1759 Log.d(this, "splitFromConference(%s)", callId); 1760 1761 Connection connection = findConnectionForAction(callId, "splitFromConference"); 1762 if (connection == getNullConnection()) { 1763 Log.w(this, "Connection missing in conference request %s.", callId); 1764 return; 1765 } 1766 1767 Conference conference = connection.getConference(); 1768 if (conference != null) { 1769 conference.onSeparate(connection); 1770 } 1771 } 1772 1773 private void mergeConference(String callId) { 1774 Log.d(this, "mergeConference(%s)", callId); 1775 Conference conference = findConferenceForAction(callId, "mergeConference"); 1776 if (conference != null) { 1777 conference.onMerge(); 1778 } 1779 } 1780 1781 private void swapConference(String callId) { 1782 Log.d(this, "swapConference(%s)", callId); 1783 Conference conference = findConferenceForAction(callId, "swapConference"); 1784 if (conference != null) { 1785 conference.onSwap(); 1786 } 1787 } 1788 1789 /** 1790 * Notifies a {@link Connection} of a request to pull an external call. 1791 * 1792 * See {@link Call#pullExternalCall()}. 1793 * 1794 * @param callId The ID of the call to pull. 1795 */ 1796 private void pullExternalCall(String callId) { 1797 Log.d(this, "pullExternalCall(%s)", callId); 1798 Connection connection = findConnectionForAction(callId, "pullExternalCall"); 1799 if (connection != null) { 1800 connection.onPullExternalCall(); 1801 } 1802 } 1803 1804 /** 1805 * Notifies a {@link Connection} of a call event. 1806 * 1807 * See {@link Call#sendCallEvent(String, Bundle)}. 1808 * 1809 * @param callId The ID of the call receiving the event. 1810 * @param event The event. 1811 * @param extras Extras associated with the event. 1812 */ 1813 private void sendCallEvent(String callId, String event, Bundle extras) { 1814 Log.d(this, "sendCallEvent(%s, %s)", callId, event); 1815 Connection connection = findConnectionForAction(callId, "sendCallEvent"); 1816 if (connection != null) { 1817 connection.onCallEvent(event, extras); 1818 } 1819 } 1820 1821 /** 1822 * Notifies a {@link Connection} that a handover has completed. 1823 * 1824 * @param callId The ID of the call which completed handover. 1825 */ 1826 private void notifyHandoverComplete(String callId) { 1827 Log.d(this, "notifyHandoverComplete(%s)", callId); 1828 Connection connection = findConnectionForAction(callId, "notifyHandoverComplete"); 1829 if (connection != null) { 1830 connection.onHandoverComplete(); 1831 } 1832 } 1833 1834 /** 1835 * Notifies a {@link Connection} or {@link Conference} of a change to the extras from Telecom. 1836 * <p> 1837 * These extra changes can originate from Telecom itself, or from an {@link InCallService} via 1838 * the {@link android.telecom.Call#putExtra(String, boolean)}, 1839 * {@link android.telecom.Call#putExtra(String, int)}, 1840 * {@link android.telecom.Call#putExtra(String, String)}, 1841 * {@link Call#removeExtras(List)}. 1842 * 1843 * @param callId The ID of the call receiving the event. 1844 * @param extras The new extras bundle. 1845 */ 1846 private void handleExtrasChanged(String callId, Bundle extras) { 1847 Log.d(this, "handleExtrasChanged(%s, %s)", callId, extras); 1848 if (mConnectionById.containsKey(callId)) { 1849 findConnectionForAction(callId, "handleExtrasChanged").handleExtrasChanged(extras); 1850 } else if (mConferenceById.containsKey(callId)) { 1851 findConferenceForAction(callId, "handleExtrasChanged").handleExtrasChanged(extras); 1852 } 1853 } 1854 1855 private void startRtt(String callId, Connection.RttTextStream rttTextStream) { 1856 Log.d(this, "startRtt(%s)", callId); 1857 if (mConnectionById.containsKey(callId)) { 1858 findConnectionForAction(callId, "startRtt").onStartRtt(rttTextStream); 1859 } else if (mConferenceById.containsKey(callId)) { 1860 Log.w(this, "startRtt called on a conference."); 1861 } 1862 } 1863 1864 private void stopRtt(String callId) { 1865 Log.d(this, "stopRtt(%s)", callId); 1866 if (mConnectionById.containsKey(callId)) { 1867 findConnectionForAction(callId, "stopRtt").onStopRtt(); 1868 } else if (mConferenceById.containsKey(callId)) { 1869 Log.w(this, "stopRtt called on a conference."); 1870 } 1871 } 1872 1873 private void handleRttUpgradeResponse(String callId, Connection.RttTextStream rttTextStream) { 1874 Log.d(this, "handleRttUpgradeResponse(%s, %s)", callId, rttTextStream == null); 1875 if (mConnectionById.containsKey(callId)) { 1876 findConnectionForAction(callId, "handleRttUpgradeResponse") 1877 .handleRttUpgradeResponse(rttTextStream); 1878 } else if (mConferenceById.containsKey(callId)) { 1879 Log.w(this, "handleRttUpgradeResponse called on a conference."); 1880 } 1881 } 1882 1883 private void onPostDialContinue(String callId, boolean proceed) { 1884 Log.d(this, "onPostDialContinue(%s)", callId); 1885 findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed); 1886 } 1887 1888 private void onAdapterAttached() { 1889 if (mAreAccountsInitialized) { 1890 // No need to query again if we already did it. 1891 return; 1892 } 1893 1894 mAdapter.queryRemoteConnectionServices(new RemoteServiceCallback.Stub() { 1895 @Override 1896 public void onResult( 1897 final List<ComponentName> componentNames, 1898 final List<IBinder> services) { 1899 mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oR", null /*lock*/) { 1900 @Override 1901 public void loggedRun() { 1902 for (int i = 0; i < componentNames.size() && i < services.size(); i++) { 1903 mRemoteConnectionManager.addConnectionService( 1904 componentNames.get(i), 1905 IConnectionService.Stub.asInterface(services.get(i))); 1906 } 1907 onAccountsInitialized(); 1908 Log.d(this, "remote connection services found: " + services); 1909 } 1910 }.prepare()); 1911 } 1912 1913 @Override 1914 public void onError() { 1915 mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oE", null /*lock*/) { 1916 @Override 1917 public void loggedRun() { 1918 mAreAccountsInitialized = true; 1919 } 1920 }.prepare()); 1921 } 1922 }); 1923 } 1924 1925 /** 1926 * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an 1927 * incoming request. This is used by {@code ConnectionService}s that are registered with 1928 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to manage 1929 * SIM-based incoming calls. 1930 * 1931 * @param connectionManagerPhoneAccount See description at 1932 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 1933 * @param request Details about the incoming call. 1934 * @return The {@code Connection} object to satisfy this call, or {@code null} to 1935 * not handle the call. 1936 */ 1937 public final RemoteConnection createRemoteIncomingConnection( 1938 PhoneAccountHandle connectionManagerPhoneAccount, 1939 ConnectionRequest request) { 1940 return mRemoteConnectionManager.createRemoteConnection( 1941 connectionManagerPhoneAccount, request, true); 1942 } 1943 1944 /** 1945 * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an 1946 * outgoing request. This is used by {@code ConnectionService}s that are registered with 1947 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to use the 1948 * SIM-based {@code ConnectionService} to place its outgoing calls. 1949 * 1950 * @param connectionManagerPhoneAccount See description at 1951 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 1952 * @param request Details about the outgoing call. 1953 * @return The {@code Connection} object to satisfy this call, or {@code null} to 1954 * not handle the call. 1955 */ 1956 public final RemoteConnection createRemoteOutgoingConnection( 1957 PhoneAccountHandle connectionManagerPhoneAccount, 1958 ConnectionRequest request) { 1959 return mRemoteConnectionManager.createRemoteConnection( 1960 connectionManagerPhoneAccount, request, false); 1961 } 1962 1963 /** 1964 * Indicates to the relevant {@code RemoteConnectionService} that the specified 1965 * {@link RemoteConnection}s should be merged into a conference call. 1966 * <p> 1967 * If the conference request is successful, the method {@link #onRemoteConferenceAdded} will 1968 * be invoked. 1969 * 1970 * @param remoteConnection1 The first of the remote connections to conference. 1971 * @param remoteConnection2 The second of the remote connections to conference. 1972 */ 1973 public final void conferenceRemoteConnections( 1974 RemoteConnection remoteConnection1, 1975 RemoteConnection remoteConnection2) { 1976 mRemoteConnectionManager.conferenceRemoteConnections(remoteConnection1, remoteConnection2); 1977 } 1978 1979 /** 1980 * Adds a new conference call. When a conference call is created either as a result of an 1981 * explicit request via {@link #onConference} or otherwise, the connection service should supply 1982 * an instance of {@link Conference} by invoking this method. A conference call provided by this 1983 * method will persist until {@link Conference#destroy} is invoked on the conference instance. 1984 * 1985 * @param conference The new conference object. 1986 */ 1987 public final void addConference(Conference conference) { 1988 Log.d(this, "addConference: conference=%s", conference); 1989 1990 String id = addConferenceInternal(conference); 1991 if (id != null) { 1992 List<String> connectionIds = new ArrayList<>(2); 1993 for (Connection connection : conference.getConnections()) { 1994 if (mIdByConnection.containsKey(connection)) { 1995 connectionIds.add(mIdByConnection.get(connection)); 1996 } 1997 } 1998 conference.setTelecomCallId(id); 1999 ParcelableConference parcelableConference = new ParcelableConference( 2000 conference.getPhoneAccountHandle(), 2001 conference.getState(), 2002 conference.getConnectionCapabilities(), 2003 conference.getConnectionProperties(), 2004 connectionIds, 2005 conference.getVideoProvider() == null ? 2006 null : conference.getVideoProvider().getInterface(), 2007 conference.getVideoState(), 2008 conference.getConnectTimeMillis(), 2009 conference.getConnectionStartElapsedRealTime(), 2010 conference.getStatusHints(), 2011 conference.getExtras()); 2012 2013 mAdapter.addConferenceCall(id, parcelableConference); 2014 mAdapter.setVideoProvider(id, conference.getVideoProvider()); 2015 mAdapter.setVideoState(id, conference.getVideoState()); 2016 2017 // Go through any child calls and set the parent. 2018 for (Connection connection : conference.getConnections()) { 2019 String connectionId = mIdByConnection.get(connection); 2020 if (connectionId != null) { 2021 mAdapter.setIsConferenced(connectionId, id); 2022 } 2023 } 2024 onConferenceAdded(conference); 2025 } 2026 } 2027 2028 /** 2029 * Adds a connection created by the {@link ConnectionService} and informs telecom of the new 2030 * connection. 2031 * 2032 * @param phoneAccountHandle The phone account handle for the connection. 2033 * @param connection The connection to add. 2034 */ 2035 public final void addExistingConnection(PhoneAccountHandle phoneAccountHandle, 2036 Connection connection) { 2037 addExistingConnection(phoneAccountHandle, connection, null /* conference */); 2038 } 2039 2040 /** 2041 * Call to inform Telecom that your {@link ConnectionService} has released call resources (e.g 2042 * microphone, camera). 2043 * 2044 * @see ConnectionService#onConnectionServiceFocusLost() 2045 */ 2046 public final void connectionServiceFocusReleased() { 2047 mAdapter.onConnectionServiceFocusReleased(); 2048 } 2049 2050 /** 2051 * Adds a connection created by the {@link ConnectionService} and informs telecom of the new 2052 * connection. 2053 * 2054 * @param phoneAccountHandle The phone account handle for the connection. 2055 * @param connection The connection to add. 2056 * @param conference The parent conference of the new connection. 2057 * @hide 2058 */ 2059 public final void addExistingConnection(PhoneAccountHandle phoneAccountHandle, 2060 Connection connection, Conference conference) { 2061 2062 String id = addExistingConnectionInternal(phoneAccountHandle, connection); 2063 if (id != null) { 2064 List<String> emptyList = new ArrayList<>(0); 2065 String conferenceId = null; 2066 if (conference != null) { 2067 conferenceId = mIdByConference.get(conference); 2068 } 2069 2070 ParcelableConnection parcelableConnection = new ParcelableConnection( 2071 phoneAccountHandle, 2072 connection.getState(), 2073 connection.getConnectionCapabilities(), 2074 connection.getConnectionProperties(), 2075 connection.getSupportedAudioRoutes(), 2076 connection.getAddress(), 2077 connection.getAddressPresentation(), 2078 connection.getCallerDisplayName(), 2079 connection.getCallerDisplayNamePresentation(), 2080 connection.getVideoProvider() == null ? 2081 null : connection.getVideoProvider().getInterface(), 2082 connection.getVideoState(), 2083 connection.isRingbackRequested(), 2084 connection.getAudioModeIsVoip(), 2085 connection.getConnectTimeMillis(), 2086 connection.getConnectElapsedTimeMillis(), 2087 connection.getStatusHints(), 2088 connection.getDisconnectCause(), 2089 emptyList, 2090 connection.getExtras(), 2091 conferenceId); 2092 mAdapter.addExistingConnection(id, parcelableConnection); 2093 } 2094 } 2095 2096 /** 2097 * Returns all the active {@code Connection}s for which this {@code ConnectionService} 2098 * has taken responsibility. 2099 * 2100 * @return A collection of {@code Connection}s created by this {@code ConnectionService}. 2101 */ 2102 public final Collection<Connection> getAllConnections() { 2103 return mConnectionById.values(); 2104 } 2105 2106 /** 2107 * Returns all the active {@code Conference}s for which this {@code ConnectionService} 2108 * has taken responsibility. 2109 * 2110 * @return A collection of {@code Conference}s created by this {@code ConnectionService}. 2111 */ 2112 public final Collection<Conference> getAllConferences() { 2113 return mConferenceById.values(); 2114 } 2115 2116 /** 2117 * Create a {@code Connection} given an incoming request. This is used to attach to existing 2118 * incoming calls. 2119 * 2120 * @param connectionManagerPhoneAccount See description at 2121 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 2122 * @param request Details about the incoming call. 2123 * @return The {@code Connection} object to satisfy this call, or {@code null} to 2124 * not handle the call. 2125 */ 2126 public Connection onCreateIncomingConnection( 2127 PhoneAccountHandle connectionManagerPhoneAccount, 2128 ConnectionRequest request) { 2129 return null; 2130 } 2131 2132 /** 2133 * Called after the {@link Connection} returned by 2134 * {@link #onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)} 2135 * or {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)} has been 2136 * added to the {@link ConnectionService} and sent to Telecom. 2137 * 2138 * @param connection the {@link Connection}. 2139 * @hide 2140 */ 2141 public void onCreateConnectionComplete(Connection connection) { 2142 } 2143 2144 /** 2145 * Called by Telecom to inform the {@link ConnectionService} that its request to create a new 2146 * incoming {@link Connection} was denied. 2147 * <p> 2148 * Used when a self-managed {@link ConnectionService} attempts to create a new incoming 2149 * {@link Connection}, but Telecom has determined that the call cannot be allowed at this time. 2150 * The {@link ConnectionService} is responsible for silently rejecting the new incoming 2151 * {@link Connection}. 2152 * <p> 2153 * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information. 2154 * 2155 * @param connectionManagerPhoneAccount See description at 2156 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 2157 * @param request The incoming connection request. 2158 */ 2159 public void onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, 2160 ConnectionRequest request) { 2161 } 2162 2163 /** 2164 * Called by Telecom to inform the {@link ConnectionService} that its request to create a new 2165 * outgoing {@link Connection} was denied. 2166 * <p> 2167 * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing 2168 * {@link Connection}, but Telecom has determined that the call cannot be placed at this time. 2169 * The {@link ConnectionService} is responisible for informing the user that the 2170 * {@link Connection} cannot be made at this time. 2171 * <p> 2172 * See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information. 2173 * 2174 * @param connectionManagerPhoneAccount See description at 2175 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 2176 * @param request The outgoing connection request. 2177 */ 2178 public void onCreateOutgoingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, 2179 ConnectionRequest request) { 2180 } 2181 2182 /** 2183 * Trigger recalculate functinality for conference calls. This is used when a Telephony 2184 * Connection is part of a conference controller but is not yet added to Connection 2185 * Service and hence cannot be added to the conference call. 2186 * 2187 * @hide 2188 */ 2189 public void triggerConferenceRecalculate() { 2190 } 2191 2192 /** 2193 * Create a {@code Connection} given an outgoing request. This is used to initiate new 2194 * outgoing calls. 2195 * 2196 * @param connectionManagerPhoneAccount The connection manager account to use for managing 2197 * this call. 2198 * <p> 2199 * If this parameter is not {@code null}, it means that this {@code ConnectionService} 2200 * has registered one or more {@code PhoneAccount}s having 2201 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain 2202 * one of these {@code PhoneAccount}s, while the {@code request} will contain another 2203 * (usually but not always distinct) {@code PhoneAccount} to be used for actually 2204 * making the connection. 2205 * <p> 2206 * If this parameter is {@code null}, it means that this {@code ConnectionService} is 2207 * being asked to make a direct connection. The 2208 * {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be 2209 * a {@code PhoneAccount} registered by this {@code ConnectionService} to use for 2210 * making the connection. 2211 * @param request Details about the outgoing call. 2212 * @return The {@code Connection} object to satisfy this call, or the result of an invocation 2213 * of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call. 2214 */ 2215 public Connection onCreateOutgoingConnection( 2216 PhoneAccountHandle connectionManagerPhoneAccount, 2217 ConnectionRequest request) { 2218 return null; 2219 } 2220 2221 /** 2222 * Called by Telecom to request that a {@link ConnectionService} creates an instance of an 2223 * outgoing handover {@link Connection}. 2224 * <p> 2225 * A call handover is the process where an ongoing call is transferred from one app (i.e. 2226 * {@link ConnectionService} to another app. The user could, for example, choose to continue a 2227 * mobile network call in a video calling app. The mobile network call via the Telephony stack 2228 * is referred to as the source of the handover, and the video calling app is referred to as the 2229 * destination. 2230 * <p> 2231 * When considering a handover scenario the <em>initiating</em> device is where a user initiated 2232 * the handover process (e.g. by calling {@link android.telecom.Call#handoverTo( 2233 * PhoneAccountHandle, int, Bundle)}, and the other device is considered the <em>receiving</em> 2234 * device. 2235 * <p> 2236 * This method is called on the destination {@link ConnectionService} on <em>initiating</em> 2237 * device when the user initiates a handover request from one app to another. The user request 2238 * originates in the {@link InCallService} via 2239 * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}. 2240 * <p> 2241 * For a full discussion of the handover process and the APIs involved, see 2242 * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}. 2243 * <p> 2244 * Implementations of this method should return an instance of {@link Connection} which 2245 * represents the handover. If your app does not wish to accept a handover to it at this time, 2246 * you can return {@code null}. The code below shows an example of how this is done. 2247 * <pre> 2248 * {@code 2249 * public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle 2250 * fromPhoneAccountHandle, ConnectionRequest request) { 2251 * if (!isHandoverAvailable()) { 2252 * return null; 2253 * } 2254 * MyConnection connection = new MyConnection(); 2255 * connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED); 2256 * connection.setVideoState(request.getVideoState()); 2257 * return connection; 2258 * } 2259 * } 2260 * </pre> 2261 * 2262 * @param fromPhoneAccountHandle {@link PhoneAccountHandle} associated with the 2263 * ConnectionService which needs to handover the call. 2264 * @param request Details about the call to handover. 2265 * @return {@link Connection} instance corresponding to the handover call. 2266 */ 2267 public Connection onCreateOutgoingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, 2268 ConnectionRequest request) { 2269 return null; 2270 } 2271 2272 /** 2273 * Called by Telecom to request that a {@link ConnectionService} creates an instance of an 2274 * incoming handover {@link Connection}. 2275 * <p> 2276 * A call handover is the process where an ongoing call is transferred from one app (i.e. 2277 * {@link ConnectionService} to another app. The user could, for example, choose to continue a 2278 * mobile network call in a video calling app. The mobile network call via the Telephony stack 2279 * is referred to as the source of the handover, and the video calling app is referred to as the 2280 * destination. 2281 * <p> 2282 * When considering a handover scenario the <em>initiating</em> device is where a user initiated 2283 * the handover process (e.g. by calling {@link android.telecom.Call#handoverTo( 2284 * PhoneAccountHandle, int, Bundle)}, and the other device is considered the <em>receiving</em> 2285 * device. 2286 * <p> 2287 * This method is called on the destination app on the <em>receiving</em> device when the 2288 * destination app calls {@link TelecomManager#acceptHandover(Uri, int, PhoneAccountHandle)} to 2289 * accept an incoming handover from the <em>initiating</em> device. 2290 * <p> 2291 * For a full discussion of the handover process and the APIs involved, see 2292 * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}. 2293 * <p> 2294 * Implementations of this method should return an instance of {@link Connection} which 2295 * represents the handover. The code below shows an example of how this is done. 2296 * <pre> 2297 * {@code 2298 * public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle 2299 * fromPhoneAccountHandle, ConnectionRequest request) { 2300 * // Given that your app requested to accept the handover, you should not return null here. 2301 * MyConnection connection = new MyConnection(); 2302 * connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED); 2303 * connection.setVideoState(request.getVideoState()); 2304 * return connection; 2305 * } 2306 * } 2307 * </pre> 2308 * 2309 * @param fromPhoneAccountHandle {@link PhoneAccountHandle} associated with the 2310 * ConnectionService which needs to handover the call. 2311 * @param request Details about the call which needs to be handover. 2312 * @return {@link Connection} instance corresponding to the handover call. 2313 */ 2314 public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, 2315 ConnectionRequest request) { 2316 return null; 2317 } 2318 2319 /** 2320 * Called by Telecom in response to a {@code TelecomManager#acceptHandover()} 2321 * invocation which failed. 2322 * <p> 2323 * For a full discussion of the handover process and the APIs involved, see 2324 * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)} 2325 * 2326 * @param request Details about the call which failed to handover. 2327 * @param error Reason for handover failure. Will be one of the 2328 */ 2329 public void onHandoverFailed(ConnectionRequest request, 2330 @Call.Callback.HandoverFailureErrors int error) { 2331 return; 2332 } 2333 2334 /** 2335 * Create a {@code Connection} for a new unknown call. An unknown call is a call originating 2336 * from the ConnectionService that was neither a user-initiated outgoing call, nor an incoming 2337 * call created using 2338 * {@code TelecomManager#addNewIncomingCall(PhoneAccountHandle, android.os.Bundle)}. 2339 * 2340 * @hide 2341 */ 2342 public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount, 2343 ConnectionRequest request) { 2344 return null; 2345 } 2346 2347 /** 2348 * Conference two specified connections. Invoked when the user has made a request to merge the 2349 * specified connections into a conference call. In response, the connection service should 2350 * create an instance of {@link Conference} and pass it into {@link #addConference}. 2351 * 2352 * @param connection1 A connection to merge into a conference call. 2353 * @param connection2 A connection to merge into a conference call. 2354 */ 2355 public void onConference(Connection connection1, Connection connection2) {} 2356 2357 /** 2358 * Called when a connection is added. 2359 * @hide 2360 */ 2361 public void onConnectionAdded(Connection connection) {} 2362 2363 /** 2364 * Called when a connection is removed. 2365 * @hide 2366 */ 2367 public void onConnectionRemoved(Connection connection) {} 2368 2369 /** 2370 * Called when a conference is added. 2371 * @hide 2372 */ 2373 public void onConferenceAdded(Conference conference) {} 2374 2375 /** 2376 * Called when a conference is removed. 2377 * @hide 2378 */ 2379 public void onConferenceRemoved(Conference conference) {} 2380 2381 /** 2382 * Indicates that a remote conference has been created for existing {@link RemoteConnection}s. 2383 * When this method is invoked, this {@link ConnectionService} should create its own 2384 * representation of the conference call and send it to telecom using {@link #addConference}. 2385 * <p> 2386 * This is only relevant to {@link ConnectionService}s which are registered with 2387 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. 2388 * 2389 * @param conference The remote conference call. 2390 */ 2391 public void onRemoteConferenceAdded(RemoteConference conference) {} 2392 2393 /** 2394 * Called when an existing connection is added remotely. 2395 * @param connection The existing connection which was added. 2396 */ 2397 public void onRemoteExistingConnectionAdded(RemoteConnection connection) {} 2398 2399 /** 2400 * Called when the {@link ConnectionService} has lost the call focus. 2401 * The {@link ConnectionService} should release the call resources and invokes 2402 * {@link ConnectionService#connectionServiceFocusReleased()} to inform telecom that it has 2403 * released the call resources. 2404 */ 2405 public void onConnectionServiceFocusLost() {} 2406 2407 /** 2408 * Called when the {@link ConnectionService} has gained the call focus. The 2409 * {@link ConnectionService} can acquire the call resources at this time. 2410 */ 2411 public void onConnectionServiceFocusGained() {} 2412 2413 /** 2414 * @hide 2415 */ 2416 public boolean containsConference(Conference conference) { 2417 return mIdByConference.containsKey(conference); 2418 } 2419 2420 /** {@hide} */ 2421 void addRemoteConference(RemoteConference remoteConference) { 2422 onRemoteConferenceAdded(remoteConference); 2423 } 2424 2425 /** {@hide} */ 2426 void addRemoteExistingConnection(RemoteConnection remoteConnection) { 2427 onRemoteExistingConnectionAdded(remoteConnection); 2428 } 2429 2430 private void onAccountsInitialized() { 2431 mAreAccountsInitialized = true; 2432 for (Runnable r : mPreInitializationConnectionRequests) { 2433 r.run(); 2434 } 2435 mPreInitializationConnectionRequests.clear(); 2436 } 2437 2438 /** 2439 * Adds an existing connection to the list of connections, identified by a new call ID unique 2440 * to this connection service. 2441 * 2442 * @param connection The connection. 2443 * @return The ID of the connection (e.g. the call-id). 2444 */ 2445 private String addExistingConnectionInternal(PhoneAccountHandle handle, Connection connection) { 2446 String id; 2447 2448 if (connection.getExtras() != null && connection.getExtras() 2449 .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) { 2450 id = connection.getExtras().getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID); 2451 Log.d(this, "addExistingConnectionInternal - conn %s reusing original id %s", 2452 connection.getTelecomCallId(), id); 2453 } else if (handle == null) { 2454 // If no phone account handle was provided, we cannot be sure the call ID is unique, 2455 // so just use a random UUID. 2456 id = UUID.randomUUID().toString(); 2457 } else { 2458 // Phone account handle was provided, so use the ConnectionService class name as a 2459 // prefix for a unique incremental call ID. 2460 id = handle.getComponentName().getClassName() + "@" + getNextCallId(); 2461 } 2462 addConnection(handle, id, connection); 2463 return id; 2464 } 2465 2466 private void addConnection(PhoneAccountHandle handle, String callId, Connection connection) { 2467 connection.setTelecomCallId(callId); 2468 mConnectionById.put(callId, connection); 2469 mIdByConnection.put(connection, callId); 2470 connection.addConnectionListener(mConnectionListener); 2471 connection.setConnectionService(this); 2472 connection.setPhoneAccountHandle(handle); 2473 onConnectionAdded(connection); 2474 } 2475 2476 /** {@hide} */ 2477 protected void removeConnection(Connection connection) { 2478 connection.unsetConnectionService(this); 2479 connection.removeConnectionListener(mConnectionListener); 2480 String id = mIdByConnection.get(connection); 2481 if (id != null) { 2482 mConnectionById.remove(id); 2483 mIdByConnection.remove(connection); 2484 mAdapter.removeCall(id); 2485 onConnectionRemoved(connection); 2486 } 2487 } 2488 2489 private String addConferenceInternal(Conference conference) { 2490 String originalId = null; 2491 if (conference.getExtras() != null && conference.getExtras() 2492 .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) { 2493 originalId = conference.getExtras().getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID); 2494 Log.d(this, "addConferenceInternal: conf %s reusing original id %s", 2495 conference.getTelecomCallId(), 2496 originalId); 2497 } 2498 if (mIdByConference.containsKey(conference)) { 2499 Log.w(this, "Re-adding an existing conference: %s.", conference); 2500 } else if (conference != null) { 2501 // Conferences do not (yet) have a PhoneAccountHandle associated with them, so we 2502 // cannot determine a ConnectionService class name to associate with the ID, so use 2503 // a unique UUID (for now). 2504 String id = originalId == null ? UUID.randomUUID().toString() : originalId; 2505 mConferenceById.put(id, conference); 2506 mIdByConference.put(conference, id); 2507 conference.addListener(mConferenceListener); 2508 return id; 2509 } 2510 2511 return null; 2512 } 2513 2514 private void removeConference(Conference conference) { 2515 if (mIdByConference.containsKey(conference)) { 2516 conference.removeListener(mConferenceListener); 2517 2518 String id = mIdByConference.get(conference); 2519 mConferenceById.remove(id); 2520 mIdByConference.remove(conference); 2521 mAdapter.removeCall(id); 2522 2523 onConferenceRemoved(conference); 2524 } 2525 } 2526 2527 private Connection findConnectionForAction(String callId, String action) { 2528 if (callId != null && mConnectionById.containsKey(callId)) { 2529 return mConnectionById.get(callId); 2530 } 2531 Log.w(this, "%s - Cannot find Connection %s", action, callId); 2532 return getNullConnection(); 2533 } 2534 2535 static synchronized Connection getNullConnection() { 2536 if (sNullConnection == null) { 2537 sNullConnection = new Connection() {}; 2538 } 2539 return sNullConnection; 2540 } 2541 2542 private Conference findConferenceForAction(String conferenceId, String action) { 2543 if (mConferenceById.containsKey(conferenceId)) { 2544 return mConferenceById.get(conferenceId); 2545 } 2546 Log.w(this, "%s - Cannot find conference %s", action, conferenceId); 2547 return getNullConference(); 2548 } 2549 2550 private List<String> createConnectionIdList(List<Connection> connections) { 2551 List<String> ids = new ArrayList<>(); 2552 for (Connection c : connections) { 2553 if (mIdByConnection.containsKey(c)) { 2554 ids.add(mIdByConnection.get(c)); 2555 } 2556 } 2557 Collections.sort(ids); 2558 return ids; 2559 } 2560 2561 /** 2562 * Builds a list of {@link Connection} and {@link Conference} IDs based on the list of 2563 * {@link Conferenceable}s passed in. 2564 * 2565 * @param conferenceables The {@link Conferenceable} connections and conferences. 2566 * @return List of string conference and call Ids. 2567 */ 2568 private List<String> createIdList(List<Conferenceable> conferenceables) { 2569 List<String> ids = new ArrayList<>(); 2570 for (Conferenceable c : conferenceables) { 2571 // Only allow Connection and Conference conferenceables. 2572 if (c instanceof Connection) { 2573 Connection connection = (Connection) c; 2574 if (mIdByConnection.containsKey(connection)) { 2575 ids.add(mIdByConnection.get(connection)); 2576 } 2577 } else if (c instanceof Conference) { 2578 Conference conference = (Conference) c; 2579 if (mIdByConference.containsKey(conference)) { 2580 ids.add(mIdByConference.get(conference)); 2581 } 2582 } 2583 } 2584 Collections.sort(ids); 2585 return ids; 2586 } 2587 2588 private Conference getNullConference() { 2589 if (sNullConference == null) { 2590 sNullConference = new Conference(null) {}; 2591 } 2592 return sNullConference; 2593 } 2594 2595 private void endAllConnections() { 2596 // Unbound from telecomm. We should end all connections and conferences. 2597 for (Connection connection : mIdByConnection.keySet()) { 2598 // only operate on top-level calls. Conference calls will be removed on their own. 2599 if (connection.getConference() == null) { 2600 connection.onDisconnect(); 2601 } 2602 } 2603 for (Conference conference : mIdByConference.keySet()) { 2604 conference.onDisconnect(); 2605 } 2606 } 2607 2608 /** 2609 * Retrieves the next call ID as maintainted by the connection service. 2610 * 2611 * @return The call ID. 2612 */ 2613 private int getNextCallId() { 2614 synchronized (mIdSyncRoot) { 2615 return ++mId; 2616 } 2617 } 2618 } 2619