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-managed {@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 @Override 1259 public void onConferenceStateChanged(Conference c, boolean isConference) { 1260 String id = mIdByConference.get(c); 1261 if (id != null) { 1262 mAdapter.setConferenceState(id, isConference); 1263 } 1264 } 1265 1266 @Override 1267 public void onAddressChanged(Conference c, Uri newAddress, int presentation) { 1268 String id = mIdByConference.get(c); 1269 if (id != null) { 1270 mAdapter.setAddress(id, newAddress, presentation); 1271 } 1272 } 1273 1274 @Override 1275 public void onCallerDisplayNameChanged(Conference c, String callerDisplayName, 1276 int presentation) { 1277 String id = mIdByConference.get(c); 1278 if (id != null) { 1279 mAdapter.setCallerDisplayName(id, callerDisplayName, presentation); 1280 } 1281 } 1282 1283 @Override 1284 public void onConnectionEvent(Conference c, String event, Bundle extras) { 1285 String id = mIdByConference.get(c); 1286 if (id != null) { 1287 mAdapter.onConnectionEvent(id, event, extras); 1288 } 1289 } 1290 }; 1291 1292 private final Connection.Listener mConnectionListener = new Connection.Listener() { 1293 @Override 1294 public void onStateChanged(Connection c, int state) { 1295 String id = mIdByConnection.get(c); 1296 Log.d(this, "Adapter set state %s %s", id, Connection.stateToString(state)); 1297 switch (state) { 1298 case Connection.STATE_ACTIVE: 1299 mAdapter.setActive(id); 1300 break; 1301 case Connection.STATE_DIALING: 1302 mAdapter.setDialing(id); 1303 break; 1304 case Connection.STATE_PULLING_CALL: 1305 mAdapter.setPulling(id); 1306 break; 1307 case Connection.STATE_DISCONNECTED: 1308 // Handled in onDisconnected() 1309 break; 1310 case Connection.STATE_HOLDING: 1311 mAdapter.setOnHold(id); 1312 break; 1313 case Connection.STATE_NEW: 1314 // Nothing to tell Telecom 1315 break; 1316 case Connection.STATE_RINGING: 1317 mAdapter.setRinging(id); 1318 break; 1319 } 1320 } 1321 1322 @Override 1323 public void onDisconnected(Connection c, DisconnectCause disconnectCause) { 1324 String id = mIdByConnection.get(c); 1325 Log.d(this, "Adapter set disconnected %s", disconnectCause); 1326 mAdapter.setDisconnected(id, disconnectCause); 1327 } 1328 1329 @Override 1330 public void onVideoStateChanged(Connection c, int videoState) { 1331 String id = mIdByConnection.get(c); 1332 Log.d(this, "Adapter set video state %d", videoState); 1333 mAdapter.setVideoState(id, videoState); 1334 } 1335 1336 @Override 1337 public void onAddressChanged(Connection c, Uri address, int presentation) { 1338 String id = mIdByConnection.get(c); 1339 mAdapter.setAddress(id, address, presentation); 1340 } 1341 1342 @Override 1343 public void onCallerDisplayNameChanged( 1344 Connection c, String callerDisplayName, int presentation) { 1345 String id = mIdByConnection.get(c); 1346 mAdapter.setCallerDisplayName(id, callerDisplayName, presentation); 1347 } 1348 1349 @Override 1350 public void onDestroyed(Connection c) { 1351 removeConnection(c); 1352 } 1353 1354 @Override 1355 public void onPostDialWait(Connection c, String remaining) { 1356 String id = mIdByConnection.get(c); 1357 Log.d(this, "Adapter onPostDialWait %s, %s", c, remaining); 1358 mAdapter.onPostDialWait(id, remaining); 1359 } 1360 1361 @Override 1362 public void onPostDialChar(Connection c, char nextChar) { 1363 String id = mIdByConnection.get(c); 1364 Log.d(this, "Adapter onPostDialChar %s, %s", c, nextChar); 1365 mAdapter.onPostDialChar(id, nextChar); 1366 } 1367 1368 @Override 1369 public void onRingbackRequested(Connection c, boolean ringback) { 1370 String id = mIdByConnection.get(c); 1371 Log.d(this, "Adapter onRingback %b", ringback); 1372 mAdapter.setRingbackRequested(id, ringback); 1373 } 1374 1375 @Override 1376 public void onConnectionCapabilitiesChanged(Connection c, int capabilities) { 1377 String id = mIdByConnection.get(c); 1378 Log.d(this, "capabilities: parcelableconnection: %s", 1379 Connection.capabilitiesToString(capabilities)); 1380 mAdapter.setConnectionCapabilities(id, capabilities); 1381 } 1382 1383 @Override 1384 public void onConnectionPropertiesChanged(Connection c, int properties) { 1385 String id = mIdByConnection.get(c); 1386 Log.d(this, "properties: parcelableconnection: %s", 1387 Connection.propertiesToString(properties)); 1388 mAdapter.setConnectionProperties(id, properties); 1389 } 1390 1391 @Override 1392 public void onVideoProviderChanged(Connection c, Connection.VideoProvider videoProvider) { 1393 String id = mIdByConnection.get(c); 1394 Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c, 1395 videoProvider); 1396 mAdapter.setVideoProvider(id, videoProvider); 1397 } 1398 1399 @Override 1400 public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) { 1401 String id = mIdByConnection.get(c); 1402 mAdapter.setIsVoipAudioMode(id, isVoip); 1403 } 1404 1405 @Override 1406 public void onStatusHintsChanged(Connection c, StatusHints statusHints) { 1407 String id = mIdByConnection.get(c); 1408 mAdapter.setStatusHints(id, statusHints); 1409 } 1410 1411 @Override 1412 public void onConferenceablesChanged( 1413 Connection connection, List<Conferenceable> conferenceables) { 1414 mAdapter.setConferenceableConnections( 1415 mIdByConnection.get(connection), 1416 createIdList(conferenceables)); 1417 } 1418 1419 @Override 1420 public void onConferenceChanged(Connection connection, Conference conference) { 1421 String id = mIdByConnection.get(connection); 1422 if (id != null) { 1423 String conferenceId = null; 1424 if (conference != null) { 1425 conferenceId = mIdByConference.get(conference); 1426 } 1427 mAdapter.setIsConferenced(id, conferenceId); 1428 } 1429 } 1430 1431 @Override 1432 public void onConferenceMergeFailed(Connection connection) { 1433 String id = mIdByConnection.get(connection); 1434 if (id != null) { 1435 mAdapter.onConferenceMergeFailed(id); 1436 } 1437 } 1438 1439 @Override 1440 public void onExtrasChanged(Connection c, Bundle extras) { 1441 String id = mIdByConnection.get(c); 1442 if (id != null) { 1443 mAdapter.putExtras(id, extras); 1444 } 1445 } 1446 1447 @Override 1448 public void onExtrasRemoved(Connection c, List<String> keys) { 1449 String id = mIdByConnection.get(c); 1450 if (id != null) { 1451 mAdapter.removeExtras(id, keys); 1452 } 1453 } 1454 1455 @Override 1456 public void onConnectionEvent(Connection connection, String event, Bundle extras) { 1457 String id = mIdByConnection.get(connection); 1458 if (id != null) { 1459 mAdapter.onConnectionEvent(id, event, extras); 1460 } 1461 } 1462 1463 @Override 1464 public void onAudioRouteChanged(Connection c, int audioRoute, String bluetoothAddress) { 1465 String id = mIdByConnection.get(c); 1466 if (id != null) { 1467 mAdapter.setAudioRoute(id, audioRoute, bluetoothAddress); 1468 } 1469 } 1470 1471 @Override 1472 public void onRttInitiationSuccess(Connection c) { 1473 String id = mIdByConnection.get(c); 1474 if (id != null) { 1475 mAdapter.onRttInitiationSuccess(id); 1476 } 1477 } 1478 1479 @Override 1480 public void onRttInitiationFailure(Connection c, int reason) { 1481 String id = mIdByConnection.get(c); 1482 if (id != null) { 1483 mAdapter.onRttInitiationFailure(id, reason); 1484 } 1485 } 1486 1487 @Override 1488 public void onRttSessionRemotelyTerminated(Connection c) { 1489 String id = mIdByConnection.get(c); 1490 if (id != null) { 1491 mAdapter.onRttSessionRemotelyTerminated(id); 1492 } 1493 } 1494 1495 @Override 1496 public void onRemoteRttRequest(Connection c) { 1497 String id = mIdByConnection.get(c); 1498 if (id != null) { 1499 mAdapter.onRemoteRttRequest(id); 1500 } 1501 } 1502 1503 @Override 1504 public void onPhoneAccountChanged(Connection c, PhoneAccountHandle pHandle) { 1505 String id = mIdByConnection.get(c); 1506 if (id != null) { 1507 mAdapter.onPhoneAccountChanged(id, pHandle); 1508 } 1509 } 1510 1511 public void onConnectionTimeReset(Connection c) { 1512 String id = mIdByConnection.get(c); 1513 if (id != null) { 1514 mAdapter.resetConnectionTime(id); 1515 } 1516 } 1517 }; 1518 1519 /** {@inheritDoc} */ 1520 @Override onBind(Intent intent)1521 public final IBinder onBind(Intent intent) { 1522 return mBinder; 1523 } 1524 1525 /** {@inheritDoc} */ 1526 @Override onUnbind(Intent intent)1527 public boolean onUnbind(Intent intent) { 1528 endAllConnections(); 1529 return super.onUnbind(intent); 1530 } 1531 1532 /** 1533 * This can be used by telecom to either create a new outgoing call or attach to an existing 1534 * incoming call. In either case, telecom will cycle through a set of services and call 1535 * createConnection util a connection service cancels the process or completes it successfully. 1536 */ createConnection( final PhoneAccountHandle callManagerAccount, final String callId, final ConnectionRequest request, boolean isIncoming, boolean isUnknown)1537 private void createConnection( 1538 final PhoneAccountHandle callManagerAccount, 1539 final String callId, 1540 final ConnectionRequest request, 1541 boolean isIncoming, 1542 boolean isUnknown) { 1543 boolean isLegacyHandover = request.getExtras() != null && 1544 request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER, false); 1545 boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean( 1546 TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false); 1547 Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " + 1548 "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b", 1549 callManagerAccount, callId, request, isIncoming, isUnknown, isLegacyHandover, 1550 isHandover); 1551 1552 Connection connection = null; 1553 if (isHandover) { 1554 PhoneAccountHandle fromPhoneAccountHandle = request.getExtras() != null 1555 ? (PhoneAccountHandle) request.getExtras().getParcelable( 1556 TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT) : null; 1557 if (!isIncoming) { 1558 connection = onCreateOutgoingHandoverConnection(fromPhoneAccountHandle, request); 1559 } else { 1560 connection = onCreateIncomingHandoverConnection(fromPhoneAccountHandle, request); 1561 } 1562 } else { 1563 connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request) 1564 : isIncoming ? onCreateIncomingConnection(callManagerAccount, request) 1565 : onCreateOutgoingConnection(callManagerAccount, request); 1566 } 1567 Log.d(this, "createConnection, connection: %s", connection); 1568 if (connection == null) { 1569 Log.i(this, "createConnection, implementation returned null connection."); 1570 connection = Connection.createFailedConnection( 1571 new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION")); 1572 } 1573 1574 boolean isSelfManaged = 1575 (connection.getConnectionProperties() & Connection.PROPERTY_SELF_MANAGED) 1576 == Connection.PROPERTY_SELF_MANAGED; 1577 // Self-managed Connections should always use voip audio mode; we default here so that the 1578 // local state within the ConnectionService matches the default we assume in Telecom. 1579 if (isSelfManaged) { 1580 connection.setAudioModeIsVoip(true); 1581 } 1582 connection.setTelecomCallId(callId); 1583 if (connection.getState() != Connection.STATE_DISCONNECTED) { 1584 addConnection(request.getAccountHandle(), callId, connection); 1585 } 1586 1587 Uri address = connection.getAddress(); 1588 String number = address == null ? "null" : address.getSchemeSpecificPart(); 1589 Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s, properties: %s", 1590 Connection.toLogSafePhoneNumber(number), 1591 Connection.stateToString(connection.getState()), 1592 Connection.capabilitiesToString(connection.getConnectionCapabilities()), 1593 Connection.propertiesToString(connection.getConnectionProperties())); 1594 1595 Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId); 1596 mAdapter.handleCreateConnectionComplete( 1597 callId, 1598 request, 1599 new ParcelableConnection( 1600 request.getAccountHandle(), 1601 connection.getState(), 1602 connection.getConnectionCapabilities(), 1603 connection.getConnectionProperties(), 1604 connection.getSupportedAudioRoutes(), 1605 connection.getAddress(), 1606 connection.getAddressPresentation(), 1607 connection.getCallerDisplayName(), 1608 connection.getCallerDisplayNamePresentation(), 1609 connection.getVideoProvider() == null ? 1610 null : connection.getVideoProvider().getInterface(), 1611 connection.getVideoState(), 1612 connection.isRingbackRequested(), 1613 connection.getAudioModeIsVoip(), 1614 connection.getConnectTimeMillis(), 1615 connection.getConnectElapsedTimeMillis(), 1616 connection.getStatusHints(), 1617 connection.getDisconnectCause(), 1618 createIdList(connection.getConferenceables()), 1619 connection.getExtras())); 1620 1621 if (isIncoming && request.shouldShowIncomingCallUi() && isSelfManaged) { 1622 // Tell ConnectionService to show its incoming call UX. 1623 connection.onShowIncomingCallUi(); 1624 } 1625 if (isUnknown) { 1626 triggerConferenceRecalculate(); 1627 } 1628 } 1629 createConnectionFailed(final PhoneAccountHandle callManagerAccount, final String callId, final ConnectionRequest request, boolean isIncoming)1630 private void createConnectionFailed(final PhoneAccountHandle callManagerAccount, 1631 final String callId, final ConnectionRequest request, 1632 boolean isIncoming) { 1633 1634 Log.i(this, "createConnectionFailed %s", callId); 1635 if (isIncoming) { 1636 onCreateIncomingConnectionFailed(callManagerAccount, request); 1637 } else { 1638 onCreateOutgoingConnectionFailed(callManagerAccount, request); 1639 } 1640 } 1641 handoverFailed(final String callId, final ConnectionRequest request, int reason)1642 private void handoverFailed(final String callId, final ConnectionRequest request, 1643 int reason) { 1644 1645 Log.i(this, "handoverFailed %s", callId); 1646 onHandoverFailed(request, reason); 1647 } 1648 1649 /** 1650 * Called by Telecom when the creation of a new Connection has completed and it is now added 1651 * to Telecom. 1652 * @param callId The ID of the connection. 1653 */ notifyCreateConnectionComplete(final String callId)1654 private void notifyCreateConnectionComplete(final String callId) { 1655 Log.i(this, "notifyCreateConnectionComplete %s", callId); 1656 if (callId == null) { 1657 // This could happen if the connection fails quickly and is removed from the 1658 // ConnectionService before Telecom sends the create connection complete callback. 1659 Log.w(this, "notifyCreateConnectionComplete: callId is null."); 1660 return; 1661 } 1662 onCreateConnectionComplete(findConnectionForAction(callId, 1663 "notifyCreateConnectionComplete")); 1664 } 1665 abort(String callId)1666 private void abort(String callId) { 1667 Log.d(this, "abort %s", callId); 1668 findConnectionForAction(callId, "abort").onAbort(); 1669 } 1670 answerVideo(String callId, int videoState)1671 private void answerVideo(String callId, int videoState) { 1672 Log.d(this, "answerVideo %s", callId); 1673 findConnectionForAction(callId, "answer").onAnswer(videoState); 1674 } 1675 answer(String callId)1676 private void answer(String callId) { 1677 Log.d(this, "answer %s", callId); 1678 findConnectionForAction(callId, "answer").onAnswer(); 1679 } 1680 deflect(String callId, Uri address)1681 private void deflect(String callId, Uri address) { 1682 Log.d(this, "deflect %s", callId); 1683 findConnectionForAction(callId, "deflect").onDeflect(address); 1684 } 1685 reject(String callId)1686 private void reject(String callId) { 1687 Log.d(this, "reject %s", callId); 1688 findConnectionForAction(callId, "reject").onReject(); 1689 } 1690 reject(String callId, String rejectWithMessage)1691 private void reject(String callId, String rejectWithMessage) { 1692 Log.d(this, "reject %s with message", callId); 1693 findConnectionForAction(callId, "reject").onReject(rejectWithMessage); 1694 } 1695 silence(String callId)1696 private void silence(String callId) { 1697 Log.d(this, "silence %s", callId); 1698 findConnectionForAction(callId, "silence").onSilence(); 1699 } 1700 disconnect(String callId)1701 private void disconnect(String callId) { 1702 Log.d(this, "disconnect %s", callId); 1703 if (mConnectionById.containsKey(callId)) { 1704 findConnectionForAction(callId, "disconnect").onDisconnect(); 1705 } else { 1706 findConferenceForAction(callId, "disconnect").onDisconnect(); 1707 } 1708 } 1709 hold(String callId)1710 private void hold(String callId) { 1711 Log.d(this, "hold %s", callId); 1712 if (mConnectionById.containsKey(callId)) { 1713 findConnectionForAction(callId, "hold").onHold(); 1714 } else { 1715 findConferenceForAction(callId, "hold").onHold(); 1716 } 1717 } 1718 unhold(String callId)1719 private void unhold(String callId) { 1720 Log.d(this, "unhold %s", callId); 1721 if (mConnectionById.containsKey(callId)) { 1722 findConnectionForAction(callId, "unhold").onUnhold(); 1723 } else { 1724 findConferenceForAction(callId, "unhold").onUnhold(); 1725 } 1726 } 1727 onCallAudioStateChanged(String callId, CallAudioState callAudioState)1728 private void onCallAudioStateChanged(String callId, CallAudioState callAudioState) { 1729 Log.d(this, "onAudioStateChanged %s %s", callId, callAudioState); 1730 if (mConnectionById.containsKey(callId)) { 1731 findConnectionForAction(callId, "onCallAudioStateChanged").setCallAudioState( 1732 callAudioState); 1733 } else { 1734 findConferenceForAction(callId, "onCallAudioStateChanged").setCallAudioState( 1735 callAudioState); 1736 } 1737 } 1738 playDtmfTone(String callId, char digit)1739 private void playDtmfTone(String callId, char digit) { 1740 Log.d(this, "playDtmfTone %s %c", callId, digit); 1741 if (mConnectionById.containsKey(callId)) { 1742 findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit); 1743 } else { 1744 findConferenceForAction(callId, "playDtmfTone").onPlayDtmfTone(digit); 1745 } 1746 } 1747 stopDtmfTone(String callId)1748 private void stopDtmfTone(String callId) { 1749 Log.d(this, "stopDtmfTone %s", callId); 1750 if (mConnectionById.containsKey(callId)) { 1751 findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone(); 1752 } else { 1753 findConferenceForAction(callId, "stopDtmfTone").onStopDtmfTone(); 1754 } 1755 } 1756 conference(String callId1, String callId2)1757 private void conference(String callId1, String callId2) { 1758 Log.d(this, "conference %s, %s", callId1, callId2); 1759 1760 // Attempt to get second connection or conference. 1761 Connection connection2 = findConnectionForAction(callId2, "conference"); 1762 Conference conference2 = getNullConference(); 1763 if (connection2 == getNullConnection()) { 1764 conference2 = findConferenceForAction(callId2, "conference"); 1765 if (conference2 == getNullConference()) { 1766 Log.w(this, "Connection2 or Conference2 missing in conference request %s.", 1767 callId2); 1768 return; 1769 } 1770 } 1771 1772 // Attempt to get first connection or conference and perform merge. 1773 Connection connection1 = findConnectionForAction(callId1, "conference"); 1774 if (connection1 == getNullConnection()) { 1775 Conference conference1 = findConferenceForAction(callId1, "addConnection"); 1776 if (conference1 == getNullConference()) { 1777 Log.w(this, 1778 "Connection1 or Conference1 missing in conference request %s.", 1779 callId1); 1780 } else { 1781 // Call 1 is a conference. 1782 if (connection2 != getNullConnection()) { 1783 // Call 2 is a connection so merge via call 1 (conference). 1784 conference1.onMerge(connection2); 1785 } else { 1786 // Call 2 is ALSO a conference; this should never happen. 1787 Log.wtf(this, "There can only be one conference and an attempt was made to " + 1788 "merge two conferences."); 1789 return; 1790 } 1791 } 1792 } else { 1793 // Call 1 is a connection. 1794 if (conference2 != getNullConference()) { 1795 // Call 2 is a conference, so merge via call 2. 1796 conference2.onMerge(connection1); 1797 } else { 1798 // Call 2 is a connection, so merge together. 1799 onConference(connection1, connection2); 1800 } 1801 } 1802 } 1803 splitFromConference(String callId)1804 private void splitFromConference(String callId) { 1805 Log.d(this, "splitFromConference(%s)", callId); 1806 1807 Connection connection = findConnectionForAction(callId, "splitFromConference"); 1808 if (connection == getNullConnection()) { 1809 Log.w(this, "Connection missing in conference request %s.", callId); 1810 return; 1811 } 1812 1813 Conference conference = connection.getConference(); 1814 if (conference != null) { 1815 conference.onSeparate(connection); 1816 } 1817 } 1818 mergeConference(String callId)1819 private void mergeConference(String callId) { 1820 Log.d(this, "mergeConference(%s)", callId); 1821 Conference conference = findConferenceForAction(callId, "mergeConference"); 1822 if (conference != null) { 1823 conference.onMerge(); 1824 } 1825 } 1826 swapConference(String callId)1827 private void swapConference(String callId) { 1828 Log.d(this, "swapConference(%s)", callId); 1829 Conference conference = findConferenceForAction(callId, "swapConference"); 1830 if (conference != null) { 1831 conference.onSwap(); 1832 } 1833 } 1834 1835 /** 1836 * Notifies a {@link Connection} of a request to pull an external call. 1837 * 1838 * See {@link Call#pullExternalCall()}. 1839 * 1840 * @param callId The ID of the call to pull. 1841 */ pullExternalCall(String callId)1842 private void pullExternalCall(String callId) { 1843 Log.d(this, "pullExternalCall(%s)", callId); 1844 Connection connection = findConnectionForAction(callId, "pullExternalCall"); 1845 if (connection != null) { 1846 connection.onPullExternalCall(); 1847 } 1848 } 1849 1850 /** 1851 * Notifies a {@link Connection} of a call event. 1852 * 1853 * See {@link Call#sendCallEvent(String, Bundle)}. 1854 * 1855 * @param callId The ID of the call receiving the event. 1856 * @param event The event. 1857 * @param extras Extras associated with the event. 1858 */ sendCallEvent(String callId, String event, Bundle extras)1859 private void sendCallEvent(String callId, String event, Bundle extras) { 1860 Log.d(this, "sendCallEvent(%s, %s)", callId, event); 1861 Connection connection = findConnectionForAction(callId, "sendCallEvent"); 1862 if (connection != null) { 1863 connection.onCallEvent(event, extras); 1864 } 1865 } 1866 1867 /** 1868 * Notifies a {@link Connection} that a handover has completed. 1869 * 1870 * @param callId The ID of the call which completed handover. 1871 */ notifyHandoverComplete(String callId)1872 private void notifyHandoverComplete(String callId) { 1873 Log.d(this, "notifyHandoverComplete(%s)", callId); 1874 Connection connection = findConnectionForAction(callId, "notifyHandoverComplete"); 1875 if (connection != null) { 1876 connection.onHandoverComplete(); 1877 } 1878 } 1879 1880 /** 1881 * Notifies a {@link Connection} or {@link Conference} of a change to the extras from Telecom. 1882 * <p> 1883 * These extra changes can originate from Telecom itself, or from an {@link InCallService} via 1884 * the {@link android.telecom.Call#putExtra(String, boolean)}, 1885 * {@link android.telecom.Call#putExtra(String, int)}, 1886 * {@link android.telecom.Call#putExtra(String, String)}, 1887 * {@link Call#removeExtras(List)}. 1888 * 1889 * @param callId The ID of the call receiving the event. 1890 * @param extras The new extras bundle. 1891 */ handleExtrasChanged(String callId, Bundle extras)1892 private void handleExtrasChanged(String callId, Bundle extras) { 1893 Log.d(this, "handleExtrasChanged(%s, %s)", callId, extras); 1894 if (mConnectionById.containsKey(callId)) { 1895 findConnectionForAction(callId, "handleExtrasChanged").handleExtrasChanged(extras); 1896 } else if (mConferenceById.containsKey(callId)) { 1897 findConferenceForAction(callId, "handleExtrasChanged").handleExtrasChanged(extras); 1898 } 1899 } 1900 startRtt(String callId, Connection.RttTextStream rttTextStream)1901 private void startRtt(String callId, Connection.RttTextStream rttTextStream) { 1902 Log.d(this, "startRtt(%s)", callId); 1903 if (mConnectionById.containsKey(callId)) { 1904 findConnectionForAction(callId, "startRtt").onStartRtt(rttTextStream); 1905 } else if (mConferenceById.containsKey(callId)) { 1906 Log.w(this, "startRtt called on a conference."); 1907 } 1908 } 1909 stopRtt(String callId)1910 private void stopRtt(String callId) { 1911 Log.d(this, "stopRtt(%s)", callId); 1912 if (mConnectionById.containsKey(callId)) { 1913 findConnectionForAction(callId, "stopRtt").onStopRtt(); 1914 } else if (mConferenceById.containsKey(callId)) { 1915 Log.w(this, "stopRtt called on a conference."); 1916 } 1917 } 1918 handleRttUpgradeResponse(String callId, Connection.RttTextStream rttTextStream)1919 private void handleRttUpgradeResponse(String callId, Connection.RttTextStream rttTextStream) { 1920 Log.d(this, "handleRttUpgradeResponse(%s, %s)", callId, rttTextStream == null); 1921 if (mConnectionById.containsKey(callId)) { 1922 findConnectionForAction(callId, "handleRttUpgradeResponse") 1923 .handleRttUpgradeResponse(rttTextStream); 1924 } else if (mConferenceById.containsKey(callId)) { 1925 Log.w(this, "handleRttUpgradeResponse called on a conference."); 1926 } 1927 } 1928 onPostDialContinue(String callId, boolean proceed)1929 private void onPostDialContinue(String callId, boolean proceed) { 1930 Log.d(this, "onPostDialContinue(%s)", callId); 1931 findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed); 1932 } 1933 onAdapterAttached()1934 private void onAdapterAttached() { 1935 if (mAreAccountsInitialized) { 1936 // No need to query again if we already did it. 1937 return; 1938 } 1939 1940 String callingPackage = getOpPackageName(); 1941 1942 mAdapter.queryRemoteConnectionServices(new RemoteServiceCallback.Stub() { 1943 @Override 1944 public void onResult( 1945 final List<ComponentName> componentNames, 1946 final List<IBinder> services) { 1947 mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oR", null /*lock*/) { 1948 @Override 1949 public void loggedRun() { 1950 for (int i = 0; i < componentNames.size() && i < services.size(); i++) { 1951 mRemoteConnectionManager.addConnectionService( 1952 componentNames.get(i), 1953 IConnectionService.Stub.asInterface(services.get(i))); 1954 } 1955 onAccountsInitialized(); 1956 Log.d(this, "remote connection services found: " + services); 1957 } 1958 }.prepare()); 1959 } 1960 1961 @Override 1962 public void onError() { 1963 mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oE", null /*lock*/) { 1964 @Override 1965 public void loggedRun() { 1966 mAreAccountsInitialized = true; 1967 } 1968 }.prepare()); 1969 } 1970 }, callingPackage); 1971 } 1972 1973 /** 1974 * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an 1975 * incoming request. This is used by {@code ConnectionService}s that are registered with 1976 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to manage 1977 * SIM-based incoming calls. 1978 * 1979 * @param connectionManagerPhoneAccount See description at 1980 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 1981 * @param request Details about the incoming call. 1982 * @return The {@code Connection} object to satisfy this call, or {@code null} to 1983 * not handle the call. 1984 */ createRemoteIncomingConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)1985 public final RemoteConnection createRemoteIncomingConnection( 1986 PhoneAccountHandle connectionManagerPhoneAccount, 1987 ConnectionRequest request) { 1988 return mRemoteConnectionManager.createRemoteConnection( 1989 connectionManagerPhoneAccount, request, true); 1990 } 1991 1992 /** 1993 * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an 1994 * outgoing request. This is used by {@code ConnectionService}s that are registered with 1995 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to use the 1996 * SIM-based {@code ConnectionService} to place its outgoing calls. 1997 * 1998 * @param connectionManagerPhoneAccount See description at 1999 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 2000 * @param request Details about the outgoing call. 2001 * @return The {@code Connection} object to satisfy this call, or {@code null} to 2002 * not handle the call. 2003 */ createRemoteOutgoingConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)2004 public final RemoteConnection createRemoteOutgoingConnection( 2005 PhoneAccountHandle connectionManagerPhoneAccount, 2006 ConnectionRequest request) { 2007 return mRemoteConnectionManager.createRemoteConnection( 2008 connectionManagerPhoneAccount, request, false); 2009 } 2010 2011 /** 2012 * Indicates to the relevant {@code RemoteConnectionService} that the specified 2013 * {@link RemoteConnection}s should be merged into a conference call. 2014 * <p> 2015 * If the conference request is successful, the method {@link #onRemoteConferenceAdded} will 2016 * be invoked. 2017 * 2018 * @param remoteConnection1 The first of the remote connections to conference. 2019 * @param remoteConnection2 The second of the remote connections to conference. 2020 */ conferenceRemoteConnections( RemoteConnection remoteConnection1, RemoteConnection remoteConnection2)2021 public final void conferenceRemoteConnections( 2022 RemoteConnection remoteConnection1, 2023 RemoteConnection remoteConnection2) { 2024 mRemoteConnectionManager.conferenceRemoteConnections(remoteConnection1, remoteConnection2); 2025 } 2026 2027 /** 2028 * Adds a new conference call. When a conference call is created either as a result of an 2029 * explicit request via {@link #onConference} or otherwise, the connection service should supply 2030 * an instance of {@link Conference} by invoking this method. A conference call provided by this 2031 * method will persist until {@link Conference#destroy} is invoked on the conference instance. 2032 * 2033 * @param conference The new conference object. 2034 */ addConference(Conference conference)2035 public final void addConference(Conference conference) { 2036 Log.d(this, "addConference: conference=%s", conference); 2037 2038 String id = addConferenceInternal(conference); 2039 if (id != null) { 2040 List<String> connectionIds = new ArrayList<>(2); 2041 for (Connection connection : conference.getConnections()) { 2042 if (mIdByConnection.containsKey(connection)) { 2043 connectionIds.add(mIdByConnection.get(connection)); 2044 } 2045 } 2046 conference.setTelecomCallId(id); 2047 ParcelableConference parcelableConference = new ParcelableConference( 2048 conference.getPhoneAccountHandle(), 2049 conference.getState(), 2050 conference.getConnectionCapabilities(), 2051 conference.getConnectionProperties(), 2052 connectionIds, 2053 conference.getVideoProvider() == null ? 2054 null : conference.getVideoProvider().getInterface(), 2055 conference.getVideoState(), 2056 conference.getConnectTimeMillis(), 2057 conference.getConnectionStartElapsedRealTime(), 2058 conference.getStatusHints(), 2059 conference.getExtras(), 2060 conference.getAddress(), 2061 conference.getAddressPresentation(), 2062 conference.getCallerDisplayName(), 2063 conference.getCallerDisplayNamePresentation()); 2064 2065 mAdapter.addConferenceCall(id, parcelableConference); 2066 mAdapter.setVideoProvider(id, conference.getVideoProvider()); 2067 mAdapter.setVideoState(id, conference.getVideoState()); 2068 2069 // Go through any child calls and set the parent. 2070 for (Connection connection : conference.getConnections()) { 2071 String connectionId = mIdByConnection.get(connection); 2072 if (connectionId != null) { 2073 mAdapter.setIsConferenced(connectionId, id); 2074 } 2075 } 2076 onConferenceAdded(conference); 2077 } 2078 } 2079 2080 /** 2081 * Adds a connection created by the {@link ConnectionService} and informs telecom of the new 2082 * connection. 2083 * 2084 * @param phoneAccountHandle The phone account handle for the connection. 2085 * @param connection The connection to add. 2086 */ addExistingConnection(PhoneAccountHandle phoneAccountHandle, Connection connection)2087 public final void addExistingConnection(PhoneAccountHandle phoneAccountHandle, 2088 Connection connection) { 2089 addExistingConnection(phoneAccountHandle, connection, null /* conference */); 2090 } 2091 2092 /** 2093 * Call to inform Telecom that your {@link ConnectionService} has released call resources (e.g 2094 * microphone, camera). 2095 * 2096 * <p> 2097 * The {@link ConnectionService} will be disconnected when it failed to call this method within 2098 * 5 seconds after {@link #onConnectionServiceFocusLost()} is called. 2099 * 2100 * @see ConnectionService#onConnectionServiceFocusLost() 2101 */ connectionServiceFocusReleased()2102 public final void connectionServiceFocusReleased() { 2103 mAdapter.onConnectionServiceFocusReleased(); 2104 } 2105 2106 /** 2107 * Adds a connection created by the {@link ConnectionService} and informs telecom of the new 2108 * connection. 2109 * 2110 * @param phoneAccountHandle The phone account handle for the connection. 2111 * @param connection The connection to add. 2112 * @param conference The parent conference of the new connection. 2113 * @hide 2114 */ addExistingConnection(PhoneAccountHandle phoneAccountHandle, Connection connection, Conference conference)2115 public final void addExistingConnection(PhoneAccountHandle phoneAccountHandle, 2116 Connection connection, Conference conference) { 2117 2118 String id = addExistingConnectionInternal(phoneAccountHandle, connection); 2119 if (id != null) { 2120 List<String> emptyList = new ArrayList<>(0); 2121 String conferenceId = null; 2122 if (conference != null) { 2123 conferenceId = mIdByConference.get(conference); 2124 } 2125 2126 ParcelableConnection parcelableConnection = new ParcelableConnection( 2127 phoneAccountHandle, 2128 connection.getState(), 2129 connection.getConnectionCapabilities(), 2130 connection.getConnectionProperties(), 2131 connection.getSupportedAudioRoutes(), 2132 connection.getAddress(), 2133 connection.getAddressPresentation(), 2134 connection.getCallerDisplayName(), 2135 connection.getCallerDisplayNamePresentation(), 2136 connection.getVideoProvider() == null ? 2137 null : connection.getVideoProvider().getInterface(), 2138 connection.getVideoState(), 2139 connection.isRingbackRequested(), 2140 connection.getAudioModeIsVoip(), 2141 connection.getConnectTimeMillis(), 2142 connection.getConnectElapsedTimeMillis(), 2143 connection.getStatusHints(), 2144 connection.getDisconnectCause(), 2145 emptyList, 2146 connection.getExtras(), 2147 conferenceId, 2148 connection.getCallDirection()); 2149 mAdapter.addExistingConnection(id, parcelableConnection); 2150 } 2151 } 2152 2153 /** 2154 * Returns all the active {@code Connection}s for which this {@code ConnectionService} 2155 * has taken responsibility. 2156 * 2157 * @return A collection of {@code Connection}s created by this {@code ConnectionService}. 2158 */ getAllConnections()2159 public final Collection<Connection> getAllConnections() { 2160 return mConnectionById.values(); 2161 } 2162 2163 /** 2164 * Returns all the active {@code Conference}s for which this {@code ConnectionService} 2165 * has taken responsibility. 2166 * 2167 * @return A collection of {@code Conference}s created by this {@code ConnectionService}. 2168 */ getAllConferences()2169 public final Collection<Conference> getAllConferences() { 2170 return mConferenceById.values(); 2171 } 2172 2173 /** 2174 * Create a {@code Connection} given an incoming request. This is used to attach to existing 2175 * incoming calls. 2176 * 2177 * @param connectionManagerPhoneAccount See description at 2178 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 2179 * @param request Details about the incoming call. 2180 * @return The {@code Connection} object to satisfy this call, or {@code null} to 2181 * not handle the call. 2182 */ onCreateIncomingConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)2183 public Connection onCreateIncomingConnection( 2184 PhoneAccountHandle connectionManagerPhoneAccount, 2185 ConnectionRequest request) { 2186 return null; 2187 } 2188 2189 /** 2190 * Called after the {@link Connection} returned by 2191 * {@link #onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)} 2192 * or {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)} has been 2193 * added to the {@link ConnectionService} and sent to Telecom. 2194 * 2195 * @param connection the {@link Connection}. 2196 * @hide 2197 */ onCreateConnectionComplete(Connection connection)2198 public void onCreateConnectionComplete(Connection connection) { 2199 } 2200 2201 /** 2202 * Called by Telecom to inform the {@link ConnectionService} that its request to create a new 2203 * incoming {@link Connection} was denied. 2204 * <p> 2205 * Used when a self-managed {@link ConnectionService} attempts to create a new incoming 2206 * {@link Connection}, but Telecom has determined that the call cannot be allowed at this time. 2207 * The {@link ConnectionService} is responsible for silently rejecting the new incoming 2208 * {@link Connection}. 2209 * <p> 2210 * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information. 2211 * 2212 * @param connectionManagerPhoneAccount See description at 2213 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 2214 * @param request The incoming connection request. 2215 */ onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)2216 public void onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, 2217 ConnectionRequest request) { 2218 } 2219 2220 /** 2221 * Called by Telecom to inform the {@link ConnectionService} that its request to create a new 2222 * outgoing {@link Connection} was denied. 2223 * <p> 2224 * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing 2225 * {@link Connection}, but Telecom has determined that the call cannot be placed at this time. 2226 * The {@link ConnectionService} is responisible for informing the user that the 2227 * {@link Connection} cannot be made at this time. 2228 * <p> 2229 * See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information. 2230 * 2231 * @param connectionManagerPhoneAccount See description at 2232 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 2233 * @param request The outgoing connection request. 2234 */ onCreateOutgoingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)2235 public void onCreateOutgoingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, 2236 ConnectionRequest request) { 2237 } 2238 2239 /** 2240 * Trigger recalculate functinality for conference calls. This is used when a Telephony 2241 * Connection is part of a conference controller but is not yet added to Connection 2242 * Service and hence cannot be added to the conference call. 2243 * 2244 * @hide 2245 */ triggerConferenceRecalculate()2246 public void triggerConferenceRecalculate() { 2247 } 2248 2249 /** 2250 * Create a {@code Connection} given an outgoing request. This is used to initiate new 2251 * outgoing calls. 2252 * 2253 * @param connectionManagerPhoneAccount The connection manager account to use for managing 2254 * this call. 2255 * <p> 2256 * If this parameter is not {@code null}, it means that this {@code ConnectionService} 2257 * has registered one or more {@code PhoneAccount}s having 2258 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain 2259 * one of these {@code PhoneAccount}s, while the {@code request} will contain another 2260 * (usually but not always distinct) {@code PhoneAccount} to be used for actually 2261 * making the connection. 2262 * <p> 2263 * If this parameter is {@code null}, it means that this {@code ConnectionService} is 2264 * being asked to make a direct connection. The 2265 * {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be 2266 * a {@code PhoneAccount} registered by this {@code ConnectionService} to use for 2267 * making the connection. 2268 * @param request Details about the outgoing call. 2269 * @return The {@code Connection} object to satisfy this call, or the result of an invocation 2270 * of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call. 2271 */ onCreateOutgoingConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)2272 public Connection onCreateOutgoingConnection( 2273 PhoneAccountHandle connectionManagerPhoneAccount, 2274 ConnectionRequest request) { 2275 return null; 2276 } 2277 2278 /** 2279 * Called by Telecom to request that a {@link ConnectionService} creates an instance of an 2280 * outgoing handover {@link Connection}. 2281 * <p> 2282 * A call handover is the process where an ongoing call is transferred from one app (i.e. 2283 * {@link ConnectionService} to another app. The user could, for example, choose to continue a 2284 * mobile network call in a video calling app. The mobile network call via the Telephony stack 2285 * is referred to as the source of the handover, and the video calling app is referred to as the 2286 * destination. 2287 * <p> 2288 * When considering a handover scenario the <em>initiating</em> device is where a user initiated 2289 * the handover process (e.g. by calling {@link android.telecom.Call#handoverTo( 2290 * PhoneAccountHandle, int, Bundle)}, and the other device is considered the <em>receiving</em> 2291 * device. 2292 * <p> 2293 * This method is called on the destination {@link ConnectionService} on <em>initiating</em> 2294 * device when the user initiates a handover request from one app to another. The user request 2295 * originates in the {@link InCallService} via 2296 * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}. 2297 * <p> 2298 * For a full discussion of the handover process and the APIs involved, see 2299 * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}. 2300 * <p> 2301 * Implementations of this method should return an instance of {@link Connection} which 2302 * represents the handover. If your app does not wish to accept a handover to it at this time, 2303 * you can return {@code null}. The code below shows an example of how this is done. 2304 * <pre> 2305 * {@code 2306 * public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle 2307 * fromPhoneAccountHandle, ConnectionRequest request) { 2308 * if (!isHandoverAvailable()) { 2309 * return null; 2310 * } 2311 * MyConnection connection = new MyConnection(); 2312 * connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED); 2313 * connection.setVideoState(request.getVideoState()); 2314 * return connection; 2315 * } 2316 * } 2317 * </pre> 2318 * 2319 * @param fromPhoneAccountHandle {@link PhoneAccountHandle} associated with the 2320 * ConnectionService which needs to handover the call. 2321 * @param request Details about the call to handover. 2322 * @return {@link Connection} instance corresponding to the handover call. 2323 */ onCreateOutgoingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, ConnectionRequest request)2324 public Connection onCreateOutgoingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, 2325 ConnectionRequest request) { 2326 return null; 2327 } 2328 2329 /** 2330 * Called by Telecom to request that a {@link ConnectionService} creates an instance of an 2331 * incoming handover {@link Connection}. 2332 * <p> 2333 * A call handover is the process where an ongoing call is transferred from one app (i.e. 2334 * {@link ConnectionService} to another app. The user could, for example, choose to continue a 2335 * mobile network call in a video calling app. The mobile network call via the Telephony stack 2336 * is referred to as the source of the handover, and the video calling app is referred to as the 2337 * destination. 2338 * <p> 2339 * When considering a handover scenario the <em>initiating</em> device is where a user initiated 2340 * the handover process (e.g. by calling {@link android.telecom.Call#handoverTo( 2341 * PhoneAccountHandle, int, Bundle)}, and the other device is considered the <em>receiving</em> 2342 * device. 2343 * <p> 2344 * This method is called on the destination app on the <em>receiving</em> device when the 2345 * destination app calls {@link TelecomManager#acceptHandover(Uri, int, PhoneAccountHandle)} to 2346 * accept an incoming handover from the <em>initiating</em> device. 2347 * <p> 2348 * For a full discussion of the handover process and the APIs involved, see 2349 * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}. 2350 * <p> 2351 * Implementations of this method should return an instance of {@link Connection} which 2352 * represents the handover. The code below shows an example of how this is done. 2353 * <pre> 2354 * {@code 2355 * public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle 2356 * fromPhoneAccountHandle, ConnectionRequest request) { 2357 * // Given that your app requested to accept the handover, you should not return null here. 2358 * MyConnection connection = new MyConnection(); 2359 * connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED); 2360 * connection.setVideoState(request.getVideoState()); 2361 * return connection; 2362 * } 2363 * } 2364 * </pre> 2365 * 2366 * @param fromPhoneAccountHandle {@link PhoneAccountHandle} associated with the 2367 * ConnectionService which needs to handover the call. 2368 * @param request Details about the call which needs to be handover. 2369 * @return {@link Connection} instance corresponding to the handover call. 2370 */ onCreateIncomingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, ConnectionRequest request)2371 public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, 2372 ConnectionRequest request) { 2373 return null; 2374 } 2375 2376 /** 2377 * Called by Telecom in response to a {@code TelecomManager#acceptHandover()} 2378 * invocation which failed. 2379 * <p> 2380 * For a full discussion of the handover process and the APIs involved, see 2381 * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)} 2382 * 2383 * @param request Details about the call which failed to handover. 2384 * @param error Reason for handover failure. Will be one of the 2385 */ onHandoverFailed(ConnectionRequest request, @Call.Callback.HandoverFailureErrors int error)2386 public void onHandoverFailed(ConnectionRequest request, 2387 @Call.Callback.HandoverFailureErrors int error) { 2388 return; 2389 } 2390 2391 /** 2392 * Create a {@code Connection} for a new unknown call. An unknown call is a call originating 2393 * from the ConnectionService that was neither a user-initiated outgoing call, nor an incoming 2394 * call created using 2395 * {@code TelecomManager#addNewIncomingCall(PhoneAccountHandle, android.os.Bundle)}. 2396 * 2397 * @hide 2398 */ onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)2399 public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount, 2400 ConnectionRequest request) { 2401 return null; 2402 } 2403 2404 /** 2405 * Conference two specified connections. Invoked when the user has made a request to merge the 2406 * specified connections into a conference call. In response, the connection service should 2407 * create an instance of {@link Conference} and pass it into {@link #addConference}. 2408 * 2409 * @param connection1 A connection to merge into a conference call. 2410 * @param connection2 A connection to merge into a conference call. 2411 */ onConference(Connection connection1, Connection connection2)2412 public void onConference(Connection connection1, Connection connection2) {} 2413 2414 /** 2415 * Called when a connection is added. 2416 * @hide 2417 */ onConnectionAdded(Connection connection)2418 public void onConnectionAdded(Connection connection) {} 2419 2420 /** 2421 * Called when a connection is removed. 2422 * @hide 2423 */ onConnectionRemoved(Connection connection)2424 public void onConnectionRemoved(Connection connection) {} 2425 2426 /** 2427 * Called when a conference is added. 2428 * @hide 2429 */ onConferenceAdded(Conference conference)2430 public void onConferenceAdded(Conference conference) {} 2431 2432 /** 2433 * Called when a conference is removed. 2434 * @hide 2435 */ onConferenceRemoved(Conference conference)2436 public void onConferenceRemoved(Conference conference) {} 2437 2438 /** 2439 * Indicates that a remote conference has been created for existing {@link RemoteConnection}s. 2440 * When this method is invoked, this {@link ConnectionService} should create its own 2441 * representation of the conference call and send it to telecom using {@link #addConference}. 2442 * <p> 2443 * This is only relevant to {@link ConnectionService}s which are registered with 2444 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. 2445 * 2446 * @param conference The remote conference call. 2447 */ onRemoteConferenceAdded(RemoteConference conference)2448 public void onRemoteConferenceAdded(RemoteConference conference) {} 2449 2450 /** 2451 * Called when an existing connection is added remotely. 2452 * @param connection The existing connection which was added. 2453 */ onRemoteExistingConnectionAdded(RemoteConnection connection)2454 public void onRemoteExistingConnectionAdded(RemoteConnection connection) {} 2455 2456 /** 2457 * Called when the {@link ConnectionService} has lost the call focus. 2458 * The {@link ConnectionService} should release the call resources and invokes 2459 * {@link ConnectionService#connectionServiceFocusReleased()} to inform telecom that it has 2460 * released the call resources. 2461 */ onConnectionServiceFocusLost()2462 public void onConnectionServiceFocusLost() {} 2463 2464 /** 2465 * Called when the {@link ConnectionService} has gained the call focus. The 2466 * {@link ConnectionService} can acquire the call resources at this time. 2467 */ onConnectionServiceFocusGained()2468 public void onConnectionServiceFocusGained() {} 2469 2470 /** 2471 * @hide 2472 */ containsConference(Conference conference)2473 public boolean containsConference(Conference conference) { 2474 return mIdByConference.containsKey(conference); 2475 } 2476 2477 /** {@hide} */ addRemoteConference(RemoteConference remoteConference)2478 void addRemoteConference(RemoteConference remoteConference) { 2479 onRemoteConferenceAdded(remoteConference); 2480 } 2481 2482 /** {@hide} */ addRemoteExistingConnection(RemoteConnection remoteConnection)2483 void addRemoteExistingConnection(RemoteConnection remoteConnection) { 2484 onRemoteExistingConnectionAdded(remoteConnection); 2485 } 2486 onAccountsInitialized()2487 private void onAccountsInitialized() { 2488 mAreAccountsInitialized = true; 2489 for (Runnable r : mPreInitializationConnectionRequests) { 2490 r.run(); 2491 } 2492 mPreInitializationConnectionRequests.clear(); 2493 } 2494 2495 /** 2496 * Adds an existing connection to the list of connections, identified by a new call ID unique 2497 * to this connection service. 2498 * 2499 * @param connection The connection. 2500 * @return The ID of the connection (e.g. the call-id). 2501 */ addExistingConnectionInternal(PhoneAccountHandle handle, Connection connection)2502 private String addExistingConnectionInternal(PhoneAccountHandle handle, Connection connection) { 2503 String id; 2504 2505 if (connection.getExtras() != null && connection.getExtras() 2506 .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) { 2507 id = connection.getExtras().getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID); 2508 Log.d(this, "addExistingConnectionInternal - conn %s reusing original id %s", 2509 connection.getTelecomCallId(), id); 2510 } else if (handle == null) { 2511 // If no phone account handle was provided, we cannot be sure the call ID is unique, 2512 // so just use a random UUID. 2513 id = UUID.randomUUID().toString(); 2514 } else { 2515 // Phone account handle was provided, so use the ConnectionService class name as a 2516 // prefix for a unique incremental call ID. 2517 id = handle.getComponentName().getClassName() + "@" + getNextCallId(); 2518 } 2519 addConnection(handle, id, connection); 2520 return id; 2521 } 2522 addConnection(PhoneAccountHandle handle, String callId, Connection connection)2523 private void addConnection(PhoneAccountHandle handle, String callId, Connection connection) { 2524 connection.setTelecomCallId(callId); 2525 mConnectionById.put(callId, connection); 2526 mIdByConnection.put(connection, callId); 2527 connection.addConnectionListener(mConnectionListener); 2528 connection.setConnectionService(this); 2529 connection.setPhoneAccountHandle(handle); 2530 onConnectionAdded(connection); 2531 } 2532 2533 /** {@hide} */ removeConnection(Connection connection)2534 protected void removeConnection(Connection connection) { 2535 connection.unsetConnectionService(this); 2536 connection.removeConnectionListener(mConnectionListener); 2537 String id = mIdByConnection.get(connection); 2538 if (id != null) { 2539 mConnectionById.remove(id); 2540 mIdByConnection.remove(connection); 2541 mAdapter.removeCall(id); 2542 onConnectionRemoved(connection); 2543 } 2544 } 2545 addConferenceInternal(Conference conference)2546 private String addConferenceInternal(Conference conference) { 2547 String originalId = null; 2548 if (conference.getExtras() != null && conference.getExtras() 2549 .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) { 2550 originalId = conference.getExtras().getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID); 2551 Log.d(this, "addConferenceInternal: conf %s reusing original id %s", 2552 conference.getTelecomCallId(), 2553 originalId); 2554 } 2555 if (mIdByConference.containsKey(conference)) { 2556 Log.w(this, "Re-adding an existing conference: %s.", conference); 2557 } else if (conference != null) { 2558 // Conferences do not (yet) have a PhoneAccountHandle associated with them, so we 2559 // cannot determine a ConnectionService class name to associate with the ID, so use 2560 // a unique UUID (for now). 2561 String id = originalId == null ? UUID.randomUUID().toString() : originalId; 2562 mConferenceById.put(id, conference); 2563 mIdByConference.put(conference, id); 2564 conference.addListener(mConferenceListener); 2565 return id; 2566 } 2567 2568 return null; 2569 } 2570 removeConference(Conference conference)2571 private void removeConference(Conference conference) { 2572 if (mIdByConference.containsKey(conference)) { 2573 conference.removeListener(mConferenceListener); 2574 2575 String id = mIdByConference.get(conference); 2576 mConferenceById.remove(id); 2577 mIdByConference.remove(conference); 2578 mAdapter.removeCall(id); 2579 2580 onConferenceRemoved(conference); 2581 } 2582 } 2583 findConnectionForAction(String callId, String action)2584 private Connection findConnectionForAction(String callId, String action) { 2585 if (callId != null && mConnectionById.containsKey(callId)) { 2586 return mConnectionById.get(callId); 2587 } 2588 Log.w(this, "%s - Cannot find Connection %s", action, callId); 2589 return getNullConnection(); 2590 } 2591 getNullConnection()2592 static synchronized Connection getNullConnection() { 2593 if (sNullConnection == null) { 2594 sNullConnection = new Connection() {}; 2595 } 2596 return sNullConnection; 2597 } 2598 findConferenceForAction(String conferenceId, String action)2599 private Conference findConferenceForAction(String conferenceId, String action) { 2600 if (mConferenceById.containsKey(conferenceId)) { 2601 return mConferenceById.get(conferenceId); 2602 } 2603 Log.w(this, "%s - Cannot find conference %s", action, conferenceId); 2604 return getNullConference(); 2605 } 2606 createConnectionIdList(List<Connection> connections)2607 private List<String> createConnectionIdList(List<Connection> connections) { 2608 List<String> ids = new ArrayList<>(); 2609 for (Connection c : connections) { 2610 if (mIdByConnection.containsKey(c)) { 2611 ids.add(mIdByConnection.get(c)); 2612 } 2613 } 2614 Collections.sort(ids); 2615 return ids; 2616 } 2617 2618 /** 2619 * Builds a list of {@link Connection} and {@link Conference} IDs based on the list of 2620 * {@link Conferenceable}s passed in. 2621 * 2622 * @param conferenceables The {@link Conferenceable} connections and conferences. 2623 * @return List of string conference and call Ids. 2624 */ createIdList(List<Conferenceable> conferenceables)2625 private List<String> createIdList(List<Conferenceable> conferenceables) { 2626 List<String> ids = new ArrayList<>(); 2627 for (Conferenceable c : conferenceables) { 2628 // Only allow Connection and Conference conferenceables. 2629 if (c instanceof Connection) { 2630 Connection connection = (Connection) c; 2631 if (mIdByConnection.containsKey(connection)) { 2632 ids.add(mIdByConnection.get(connection)); 2633 } 2634 } else if (c instanceof Conference) { 2635 Conference conference = (Conference) c; 2636 if (mIdByConference.containsKey(conference)) { 2637 ids.add(mIdByConference.get(conference)); 2638 } 2639 } 2640 } 2641 Collections.sort(ids); 2642 return ids; 2643 } 2644 getNullConference()2645 private Conference getNullConference() { 2646 if (sNullConference == null) { 2647 sNullConference = new Conference(null) {}; 2648 } 2649 return sNullConference; 2650 } 2651 endAllConnections()2652 private void endAllConnections() { 2653 // Unbound from telecomm. We should end all connections and conferences. 2654 for (Connection connection : mIdByConnection.keySet()) { 2655 // only operate on top-level calls. Conference calls will be removed on their own. 2656 if (connection.getConference() == null) { 2657 connection.onDisconnect(); 2658 } 2659 } 2660 for (Conference conference : mIdByConference.keySet()) { 2661 conference.onDisconnect(); 2662 } 2663 } 2664 2665 /** 2666 * Retrieves the next call ID as maintainted by the connection service. 2667 * 2668 * @return The call ID. 2669 */ getNextCallId()2670 private int getNextCallId() { 2671 synchronized (mIdSyncRoot) { 2672 return ++mId; 2673 } 2674 } 2675 } 2676