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