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