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