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