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