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