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