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