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 1321 public final IBinder onBind(Intent intent) { 1322 return mBinder; 1323 } 1324 1325 /** {@inheritDoc} */ 1326 @Override 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 */ 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 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 */ 1423 private void notifyCreateConnectionComplete(final String callId) { 1424 Log.i(this, "notifyCreateConnectionComplete %s", callId); 1425 onCreateConnectionComplete(findConnectionForAction(callId, 1426 "notifyCreateConnectionComplete")); 1427 } 1428 1429 private void abort(String callId) { 1430 Log.d(this, "abort %s", callId); 1431 findConnectionForAction(callId, "abort").onAbort(); 1432 } 1433 1434 private void answerVideo(String callId, int videoState) { 1435 Log.d(this, "answerVideo %s", callId); 1436 findConnectionForAction(callId, "answer").onAnswer(videoState); 1437 } 1438 1439 private void answer(String callId) { 1440 Log.d(this, "answer %s", callId); 1441 findConnectionForAction(callId, "answer").onAnswer(); 1442 } 1443 1444 private void reject(String callId) { 1445 Log.d(this, "reject %s", callId); 1446 findConnectionForAction(callId, "reject").onReject(); 1447 } 1448 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 1454 private void silence(String callId) { 1455 Log.d(this, "silence %s", callId); 1456 findConnectionForAction(callId, "silence").onSilence(); 1457 } 1458 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 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 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 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 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 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 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 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 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 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 */ 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 */ 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 */ 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 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 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 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 1674 private void onPostDialContinue(String callId, boolean proceed) { 1675 Log.d(this, "onPostDialContinue(%s)", callId); 1676 findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed); 1677 } 1678 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 2038 public void onRemoteExistingConnectionAdded(RemoteConnection connection) {} 2039 2040 /** 2041 * @hide 2042 */ 2043 public boolean containsConference(Conference conference) { 2044 return mIdByConference.containsKey(conference); 2045 } 2046 2047 /** {@hide} */ 2048 void addRemoteConference(RemoteConference remoteConference) { 2049 onRemoteConferenceAdded(remoteConference); 2050 } 2051 2052 /** {@hide} */ 2053 void addRemoteExistingConnection(RemoteConnection remoteConnection) { 2054 onRemoteExistingConnectionAdded(remoteConnection); 2055 } 2056 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 */ 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 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} */ 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 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 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 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 2157 static synchronized Connection getNullConnection() { 2158 if (sNullConnection == null) { 2159 sNullConnection = new Connection() {}; 2160 } 2161 return sNullConnection; 2162 } 2163 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 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 */ 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 2210 private Conference getNullConference() { 2211 if (sNullConference == null) { 2212 sNullConference = new Conference(null) {}; 2213 } 2214 return sNullConference; 2215 } 2216 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 */ 2235 private int getNextCallId() { 2236 synchronized (mIdSyncRoot) { 2237 return ++mId; 2238 } 2239 } 2240 } 2241