1/* 2 * Copyright 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 com.android.server.telecom; 18 19import android.content.ComponentName; 20import android.content.Context; 21import android.net.Uri; 22import android.os.Bundle; 23import android.os.Handler; 24import android.os.IBinder; 25import android.os.Message; 26import android.os.RemoteException; 27import android.telecom.AudioState; 28import android.telecom.Connection; 29import android.telecom.ConnectionRequest; 30import android.telecom.ConnectionService; 31import android.telecom.DisconnectCause; 32import android.telecom.GatewayInfo; 33import android.telecom.ParcelableConference; 34import android.telecom.ParcelableConnection; 35import android.telecom.PhoneAccount; 36import android.telecom.PhoneAccountHandle; 37import android.telecom.StatusHints; 38import android.telecom.TelecomManager; 39import android.telecom.VideoProfile; 40 41import com.android.internal.os.SomeArgs; 42import com.android.internal.telecom.IConnectionService; 43import com.android.internal.telecom.IConnectionServiceAdapter; 44import com.android.internal.telecom.IVideoProvider; 45import com.android.internal.telecom.RemoteServiceCallback; 46import com.android.internal.util.Preconditions; 47 48import java.util.ArrayList; 49import java.util.Collections; 50import java.util.HashMap; 51import java.util.List; 52import java.util.Map; 53import java.util.Set; 54import java.util.concurrent.ConcurrentHashMap; 55 56/** 57 * Wrapper for {@link IConnectionService}s, handles binding to {@link IConnectionService} and keeps 58 * track of when the object can safely be unbound. Other classes should not use 59 * {@link IConnectionService} directly and instead should use this class to invoke methods of 60 * {@link IConnectionService}. 61 */ 62final class ConnectionServiceWrapper extends ServiceBinder<IConnectionService> { 63 private static final int MSG_HANDLE_CREATE_CONNECTION_COMPLETE = 1; 64 private static final int MSG_SET_ACTIVE = 2; 65 private static final int MSG_SET_RINGING = 3; 66 private static final int MSG_SET_DIALING = 4; 67 private static final int MSG_SET_DISCONNECTED = 5; 68 private static final int MSG_SET_ON_HOLD = 6; 69 private static final int MSG_SET_RINGBACK_REQUESTED = 7; 70 private static final int MSG_SET_CALL_CAPABILITIES = 8; 71 private static final int MSG_SET_IS_CONFERENCED = 9; 72 private static final int MSG_ADD_CONFERENCE_CALL = 10; 73 private static final int MSG_REMOVE_CALL = 11; 74 private static final int MSG_ON_POST_DIAL_WAIT = 12; 75 private static final int MSG_QUERY_REMOTE_CALL_SERVICES = 13; 76 private static final int MSG_SET_VIDEO_PROVIDER = 14; 77 private static final int MSG_SET_IS_VOIP_AUDIO_MODE = 15; 78 private static final int MSG_SET_STATUS_HINTS = 16; 79 private static final int MSG_SET_ADDRESS = 17; 80 private static final int MSG_SET_CALLER_DISPLAY_NAME = 18; 81 private static final int MSG_SET_VIDEO_STATE = 19; 82 private static final int MSG_SET_CONFERENCEABLE_CONNECTIONS = 20; 83 84 private final Handler mHandler = new Handler() { 85 @Override 86 public void handleMessage(Message msg) { 87 Call call; 88 switch (msg.what) { 89 case MSG_HANDLE_CREATE_CONNECTION_COMPLETE: { 90 SomeArgs args = (SomeArgs) msg.obj; 91 try { 92 String callId = (String) args.arg1; 93 ConnectionRequest request = (ConnectionRequest) args.arg2; 94 ParcelableConnection connection = (ParcelableConnection) args.arg3; 95 handleCreateConnectionComplete(callId, request, connection); 96 } finally { 97 args.recycle(); 98 } 99 break; 100 } 101 case MSG_SET_ACTIVE: 102 call = mCallIdMapper.getCall(msg.obj); 103 if (call != null) { 104 mCallsManager.markCallAsActive(call); 105 } else { 106 //Log.w(this, "setActive, unknown call id: %s", msg.obj); 107 } 108 break; 109 case MSG_SET_RINGING: 110 call = mCallIdMapper.getCall(msg.obj); 111 if (call != null) { 112 mCallsManager.markCallAsRinging(call); 113 } else { 114 //Log.w(this, "setRinging, unknown call id: %s", msg.obj); 115 } 116 break; 117 case MSG_SET_DIALING: 118 call = mCallIdMapper.getCall(msg.obj); 119 if (call != null) { 120 mCallsManager.markCallAsDialing(call); 121 } else { 122 //Log.w(this, "setDialing, unknown call id: %s", msg.obj); 123 } 124 break; 125 case MSG_SET_DISCONNECTED: { 126 SomeArgs args = (SomeArgs) msg.obj; 127 try { 128 call = mCallIdMapper.getCall(args.arg1); 129 DisconnectCause disconnectCause = (DisconnectCause) args.arg2; 130 Log.d(this, "disconnect call %s %s", disconnectCause, call); 131 if (call != null) { 132 mCallsManager.markCallAsDisconnected(call, disconnectCause); 133 } else { 134 //Log.w(this, "setDisconnected, unknown call id: %s", args.arg1); 135 } 136 } finally { 137 args.recycle(); 138 } 139 break; 140 } 141 case MSG_SET_ON_HOLD: 142 call = mCallIdMapper.getCall(msg.obj); 143 if (call != null) { 144 mCallsManager.markCallAsOnHold(call); 145 } else { 146 //Log.w(this, "setOnHold, unknown call id: %s", msg.obj); 147 } 148 break; 149 case MSG_SET_RINGBACK_REQUESTED: { 150 call = mCallIdMapper.getCall(msg.obj); 151 if (call != null) { 152 call.setRingbackRequested(msg.arg1 == 1); 153 } else { 154 //Log.w(this, "setRingback, unknown call id: %s", args.arg1); 155 } 156 break; 157 } 158 case MSG_SET_CALL_CAPABILITIES: { 159 call = mCallIdMapper.getCall(msg.obj); 160 if (call != null) { 161 call.setCallCapabilities(msg.arg1); 162 } else { 163 //Log.w(ConnectionServiceWrapper.this, 164 // "setCallCapabilities, unknown call id: %s", msg.obj); 165 } 166 break; 167 } 168 case MSG_SET_IS_CONFERENCED: { 169 SomeArgs args = (SomeArgs) msg.obj; 170 try { 171 Call childCall = mCallIdMapper.getCall(args.arg1); 172 Log.d(this, "SET_IS_CONFERENCE: %s %s", args.arg1, args.arg2); 173 if (childCall != null) { 174 String conferenceCallId = (String) args.arg2; 175 if (conferenceCallId == null) { 176 Log.d(this, "unsetting parent: %s", args.arg1); 177 childCall.setParentCall(null); 178 } else { 179 Call conferenceCall = mCallIdMapper.getCall(conferenceCallId); 180 childCall.setParentCall(conferenceCall); 181 } 182 } else { 183 //Log.w(this, "setIsConferenced, unknown call id: %s", args.arg1); 184 } 185 } finally { 186 args.recycle(); 187 } 188 break; 189 } 190 case MSG_ADD_CONFERENCE_CALL: { 191 SomeArgs args = (SomeArgs) msg.obj; 192 try { 193 String id = (String) args.arg1; 194 if (mCallIdMapper.getCall(id) != null) { 195 Log.w(this, "Attempting to add a conference call using an existing " + 196 "call id %s", id); 197 break; 198 } 199 ParcelableConference parcelableConference = 200 (ParcelableConference) args.arg2; 201 202 // Make sure that there's at least one valid call. For remote connections 203 // we'll get a add conference msg from both the remote connection service 204 // and from the real connection service. 205 boolean hasValidCalls = false; 206 for (String callId : parcelableConference.getConnectionIds()) { 207 if (mCallIdMapper.getCall(callId) != null) { 208 hasValidCalls = true; 209 } 210 } 211 if (!hasValidCalls) { 212 Log.d(this, "Attempting to add a conference with no valid calls"); 213 break; 214 } 215 216 // need to create a new Call 217 Call conferenceCall = mCallsManager.createConferenceCall( 218 null, parcelableConference); 219 mCallIdMapper.addCall(conferenceCall, id); 220 conferenceCall.setConnectionService(ConnectionServiceWrapper.this); 221 222 Log.d(this, "adding children to conference %s", 223 parcelableConference.getConnectionIds()); 224 for (String callId : parcelableConference.getConnectionIds()) { 225 Call childCall = mCallIdMapper.getCall(callId); 226 Log.d(this, "found child: %s", callId); 227 if (childCall != null) { 228 childCall.setParentCall(conferenceCall); 229 } 230 } 231 } finally { 232 args.recycle(); 233 } 234 break; 235 } 236 case MSG_REMOVE_CALL: { 237 call = mCallIdMapper.getCall(msg.obj); 238 if (call != null) { 239 if (call.isActive()) { 240 mCallsManager.markCallAsDisconnected( 241 call, new DisconnectCause(DisconnectCause.REMOTE)); 242 } else { 243 mCallsManager.markCallAsRemoved(call); 244 } 245 } 246 break; 247 } 248 case MSG_ON_POST_DIAL_WAIT: { 249 SomeArgs args = (SomeArgs) msg.obj; 250 try { 251 call = mCallIdMapper.getCall(args.arg1); 252 if (call != null) { 253 String remaining = (String) args.arg2; 254 call.onPostDialWait(remaining); 255 } else { 256 //Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1); 257 } 258 } finally { 259 args.recycle(); 260 } 261 break; 262 } 263 case MSG_QUERY_REMOTE_CALL_SERVICES: { 264 queryRemoteConnectionServices((RemoteServiceCallback) msg.obj); 265 break; 266 } 267 case MSG_SET_VIDEO_PROVIDER: { 268 SomeArgs args = (SomeArgs) msg.obj; 269 try { 270 call = mCallIdMapper.getCall(args.arg1); 271 IVideoProvider videoProvider = (IVideoProvider) args.arg2; 272 if (call != null) { 273 call.setVideoProvider(videoProvider); 274 } 275 } finally { 276 args.recycle(); 277 } 278 break; 279 } 280 case MSG_SET_IS_VOIP_AUDIO_MODE: { 281 call = mCallIdMapper.getCall(msg.obj); 282 if (call != null) { 283 call.setIsVoipAudioMode(msg.arg1 == 1); 284 } 285 break; 286 } 287 case MSG_SET_STATUS_HINTS: { 288 SomeArgs args = (SomeArgs) msg.obj; 289 try { 290 call = mCallIdMapper.getCall(args.arg1); 291 StatusHints statusHints = (StatusHints) args.arg2; 292 if (call != null) { 293 call.setStatusHints(statusHints); 294 } 295 } finally { 296 args.recycle(); 297 } 298 break; 299 } 300 case MSG_SET_ADDRESS: { 301 SomeArgs args = (SomeArgs) msg.obj; 302 try { 303 call = mCallIdMapper.getCall(args.arg1); 304 if (call != null) { 305 call.setHandle((Uri) args.arg2, args.argi1); 306 } 307 } finally { 308 args.recycle(); 309 } 310 break; 311 } 312 case MSG_SET_CALLER_DISPLAY_NAME: { 313 SomeArgs args = (SomeArgs) msg.obj; 314 try { 315 call = mCallIdMapper.getCall(args.arg1); 316 if (call != null) { 317 call.setCallerDisplayName((String) args.arg2, args.argi1); 318 } 319 } finally { 320 args.recycle(); 321 } 322 break; 323 } 324 case MSG_SET_VIDEO_STATE: { 325 call = mCallIdMapper.getCall(msg.obj); 326 if (call != null) { 327 call.setVideoState(msg.arg1); 328 } 329 break; 330 } 331 case MSG_SET_CONFERENCEABLE_CONNECTIONS: { 332 SomeArgs args = (SomeArgs) msg.obj; 333 try { 334 call = mCallIdMapper.getCall(args.arg1); 335 if (call != null ){ 336 @SuppressWarnings("unchecked") 337 List<String> conferenceableIds = (List<String>) args.arg2; 338 List<Call> conferenceableCalls = 339 new ArrayList<>(conferenceableIds.size()); 340 for (String otherId : (List<String>) args.arg2) { 341 Call otherCall = mCallIdMapper.getCall(otherId); 342 if (otherCall != null && otherCall != call) { 343 conferenceableCalls.add(otherCall); 344 } 345 } 346 call.setConferenceableCalls(conferenceableCalls); 347 } 348 } finally { 349 args.recycle(); 350 } 351 break; 352 } 353 } 354 } 355 }; 356 357 private final class Adapter extends IConnectionServiceAdapter.Stub { 358 359 @Override 360 public void handleCreateConnectionComplete( 361 String callId, 362 ConnectionRequest request, 363 ParcelableConnection connection) { 364 logIncoming("handleCreateConnectionComplete %s", request); 365 if (mCallIdMapper.isValidCallId(callId)) { 366 SomeArgs args = SomeArgs.obtain(); 367 args.arg1 = callId; 368 args.arg2 = request; 369 args.arg3 = connection; 370 mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_COMPLETE, args) 371 .sendToTarget(); 372 } 373 } 374 375 @Override 376 public void setActive(String callId) { 377 logIncoming("setActive %s", callId); 378 if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) { 379 mHandler.obtainMessage(MSG_SET_ACTIVE, callId).sendToTarget(); 380 } 381 } 382 383 @Override 384 public void setRinging(String callId) { 385 logIncoming("setRinging %s", callId); 386 if (mCallIdMapper.isValidCallId(callId)) { 387 mHandler.obtainMessage(MSG_SET_RINGING, callId).sendToTarget(); 388 } 389 } 390 391 @Override 392 public void setVideoProvider(String callId, IVideoProvider videoProvider) { 393 logIncoming("setVideoProvider %s", callId); 394 if (mCallIdMapper.isValidCallId(callId)) { 395 SomeArgs args = SomeArgs.obtain(); 396 args.arg1 = callId; 397 args.arg2 = videoProvider; 398 mHandler.obtainMessage(MSG_SET_VIDEO_PROVIDER, args).sendToTarget(); 399 } 400 } 401 402 @Override 403 public void setDialing(String callId) { 404 logIncoming("setDialing %s", callId); 405 if (mCallIdMapper.isValidCallId(callId)) { 406 mHandler.obtainMessage(MSG_SET_DIALING, callId).sendToTarget(); 407 } 408 } 409 410 @Override 411 public void setDisconnected(String callId, DisconnectCause disconnectCause) { 412 logIncoming("setDisconnected %s %s", callId, disconnectCause); 413 if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) { 414 Log.d(this, "disconnect call %s", callId); 415 SomeArgs args = SomeArgs.obtain(); 416 args.arg1 = callId; 417 args.arg2 = disconnectCause; 418 mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget(); 419 } 420 } 421 422 @Override 423 public void setOnHold(String callId) { 424 logIncoming("setOnHold %s", callId); 425 if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) { 426 mHandler.obtainMessage(MSG_SET_ON_HOLD, callId).sendToTarget(); 427 } 428 } 429 430 @Override 431 public void setRingbackRequested(String callId, boolean ringback) { 432 logIncoming("setRingbackRequested %s %b", callId, ringback); 433 if (mCallIdMapper.isValidCallId(callId)) { 434 mHandler.obtainMessage(MSG_SET_RINGBACK_REQUESTED, ringback ? 1 : 0, 0, callId) 435 .sendToTarget(); 436 } 437 } 438 439 @Override 440 public void removeCall(String callId) { 441 logIncoming("removeCall %s", callId); 442 if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) { 443 mHandler.obtainMessage(MSG_REMOVE_CALL, callId).sendToTarget(); 444 } 445 } 446 447 @Override 448 public void setCallCapabilities(String callId, int callCapabilities) { 449 logIncoming("setCallCapabilities %s %d", callId, callCapabilities); 450 if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) { 451 mHandler.obtainMessage(MSG_SET_CALL_CAPABILITIES, callCapabilities, 0, callId) 452 .sendToTarget(); 453 } else { 454 Log.w(this, "ID not valid for setCallCapabilities"); 455 } 456 } 457 458 @Override 459 public void setIsConferenced(String callId, String conferenceCallId) { 460 logIncoming("setIsConferenced %s %s", callId, conferenceCallId); 461 if (mCallIdMapper.isValidCallId(callId)) { 462 SomeArgs args = SomeArgs.obtain(); 463 args.arg1 = callId; 464 args.arg2 = conferenceCallId; 465 mHandler.obtainMessage(MSG_SET_IS_CONFERENCED, args).sendToTarget(); 466 } 467 } 468 469 @Override 470 public void addConferenceCall(String callId, ParcelableConference parcelableConference) { 471 logIncoming("addConferenceCall %s %s", callId, parcelableConference); 472 // We do not check call Ids here because we do not yet know the call ID for new 473 // conference calls. 474 SomeArgs args = SomeArgs.obtain(); 475 args.arg1 = callId; 476 args.arg2 = parcelableConference; 477 mHandler.obtainMessage(MSG_ADD_CONFERENCE_CALL, args).sendToTarget(); 478 } 479 480 @Override 481 public void onPostDialWait(String callId, String remaining) throws RemoteException { 482 logIncoming("onPostDialWait %s %s", callId, remaining); 483 if (mCallIdMapper.isValidCallId(callId)) { 484 SomeArgs args = SomeArgs.obtain(); 485 args.arg1 = callId; 486 args.arg2 = remaining; 487 mHandler.obtainMessage(MSG_ON_POST_DIAL_WAIT, args).sendToTarget(); 488 } 489 } 490 491 @Override 492 public void queryRemoteConnectionServices(RemoteServiceCallback callback) { 493 logIncoming("queryRemoteCSs"); 494 mHandler.obtainMessage(MSG_QUERY_REMOTE_CALL_SERVICES, callback).sendToTarget(); 495 } 496 497 @Override 498 public void setVideoState(String callId, int videoState) { 499 logIncoming("setVideoState %s %d", callId, videoState); 500 if (mCallIdMapper.isValidCallId(callId)) { 501 mHandler.obtainMessage(MSG_SET_VIDEO_STATE, videoState, 0, callId).sendToTarget(); 502 } 503 } 504 505 @Override 506 public void setIsVoipAudioMode(String callId, boolean isVoip) { 507 logIncoming("setIsVoipAudioMode %s %b", callId, isVoip); 508 if (mCallIdMapper.isValidCallId(callId)) { 509 mHandler.obtainMessage(MSG_SET_IS_VOIP_AUDIO_MODE, isVoip ? 1 : 0, 0, 510 callId).sendToTarget(); 511 } 512 } 513 514 @Override 515 public void setStatusHints(String callId, StatusHints statusHints) { 516 logIncoming("setStatusHints %s %s", callId, statusHints); 517 if (mCallIdMapper.isValidCallId(callId)) { 518 SomeArgs args = SomeArgs.obtain(); 519 args.arg1 = callId; 520 args.arg2 = statusHints; 521 mHandler.obtainMessage(MSG_SET_STATUS_HINTS, args).sendToTarget(); 522 } 523 } 524 525 @Override 526 public void setAddress(String callId, Uri address, int presentation) { 527 logIncoming("setAddress %s %s %d", callId, address, presentation); 528 if (mCallIdMapper.isValidCallId(callId)) { 529 SomeArgs args = SomeArgs.obtain(); 530 args.arg1 = callId; 531 args.arg2 = address; 532 args.argi1 = presentation; 533 mHandler.obtainMessage(MSG_SET_ADDRESS, args).sendToTarget(); 534 } 535 } 536 537 @Override 538 public void setCallerDisplayName( 539 String callId, String callerDisplayName, int presentation) { 540 logIncoming("setCallerDisplayName %s %s %d", callId, callerDisplayName, presentation); 541 if (mCallIdMapper.isValidCallId(callId)) { 542 SomeArgs args = SomeArgs.obtain(); 543 args.arg1 = callId; 544 args.arg2 = callerDisplayName; 545 args.argi1 = presentation; 546 mHandler.obtainMessage(MSG_SET_CALLER_DISPLAY_NAME, args).sendToTarget(); 547 } 548 } 549 550 @Override 551 public void setConferenceableConnections( 552 String callId, List<String> conferenceableCallIds) { 553 logIncoming("setConferenceableConnections %s %s", callId, conferenceableCallIds); 554 if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) { 555 SomeArgs args = SomeArgs.obtain(); 556 args.arg1 = callId; 557 args.arg2 = conferenceableCallIds; 558 mHandler.obtainMessage(MSG_SET_CONFERENCEABLE_CONNECTIONS, args).sendToTarget(); 559 } 560 } 561 } 562 563 private final Adapter mAdapter = new Adapter(); 564 private final CallsManager mCallsManager = CallsManager.getInstance(); 565 /** 566 * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is 567 * load factor before resizing, 1 means we only expect a single thread to 568 * access the map so make only a single shard 569 */ 570 private final Set<Call> mPendingConferenceCalls = Collections.newSetFromMap( 571 new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1)); 572 private final CallIdMapper mCallIdMapper = new CallIdMapper("ConnectionService"); 573 private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>(); 574 575 private Binder mBinder = new Binder(); 576 private IConnectionService mServiceInterface; 577 private final ConnectionServiceRepository mConnectionServiceRepository; 578 private final PhoneAccountRegistrar mPhoneAccountRegistrar; 579 580 /** 581 * Creates a connection service. 582 * 583 * @param componentName The component name of the service with which to bind. 584 * @param connectionServiceRepository Connection service repository. 585 * @param phoneAccountRegistrar Phone account registrar 586 * @param context The context. 587 */ 588 ConnectionServiceWrapper( 589 ComponentName componentName, 590 ConnectionServiceRepository connectionServiceRepository, 591 PhoneAccountRegistrar phoneAccountRegistrar, 592 Context context) { 593 super(ConnectionService.SERVICE_INTERFACE, componentName, context); 594 mConnectionServiceRepository = connectionServiceRepository; 595 phoneAccountRegistrar.addListener(new PhoneAccountRegistrar.Listener() { 596 // TODO -- Upon changes to PhoneAccountRegistrar, need to re-wire connections 597 // To do this, we must proxy remote ConnectionService objects 598 }); 599 mPhoneAccountRegistrar = phoneAccountRegistrar; 600 } 601 602 /** See {@link IConnectionService#addConnectionServiceAdapter}. */ 603 private void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) { 604 if (isServiceValid("addConnectionServiceAdapter")) { 605 try { 606 logOutgoing("addConnectionServiceAdapter %s", adapter); 607 mServiceInterface.addConnectionServiceAdapter(adapter); 608 } catch (RemoteException e) { 609 } 610 } 611 } 612 613 /** 614 * Creates a new connection for a new outgoing call or to attach to an existing incoming call. 615 */ 616 void createConnection(final Call call, final CreateConnectionResponse response) { 617 Log.d(this, "createConnection(%s) via %s.", call, getComponentName()); 618 BindCallback callback = new BindCallback() { 619 @Override 620 public void onSuccess() { 621 String callId = mCallIdMapper.getCallId(call); 622 mPendingResponses.put(callId, response); 623 624 GatewayInfo gatewayInfo = call.getGatewayInfo(); 625 Bundle extras = call.getExtras(); 626 if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null && 627 gatewayInfo.getOriginalAddress() != null) { 628 extras = (Bundle) extras.clone(); 629 extras.putString( 630 TelecomManager.GATEWAY_PROVIDER_PACKAGE, 631 gatewayInfo.getGatewayProviderPackageName()); 632 extras.putParcelable( 633 TelecomManager.GATEWAY_ORIGINAL_ADDRESS, 634 gatewayInfo.getOriginalAddress()); 635 } 636 637 try { 638 mServiceInterface.createConnection( 639 call.getConnectionManagerPhoneAccount(), 640 callId, 641 new ConnectionRequest( 642 call.getTargetPhoneAccount(), 643 call.getHandle(), 644 extras, 645 call.getVideoState()), 646 call.isIncoming(), 647 call.isUnknown()); 648 } catch (RemoteException e) { 649 Log.e(this, e, "Failure to createConnection -- %s", getComponentName()); 650 mPendingResponses.remove(callId).handleCreateConnectionFailure( 651 new DisconnectCause(DisconnectCause.ERROR, e.toString())); 652 } 653 } 654 655 @Override 656 public void onFailure() { 657 Log.e(this, new Exception(), "Failure to call %s", getComponentName()); 658 response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR)); 659 } 660 }; 661 662 mBinder.bind(callback); 663 } 664 665 /** @see ConnectionService#abort(String) */ 666 void abort(Call call) { 667 // Clear out any pending outgoing call data 668 final String callId = mCallIdMapper.getCallId(call); 669 670 // If still bound, tell the connection service to abort. 671 if (callId != null && isServiceValid("abort")) { 672 try { 673 logOutgoing("abort %s", callId); 674 mServiceInterface.abort(callId); 675 } catch (RemoteException e) { 676 } 677 } 678 679 removeCall(call, new DisconnectCause(DisconnectCause.LOCAL)); 680 } 681 682 /** @see ConnectionService#hold(String) */ 683 void hold(Call call) { 684 final String callId = mCallIdMapper.getCallId(call); 685 if (callId != null && isServiceValid("hold")) { 686 try { 687 logOutgoing("hold %s", callId); 688 mServiceInterface.hold(callId); 689 } catch (RemoteException e) { 690 } 691 } 692 } 693 694 /** @see ConnectionService#unhold(String) */ 695 void unhold(Call call) { 696 final String callId = mCallIdMapper.getCallId(call); 697 if (callId != null && isServiceValid("unhold")) { 698 try { 699 logOutgoing("unhold %s", callId); 700 mServiceInterface.unhold(callId); 701 } catch (RemoteException e) { 702 } 703 } 704 } 705 706 /** @see ConnectionService#onAudioStateChanged(String,AudioState) */ 707 void onAudioStateChanged(Call activeCall, AudioState audioState) { 708 final String callId = mCallIdMapper.getCallId(activeCall); 709 if (callId != null && isServiceValid("onAudioStateChanged")) { 710 try { 711 logOutgoing("onAudioStateChanged %s %s", callId, audioState); 712 mServiceInterface.onAudioStateChanged(callId, audioState); 713 } catch (RemoteException e) { 714 } 715 } 716 } 717 718 /** @see ConnectionService#disconnect(String) */ 719 void disconnect(Call call) { 720 final String callId = mCallIdMapper.getCallId(call); 721 if (callId != null && isServiceValid("disconnect")) { 722 try { 723 logOutgoing("disconnect %s", callId); 724 mServiceInterface.disconnect(callId); 725 } catch (RemoteException e) { 726 } 727 } 728 } 729 730 /** @see ConnectionService#answer(String,int) */ 731 void answer(Call call, int videoState) { 732 final String callId = mCallIdMapper.getCallId(call); 733 if (callId != null && isServiceValid("answer")) { 734 try { 735 logOutgoing("answer %s %d", callId, videoState); 736 if (videoState == VideoProfile.VideoState.AUDIO_ONLY) { 737 mServiceInterface.answer(callId); 738 } else { 739 mServiceInterface.answerVideo(callId, videoState); 740 } 741 } catch (RemoteException e) { 742 } 743 } 744 } 745 746 /** @see ConnectionService#reject(String) */ 747 void reject(Call call) { 748 final String callId = mCallIdMapper.getCallId(call); 749 if (callId != null && isServiceValid("reject")) { 750 try { 751 logOutgoing("reject %s", callId); 752 mServiceInterface.reject(callId); 753 } catch (RemoteException e) { 754 } 755 } 756 } 757 758 /** @see ConnectionService#playDtmfTone(String,char) */ 759 void playDtmfTone(Call call, char digit) { 760 final String callId = mCallIdMapper.getCallId(call); 761 if (callId != null && isServiceValid("playDtmfTone")) { 762 try { 763 logOutgoing("playDtmfTone %s %c", callId, digit); 764 mServiceInterface.playDtmfTone(callId, digit); 765 } catch (RemoteException e) { 766 } 767 } 768 } 769 770 /** @see ConnectionService#stopDtmfTone(String) */ 771 void stopDtmfTone(Call call) { 772 final String callId = mCallIdMapper.getCallId(call); 773 if (callId != null && isServiceValid("stopDtmfTone")) { 774 try { 775 logOutgoing("stopDtmfTone %s",callId); 776 mServiceInterface.stopDtmfTone(callId); 777 } catch (RemoteException e) { 778 } 779 } 780 } 781 782 void addCall(Call call) { 783 if (mCallIdMapper.getCallId(call) == null) { 784 mCallIdMapper.addCall(call); 785 } 786 } 787 788 /** 789 * Associates newCall with this connection service by replacing callToReplace. 790 */ 791 void replaceCall(Call newCall, Call callToReplace) { 792 Preconditions.checkState(callToReplace.getConnectionService() == this); 793 mCallIdMapper.replaceCall(newCall, callToReplace); 794 } 795 796 void removeCall(Call call) { 797 removeCall(call, new DisconnectCause(DisconnectCause.ERROR)); 798 } 799 800 void removeCall(String callId, DisconnectCause disconnectCause) { 801 CreateConnectionResponse response = mPendingResponses.remove(callId); 802 if (response != null) { 803 response.handleCreateConnectionFailure(disconnectCause); 804 } 805 806 mCallIdMapper.removeCall(callId); 807 } 808 809 void removeCall(Call call, DisconnectCause disconnectCause) { 810 CreateConnectionResponse response = mPendingResponses.remove(mCallIdMapper.getCallId(call)); 811 if (response != null) { 812 response.handleCreateConnectionFailure(disconnectCause); 813 } 814 815 mCallIdMapper.removeCall(call); 816 } 817 818 void onPostDialContinue(Call call, boolean proceed) { 819 final String callId = mCallIdMapper.getCallId(call); 820 if (callId != null && isServiceValid("onPostDialContinue")) { 821 try { 822 logOutgoing("onPostDialContinue %s %b", callId, proceed); 823 mServiceInterface.onPostDialContinue(callId, proceed); 824 } catch (RemoteException ignored) { 825 } 826 } 827 } 828 829 void conference(final Call call, Call otherCall) { 830 final String callId = mCallIdMapper.getCallId(call); 831 final String otherCallId = mCallIdMapper.getCallId(otherCall); 832 if (callId != null && otherCallId != null && isServiceValid("conference")) { 833 try { 834 logOutgoing("conference %s %s", callId, otherCallId); 835 mServiceInterface.conference(callId, otherCallId); 836 } catch (RemoteException ignored) { 837 } 838 } 839 } 840 841 void splitFromConference(Call call) { 842 final String callId = mCallIdMapper.getCallId(call); 843 if (callId != null && isServiceValid("splitFromConference")) { 844 try { 845 logOutgoing("splitFromConference %s", callId); 846 mServiceInterface.splitFromConference(callId); 847 } catch (RemoteException ignored) { 848 } 849 } 850 } 851 852 void mergeConference(Call call) { 853 final String callId = mCallIdMapper.getCallId(call); 854 if (callId != null && isServiceValid("mergeConference")) { 855 try { 856 logOutgoing("mergeConference %s", callId); 857 mServiceInterface.mergeConference(callId); 858 } catch (RemoteException ignored) { 859 } 860 } 861 } 862 863 void swapConference(Call call) { 864 final String callId = mCallIdMapper.getCallId(call); 865 if (callId != null && isServiceValid("swapConference")) { 866 try { 867 logOutgoing("swapConference %s", callId); 868 mServiceInterface.swapConference(callId); 869 } catch (RemoteException ignored) { 870 } 871 } 872 } 873 874 /** {@inheritDoc} */ 875 @Override 876 protected void setServiceInterface(IBinder binder) { 877 if (binder == null) { 878 // We have lost our service connection. Notify the world that this service is done. 879 // We must notify the adapter before CallsManager. The adapter will force any pending 880 // outgoing calls to try the next service. This needs to happen before CallsManager 881 // tries to clean up any calls still associated with this service. 882 handleConnectionServiceDeath(); 883 CallsManager.getInstance().handleConnectionServiceDeath(this); 884 mServiceInterface = null; 885 } else { 886 mServiceInterface = IConnectionService.Stub.asInterface(binder); 887 addConnectionServiceAdapter(mAdapter); 888 } 889 } 890 891 private void handleCreateConnectionComplete( 892 String callId, 893 ConnectionRequest request, 894 ParcelableConnection connection) { 895 // TODO: Note we are not using parameter "request", which is a side effect of our tacit 896 // assumption that we have at most one outgoing connection attempt per ConnectionService. 897 // This may not continue to be the case. 898 if (connection.getState() == Connection.STATE_DISCONNECTED) { 899 // A connection that begins in the DISCONNECTED state is an indication of 900 // failure to connect; we handle all failures uniformly 901 removeCall(callId, connection.getDisconnectCause()); 902 } else { 903 // Successful connection 904 if (mPendingResponses.containsKey(callId)) { 905 mPendingResponses.remove(callId) 906 .handleCreateConnectionSuccess(mCallIdMapper, connection); 907 } 908 } 909 } 910 911 /** 912 * Called when the associated connection service dies. 913 */ 914 private void handleConnectionServiceDeath() { 915 if (!mPendingResponses.isEmpty()) { 916 CreateConnectionResponse[] responses = mPendingResponses.values().toArray( 917 new CreateConnectionResponse[mPendingResponses.values().size()]); 918 mPendingResponses.clear(); 919 for (int i = 0; i < responses.length; i++) { 920 responses[i].handleCreateConnectionFailure( 921 new DisconnectCause(DisconnectCause.ERROR)); 922 } 923 } 924 mCallIdMapper.clear(); 925 } 926 927 private void logIncoming(String msg, Object... params) { 928 Log.d(this, "ConnectionService -> Telecom: " + msg, params); 929 } 930 931 private void logOutgoing(String msg, Object... params) { 932 Log.d(this, "Telecom -> ConnectionService: " + msg, params); 933 } 934 935 private void queryRemoteConnectionServices(final RemoteServiceCallback callback) { 936 // Only give remote connection services to this connection service if it is listed as 937 // the connection manager. 938 PhoneAccountHandle simCallManager = mPhoneAccountRegistrar.getSimCallManager(); 939 Log.d(this, "queryRemoteConnectionServices finds simCallManager = %s", simCallManager); 940 if (simCallManager == null || 941 !simCallManager.getComponentName().equals(getComponentName())) { 942 noRemoteServices(callback); 943 return; 944 } 945 946 // Make a list of ConnectionServices that are listed as being associated with SIM accounts 947 final Set<ConnectionServiceWrapper> simServices = Collections.newSetFromMap( 948 new ConcurrentHashMap<ConnectionServiceWrapper, Boolean>(8, 0.9f, 1)); 949 for (PhoneAccountHandle handle : mPhoneAccountRegistrar.getCallCapablePhoneAccounts()) { 950 PhoneAccount account = mPhoneAccountRegistrar.getPhoneAccount(handle); 951 if ((account.getCapabilities() & PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) != 0) { 952 ConnectionServiceWrapper service = 953 mConnectionServiceRepository.getService(handle.getComponentName()); 954 if (service != null) { 955 simServices.add(service); 956 } 957 } 958 } 959 960 final List<ComponentName> simServiceComponentNames = new ArrayList<>(); 961 final List<IBinder> simServiceBinders = new ArrayList<>(); 962 963 Log.v(this, "queryRemoteConnectionServices, simServices = %s", simServices); 964 965 for (ConnectionServiceWrapper simService : simServices) { 966 if (simService == this) { 967 // Only happens in the unlikely case that a SIM service is also a SIM call manager 968 continue; 969 } 970 971 final ConnectionServiceWrapper currentSimService = simService; 972 973 currentSimService.mBinder.bind(new BindCallback() { 974 @Override 975 public void onSuccess() { 976 Log.d(this, "Adding simService %s", currentSimService.getComponentName()); 977 simServiceComponentNames.add(currentSimService.getComponentName()); 978 simServiceBinders.add(currentSimService.mServiceInterface.asBinder()); 979 maybeComplete(); 980 } 981 982 @Override 983 public void onFailure() { 984 Log.d(this, "Failed simService %s", currentSimService.getComponentName()); 985 // We know maybeComplete() will always be a no-op from now on, so go ahead and 986 // signal failure of the entire request 987 noRemoteServices(callback); 988 } 989 990 private void maybeComplete() { 991 if (simServiceComponentNames.size() == simServices.size()) { 992 setRemoteServices(callback, simServiceComponentNames, simServiceBinders); 993 } 994 } 995 }); 996 } 997 } 998 999 private void setRemoteServices( 1000 RemoteServiceCallback callback, 1001 List<ComponentName> componentNames, 1002 List<IBinder> binders) { 1003 try { 1004 callback.onResult(componentNames, binders); 1005 } catch (RemoteException e) { 1006 Log.e(this, e, "Contacting ConnectionService %s", 1007 ConnectionServiceWrapper.this.getComponentName()); 1008 } 1009 } 1010 1011 private void noRemoteServices(RemoteServiceCallback callback) { 1012 try { 1013 callback.onResult(Collections.EMPTY_LIST, Collections.EMPTY_LIST); 1014 } catch (RemoteException e) { 1015 Log.e(this, e, "Contacting ConnectionService %s", this.getComponentName()); 1016 } 1017 } 1018} 1019