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