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