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