ConnectionService.java revision a39fefc2fc9746757a5430d2357f719139b0e302
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.getStatusHints(), 1409 connection.getDisconnectCause(), 1410 createIdList(connection.getConferenceables()), 1411 connection.getExtras())); 1412 1413 if (isIncoming && request.shouldShowIncomingCallUi() && 1414 (connection.getConnectionProperties() & Connection.PROPERTY_SELF_MANAGED) == 1415 Connection.PROPERTY_SELF_MANAGED) { 1416 // Tell ConnectionService to show its incoming call UX. 1417 connection.onShowIncomingCallUi(); 1418 } 1419 if (isUnknown) { 1420 triggerConferenceRecalculate(); 1421 } 1422 } 1423 1424 private void createConnectionFailed(final PhoneAccountHandle callManagerAccount, 1425 final String callId, final ConnectionRequest request, 1426 boolean isIncoming) { 1427 1428 Log.i(this, "createConnectionFailed %s", callId); 1429 if (isIncoming) { 1430 onCreateIncomingConnectionFailed(callManagerAccount, request); 1431 } else { 1432 onCreateOutgoingConnectionFailed(callManagerAccount, request); 1433 } 1434 } 1435 1436 /** 1437 * Called by Telecom when the creation of a new Connection has completed and it is now added 1438 * to Telecom. 1439 * @param callId The ID of the connection. 1440 */ 1441 private void notifyCreateConnectionComplete(final String callId) { 1442 Log.i(this, "notifyCreateConnectionComplete %s", callId); 1443 onCreateConnectionComplete(findConnectionForAction(callId, 1444 "notifyCreateConnectionComplete")); 1445 } 1446 1447 private void abort(String callId) { 1448 Log.d(this, "abort %s", callId); 1449 findConnectionForAction(callId, "abort").onAbort(); 1450 } 1451 1452 private void answerVideo(String callId, int videoState) { 1453 Log.d(this, "answerVideo %s", callId); 1454 findConnectionForAction(callId, "answer").onAnswer(videoState); 1455 } 1456 1457 private void answer(String callId) { 1458 Log.d(this, "answer %s", callId); 1459 findConnectionForAction(callId, "answer").onAnswer(); 1460 } 1461 1462 private void reject(String callId) { 1463 Log.d(this, "reject %s", callId); 1464 findConnectionForAction(callId, "reject").onReject(); 1465 } 1466 1467 private void reject(String callId, String rejectWithMessage) { 1468 Log.d(this, "reject %s with message", callId); 1469 findConnectionForAction(callId, "reject").onReject(rejectWithMessage); 1470 } 1471 1472 private void silence(String callId) { 1473 Log.d(this, "silence %s", callId); 1474 findConnectionForAction(callId, "silence").onSilence(); 1475 } 1476 1477 private void disconnect(String callId) { 1478 Log.d(this, "disconnect %s", callId); 1479 if (mConnectionById.containsKey(callId)) { 1480 findConnectionForAction(callId, "disconnect").onDisconnect(); 1481 } else { 1482 findConferenceForAction(callId, "disconnect").onDisconnect(); 1483 } 1484 } 1485 1486 private void hold(String callId) { 1487 Log.d(this, "hold %s", callId); 1488 if (mConnectionById.containsKey(callId)) { 1489 findConnectionForAction(callId, "hold").onHold(); 1490 } else { 1491 findConferenceForAction(callId, "hold").onHold(); 1492 } 1493 } 1494 1495 private void unhold(String callId) { 1496 Log.d(this, "unhold %s", callId); 1497 if (mConnectionById.containsKey(callId)) { 1498 findConnectionForAction(callId, "unhold").onUnhold(); 1499 } else { 1500 findConferenceForAction(callId, "unhold").onUnhold(); 1501 } 1502 } 1503 1504 private void onCallAudioStateChanged(String callId, CallAudioState callAudioState) { 1505 Log.d(this, "onAudioStateChanged %s %s", callId, callAudioState); 1506 if (mConnectionById.containsKey(callId)) { 1507 findConnectionForAction(callId, "onCallAudioStateChanged").setCallAudioState( 1508 callAudioState); 1509 } else { 1510 findConferenceForAction(callId, "onCallAudioStateChanged").setCallAudioState( 1511 callAudioState); 1512 } 1513 } 1514 1515 private void playDtmfTone(String callId, char digit) { 1516 Log.d(this, "playDtmfTone %s %c", callId, digit); 1517 if (mConnectionById.containsKey(callId)) { 1518 findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit); 1519 } else { 1520 findConferenceForAction(callId, "playDtmfTone").onPlayDtmfTone(digit); 1521 } 1522 } 1523 1524 private void stopDtmfTone(String callId) { 1525 Log.d(this, "stopDtmfTone %s", callId); 1526 if (mConnectionById.containsKey(callId)) { 1527 findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone(); 1528 } else { 1529 findConferenceForAction(callId, "stopDtmfTone").onStopDtmfTone(); 1530 } 1531 } 1532 1533 private void conference(String callId1, String callId2) { 1534 Log.d(this, "conference %s, %s", callId1, callId2); 1535 1536 // Attempt to get second connection or conference. 1537 Connection connection2 = findConnectionForAction(callId2, "conference"); 1538 Conference conference2 = getNullConference(); 1539 if (connection2 == getNullConnection()) { 1540 conference2 = findConferenceForAction(callId2, "conference"); 1541 if (conference2 == getNullConference()) { 1542 Log.w(this, "Connection2 or Conference2 missing in conference request %s.", 1543 callId2); 1544 return; 1545 } 1546 } 1547 1548 // Attempt to get first connection or conference and perform merge. 1549 Connection connection1 = findConnectionForAction(callId1, "conference"); 1550 if (connection1 == getNullConnection()) { 1551 Conference conference1 = findConferenceForAction(callId1, "addConnection"); 1552 if (conference1 == getNullConference()) { 1553 Log.w(this, 1554 "Connection1 or Conference1 missing in conference request %s.", 1555 callId1); 1556 } else { 1557 // Call 1 is a conference. 1558 if (connection2 != getNullConnection()) { 1559 // Call 2 is a connection so merge via call 1 (conference). 1560 conference1.onMerge(connection2); 1561 } else { 1562 // Call 2 is ALSO a conference; this should never happen. 1563 Log.wtf(this, "There can only be one conference and an attempt was made to " + 1564 "merge two conferences."); 1565 return; 1566 } 1567 } 1568 } else { 1569 // Call 1 is a connection. 1570 if (conference2 != getNullConference()) { 1571 // Call 2 is a conference, so merge via call 2. 1572 conference2.onMerge(connection1); 1573 } else { 1574 // Call 2 is a connection, so merge together. 1575 onConference(connection1, connection2); 1576 } 1577 } 1578 } 1579 1580 private void splitFromConference(String callId) { 1581 Log.d(this, "splitFromConference(%s)", callId); 1582 1583 Connection connection = findConnectionForAction(callId, "splitFromConference"); 1584 if (connection == getNullConnection()) { 1585 Log.w(this, "Connection missing in conference request %s.", callId); 1586 return; 1587 } 1588 1589 Conference conference = connection.getConference(); 1590 if (conference != null) { 1591 conference.onSeparate(connection); 1592 } 1593 } 1594 1595 private void mergeConference(String callId) { 1596 Log.d(this, "mergeConference(%s)", callId); 1597 Conference conference = findConferenceForAction(callId, "mergeConference"); 1598 if (conference != null) { 1599 conference.onMerge(); 1600 } 1601 } 1602 1603 private void swapConference(String callId) { 1604 Log.d(this, "swapConference(%s)", callId); 1605 Conference conference = findConferenceForAction(callId, "swapConference"); 1606 if (conference != null) { 1607 conference.onSwap(); 1608 } 1609 } 1610 1611 /** 1612 * Notifies a {@link Connection} of a request to pull an external call. 1613 * 1614 * See {@link Call#pullExternalCall()}. 1615 * 1616 * @param callId The ID of the call to pull. 1617 */ 1618 private void pullExternalCall(String callId) { 1619 Log.d(this, "pullExternalCall(%s)", callId); 1620 Connection connection = findConnectionForAction(callId, "pullExternalCall"); 1621 if (connection != null) { 1622 connection.onPullExternalCall(); 1623 } 1624 } 1625 1626 /** 1627 * Notifies a {@link Connection} of a call event. 1628 * 1629 * See {@link Call#sendCallEvent(String, Bundle)}. 1630 * 1631 * @param callId The ID of the call receiving the event. 1632 * @param event The event. 1633 * @param extras Extras associated with the event. 1634 */ 1635 private void sendCallEvent(String callId, String event, Bundle extras) { 1636 Log.d(this, "sendCallEvent(%s, %s)", callId, event); 1637 Connection connection = findConnectionForAction(callId, "sendCallEvent"); 1638 if (connection != null) { 1639 connection.onCallEvent(event, extras); 1640 } 1641 } 1642 1643 /** 1644 * Notifies a {@link Connection} or {@link Conference} of a change to the extras from Telecom. 1645 * <p> 1646 * These extra changes can originate from Telecom itself, or from an {@link InCallService} via 1647 * the {@link android.telecom.Call#putExtra(String, boolean)}, 1648 * {@link android.telecom.Call#putExtra(String, int)}, 1649 * {@link android.telecom.Call#putExtra(String, String)}, 1650 * {@link Call#removeExtras(List)}. 1651 * 1652 * @param callId The ID of the call receiving the event. 1653 * @param extras The new extras bundle. 1654 */ 1655 private void handleExtrasChanged(String callId, Bundle extras) { 1656 Log.d(this, "handleExtrasChanged(%s, %s)", callId, extras); 1657 if (mConnectionById.containsKey(callId)) { 1658 findConnectionForAction(callId, "handleExtrasChanged").handleExtrasChanged(extras); 1659 } else if (mConferenceById.containsKey(callId)) { 1660 findConferenceForAction(callId, "handleExtrasChanged").handleExtrasChanged(extras); 1661 } 1662 } 1663 1664 private void startRtt(String callId, Connection.RttTextStream rttTextStream) { 1665 Log.d(this, "startRtt(%s)", callId); 1666 if (mConnectionById.containsKey(callId)) { 1667 findConnectionForAction(callId, "startRtt").onStartRtt(rttTextStream); 1668 } else if (mConferenceById.containsKey(callId)) { 1669 Log.w(this, "startRtt called on a conference."); 1670 } 1671 } 1672 1673 private void stopRtt(String callId) { 1674 Log.d(this, "stopRtt(%s)", callId); 1675 if (mConnectionById.containsKey(callId)) { 1676 findConnectionForAction(callId, "stopRtt").onStopRtt(); 1677 findConnectionForAction(callId, "stopRtt").unsetRttProperty(); 1678 } else if (mConferenceById.containsKey(callId)) { 1679 Log.w(this, "stopRtt called on a conference."); 1680 } 1681 } 1682 1683 private void handleRttUpgradeResponse(String callId, Connection.RttTextStream rttTextStream) { 1684 Log.d(this, "handleRttUpgradeResponse(%s, %s)", callId, rttTextStream == null); 1685 if (mConnectionById.containsKey(callId)) { 1686 findConnectionForAction(callId, "handleRttUpgradeResponse") 1687 .handleRttUpgradeResponse(rttTextStream); 1688 } else if (mConferenceById.containsKey(callId)) { 1689 Log.w(this, "handleRttUpgradeResponse called on a conference."); 1690 } 1691 } 1692 1693 private void onPostDialContinue(String callId, boolean proceed) { 1694 Log.d(this, "onPostDialContinue(%s)", callId); 1695 findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed); 1696 } 1697 1698 private void onAdapterAttached() { 1699 if (mAreAccountsInitialized) { 1700 // No need to query again if we already did it. 1701 return; 1702 } 1703 1704 mAdapter.queryRemoteConnectionServices(new RemoteServiceCallback.Stub() { 1705 @Override 1706 public void onResult( 1707 final List<ComponentName> componentNames, 1708 final List<IBinder> services) { 1709 mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oR", null /*lock*/) { 1710 @Override 1711 public void loggedRun() { 1712 for (int i = 0; i < componentNames.size() && i < services.size(); i++) { 1713 mRemoteConnectionManager.addConnectionService( 1714 componentNames.get(i), 1715 IConnectionService.Stub.asInterface(services.get(i))); 1716 } 1717 onAccountsInitialized(); 1718 Log.d(this, "remote connection services found: " + services); 1719 } 1720 }.prepare()); 1721 } 1722 1723 @Override 1724 public void onError() { 1725 mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oE", null /*lock*/) { 1726 @Override 1727 public void loggedRun() { 1728 mAreAccountsInitialized = true; 1729 } 1730 }.prepare()); 1731 } 1732 }); 1733 } 1734 1735 /** 1736 * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an 1737 * incoming request. This is used by {@code ConnectionService}s that are registered with 1738 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to manage 1739 * SIM-based incoming calls. 1740 * 1741 * @param connectionManagerPhoneAccount See description at 1742 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 1743 * @param request Details about the incoming call. 1744 * @return The {@code Connection} object to satisfy this call, or {@code null} to 1745 * not handle the call. 1746 */ 1747 public final RemoteConnection createRemoteIncomingConnection( 1748 PhoneAccountHandle connectionManagerPhoneAccount, 1749 ConnectionRequest request) { 1750 return mRemoteConnectionManager.createRemoteConnection( 1751 connectionManagerPhoneAccount, request, true); 1752 } 1753 1754 /** 1755 * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an 1756 * outgoing request. This is used by {@code ConnectionService}s that are registered with 1757 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to use the 1758 * SIM-based {@code ConnectionService} to place its outgoing calls. 1759 * 1760 * @param connectionManagerPhoneAccount See description at 1761 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 1762 * @param request Details about the outgoing call. 1763 * @return The {@code Connection} object to satisfy this call, or {@code null} to 1764 * not handle the call. 1765 */ 1766 public final RemoteConnection createRemoteOutgoingConnection( 1767 PhoneAccountHandle connectionManagerPhoneAccount, 1768 ConnectionRequest request) { 1769 return mRemoteConnectionManager.createRemoteConnection( 1770 connectionManagerPhoneAccount, request, false); 1771 } 1772 1773 /** 1774 * Indicates to the relevant {@code RemoteConnectionService} that the specified 1775 * {@link RemoteConnection}s should be merged into a conference call. 1776 * <p> 1777 * If the conference request is successful, the method {@link #onRemoteConferenceAdded} will 1778 * be invoked. 1779 * 1780 * @param remoteConnection1 The first of the remote connections to conference. 1781 * @param remoteConnection2 The second of the remote connections to conference. 1782 */ 1783 public final void conferenceRemoteConnections( 1784 RemoteConnection remoteConnection1, 1785 RemoteConnection remoteConnection2) { 1786 mRemoteConnectionManager.conferenceRemoteConnections(remoteConnection1, remoteConnection2); 1787 } 1788 1789 /** 1790 * Adds a new conference call. When a conference call is created either as a result of an 1791 * explicit request via {@link #onConference} or otherwise, the connection service should supply 1792 * an instance of {@link Conference} by invoking this method. A conference call provided by this 1793 * method will persist until {@link Conference#destroy} is invoked on the conference instance. 1794 * 1795 * @param conference The new conference object. 1796 */ 1797 public final void addConference(Conference conference) { 1798 Log.d(this, "addConference: conference=%s", conference); 1799 1800 String id = addConferenceInternal(conference); 1801 if (id != null) { 1802 List<String> connectionIds = new ArrayList<>(2); 1803 for (Connection connection : conference.getConnections()) { 1804 if (mIdByConnection.containsKey(connection)) { 1805 connectionIds.add(mIdByConnection.get(connection)); 1806 } 1807 } 1808 conference.setTelecomCallId(id); 1809 ParcelableConference parcelableConference = new ParcelableConference( 1810 conference.getPhoneAccountHandle(), 1811 conference.getState(), 1812 conference.getConnectionCapabilities(), 1813 conference.getConnectionProperties(), 1814 connectionIds, 1815 conference.getVideoProvider() == null ? 1816 null : conference.getVideoProvider().getInterface(), 1817 conference.getVideoState(), 1818 conference.getConnectTimeMillis(), 1819 conference.getStatusHints(), 1820 conference.getExtras()); 1821 1822 mAdapter.addConferenceCall(id, parcelableConference); 1823 mAdapter.setVideoProvider(id, conference.getVideoProvider()); 1824 mAdapter.setVideoState(id, conference.getVideoState()); 1825 1826 // Go through any child calls and set the parent. 1827 for (Connection connection : conference.getConnections()) { 1828 String connectionId = mIdByConnection.get(connection); 1829 if (connectionId != null) { 1830 mAdapter.setIsConferenced(connectionId, id); 1831 } 1832 } 1833 } 1834 } 1835 1836 /** 1837 * Adds a connection created by the {@link ConnectionService} and informs telecom of the new 1838 * connection. 1839 * 1840 * @param phoneAccountHandle The phone account handle for the connection. 1841 * @param connection The connection to add. 1842 */ 1843 public final void addExistingConnection(PhoneAccountHandle phoneAccountHandle, 1844 Connection connection) { 1845 addExistingConnection(phoneAccountHandle, connection, null /* conference */); 1846 } 1847 1848 /** 1849 * Adds a connection created by the {@link ConnectionService} and informs telecom of the new 1850 * connection. 1851 * 1852 * @param phoneAccountHandle The phone account handle for the connection. 1853 * @param connection The connection to add. 1854 * @param conference The parent conference of the new connection. 1855 * @hide 1856 */ 1857 public final void addExistingConnection(PhoneAccountHandle phoneAccountHandle, 1858 Connection connection, Conference conference) { 1859 1860 String id = addExistingConnectionInternal(phoneAccountHandle, connection); 1861 if (id != null) { 1862 List<String> emptyList = new ArrayList<>(0); 1863 String conferenceId = null; 1864 if (conference != null) { 1865 conferenceId = mIdByConference.get(conference); 1866 } 1867 1868 ParcelableConnection parcelableConnection = new ParcelableConnection( 1869 phoneAccountHandle, 1870 connection.getState(), 1871 connection.getConnectionCapabilities(), 1872 connection.getConnectionProperties(), 1873 connection.getSupportedAudioRoutes(), 1874 connection.getAddress(), 1875 connection.getAddressPresentation(), 1876 connection.getCallerDisplayName(), 1877 connection.getCallerDisplayNamePresentation(), 1878 connection.getVideoProvider() == null ? 1879 null : connection.getVideoProvider().getInterface(), 1880 connection.getVideoState(), 1881 connection.isRingbackRequested(), 1882 connection.getAudioModeIsVoip(), 1883 connection.getConnectTimeMillis(), 1884 connection.getStatusHints(), 1885 connection.getDisconnectCause(), 1886 emptyList, 1887 connection.getExtras(), 1888 conferenceId); 1889 mAdapter.addExistingConnection(id, parcelableConnection); 1890 } 1891 } 1892 1893 /** 1894 * Returns all the active {@code Connection}s for which this {@code ConnectionService} 1895 * has taken responsibility. 1896 * 1897 * @return A collection of {@code Connection}s created by this {@code ConnectionService}. 1898 */ 1899 public final Collection<Connection> getAllConnections() { 1900 return mConnectionById.values(); 1901 } 1902 1903 /** 1904 * Returns all the active {@code Conference}s for which this {@code ConnectionService} 1905 * has taken responsibility. 1906 * 1907 * @return A collection of {@code Conference}s created by this {@code ConnectionService}. 1908 */ 1909 public final Collection<Conference> getAllConferences() { 1910 return mConferenceById.values(); 1911 } 1912 1913 /** 1914 * Create a {@code Connection} given an incoming request. This is used to attach to existing 1915 * incoming calls. 1916 * 1917 * @param connectionManagerPhoneAccount See description at 1918 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 1919 * @param request Details about the incoming call. 1920 * @return The {@code Connection} object to satisfy this call, or {@code null} to 1921 * not handle the call. 1922 */ 1923 public Connection onCreateIncomingConnection( 1924 PhoneAccountHandle connectionManagerPhoneAccount, 1925 ConnectionRequest request) { 1926 return null; 1927 } 1928 1929 /** 1930 * Called after the {@link Connection} returned by 1931 * {@link #onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)} 1932 * or {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)} has been 1933 * added to the {@link ConnectionService} and sent to Telecom. 1934 * 1935 * @param connection the {@link Connection}. 1936 * @hide 1937 */ 1938 public void onCreateConnectionComplete(Connection connection) { 1939 } 1940 1941 /** 1942 * Called by Telecom to inform the {@link ConnectionService} that its request to create a new 1943 * incoming {@link Connection} was denied. 1944 * <p> 1945 * Used when a self-managed {@link ConnectionService} attempts to create a new incoming 1946 * {@link Connection}, but Telecom has determined that the call cannot be allowed at this time. 1947 * The {@link ConnectionService} is responsible for silently rejecting the new incoming 1948 * {@link Connection}. 1949 * <p> 1950 * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information. 1951 * 1952 * @param connectionManagerPhoneAccount See description at 1953 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 1954 * @param request The incoming connection request. 1955 */ 1956 public void onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, 1957 ConnectionRequest request) { 1958 } 1959 1960 /** 1961 * Called by Telecom to inform the {@link ConnectionService} that its request to create a new 1962 * outgoing {@link Connection} was denied. 1963 * <p> 1964 * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing 1965 * {@link Connection}, but Telecom has determined that the call cannot be placed at this time. 1966 * The {@link ConnectionService} is responisible for informing the user that the 1967 * {@link Connection} cannot be made at this time. 1968 * <p> 1969 * See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information. 1970 * 1971 * @param connectionManagerPhoneAccount See description at 1972 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 1973 * @param request The outgoing connection request. 1974 */ 1975 public void onCreateOutgoingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, 1976 ConnectionRequest request) { 1977 } 1978 1979 /** 1980 * Trigger recalculate functinality for conference calls. This is used when a Telephony 1981 * Connection is part of a conference controller but is not yet added to Connection 1982 * Service and hence cannot be added to the conference call. 1983 * 1984 * @hide 1985 */ 1986 public void triggerConferenceRecalculate() { 1987 } 1988 1989 /** 1990 * Create a {@code Connection} given an outgoing request. This is used to initiate new 1991 * outgoing calls. 1992 * 1993 * @param connectionManagerPhoneAccount The connection manager account to use for managing 1994 * this call. 1995 * <p> 1996 * If this parameter is not {@code null}, it means that this {@code ConnectionService} 1997 * has registered one or more {@code PhoneAccount}s having 1998 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain 1999 * one of these {@code PhoneAccount}s, while the {@code request} will contain another 2000 * (usually but not always distinct) {@code PhoneAccount} to be used for actually 2001 * making the connection. 2002 * <p> 2003 * If this parameter is {@code null}, it means that this {@code ConnectionService} is 2004 * being asked to make a direct connection. The 2005 * {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be 2006 * a {@code PhoneAccount} registered by this {@code ConnectionService} to use for 2007 * making the connection. 2008 * @param request Details about the outgoing call. 2009 * @return The {@code Connection} object to satisfy this call, or the result of an invocation 2010 * of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call. 2011 */ 2012 public Connection onCreateOutgoingConnection( 2013 PhoneAccountHandle connectionManagerPhoneAccount, 2014 ConnectionRequest request) { 2015 return null; 2016 } 2017 2018 /** 2019 * Create a {@code Connection} for a new unknown call. An unknown call is a call originating 2020 * from the ConnectionService that was neither a user-initiated outgoing call, nor an incoming 2021 * call created using 2022 * {@code TelecomManager#addNewIncomingCall(PhoneAccountHandle, android.os.Bundle)}. 2023 * 2024 * @hide 2025 */ 2026 public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount, 2027 ConnectionRequest request) { 2028 return null; 2029 } 2030 2031 /** 2032 * Conference two specified connections. Invoked when the user has made a request to merge the 2033 * specified connections into a conference call. In response, the connection service should 2034 * create an instance of {@link Conference} and pass it into {@link #addConference}. 2035 * 2036 * @param connection1 A connection to merge into a conference call. 2037 * @param connection2 A connection to merge into a conference call. 2038 */ 2039 public void onConference(Connection connection1, Connection connection2) {} 2040 2041 /** 2042 * Indicates that a remote conference has been created for existing {@link RemoteConnection}s. 2043 * When this method is invoked, this {@link ConnectionService} should create its own 2044 * representation of the conference call and send it to telecom using {@link #addConference}. 2045 * <p> 2046 * This is only relevant to {@link ConnectionService}s which are registered with 2047 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. 2048 * 2049 * @param conference The remote conference call. 2050 */ 2051 public void onRemoteConferenceAdded(RemoteConference conference) {} 2052 2053 /** 2054 * Called when an existing connection is added remotely. 2055 * @param connection The existing connection which was added. 2056 */ 2057 public void onRemoteExistingConnectionAdded(RemoteConnection connection) {} 2058 2059 /** 2060 * @hide 2061 */ 2062 public boolean containsConference(Conference conference) { 2063 return mIdByConference.containsKey(conference); 2064 } 2065 2066 /** {@hide} */ 2067 void addRemoteConference(RemoteConference remoteConference) { 2068 onRemoteConferenceAdded(remoteConference); 2069 } 2070 2071 /** {@hide} */ 2072 void addRemoteExistingConnection(RemoteConnection remoteConnection) { 2073 onRemoteExistingConnectionAdded(remoteConnection); 2074 } 2075 2076 private void onAccountsInitialized() { 2077 mAreAccountsInitialized = true; 2078 for (Runnable r : mPreInitializationConnectionRequests) { 2079 r.run(); 2080 } 2081 mPreInitializationConnectionRequests.clear(); 2082 } 2083 2084 /** 2085 * Adds an existing connection to the list of connections, identified by a new call ID unique 2086 * to this connection service. 2087 * 2088 * @param connection The connection. 2089 * @return The ID of the connection (e.g. the call-id). 2090 */ 2091 private String addExistingConnectionInternal(PhoneAccountHandle handle, Connection connection) { 2092 String id; 2093 2094 if (connection.getExtras() != null && connection.getExtras() 2095 .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) { 2096 id = connection.getExtras().getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID); 2097 Log.d(this, "addExistingConnectionInternal - conn %s reusing original id %s", 2098 connection.getTelecomCallId(), id); 2099 } else if (handle == null) { 2100 // If no phone account handle was provided, we cannot be sure the call ID is unique, 2101 // so just use a random UUID. 2102 id = UUID.randomUUID().toString(); 2103 } else { 2104 // Phone account handle was provided, so use the ConnectionService class name as a 2105 // prefix for a unique incremental call ID. 2106 id = handle.getComponentName().getClassName() + "@" + getNextCallId(); 2107 } 2108 addConnection(id, connection); 2109 return id; 2110 } 2111 2112 private void addConnection(String callId, Connection connection) { 2113 connection.setTelecomCallId(callId); 2114 mConnectionById.put(callId, connection); 2115 mIdByConnection.put(connection, callId); 2116 connection.addConnectionListener(mConnectionListener); 2117 connection.setConnectionService(this); 2118 } 2119 2120 /** {@hide} */ 2121 protected void removeConnection(Connection connection) { 2122 connection.unsetConnectionService(this); 2123 connection.removeConnectionListener(mConnectionListener); 2124 String id = mIdByConnection.get(connection); 2125 if (id != null) { 2126 mConnectionById.remove(id); 2127 mIdByConnection.remove(connection); 2128 mAdapter.removeCall(id); 2129 } 2130 } 2131 2132 private String addConferenceInternal(Conference conference) { 2133 String originalId = null; 2134 if (conference.getExtras() != null && conference.getExtras() 2135 .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) { 2136 originalId = conference.getExtras().getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID); 2137 Log.d(this, "addConferenceInternal: conf %s reusing original id %s", 2138 conference.getTelecomCallId(), 2139 originalId); 2140 } 2141 if (mIdByConference.containsKey(conference)) { 2142 Log.w(this, "Re-adding an existing conference: %s.", conference); 2143 } else if (conference != null) { 2144 // Conferences do not (yet) have a PhoneAccountHandle associated with them, so we 2145 // cannot determine a ConnectionService class name to associate with the ID, so use 2146 // a unique UUID (for now). 2147 String id = originalId == null ? UUID.randomUUID().toString() : originalId; 2148 mConferenceById.put(id, conference); 2149 mIdByConference.put(conference, id); 2150 conference.addListener(mConferenceListener); 2151 return id; 2152 } 2153 2154 return null; 2155 } 2156 2157 private void removeConference(Conference conference) { 2158 if (mIdByConference.containsKey(conference)) { 2159 conference.removeListener(mConferenceListener); 2160 2161 String id = mIdByConference.get(conference); 2162 mConferenceById.remove(id); 2163 mIdByConference.remove(conference); 2164 mAdapter.removeCall(id); 2165 } 2166 } 2167 2168 private Connection findConnectionForAction(String callId, String action) { 2169 if (mConnectionById.containsKey(callId)) { 2170 return mConnectionById.get(callId); 2171 } 2172 Log.w(this, "%s - Cannot find Connection %s", action, callId); 2173 return getNullConnection(); 2174 } 2175 2176 static synchronized Connection getNullConnection() { 2177 if (sNullConnection == null) { 2178 sNullConnection = new Connection() {}; 2179 } 2180 return sNullConnection; 2181 } 2182 2183 private Conference findConferenceForAction(String conferenceId, String action) { 2184 if (mConferenceById.containsKey(conferenceId)) { 2185 return mConferenceById.get(conferenceId); 2186 } 2187 Log.w(this, "%s - Cannot find conference %s", action, conferenceId); 2188 return getNullConference(); 2189 } 2190 2191 private List<String> createConnectionIdList(List<Connection> connections) { 2192 List<String> ids = new ArrayList<>(); 2193 for (Connection c : connections) { 2194 if (mIdByConnection.containsKey(c)) { 2195 ids.add(mIdByConnection.get(c)); 2196 } 2197 } 2198 Collections.sort(ids); 2199 return ids; 2200 } 2201 2202 /** 2203 * Builds a list of {@link Connection} and {@link Conference} IDs based on the list of 2204 * {@link Conferenceable}s passed in. 2205 * 2206 * @param conferenceables The {@link Conferenceable} connections and conferences. 2207 * @return List of string conference and call Ids. 2208 */ 2209 private List<String> createIdList(List<Conferenceable> conferenceables) { 2210 List<String> ids = new ArrayList<>(); 2211 for (Conferenceable c : conferenceables) { 2212 // Only allow Connection and Conference conferenceables. 2213 if (c instanceof Connection) { 2214 Connection connection = (Connection) c; 2215 if (mIdByConnection.containsKey(connection)) { 2216 ids.add(mIdByConnection.get(connection)); 2217 } 2218 } else if (c instanceof Conference) { 2219 Conference conference = (Conference) c; 2220 if (mIdByConference.containsKey(conference)) { 2221 ids.add(mIdByConference.get(conference)); 2222 } 2223 } 2224 } 2225 Collections.sort(ids); 2226 return ids; 2227 } 2228 2229 private Conference getNullConference() { 2230 if (sNullConference == null) { 2231 sNullConference = new Conference(null) {}; 2232 } 2233 return sNullConference; 2234 } 2235 2236 private void endAllConnections() { 2237 // Unbound from telecomm. We should end all connections and conferences. 2238 for (Connection connection : mIdByConnection.keySet()) { 2239 // only operate on top-level calls. Conference calls will be removed on their own. 2240 if (connection.getConference() == null) { 2241 connection.onDisconnect(); 2242 } 2243 } 2244 for (Conference conference : mIdByConference.keySet()) { 2245 conference.onDisconnect(); 2246 } 2247 } 2248 2249 /** 2250 * Retrieves the next call ID as maintainted by the connection service. 2251 * 2252 * @return The call ID. 2253 */ 2254 private int getNextCallId() { 2255 synchronized (mIdSyncRoot) { 2256 return ++mId; 2257 } 2258 } 2259} 2260