TelephonyConnection.java revision 53b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0f
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 com.android.services.telephony; 18 19import android.net.Uri; 20import android.os.AsyncResult; 21import android.os.Handler; 22import android.os.Message; 23import android.telecomm.AudioState; 24import android.telecomm.Connection; 25import android.telecomm.PhoneCapabilities; 26import android.telephony.DisconnectCause; 27 28import com.android.internal.telephony.Call; 29import com.android.internal.telephony.CallStateException; 30import com.android.internal.telephony.Connection.PostDialListener; 31import com.android.internal.telephony.Phone; 32 33import java.lang.Override; 34import java.util.Objects; 35 36/** 37 * Base class for CDMA and GSM connections. 38 */ 39abstract class TelephonyConnection extends Connection { 40 private static final int MSG_PRECISE_CALL_STATE_CHANGED = 1; 41 private static final int MSG_RINGBACK_TONE = 2; 42 43 private final Handler mHandler = new Handler() { 44 @Override 45 public void handleMessage(Message msg) { 46 switch (msg.what) { 47 case MSG_PRECISE_CALL_STATE_CHANGED: 48 Log.v(TelephonyConnection.this, "MSG_PRECISE_CALL_STATE_CHANGED"); 49 updateState(); 50 break; 51 case MSG_RINGBACK_TONE: 52 Log.v(TelephonyConnection.this, "MSG_RINGBACK_TONE"); 53 // TODO: This code assumes that there is only one connection in the foreground 54 // call, in other words, it punts on network-mediated conference calling. 55 if (getOriginalConnection() != getForegroundConnection()) { 56 Log.v(TelephonyConnection.this, "handleMessage, original connection is " + 57 "not foreground connection, skipping"); 58 return; 59 } 60 setRequestingRingback((Boolean) ((AsyncResult) msg.obj).result); 61 break; 62 } 63 } 64 }; 65 66 private final PostDialListener mPostDialListener = new PostDialListener() { 67 @Override 68 public void onPostDialWait() { 69 Log.v(TelephonyConnection.this, "onPostDialWait"); 70 if (mOriginalConnection != null) { 71 setPostDialWait(mOriginalConnection.getRemainingPostDialString()); 72 } 73 } 74 }; 75 76 /** 77 * Listener for listening to events in the {@link com.android.internal.telephony.Connection}. 78 */ 79 private final com.android.internal.telephony.Connection.Listener mOriginalConnectionListener = 80 new com.android.internal.telephony.Connection.ListenerBase() { 81 @Override 82 public void onVideoStateChanged(int videoState) { 83 setVideoState(videoState); 84 } 85 86 /** 87 * The {@link com.android.internal.telephony.Connection} has reported a change in local 88 * video capability. 89 * 90 * @param capable True if capable. 91 */ 92 @Override 93 public void onLocalVideoCapabilityChanged(boolean capable) { 94 setLocalVideoCapable(capable); 95 } 96 97 /** 98 * The {@link com.android.internal.telephony.Connection} has reported a change in remote 99 * video capability. 100 * 101 * @param capable True if capable. 102 */ 103 @Override 104 public void onRemoteVideoCapabilityChanged(boolean capable) { 105 setRemoteVideoCapable(capable); 106 } 107 108 /** 109 * The {@link com.android.internal.telephony.Connection} has reported a change in the 110 * video call provider. 111 * 112 * @param videoProvider The video call provider. 113 */ 114 @Override 115 public void onVideoProviderChanged(VideoProvider videoProvider) { 116 setVideoProvider(videoProvider); 117 } 118 119 /** 120 * Used by the {@link com.android.internal.telephony.Connection} to report a change in the 121 * audio quality for the current call. 122 * 123 * @param audioQuality The audio quality. 124 */ 125 @Override 126 public void onAudioQualityChanged(int audioQuality) { 127 setAudioQuality(audioQuality); 128 } 129 }; 130 131 private com.android.internal.telephony.Connection mOriginalConnection; 132 private Call.State mOriginalConnectionState = Call.State.IDLE; 133 134 /** 135 * Determines if the {@link TelephonyConnection} has local video capabilities. 136 * This is used when {@link TelephonyConnection#updateCallCapabilities()}} is called, 137 * ensuring the appropriate {@link PhoneCapabilities} are set. Since {@link PhoneCapabilities} 138 * can be rebuilt at any time it is necessary to track the video capabilities between rebuild. 139 * The {@link PhoneCapabilities} (including video capabilities) are communicated to the telecomm 140 * layer. 141 */ 142 private boolean mLocalVideoCapable; 143 144 /** 145 * Determines if the {@link TelephonyConnection} has remote video capabilities. 146 * This is used when {@link TelephonyConnection#updateCallCapabilities()}} is called, 147 * ensuring the appropriate {@link PhoneCapabilities} are set. Since {@link PhoneCapabilities} 148 * can be rebuilt at any time it is necessary to track the video capabilities between rebuild. 149 * The {@link PhoneCapabilities} (including video capabilities) are communicated to the telecomm 150 * layer. 151 */ 152 private boolean mRemoteVideoCapable; 153 154 /** 155 * Determines the current audio quality for the {@link TelephonyConnection}. 156 * This is used when {@link TelephonyConnection#updateCallCapabilities}} is called to indicate 157 * whether a call has the {@link android.telecomm.CallCapabilities#VoLTE} capability. 158 */ 159 private int mAudioQuality; 160 161 protected TelephonyConnection(com.android.internal.telephony.Connection originalConnection) { 162 if (originalConnection != null) { 163 setOriginalConnection(originalConnection); 164 } 165 } 166 167 @Override 168 public void onSetAudioState(AudioState audioState) { 169 // TODO: update TTY mode. 170 if (getPhone() != null) { 171 getPhone().setEchoSuppressionEnabled(); 172 } 173 } 174 175 @Override 176 public void onSetState(int state) { 177 Log.v(this, "onSetState, state: " + Connection.stateToString(state)); 178 } 179 180 @Override 181 public void onDisconnect() { 182 Log.v(this, "onDisconnect"); 183 hangup(DisconnectCause.LOCAL); 184 } 185 186 @Override 187 public void onSeparate() { 188 Log.v(this, "onSeparate"); 189 if (mOriginalConnection != null) { 190 try { 191 mOriginalConnection.separate(); 192 } catch (CallStateException e) { 193 Log.e(this, e, "Call to Connection.separate failed with exception"); 194 } 195 } 196 } 197 198 @Override 199 public void onAbort() { 200 Log.v(this, "onAbort"); 201 hangup(DisconnectCause.LOCAL); 202 } 203 204 @Override 205 public void onHold() { 206 performHold(); 207 } 208 209 @Override 210 public void onUnhold() { 211 performUnhold(); 212 } 213 214 @Override 215 public void onAnswer(int videoState) { 216 Log.v(this, "onAnswer"); 217 // TODO: Tons of hairy logic is missing here around multiple active calls on 218 // CDMA devices. See {@link CallManager.acceptCall}. 219 220 if (isValidRingingCall() && getPhone() != null) { 221 try { 222 getPhone().acceptCall(videoState); 223 } catch (CallStateException e) { 224 Log.e(this, e, "Failed to accept call."); 225 } 226 } 227 } 228 229 @Override 230 public void onReject() { 231 Log.v(this, "onReject"); 232 if (isValidRingingCall()) { 233 hangup(DisconnectCause.INCOMING_REJECTED); 234 } 235 super.onReject(); 236 } 237 238 @Override 239 public void onPostDialContinue(boolean proceed) { 240 Log.v(this, "onPostDialContinue, proceed: " + proceed); 241 if (mOriginalConnection != null) { 242 if (proceed) { 243 mOriginalConnection.proceedAfterWaitChar(); 244 } else { 245 mOriginalConnection.cancelPostDial(); 246 } 247 } 248 } 249 250 @Override 251 public void onPhoneAccountClicked() { 252 Log.v(this, "onPhoneAccountClicked"); 253 } 254 255 public void performHold() { 256 Log.v(this, "performHold"); 257 // TODO: Can dialing calls be put on hold as well since they take up the 258 // foreground call slot? 259 if (Call.State.ACTIVE == mOriginalConnectionState) { 260 Log.v(this, "Holding active call"); 261 try { 262 Phone phone = mOriginalConnection.getCall().getPhone(); 263 Call ringingCall = phone.getRingingCall(); 264 265 // Although the method says switchHoldingAndActive, it eventually calls a RIL method 266 // called switchWaitingOrHoldingAndActive. What this means is that if we try to put 267 // a call on hold while a call-waiting call exists, it'll end up accepting the 268 // call-waiting call, which is bad if that was not the user's intention. We are 269 // cheating here and simply skipping it because we know any attempt to hold a call 270 // while a call-waiting call is happening is likely a request from Telecomm prior to 271 // accepting the call-waiting call. 272 // TODO: Investigate a better solution. It would be great here if we 273 // could "fake" hold by silencing the audio and microphone streams for this call 274 // instead of actually putting it on hold. 275 if (ringingCall.getState() != Call.State.WAITING) { 276 phone.switchHoldingAndActive(); 277 } 278 279 // TODO: Cdma calls are slightly different. 280 } catch (CallStateException e) { 281 Log.e(this, e, "Exception occurred while trying to put call on hold."); 282 } 283 } else { 284 Log.w(this, "Cannot put a call that is not currently active on hold."); 285 } 286 } 287 288 public void performUnhold() { 289 Log.v(this, "performUnhold"); 290 if (Call.State.HOLDING == mOriginalConnectionState) { 291 try { 292 // TODO: This doesn't handle multiple calls across connection services yet 293 mOriginalConnection.getCall().getPhone().switchHoldingAndActive(); 294 } catch (CallStateException e) { 295 Log.e(this, e, "Exception occurred while trying to release call from hold."); 296 } 297 } else { 298 Log.w(this, "Cannot release a call that is not already on hold from hold."); 299 } 300 } 301 302 public void performConference(TelephonyConnection otherConnection) {} 303 304 protected abstract int buildCallCapabilities(); 305 306 protected final void updateCallCapabilities() { 307 int newCallCapabilities = buildCallCapabilities(); 308 newCallCapabilities = applyVideoCapabilities(newCallCapabilities); 309 newCallCapabilities = applyAudioQualityCapabilities(newCallCapabilities); 310 311 if (getCallCapabilities() != newCallCapabilities) { 312 setCallCapabilities(newCallCapabilities); 313 } 314 } 315 316 protected final void updateHandle() { 317 updateCallCapabilities(); 318 if (mOriginalConnection != null) { 319 Uri handle = getHandleFromAddress(mOriginalConnection.getAddress()); 320 int presentation = mOriginalConnection.getNumberPresentation(); 321 if (!Objects.equals(handle, getHandle()) || 322 presentation != getHandlePresentation()) { 323 Log.v(this, "updateHandle, handle changed"); 324 setHandle(handle, presentation); 325 } 326 327 String name = mOriginalConnection.getCnapName(); 328 int namePresentation = mOriginalConnection.getCnapNamePresentation(); 329 if (!Objects.equals(name, getCallerDisplayName()) || 330 namePresentation != getCallerDisplayNamePresentation()) { 331 Log.v(this, "updateHandle, caller display name changed"); 332 setCallerDisplayName(name, namePresentation); 333 } 334 } 335 } 336 337 void onRemovedFromCallService() { 338 // Subclass can override this to do cleanup. 339 } 340 341 void setOriginalConnection(com.android.internal.telephony.Connection originalConnection) { 342 Log.v(this, "new TelephonyConnection, originalConnection: " + originalConnection); 343 mOriginalConnection = originalConnection; 344 getPhone().registerForPreciseCallStateChanged( 345 mHandler, MSG_PRECISE_CALL_STATE_CHANGED, null); 346 getPhone().registerForRingbackTone(mHandler, MSG_RINGBACK_TONE, null); 347 mOriginalConnection.addPostDialListener(mPostDialListener); 348 mOriginalConnection.addListener(mOriginalConnectionListener); 349 350 // Set video state and capabilities 351 setVideoState(mOriginalConnection.getVideoState()); 352 setLocalVideoCapable(mOriginalConnection.isLocalVideoCapable()); 353 setRemoteVideoCapable(mOriginalConnection.isRemoteVideoCapable()); 354 setVideoProvider(mOriginalConnection.getVideoProvider()); 355 setAudioQuality(mOriginalConnection.getAudioQuality()); 356 357 updateHandle(); 358 } 359 360 private void hangup(int disconnectCause) { 361 if (mOriginalConnection != null) { 362 try { 363 mOriginalConnection.hangup(); 364 365 // Set state deliberately since we are going to close() and will no longer be 366 // listening to state updates from mOriginalConnection 367 setDisconnected(disconnectCause, null); 368 } catch (CallStateException e) { 369 Log.e(this, e, "Call to Connection.hangup failed with exception"); 370 } 371 } 372 close(); 373 } 374 375 com.android.internal.telephony.Connection getOriginalConnection() { 376 return mOriginalConnection; 377 } 378 379 protected Call getCall() { 380 if (mOriginalConnection != null) { 381 return mOriginalConnection.getCall(); 382 } 383 return null; 384 } 385 386 Phone getPhone() { 387 Call call = getCall(); 388 if (call != null) { 389 return call.getPhone(); 390 } 391 return null; 392 } 393 394 private com.android.internal.telephony.Connection getForegroundConnection() { 395 if (getPhone() != null) { 396 return getPhone().getForegroundCall().getEarliestConnection(); 397 } 398 return null; 399 } 400 401 /** 402 * Checks to see the original connection corresponds to an active incoming call. Returns false 403 * if there is no such actual call, or if the associated call is not incoming (See 404 * {@link Call.State#isRinging}). 405 */ 406 private boolean isValidRingingCall() { 407 if (getPhone() == null) { 408 Log.v(this, "isValidRingingCall, phone is null"); 409 return false; 410 } 411 412 Call ringingCall = getPhone().getRingingCall(); 413 if (!ringingCall.getState().isRinging()) { 414 Log.v(this, "isValidRingingCall, ringing call is not in ringing state"); 415 return false; 416 } 417 418 if (ringingCall.getEarliestConnection() != mOriginalConnection) { 419 Log.v(this, "isValidRingingCall, ringing call connection does not match"); 420 return false; 421 } 422 423 Log.v(this, "isValidRingingCall, returning true"); 424 return true; 425 } 426 427 private void updateState() { 428 if (mOriginalConnection == null) { 429 return; 430 } 431 432 Call.State newState = mOriginalConnection.getState(); 433 Log.v(this, "Update state from %s to %s for %s", mOriginalConnectionState, newState, this); 434 if (mOriginalConnectionState != newState) { 435 mOriginalConnectionState = newState; 436 switch (newState) { 437 case IDLE: 438 break; 439 case ACTIVE: 440 setActive(); 441 break; 442 case HOLDING: 443 setOnHold(); 444 break; 445 case DIALING: 446 case ALERTING: 447 setDialing(); 448 break; 449 case INCOMING: 450 case WAITING: 451 setRinging(); 452 break; 453 case DISCONNECTED: 454 setDisconnected(mOriginalConnection.getDisconnectCause(), null); 455 close(); 456 break; 457 case DISCONNECTING: 458 break; 459 } 460 } 461 updateCallCapabilities(); 462 updateHandle(); 463 } 464 465 private void close() { 466 Log.v(this, "close"); 467 if (getPhone() != null) { 468 getPhone().unregisterForPreciseCallStateChanged(mHandler); 469 getPhone().unregisterForRingbackTone(mHandler); 470 } 471 mOriginalConnection = null; 472 destroy(); 473 } 474 475 /** 476 * Applies the video capability states to the CallCapabilities bit-mask. 477 * 478 * @param capabilities The CallCapabilities bit-mask. 479 * @return The capabilities with video capabilities applied. 480 */ 481 private int applyVideoCapabilities(int capabilities) { 482 int currentCapabilities = capabilities; 483 if (mRemoteVideoCapable) { 484 currentCapabilities = applyCapability(currentCapabilities, 485 PhoneCapabilities.SUPPORTS_VT_REMOTE); 486 } else { 487 currentCapabilities = removeCapability(currentCapabilities, 488 PhoneCapabilities.SUPPORTS_VT_REMOTE); 489 } 490 491 if (mLocalVideoCapable) { 492 currentCapabilities = applyCapability(currentCapabilities, 493 PhoneCapabilities.SUPPORTS_VT_LOCAL); 494 } else { 495 currentCapabilities = removeCapability(currentCapabilities, 496 PhoneCapabilities.SUPPORTS_VT_LOCAL); 497 } 498 return currentCapabilities; 499 } 500 501 /** 502 * Applies the audio capabilities to the {@code CallCapabilities} bit-mask. A call with high 503 * definition audio is considered to have the {@code VoLTE} call capability as VoLTE uses high 504 * definition audio. 505 * 506 * @param callCapabilities The {@code CallCapabilities} bit-mask. 507 * @return The capabilities with the audio capabilities applied. 508 */ 509 private int applyAudioQualityCapabilities(int callCapabilities) { 510 int currentCapabilities = callCapabilities; 511 512 if (mAudioQuality == 513 com.android.internal.telephony.Connection.AUDIO_QUALITY_HIGH_DEFINITION) { 514 currentCapabilities = applyCapability(currentCapabilities, PhoneCapabilities.VoLTE); 515 } else { 516 currentCapabilities = removeCapability(currentCapabilities, PhoneCapabilities.VoLTE); 517 } 518 519 return currentCapabilities; 520 } 521 522 /** 523 * Returns the local video capability state for the connection. 524 * 525 * @return {@code True} if the connection has local video capabilities. 526 */ 527 public boolean isLocalVideoCapable() { 528 return mLocalVideoCapable; 529 } 530 531 /** 532 * Returns the remote video capability state for the connection. 533 * 534 * @return {@code True} if the connection has remote video capabilities. 535 */ 536 public boolean isRemoteVideoCapable() { 537 return mRemoteVideoCapable; 538 } 539 540 /** 541 * Sets whether video capability is present locally. Used during rebuild of the 542 * {@link PhoneCapabilities} to set the video call capabilities. 543 * 544 * @param capable {@code True} if video capable. 545 */ 546 public void setLocalVideoCapable(boolean capable) { 547 mLocalVideoCapable = capable; 548 updateCallCapabilities(); 549 } 550 551 /** 552 * Sets whether video capability is present remotely. Used during rebuild of the 553 * {@link PhoneCapabilities} to set the video call capabilities. 554 * 555 * @param capable {@code True} if video capable. 556 */ 557 public void setRemoteVideoCapable(boolean capable) { 558 mRemoteVideoCapable = capable; 559 updateCallCapabilities(); 560 } 561 562 /** 563 * Sets the current call audio quality. Used during rebuild of the 564 * {@link PhoneCapabilities} to set or unset the {@link PhoneCapabilities#VoLTE} capability. 565 * 566 * @param audioQuality The audio quality. 567 */ 568 public void setAudioQuality(int audioQuality) { 569 mAudioQuality = audioQuality; 570 updateCallCapabilities(); 571 } 572 573 private static Uri getHandleFromAddress(String address) { 574 // Address can be null for blocked calls. 575 if (address == null) { 576 address = ""; 577 } 578 return Uri.fromParts(TelephonyConnectionService.SCHEME_TEL, address, null); 579 } 580 581 /** 582 * Applies a capability to a capabilities bit-mask. 583 * 584 * @param capabilities The capabilities bit-mask. 585 * @param capability The capability to apply. 586 * @return The capabilities bit-mask with the capability applied. 587 */ 588 private int applyCapability(int capabilities, int capability) { 589 int newCapabilities = capabilities | capability; 590 return newCapabilities; 591 } 592 593 /** 594 * Removes a capability from a capabilities bit-mask. 595 * 596 * @param capabilities The capabilities bit-mask. 597 * @param capability The capability to remove. 598 * @return The capabilities bit-mask with the capability removed. 599 */ 600 private int removeCapability(int capabilities, int capability) { 601 int newCapabilities = capabilities & ~capability; 602 return newCapabilities; 603 } 604} 605