ConnectionService.java revision ef9f6f957d897ea0ed82114185b8fa3fefd4917b
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.Handler; 25import android.os.IBinder; 26import android.os.Looper; 27import android.os.Message; 28import android.telephony.DisconnectCause; 29 30import com.android.internal.os.SomeArgs; 31import com.android.internal.telecom.IConnectionService; 32import com.android.internal.telecom.IConnectionServiceAdapter; 33import com.android.internal.telecom.RemoteServiceCallback; 34 35import java.util.ArrayList; 36import java.util.Collection; 37import java.util.Collections; 38import java.util.HashMap; 39import java.util.List; 40import java.util.Map; 41import java.util.UUID; 42 43/** 44 * A {@link android.app.Service} that provides telephone connections to processes running on an 45 * Android device. 46 */ 47public abstract class ConnectionService extends Service { 48 /** 49 * The {@link Intent} that must be declared as handled by the service. 50 */ 51 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) 52 public static final String SERVICE_INTERFACE = "android.telecom.ConnectionService"; 53 54 // Flag controlling whether PII is emitted into the logs 55 private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG); 56 57 private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1; 58 private static final int MSG_CREATE_CONNECTION = 2; 59 private static final int MSG_ABORT = 3; 60 private static final int MSG_ANSWER = 4; 61 private static final int MSG_REJECT = 5; 62 private static final int MSG_DISCONNECT = 6; 63 private static final int MSG_HOLD = 7; 64 private static final int MSG_UNHOLD = 8; 65 private static final int MSG_ON_AUDIO_STATE_CHANGED = 9; 66 private static final int MSG_PLAY_DTMF_TONE = 10; 67 private static final int MSG_STOP_DTMF_TONE = 11; 68 private static final int MSG_CONFERENCE = 12; 69 private static final int MSG_SPLIT_FROM_CONFERENCE = 13; 70 private static final int MSG_ON_POST_DIAL_CONTINUE = 14; 71 private static final int MSG_REMOVE_CONNECTION_SERVICE_ADAPTER = 16; 72 private static final int MSG_ANSWER_VIDEO = 17; 73 private static final int MSG_MERGE_CONFERENCE = 18; 74 private static final int MSG_SWAP_CONFERENCE = 19; 75 76 private static Connection sNullConnection; 77 78 private final Map<String, Connection> mConnectionById = new HashMap<>(); 79 private final Map<Connection, String> mIdByConnection = new HashMap<>(); 80 private final Map<String, Conference> mConferenceById = new HashMap<>(); 81 private final Map<Conference, String> mIdByConference = new HashMap<>(); 82 private final RemoteConnectionManager mRemoteConnectionManager = 83 new RemoteConnectionManager(this); 84 private final List<Runnable> mPreInitializationConnectionRequests = new ArrayList<>(); 85 private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter(); 86 87 private boolean mAreAccountsInitialized = false; 88 private Conference sNullConference; 89 90 private final IBinder mBinder = new IConnectionService.Stub() { 91 @Override 92 public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) { 93 mHandler.obtainMessage(MSG_ADD_CONNECTION_SERVICE_ADAPTER, adapter).sendToTarget(); 94 } 95 96 public void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter) { 97 mHandler.obtainMessage(MSG_REMOVE_CONNECTION_SERVICE_ADAPTER, adapter).sendToTarget(); 98 } 99 100 @Override 101 public void createConnection( 102 PhoneAccountHandle connectionManagerPhoneAccount, 103 String id, 104 ConnectionRequest request, 105 boolean isIncoming) { 106 SomeArgs args = SomeArgs.obtain(); 107 args.arg1 = connectionManagerPhoneAccount; 108 args.arg2 = id; 109 args.arg3 = request; 110 args.argi1 = isIncoming ? 1 : 0; 111 mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget(); 112 } 113 114 @Override 115 public void abort(String callId) { 116 mHandler.obtainMessage(MSG_ABORT, callId).sendToTarget(); 117 } 118 119 @Override 120 /** @hide */ 121 public void answerVideo(String callId, int videoState) { 122 SomeArgs args = SomeArgs.obtain(); 123 args.arg1 = callId; 124 args.argi1 = videoState; 125 mHandler.obtainMessage(MSG_ANSWER_VIDEO, args).sendToTarget(); 126 } 127 128 @Override 129 public void answer(String callId) { 130 mHandler.obtainMessage(MSG_ANSWER, callId).sendToTarget(); 131 } 132 133 @Override 134 public void reject(String callId) { 135 mHandler.obtainMessage(MSG_REJECT, callId).sendToTarget(); 136 } 137 138 @Override 139 public void disconnect(String callId) { 140 mHandler.obtainMessage(MSG_DISCONNECT, callId).sendToTarget(); 141 } 142 143 @Override 144 public void hold(String callId) { 145 mHandler.obtainMessage(MSG_HOLD, callId).sendToTarget(); 146 } 147 148 @Override 149 public void unhold(String callId) { 150 mHandler.obtainMessage(MSG_UNHOLD, callId).sendToTarget(); 151 } 152 153 @Override 154 public void onAudioStateChanged(String callId, AudioState audioState) { 155 SomeArgs args = SomeArgs.obtain(); 156 args.arg1 = callId; 157 args.arg2 = audioState; 158 mHandler.obtainMessage(MSG_ON_AUDIO_STATE_CHANGED, args).sendToTarget(); 159 } 160 161 @Override 162 public void playDtmfTone(String callId, char digit) { 163 mHandler.obtainMessage(MSG_PLAY_DTMF_TONE, digit, 0, callId).sendToTarget(); 164 } 165 166 @Override 167 public void stopDtmfTone(String callId) { 168 mHandler.obtainMessage(MSG_STOP_DTMF_TONE, callId).sendToTarget(); 169 } 170 171 @Override 172 public void conference(String callId1, String callId2) { 173 SomeArgs args = SomeArgs.obtain(); 174 args.arg1 = callId1; 175 args.arg2 = callId2; 176 mHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget(); 177 } 178 179 @Override 180 public void splitFromConference(String callId) { 181 mHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, callId).sendToTarget(); 182 } 183 184 @Override 185 public void mergeConference(String callId) { 186 mHandler.obtainMessage(MSG_MERGE_CONFERENCE, callId).sendToTarget(); 187 } 188 189 @Override 190 public void swapConference(String callId) { 191 mHandler.obtainMessage(MSG_SWAP_CONFERENCE, callId).sendToTarget(); 192 } 193 194 @Override 195 public void onPostDialContinue(String callId, boolean proceed) { 196 SomeArgs args = SomeArgs.obtain(); 197 args.arg1 = callId; 198 args.argi1 = proceed ? 1 : 0; 199 mHandler.obtainMessage(MSG_ON_POST_DIAL_CONTINUE, args).sendToTarget(); 200 } 201 }; 202 203 private final Handler mHandler = new Handler(Looper.getMainLooper()) { 204 @Override 205 public void handleMessage(Message msg) { 206 switch (msg.what) { 207 case MSG_ADD_CONNECTION_SERVICE_ADAPTER: 208 mAdapter.addAdapter((IConnectionServiceAdapter) msg.obj); 209 onAdapterAttached(); 210 break; 211 case MSG_REMOVE_CONNECTION_SERVICE_ADAPTER: 212 mAdapter.removeAdapter((IConnectionServiceAdapter) msg.obj); 213 break; 214 case MSG_CREATE_CONNECTION: { 215 SomeArgs args = (SomeArgs) msg.obj; 216 try { 217 final PhoneAccountHandle connectionManagerPhoneAccount = 218 (PhoneAccountHandle) args.arg1; 219 final String id = (String) args.arg2; 220 final ConnectionRequest request = (ConnectionRequest) args.arg3; 221 final boolean isIncoming = args.argi1 == 1; 222 if (!mAreAccountsInitialized) { 223 Log.d(this, "Enqueueing pre-init request %s", id); 224 mPreInitializationConnectionRequests.add(new Runnable() { 225 @Override 226 public void run() { 227 createConnection( 228 connectionManagerPhoneAccount, 229 id, 230 request, 231 isIncoming); 232 } 233 }); 234 } else { 235 createConnection( 236 connectionManagerPhoneAccount, 237 id, 238 request, 239 isIncoming); 240 } 241 } finally { 242 args.recycle(); 243 } 244 break; 245 } 246 case MSG_ABORT: 247 abort((String) msg.obj); 248 break; 249 case MSG_ANSWER: 250 answer((String) msg.obj); 251 break; 252 case MSG_ANSWER_VIDEO: { 253 SomeArgs args = (SomeArgs) msg.obj; 254 try { 255 String callId = (String) args.arg1; 256 int videoState = args.argi1; 257 answerVideo(callId, videoState); 258 } finally { 259 args.recycle(); 260 } 261 break; 262 } 263 case MSG_REJECT: 264 reject((String) msg.obj); 265 break; 266 case MSG_DISCONNECT: 267 disconnect((String) msg.obj); 268 break; 269 case MSG_HOLD: 270 hold((String) msg.obj); 271 break; 272 case MSG_UNHOLD: 273 unhold((String) msg.obj); 274 break; 275 case MSG_ON_AUDIO_STATE_CHANGED: { 276 SomeArgs args = (SomeArgs) msg.obj; 277 try { 278 String callId = (String) args.arg1; 279 AudioState audioState = (AudioState) args.arg2; 280 onAudioStateChanged(callId, audioState); 281 } finally { 282 args.recycle(); 283 } 284 break; 285 } 286 case MSG_PLAY_DTMF_TONE: 287 playDtmfTone((String) msg.obj, (char) msg.arg1); 288 break; 289 case MSG_STOP_DTMF_TONE: 290 stopDtmfTone((String) msg.obj); 291 break; 292 case MSG_CONFERENCE: { 293 SomeArgs args = (SomeArgs) msg.obj; 294 try { 295 String callId1 = (String) args.arg1; 296 String callId2 = (String) args.arg2; 297 conference(callId1, callId2); 298 } finally { 299 args.recycle(); 300 } 301 break; 302 } 303 case MSG_SPLIT_FROM_CONFERENCE: 304 splitFromConference((String) msg.obj); 305 break; 306 case MSG_MERGE_CONFERENCE: 307 mergeConference((String) msg.obj); 308 break; 309 case MSG_SWAP_CONFERENCE: 310 swapConference((String) msg.obj); 311 break; 312 case MSG_ON_POST_DIAL_CONTINUE: { 313 SomeArgs args = (SomeArgs) msg.obj; 314 try { 315 String callId = (String) args.arg1; 316 boolean proceed = (args.argi1 == 1); 317 onPostDialContinue(callId, proceed); 318 } finally { 319 args.recycle(); 320 } 321 break; 322 } 323 default: 324 break; 325 } 326 } 327 }; 328 329 private final Conference.Listener mConferenceListener = new Conference.Listener() { 330 @Override 331 public void onStateChanged(Conference conference, int oldState, int newState) { 332 String id = mIdByConference.get(conference); 333 switch (newState) { 334 case Connection.STATE_ACTIVE: 335 mAdapter.setActive(id); 336 break; 337 case Connection.STATE_HOLDING: 338 mAdapter.setOnHold(id); 339 break; 340 case Connection.STATE_DISCONNECTED: 341 // handled by onDisconnected 342 break; 343 } 344 } 345 346 @Override 347 public void onDisconnected(Conference conference, int cause, String message) { 348 String id = mIdByConference.get(conference); 349 mAdapter.setDisconnected(id, cause, message); 350 } 351 352 @Override 353 public void onConnectionAdded(Conference conference, Connection connection) { 354 } 355 356 @Override 357 public void onConnectionRemoved(Conference conference, Connection connection) { 358 } 359 360 @Override 361 public void onDestroyed(Conference conference) { 362 removeConference(conference); 363 } 364 365 @Override 366 public void onCapabilitiesChanged(Conference conference, int capabilities) { 367 String id = mIdByConference.get(conference); 368 Log.d(this, "call capabilities: conference: %s", 369 PhoneCapabilities.toString(capabilities)); 370 mAdapter.setCallCapabilities(id, capabilities); 371 } 372 }; 373 374 private final Connection.Listener mConnectionListener = new Connection.Listener() { 375 @Override 376 public void onStateChanged(Connection c, int state) { 377 String id = mIdByConnection.get(c); 378 Log.d(this, "Adapter set state %s %s", id, Connection.stateToString(state)); 379 switch (state) { 380 case Connection.STATE_ACTIVE: 381 mAdapter.setActive(id); 382 break; 383 case Connection.STATE_DIALING: 384 mAdapter.setDialing(id); 385 break; 386 case Connection.STATE_DISCONNECTED: 387 // Handled in onDisconnected() 388 break; 389 case Connection.STATE_HOLDING: 390 mAdapter.setOnHold(id); 391 break; 392 case Connection.STATE_NEW: 393 // Nothing to tell Telecom 394 break; 395 case Connection.STATE_RINGING: 396 mAdapter.setRinging(id); 397 break; 398 } 399 } 400 401 @Override 402 public void onDisconnected(Connection c, int cause, String message) { 403 String id = mIdByConnection.get(c); 404 Log.d(this, "Adapter set disconnected %d %s", cause, message); 405 mAdapter.setDisconnected(id, cause, message); 406 } 407 408 @Override 409 public void onVideoStateChanged(Connection c, int videoState) { 410 String id = mIdByConnection.get(c); 411 Log.d(this, "Adapter set video state %d", videoState); 412 mAdapter.setVideoState(id, videoState); 413 } 414 415 @Override 416 public void onAddressChanged(Connection c, Uri address, int presentation) { 417 String id = mIdByConnection.get(c); 418 mAdapter.setAddress(id, address, presentation); 419 } 420 421 @Override 422 public void onCallerDisplayNameChanged( 423 Connection c, String callerDisplayName, int presentation) { 424 String id = mIdByConnection.get(c); 425 mAdapter.setCallerDisplayName(id, callerDisplayName, presentation); 426 } 427 428 @Override 429 public void onDestroyed(Connection c) { 430 removeConnection(c); 431 } 432 433 @Override 434 public void onPostDialWait(Connection c, String remaining) { 435 String id = mIdByConnection.get(c); 436 Log.d(this, "Adapter onPostDialWait %s, %s", c, remaining); 437 mAdapter.onPostDialWait(id, remaining); 438 } 439 440 @Override 441 public void onRingbackRequested(Connection c, boolean ringback) { 442 String id = mIdByConnection.get(c); 443 Log.d(this, "Adapter onRingback %b", ringback); 444 mAdapter.setRingbackRequested(id, ringback); 445 } 446 447 @Override 448 public void onCallCapabilitiesChanged(Connection c, int capabilities) { 449 String id = mIdByConnection.get(c); 450 Log.d(this, "capabilities: parcelableconnection: %s", 451 PhoneCapabilities.toString(capabilities)); 452 mAdapter.setCallCapabilities(id, capabilities); 453 } 454 455 @Override 456 public void onVideoProviderChanged(Connection c, Connection.VideoProvider videoProvider) { 457 String id = mIdByConnection.get(c); 458 mAdapter.setVideoProvider(id, videoProvider); 459 } 460 461 @Override 462 public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) { 463 String id = mIdByConnection.get(c); 464 mAdapter.setIsVoipAudioMode(id, isVoip); 465 } 466 467 @Override 468 public void onStatusHintsChanged(Connection c, StatusHints statusHints) { 469 String id = mIdByConnection.get(c); 470 mAdapter.setStatusHints(id, statusHints); 471 } 472 473 @Override 474 public void onConferenceableConnectionsChanged( 475 Connection connection, List<Connection> conferenceableConnections) { 476 mAdapter.setConferenceableConnections( 477 mIdByConnection.get(connection), 478 createConnectionIdList(conferenceableConnections)); 479 } 480 481 @Override 482 public void onConferenceChanged(Connection connection, Conference conference) { 483 String id = mIdByConnection.get(connection); 484 if (id != null) { 485 String conferenceId = null; 486 if (conference != null) { 487 conferenceId = mIdByConference.get(conference); 488 } 489 mAdapter.setIsConferenced(id, conferenceId); 490 } 491 } 492 }; 493 494 /** {@inheritDoc} */ 495 @Override 496 public final IBinder onBind(Intent intent) { 497 return mBinder; 498 } 499 500 /** {@inheritDoc} */ 501 @Override 502 public boolean onUnbind(Intent intent) { 503 endAllConnections(); 504 return super.onUnbind(intent); 505 } 506 507 /** 508 * This can be used by telecom to either create a new outgoing call or attach to an existing 509 * incoming call. In either case, telecom will cycle through a set of services and call 510 * createConnection util a connection service cancels the process or completes it successfully. 511 */ 512 private void createConnection( 513 final PhoneAccountHandle callManagerAccount, 514 final String callId, 515 final ConnectionRequest request, 516 boolean isIncoming) { 517 Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " + 518 "isIncoming: %b", callManagerAccount, callId, request, isIncoming); 519 520 Connection connection = isIncoming 521 ? onCreateIncomingConnection(callManagerAccount, request) 522 : onCreateOutgoingConnection(callManagerAccount, request); 523 Log.d(this, "createConnection, connection: %s", connection); 524 if (connection == null) { 525 connection = Connection.createFailedConnection(DisconnectCause.OUTGOING_FAILURE, null); 526 } 527 528 if (connection.getState() != Connection.STATE_DISCONNECTED) { 529 addConnection(callId, connection); 530 } 531 532 Uri address = connection.getAddress(); 533 String number = address == null ? "null" : address.getSchemeSpecificPart(); 534 Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s", 535 Connection.toLogSafePhoneNumber(number), 536 Connection.stateToString(connection.getState()), 537 PhoneCapabilities.toString(connection.getCallCapabilities())); 538 539 Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId); 540 mAdapter.handleCreateConnectionComplete( 541 callId, 542 request, 543 new ParcelableConnection( 544 request.getAccountHandle(), 545 connection.getState(), 546 connection.getCallCapabilities(), 547 connection.getAddress(), 548 connection.getAddressPresentation(), 549 connection.getCallerDisplayName(), 550 connection.getCallerDisplayNamePresentation(), 551 connection.getVideoProvider() == null ? 552 null : connection.getVideoProvider().getInterface(), 553 connection.getVideoState(), 554 connection.isRingbackRequested(), 555 connection.getAudioModeIsVoip(), 556 connection.getStatusHints(), 557 connection.getDisconnectCause(), 558 connection.getDisconnectMessage(), 559 createConnectionIdList(connection.getConferenceableConnections()))); 560 } 561 562 private void abort(String callId) { 563 Log.d(this, "abort %s", callId); 564 findConnectionForAction(callId, "abort").onAbort(); 565 } 566 567 private void answerVideo(String callId, int videoState) { 568 Log.d(this, "answerVideo %s", callId); 569 findConnectionForAction(callId, "answer").onAnswer(videoState); 570 } 571 572 private void answer(String callId) { 573 Log.d(this, "answer %s", callId); 574 findConnectionForAction(callId, "answer").onAnswer(); 575 } 576 577 private void reject(String callId) { 578 Log.d(this, "reject %s", callId); 579 findConnectionForAction(callId, "reject").onReject(); 580 } 581 582 private void disconnect(String callId) { 583 Log.d(this, "disconnect %s", callId); 584 if (mConnectionById.containsKey(callId)) { 585 findConnectionForAction(callId, "disconnect").onDisconnect(); 586 } else { 587 findConferenceForAction(callId, "disconnect").onDisconnect(); 588 } 589 } 590 591 private void hold(String callId) { 592 Log.d(this, "hold %s", callId); 593 if (mConnectionById.containsKey(callId)) { 594 findConnectionForAction(callId, "hold").onHold(); 595 } else { 596 findConferenceForAction(callId, "hold").onHold(); 597 } 598 } 599 600 private void unhold(String callId) { 601 Log.d(this, "unhold %s", callId); 602 if (mConnectionById.containsKey(callId)) { 603 findConnectionForAction(callId, "unhold").onUnhold(); 604 } else { 605 findConferenceForAction(callId, "unhold").onUnhold(); 606 } 607 } 608 609 private void onAudioStateChanged(String callId, AudioState audioState) { 610 Log.d(this, "onAudioStateChanged %s %s", callId, audioState); 611 findConnectionForAction(callId, "onAudioStateChanged").setAudioState(audioState); 612 } 613 614 private void playDtmfTone(String callId, char digit) { 615 Log.d(this, "playDtmfTone %s %c", callId, digit); 616 findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit); 617 } 618 619 private void stopDtmfTone(String callId) { 620 Log.d(this, "stopDtmfTone %s", callId); 621 findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone(); 622 } 623 624 private void conference(String callId1, String callId2) { 625 Log.d(this, "conference %s, %s", callId1, callId2); 626 627 Connection connection1 = findConnectionForAction(callId1, "conference"); 628 if (connection1 == getNullConnection()) { 629 Log.w(this, "Connection1 missing in conference request %s.", callId1); 630 return; 631 } 632 633 Connection connection2 = findConnectionForAction(callId2, "conference"); 634 if (connection2 == getNullConnection()) { 635 Log.w(this, "Connection2 missing in conference request %s.", callId2); 636 return; 637 } 638 639 onConference(connection1, connection2); 640 } 641 642 private void splitFromConference(String callId) { 643 Log.d(this, "splitFromConference(%s)", callId); 644 645 Connection connection = findConnectionForAction(callId, "splitFromConference"); 646 if (connection == getNullConnection()) { 647 Log.w(this, "Connection missing in conference request %s.", callId); 648 return; 649 } 650 651 Conference conference = connection.getConference(); 652 if (conference != null) { 653 conference.onSeparate(connection); 654 } 655 } 656 657 private void mergeConference(String callId) { 658 Log.d(this, "mergeConference(%s)", callId); 659 Conference conference = findConferenceForAction(callId, "mergeConference"); 660 if (conference != null) { 661 conference.onMerge(); 662 } 663 } 664 665 private void swapConference(String callId) { 666 Log.d(this, "swapConference(%s)", callId); 667 Conference conference = findConferenceForAction(callId, "swapConference"); 668 if (conference != null) { 669 conference.onSwap(); 670 } 671 } 672 673 private void onPostDialContinue(String callId, boolean proceed) { 674 Log.d(this, "onPostDialContinue(%s)", callId); 675 findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed); 676 } 677 678 private void onAdapterAttached() { 679 if (mAreAccountsInitialized) { 680 // No need to query again if we already did it. 681 return; 682 } 683 684 mAdapter.queryRemoteConnectionServices(new RemoteServiceCallback.Stub() { 685 @Override 686 public void onResult( 687 final List<ComponentName> componentNames, 688 final List<IBinder> services) { 689 mHandler.post(new Runnable() { 690 @Override 691 public void run() { 692 for (int i = 0; i < componentNames.size() && i < services.size(); i++) { 693 mRemoteConnectionManager.addConnectionService( 694 componentNames.get(i), 695 IConnectionService.Stub.asInterface(services.get(i))); 696 } 697 onAccountsInitialized(); 698 Log.d(this, "remote connection services found: " + services); 699 } 700 }); 701 } 702 703 @Override 704 public void onError() { 705 mHandler.post(new Runnable() { 706 @Override 707 public void run() { 708 mAreAccountsInitialized = true; 709 } 710 }); 711 } 712 }); 713 } 714 715 /** 716 * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an 717 * incoming request. This is used to attach to existing incoming calls. 718 * 719 * @param connectionManagerPhoneAccount See description at 720 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 721 * @param request Details about the incoming call. 722 * @return The {@code Connection} object to satisfy this call, or {@code null} to 723 * not handle the call. 724 */ 725 public final RemoteConnection createRemoteIncomingConnection( 726 PhoneAccountHandle connectionManagerPhoneAccount, 727 ConnectionRequest request) { 728 return mRemoteConnectionManager.createRemoteConnection( 729 connectionManagerPhoneAccount, request, true); 730 } 731 732 /** 733 * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an 734 * outgoing request. This is used to initiate new outgoing calls. 735 * 736 * @param connectionManagerPhoneAccount See description at 737 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 738 * @param request Details about the incoming call. 739 * @return The {@code Connection} object to satisfy this call, or {@code null} to 740 * not handle the call. 741 */ 742 public final RemoteConnection createRemoteOutgoingConnection( 743 PhoneAccountHandle connectionManagerPhoneAccount, 744 ConnectionRequest request) { 745 return mRemoteConnectionManager.createRemoteConnection( 746 connectionManagerPhoneAccount, request, false); 747 } 748 749 /** 750 * Adds two {@code RemoteConnection}s to some {@code RemoteConference}. 751 */ 752 public final void conferenceRemoteConnections( 753 RemoteConnection a, 754 RemoteConnection b) { 755 mRemoteConnectionManager.conferenceRemoteConnections(a, b); 756 } 757 758 /** 759 * Adds a new conference call. When a conference call is created either as a result of an 760 * explicit request via {@link #onConference} or otherwise, the connection service should supply 761 * an instance of {@link Conference} by invoking this method. A conference call provided by this 762 * method will persist until {@link Conference#destroy} is invoked on the conference instance. 763 * 764 * @param conference The new conference object. 765 */ 766 public final void addConference(Conference conference) { 767 String id = addConferenceInternal(conference); 768 if (id != null) { 769 List<String> connectionIds = new ArrayList<>(2); 770 for (Connection connection : conference.getConnections()) { 771 if (mIdByConnection.containsKey(connection)) { 772 connectionIds.add(mIdByConnection.get(connection)); 773 } 774 } 775 ParcelableConference parcelableConference = new ParcelableConference( 776 conference.getPhoneAccountHandle(), 777 conference.getState(), 778 conference.getCapabilities(), 779 connectionIds); 780 mAdapter.addConferenceCall(id, parcelableConference); 781 782 // Go through any child calls and set the parent. 783 for (Connection connection : conference.getConnections()) { 784 String connectionId = mIdByConnection.get(connection); 785 if (connectionId != null) { 786 mAdapter.setIsConferenced(connectionId, id); 787 } 788 } 789 } 790 } 791 792 /** 793 * Returns all the active {@code Connection}s for which this {@code ConnectionService} 794 * has taken responsibility. 795 * 796 * @return A collection of {@code Connection}s created by this {@code ConnectionService}. 797 */ 798 public final Collection<Connection> getAllConnections() { 799 return mConnectionById.values(); 800 } 801 802 /** 803 * Create a {@code Connection} given an incoming request. This is used to attach to existing 804 * incoming calls. 805 * 806 * @param connectionManagerPhoneAccount See description at 807 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 808 * @param request Details about the incoming call. 809 * @return The {@code Connection} object to satisfy this call, or {@code null} to 810 * not handle the call. 811 */ 812 public Connection onCreateIncomingConnection( 813 PhoneAccountHandle connectionManagerPhoneAccount, 814 ConnectionRequest request) { 815 return null; 816 } 817 818 /** 819 * Create a {@code Connection} given an outgoing request. This is used to initiate new 820 * outgoing calls. 821 * 822 * @param connectionManagerPhoneAccount The connection manager account to use for managing 823 * this call. 824 * <p> 825 * If this parameter is not {@code null}, it means that this {@code ConnectionService} 826 * has registered one or more {@code PhoneAccount}s having 827 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain 828 * one of these {@code PhoneAccount}s, while the {@code request} will contain another 829 * (usually but not always distinct) {@code PhoneAccount} to be used for actually 830 * making the connection. 831 * <p> 832 * If this parameter is {@code null}, it means that this {@code ConnectionService} is 833 * being asked to make a direct connection. The 834 * {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be 835 * a {@code PhoneAccount} registered by this {@code ConnectionService} to use for 836 * making the connection. 837 * @param request Details about the outgoing call. 838 * @return The {@code Connection} object to satisfy this call, or the result of an invocation 839 * of {@link Connection#createFailedConnection(int, String)} to not handle the call. 840 */ 841 public Connection onCreateOutgoingConnection( 842 PhoneAccountHandle connectionManagerPhoneAccount, 843 ConnectionRequest request) { 844 return null; 845 } 846 847 /** 848 * Conference two specified connections. Invoked when the user has made a request to merge the 849 * specified connections into a conference call. In response, the connection service should 850 * create an instance of {@link Conference} and pass it into {@link #addConference}. 851 * 852 * @param connection1 A connection to merge into a conference call. 853 * @param connection2 A connection to merge into a conference call. 854 */ 855 public void onConference(Connection connection1, Connection connection2) {} 856 857 public void onRemoteConferenceAdded(RemoteConference conference) {} 858 859 /** 860 * @hide 861 */ 862 public boolean containsConference(Conference conference) { 863 return mIdByConference.containsKey(conference); 864 } 865 866 /** {@hide} */ 867 void addRemoteConference(RemoteConference remoteConference) { 868 onRemoteConferenceAdded(remoteConference); 869 } 870 871 private void onAccountsInitialized() { 872 mAreAccountsInitialized = true; 873 for (Runnable r : mPreInitializationConnectionRequests) { 874 r.run(); 875 } 876 mPreInitializationConnectionRequests.clear(); 877 } 878 879 private void addConnection(String callId, Connection connection) { 880 mConnectionById.put(callId, connection); 881 mIdByConnection.put(connection, callId); 882 connection.addConnectionListener(mConnectionListener); 883 connection.setConnectionService(this); 884 } 885 886 private void removeConnection(Connection connection) { 887 String id = mIdByConnection.get(connection); 888 connection.unsetConnectionService(this); 889 connection.removeConnectionListener(mConnectionListener); 890 mConnectionById.remove(mIdByConnection.get(connection)); 891 mIdByConnection.remove(connection); 892 mAdapter.removeCall(id); 893 } 894 895 private String addConferenceInternal(Conference conference) { 896 if (mIdByConference.containsKey(conference)) { 897 Log.w(this, "Re-adding an existing conference: %s.", conference); 898 } else if (conference != null) { 899 String id = UUID.randomUUID().toString(); 900 mConferenceById.put(id, conference); 901 mIdByConference.put(conference, id); 902 conference.addListener(mConferenceListener); 903 return id; 904 } 905 906 return null; 907 } 908 909 private void removeConference(Conference conference) { 910 if (mIdByConference.containsKey(conference)) { 911 conference.removeListener(mConferenceListener); 912 913 String id = mIdByConference.get(conference); 914 mConferenceById.remove(id); 915 mIdByConference.remove(conference); 916 mAdapter.removeCall(id); 917 } 918 } 919 920 private Connection findConnectionForAction(String callId, String action) { 921 if (mConnectionById.containsKey(callId)) { 922 return mConnectionById.get(callId); 923 } 924 Log.w(this, "%s - Cannot find Connection %s", action, callId); 925 return getNullConnection(); 926 } 927 928 static synchronized Connection getNullConnection() { 929 if (sNullConnection == null) { 930 sNullConnection = new Connection() {}; 931 } 932 return sNullConnection; 933 } 934 935 private Conference findConferenceForAction(String conferenceId, String action) { 936 if (mConferenceById.containsKey(conferenceId)) { 937 return mConferenceById.get(conferenceId); 938 } 939 Log.w(this, "%s - Cannot find conference %s", action, conferenceId); 940 return getNullConference(); 941 } 942 943 private List<String> createConnectionIdList(List<Connection> connections) { 944 List<String> ids = new ArrayList<>(); 945 for (Connection c : connections) { 946 if (mIdByConnection.containsKey(c)) { 947 ids.add(mIdByConnection.get(c)); 948 } 949 } 950 Collections.sort(ids); 951 return ids; 952 } 953 954 private Conference getNullConference() { 955 if (sNullConference == null) { 956 sNullConference = new Conference(null) {}; 957 } 958 return sNullConference; 959 } 960 961 private void endAllConnections() { 962 // Unbound from telecomm. We should end all connections and conferences. 963 for (Connection connection : mIdByConnection.keySet()) { 964 // only operate on top-level calls. Conference calls will be removed on their own. 965 if (connection.getConference() == null) { 966 connection.onDisconnect(); 967 } 968 } 969 for (Conference conference : mIdByConference.keySet()) { 970 conference.onDisconnect(); 971 } 972 } 973} 974