ImsPhoneConnection.java revision 707644d8250cc5c3c2287068a9df210d7eb3e863
1/* 2 * Copyright (C) 2013 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.internal.telephony.imsphone; 18 19import android.content.Context; 20import android.net.Uri; 21import android.os.AsyncResult; 22import android.os.Handler; 23import android.os.Looper; 24import android.os.Message; 25import android.os.PowerManager; 26import android.os.Registrant; 27import android.os.SystemClock; 28import android.telecom.Log; 29import android.telephony.DisconnectCause; 30import android.telephony.PhoneNumberUtils; 31import android.telephony.Rlog; 32import android.text.TextUtils; 33 34import com.android.ims.ImsException; 35import com.android.ims.ImsStreamMediaProfile; 36import com.android.internal.telephony.CallStateException; 37import com.android.internal.telephony.Connection; 38import com.android.internal.telephony.Phone; 39import com.android.internal.telephony.PhoneConstants; 40import com.android.internal.telephony.UUSInfo; 41 42import com.android.ims.ImsCall; 43import com.android.ims.ImsCallProfile; 44 45/** 46 * {@hide} 47 */ 48public class ImsPhoneConnection extends Connection { 49 private static final String LOG_TAG = "ImsPhoneConnection"; 50 private static final boolean DBG = true; 51 52 //***** Instance Variables 53 54 private ImsPhoneCallTracker mOwner; 55 private ImsPhoneCall mParent; 56 private ImsCall mImsCall; 57 58 private String mPostDialString; // outgoing calls only 59 private boolean mDisconnected; 60 61 /* 62 int mIndex; // index in ImsPhoneCallTracker.connections[], -1 if unassigned 63 // The GSM index is 1 + this 64 */ 65 66 /* 67 * These time/timespan values are based on System.currentTimeMillis(), 68 * i.e., "wall clock" time. 69 */ 70 private long mDisconnectTime; 71 72 private int mNextPostDialChar; // index into postDialString 73 74 private int mCause = DisconnectCause.NOT_DISCONNECTED; 75 private PostDialState mPostDialState = PostDialState.NOT_STARTED; 76 private UUSInfo mUusInfo; 77 private Handler mHandler; 78 79 private PowerManager.WakeLock mPartialWakeLock; 80 81 // The cached connect time of the connection when it turns into a conference. 82 private long mConferenceConnectTime = 0; 83 84 //***** Event Constants 85 private static final int EVENT_DTMF_DONE = 1; 86 private static final int EVENT_PAUSE_DONE = 2; 87 private static final int EVENT_NEXT_POST_DIAL = 3; 88 private static final int EVENT_WAKE_LOCK_TIMEOUT = 4; 89 90 //***** Constants 91 private static final int PAUSE_DELAY_MILLIS = 3 * 1000; 92 private static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000; 93 94 //***** Inner Classes 95 96 class MyHandler extends Handler { 97 MyHandler(Looper l) {super(l);} 98 99 @Override 100 public void 101 handleMessage(Message msg) { 102 103 switch (msg.what) { 104 case EVENT_NEXT_POST_DIAL: 105 case EVENT_DTMF_DONE: 106 case EVENT_PAUSE_DONE: 107 processNextPostDialChar(); 108 break; 109 case EVENT_WAKE_LOCK_TIMEOUT: 110 releaseWakeLock(); 111 break; 112 } 113 } 114 } 115 116 //***** Constructors 117 118 /** This is probably an MT call */ 119 /*package*/ 120 ImsPhoneConnection(Context context, ImsCall imsCall, ImsPhoneCallTracker ct, ImsPhoneCall parent) { 121 createWakeLock(context); 122 acquireWakeLock(); 123 124 mOwner = ct; 125 mHandler = new MyHandler(mOwner.getLooper()); 126 mImsCall = imsCall; 127 128 if ((imsCall != null) && (imsCall.getCallProfile() != null)) { 129 mAddress = imsCall.getCallProfile().getCallExtra(ImsCallProfile.EXTRA_OI); 130 mCnapName = imsCall.getCallProfile().getCallExtra(ImsCallProfile.EXTRA_CNA); 131 mNumberPresentation = ImsCallProfile.OIRToPresentation( 132 imsCall.getCallProfile().getCallExtraInt(ImsCallProfile.EXTRA_OIR)); 133 mCnapNamePresentation = ImsCallProfile.OIRToPresentation( 134 imsCall.getCallProfile().getCallExtraInt(ImsCallProfile.EXTRA_CNAP)); 135 updateMediaCapabilities(imsCall); 136 } else { 137 mNumberPresentation = PhoneConstants.PRESENTATION_UNKNOWN; 138 mCnapNamePresentation = PhoneConstants.PRESENTATION_UNKNOWN; 139 } 140 141 mIsIncoming = true; 142 mCreateTime = System.currentTimeMillis(); 143 mUusInfo = null; 144 145 //mIndex = index; 146 147 updateWifiState(); 148 149 mParent = parent; 150 mParent.attach(this, ImsPhoneCall.State.INCOMING); 151 } 152 153 /** This is an MO call, created when dialing */ 154 /*package*/ 155 ImsPhoneConnection(Context context, String dialString, ImsPhoneCallTracker ct, ImsPhoneCall parent) { 156 createWakeLock(context); 157 acquireWakeLock(); 158 159 mOwner = ct; 160 mHandler = new MyHandler(mOwner.getLooper()); 161 162 mDialString = dialString; 163 164 mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString); 165 mPostDialString = PhoneNumberUtils.extractPostDialPortion(dialString); 166 167 //mIndex = -1; 168 169 mIsIncoming = false; 170 mCnapName = null; 171 mCnapNamePresentation = PhoneConstants.PRESENTATION_ALLOWED; 172 mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED; 173 mCreateTime = System.currentTimeMillis(); 174 175 mParent = parent; 176 parent.attachFake(this, ImsPhoneCall.State.DIALING); 177 } 178 179 public void dispose() { 180 } 181 182 static boolean 183 equalsHandlesNulls (Object a, Object b) { 184 return (a == null) ? (b == null) : a.equals (b); 185 } 186 187 @Override 188 public String getOrigDialString(){ 189 return mDialString; 190 } 191 192 @Override 193 public ImsPhoneCall getCall() { 194 return mParent; 195 } 196 197 @Override 198 public long getDisconnectTime() { 199 return mDisconnectTime; 200 } 201 202 @Override 203 public long getHoldingStartTime() { 204 return mHoldingStartTime; 205 } 206 207 @Override 208 public long getHoldDurationMillis() { 209 if (getState() != ImsPhoneCall.State.HOLDING) { 210 // If not holding, return 0 211 return 0; 212 } else { 213 return SystemClock.elapsedRealtime() - mHoldingStartTime; 214 } 215 } 216 217 @Override 218 public int getDisconnectCause() { 219 return mCause; 220 } 221 222 public void setDisconnectCause(int cause) { 223 mCause = cause; 224 } 225 226 @Override 227 public String getVendorDisconnectCause() { 228 return null; 229 } 230 231 public ImsPhoneCallTracker getOwner () { 232 return mOwner; 233 } 234 235 @Override 236 public ImsPhoneCall.State getState() { 237 if (mDisconnected) { 238 return ImsPhoneCall.State.DISCONNECTED; 239 } else { 240 return super.getState(); 241 } 242 } 243 244 @Override 245 public void hangup() throws CallStateException { 246 if (!mDisconnected) { 247 mOwner.hangup(this); 248 } else { 249 throw new CallStateException ("disconnected"); 250 } 251 } 252 253 @Override 254 public void separate() throws CallStateException { 255 throw new CallStateException ("not supported"); 256 } 257 258 @Override 259 public PostDialState getPostDialState() { 260 return mPostDialState; 261 } 262 263 @Override 264 public void proceedAfterWaitChar() { 265 if (mPostDialState != PostDialState.WAIT) { 266 Rlog.w(LOG_TAG, "ImsPhoneConnection.proceedAfterWaitChar(): Expected " 267 + "getPostDialState() to be WAIT but was " + mPostDialState); 268 return; 269 } 270 271 setPostDialState(PostDialState.STARTED); 272 273 processNextPostDialChar(); 274 } 275 276 @Override 277 public void proceedAfterWildChar(String str) { 278 if (mPostDialState != PostDialState.WILD) { 279 Rlog.w(LOG_TAG, "ImsPhoneConnection.proceedAfterWaitChar(): Expected " 280 + "getPostDialState() to be WILD but was " + mPostDialState); 281 return; 282 } 283 284 setPostDialState(PostDialState.STARTED); 285 286 // make a new postDialString, with the wild char replacement string 287 // at the beginning, followed by the remaining postDialString. 288 289 StringBuilder buf = new StringBuilder(str); 290 buf.append(mPostDialString.substring(mNextPostDialChar)); 291 mPostDialString = buf.toString(); 292 mNextPostDialChar = 0; 293 if (Phone.DEBUG_PHONE) { 294 Rlog.d(LOG_TAG, "proceedAfterWildChar: new postDialString is " + 295 mPostDialString); 296 } 297 298 processNextPostDialChar(); 299 } 300 301 @Override 302 public void cancelPostDial() { 303 setPostDialState(PostDialState.CANCELLED); 304 } 305 306 /** 307 * Called when this Connection is being hung up locally (eg, user pressed "end") 308 */ 309 void 310 onHangupLocal() { 311 mCause = DisconnectCause.LOCAL; 312 } 313 314 /** Called when the connection has been disconnected */ 315 public boolean 316 onDisconnect(int cause) { 317 Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause); 318 if (mCause != DisconnectCause.LOCAL) mCause = cause; 319 return onDisconnect(); 320 } 321 322 /*package*/ boolean 323 onDisconnect() { 324 boolean changed = false; 325 326 if (!mDisconnected) { 327 //mIndex = -1; 328 329 mDisconnectTime = System.currentTimeMillis(); 330 mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal; 331 mDisconnected = true; 332 333 mOwner.mPhone.notifyDisconnect(this); 334 335 if (mParent != null) { 336 changed = mParent.connectionDisconnected(this); 337 } else { 338 Rlog.d(LOG_TAG, "onDisconnect: no parent"); 339 } 340 if (mImsCall != null) mImsCall.close(); 341 mImsCall = null; 342 } 343 releaseWakeLock(); 344 return changed; 345 } 346 347 /** 348 * An incoming or outgoing call has connected 349 */ 350 void 351 onConnectedInOrOut() { 352 mConnectTime = System.currentTimeMillis(); 353 mConnectTimeReal = SystemClock.elapsedRealtime(); 354 mDuration = 0; 355 356 if (Phone.DEBUG_PHONE) { 357 Rlog.d(LOG_TAG, "onConnectedInOrOut: connectTime=" + mConnectTime); 358 } 359 360 if (!mIsIncoming) { 361 // outgoing calls only 362 processNextPostDialChar(); 363 } 364 releaseWakeLock(); 365 } 366 367 /*package*/ void 368 onStartedHolding() { 369 mHoldingStartTime = SystemClock.elapsedRealtime(); 370 } 371 /** 372 * Performs the appropriate action for a post-dial char, but does not 373 * notify application. returns false if the character is invalid and 374 * should be ignored 375 */ 376 private boolean 377 processPostDialChar(char c) { 378 if (PhoneNumberUtils.is12Key(c)) { 379 mOwner.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE)); 380 } else if (c == PhoneNumberUtils.PAUSE) { 381 // From TS 22.101: 382 // It continues... 383 // Upon the called party answering the UE shall send the DTMF digits 384 // automatically to the network after a delay of 3 seconds( 20 ). 385 // The digits shall be sent according to the procedures and timing 386 // specified in 3GPP TS 24.008 [13]. The first occurrence of the 387 // "DTMF Control Digits Separator" shall be used by the ME to 388 // distinguish between the addressing digits (i.e. the phone number) 389 // and the DTMF digits. Upon subsequent occurrences of the 390 // separator, 391 // the UE shall pause again for 3 seconds ( 20 ) before sending 392 // any further DTMF digits. 393 mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_PAUSE_DONE), 394 PAUSE_DELAY_MILLIS); 395 } else if (c == PhoneNumberUtils.WAIT) { 396 setPostDialState(PostDialState.WAIT); 397 } else if (c == PhoneNumberUtils.WILD) { 398 setPostDialState(PostDialState.WILD); 399 } else { 400 return false; 401 } 402 403 return true; 404 } 405 406 @Override 407 public String 408 getRemainingPostDialString() { 409 if (mPostDialState == PostDialState.CANCELLED 410 || mPostDialState == PostDialState.COMPLETE 411 || mPostDialString == null 412 || mPostDialString.length() <= mNextPostDialChar 413 ) { 414 return ""; 415 } 416 417 return mPostDialString.substring(mNextPostDialChar); 418 } 419 420 @Override 421 protected void finalize() 422 { 423 releaseWakeLock(); 424 } 425 426 private void 427 processNextPostDialChar() { 428 char c = 0; 429 Registrant postDialHandler; 430 431 if (mPostDialState == PostDialState.CANCELLED) { 432 //Rlog.d(LOG_TAG, "##### processNextPostDialChar: postDialState == CANCELLED, bail"); 433 return; 434 } 435 436 if (mPostDialString == null || mPostDialString.length() <= mNextPostDialChar) { 437 setPostDialState(PostDialState.COMPLETE); 438 439 // notifyMessage.arg1 is 0 on complete 440 c = 0; 441 } else { 442 boolean isValid; 443 444 setPostDialState(PostDialState.STARTED); 445 446 c = mPostDialString.charAt(mNextPostDialChar++); 447 448 isValid = processPostDialChar(c); 449 450 if (!isValid) { 451 // Will call processNextPostDialChar 452 mHandler.obtainMessage(EVENT_NEXT_POST_DIAL).sendToTarget(); 453 // Don't notify application 454 Rlog.e(LOG_TAG, "processNextPostDialChar: c=" + c + " isn't valid!"); 455 return; 456 } 457 } 458 459 notifyPostDialListenersNextChar(c); 460 461 // TODO: remove the following code since the handler no longer executes anything. 462 postDialHandler = mOwner.mPhone.mPostDialHandler; 463 464 Message notifyMessage; 465 466 if (postDialHandler != null 467 && (notifyMessage = postDialHandler.messageForRegistrant()) != null) { 468 // The AsyncResult.result is the Connection object 469 PostDialState state = mPostDialState; 470 AsyncResult ar = AsyncResult.forMessage(notifyMessage); 471 ar.result = this; 472 ar.userObj = state; 473 474 // arg1 is the character that was/is being processed 475 notifyMessage.arg1 = c; 476 477 //Rlog.v(LOG_TAG, "##### processNextPostDialChar: send msg to postDialHandler, arg1=" + c); 478 notifyMessage.sendToTarget(); 479 } 480 } 481 482 /** 483 * Set post dial state and acquire wake lock while switching to "started" 484 * state, the wake lock will be released if state switches out of "started" 485 * state or after WAKE_LOCK_TIMEOUT_MILLIS. 486 * @param s new PostDialState 487 */ 488 private void setPostDialState(PostDialState s) { 489 if (mPostDialState != PostDialState.STARTED 490 && s == PostDialState.STARTED) { 491 acquireWakeLock(); 492 Message msg = mHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT); 493 mHandler.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS); 494 } else if (mPostDialState == PostDialState.STARTED 495 && s != PostDialState.STARTED) { 496 mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); 497 releaseWakeLock(); 498 } 499 mPostDialState = s; 500 notifyPostDialListeners(); 501 } 502 503 private void 504 createWakeLock(Context context) { 505 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 506 mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG); 507 } 508 509 private void 510 acquireWakeLock() { 511 Rlog.d(LOG_TAG, "acquireWakeLock"); 512 mPartialWakeLock.acquire(); 513 } 514 515 void 516 releaseWakeLock() { 517 synchronized(mPartialWakeLock) { 518 if (mPartialWakeLock.isHeld()) { 519 Rlog.d(LOG_TAG, "releaseWakeLock"); 520 mPartialWakeLock.release(); 521 } 522 } 523 } 524 525 @Override 526 public int getNumberPresentation() { 527 return mNumberPresentation; 528 } 529 530 @Override 531 public UUSInfo getUUSInfo() { 532 return mUusInfo; 533 } 534 535 @Override 536 public Connection getOrigConnection() { 537 return null; 538 } 539 540 @Override 541 public boolean isMultiparty() { 542 return mImsCall != null && mImsCall.isMultiparty(); 543 } 544 545 /** 546 * Where {@link #isMultiparty()} is {@code true}, determines if this {@link ImsCall} is the 547 * origin of the conference call (i.e. {@code #isConferenceHost()} is {@code true}), or if this 548 * {@link ImsCall} is a member of a conference hosted on another device. 549 * 550 * @return {@code true} if this call is the origin of the conference call it is a member of, 551 * {@code false} otherwise. 552 */ 553 public boolean isConferenceHost() { 554 if (mImsCall == null) { 555 return false; 556 } 557 return mImsCall.isConferenceHost(); 558 } 559 560 /*package*/ ImsCall getImsCall() { 561 return mImsCall; 562 } 563 564 /*package*/ void setImsCall(ImsCall imsCall) { 565 mImsCall = imsCall; 566 } 567 568 /*package*/ void changeParent(ImsPhoneCall parent) { 569 mParent = parent; 570 } 571 572 /** 573 * @return {@code true} if the {@link ImsPhoneConnection} or its media capabilities have been 574 * changed, and {@code false} otherwise. 575 */ 576 /*package*/ boolean update(ImsCall imsCall, ImsPhoneCall.State state) { 577 if (state == ImsPhoneCall.State.ACTIVE) { 578 // If the state of the call is active, but there is a pending request to the RIL to hold 579 // the call, we will skip this update. This is really a signalling delay or failure 580 // from the RIL, but we will prevent it from going through as we will end up erroneously 581 // making this call active when really it should be on hold. 582 if (imsCall.isPendingHold()) { 583 Rlog.w(LOG_TAG, "update : state is ACTIVE, but call is pending hold, skipping"); 584 return false; 585 } 586 587 if (mParent.getState().isRinging() || mParent.getState().isDialing()) { 588 onConnectedInOrOut(); 589 } 590 591 if (mParent.getState().isRinging() || mParent == mOwner.mBackgroundCall) { 592 //mForegroundCall should be IDLE 593 //when accepting WAITING call 594 //before accept WAITING call, 595 //the ACTIVE call should be held ahead 596 mParent.detach(this); 597 mParent = mOwner.mForegroundCall; 598 mParent.attach(this); 599 } 600 } else if (state == ImsPhoneCall.State.HOLDING) { 601 onStartedHolding(); 602 } 603 604 boolean updateParent = mParent.update(this, imsCall, state); 605 boolean updateMediaCapabilities = updateMediaCapabilities(imsCall); 606 boolean updateWifiState = updateWifiState(); 607 boolean updateAddressDisplay = updateAddressDisplay(imsCall); 608 609 return updateParent || updateMediaCapabilities || updateWifiState 610 || updateAddressDisplay; 611 } 612 613 @Override 614 public int getPreciseDisconnectCause() { 615 return 0; 616 } 617 618 /** 619 * Notifies this Connection of a request to disconnect a participant of the conference managed 620 * by the connection. 621 * 622 * @param endpoint the {@link android.net.Uri} of the participant to disconnect. 623 */ 624 @Override 625 public void onDisconnectConferenceParticipant(Uri endpoint) { 626 ImsCall imsCall = getImsCall(); 627 if (imsCall == null) { 628 return; 629 } 630 try { 631 imsCall.removeParticipants(new String[]{endpoint.toString()}); 632 } catch (ImsException e) { 633 // No session in place -- no change 634 Rlog.e(LOG_TAG, "onDisconnectConferenceParticipant: no session in place. "+ 635 "Failed to disconnect endpoint = " + endpoint); 636 } 637 } 638 639 /** 640 * Sets the conference connect time. Used when an {@code ImsConference} is created to out of 641 * this phone connection. 642 * 643 * @param conferenceConnectTime The conference connect time. 644 */ 645 public void setConferenceConnectTime(long conferenceConnectTime) { 646 mConferenceConnectTime = conferenceConnectTime; 647 } 648 649 /** 650 * @return The conference connect time. 651 */ 652 public long getConferenceConnectTime() { 653 return mConferenceConnectTime; 654 } 655 656 /** 657 * Check for a change in the address display related fields for the {@link ImsCall}, and 658 * update the {@link ImsPhoneConnection} with this information. 659 * 660 * @param imsCall The call to check for changes in address display fields. 661 * @return Whether the address display fields have been changed. 662 */ 663 private boolean updateAddressDisplay(ImsCall imsCall) { 664 if (imsCall == null) { 665 return false; 666 } 667 668 boolean changed = false; 669 ImsCallProfile callProfile = imsCall.getCallProfile(); 670 if (callProfile != null) { 671 String address = callProfile.getCallExtra(ImsCallProfile.EXTRA_OI); 672 String name = callProfile.getCallExtra(ImsCallProfile.EXTRA_CNA); 673 int nump = ImsCallProfile.OIRToPresentation( 674 callProfile.getCallExtraInt(ImsCallProfile.EXTRA_OIR)); 675 int namep = ImsCallProfile.OIRToPresentation( 676 callProfile.getCallExtraInt(ImsCallProfile.EXTRA_CNAP)); 677 if (Phone.DEBUG_PHONE) { 678 Rlog.d(LOG_TAG, "address = " + address + " name = " + name + 679 " nump = " + nump + " namep = " + namep); 680 } 681 if(equalsHandlesNulls(mAddress, address)) { 682 mAddress = address; 683 changed = true; 684 } 685 if (TextUtils.isEmpty(name)) { 686 if (!TextUtils.isEmpty(mCnapName)) { 687 mCnapName = ""; 688 changed = true; 689 } 690 } else if (!name.equals(mCnapName)) { 691 mCnapName = name; 692 changed = true; 693 } 694 if (mNumberPresentation != nump) { 695 mNumberPresentation = nump; 696 changed = true; 697 } 698 if (mCnapNamePresentation != namep) { 699 mCnapNamePresentation = namep; 700 changed = true; 701 } 702 } 703 return changed; 704 } 705 706 /** 707 * Check for a change in the video capabilities and audio quality for the {@link ImsCall}, and 708 * update the {@link ImsPhoneConnection} with this information. 709 * 710 * @param imsCall The call to check for changes in media capabilities. 711 * @return Whether the media capabilities have been changed. 712 */ 713 private boolean updateMediaCapabilities(ImsCall imsCall) { 714 if (imsCall == null) { 715 return false; 716 } 717 718 boolean changed = false; 719 720 try { 721 // The actual call profile (negotiated between local and peer). 722 ImsCallProfile negotiatedCallProfile = imsCall.getCallProfile(); 723 // The capabilities of the local device. 724 ImsCallProfile localCallProfile = imsCall.getLocalCallProfile(); 725 // The capabilities of the peer device. 726 ImsCallProfile remoteCallProfile = imsCall.getRemoteCallProfile(); 727 728 if (negotiatedCallProfile != null) { 729 int oldVideoState = getVideoState(); 730 int newVideoState = ImsCallProfile 731 .getVideoStateFromImsCallProfile(negotiatedCallProfile); 732 733 if (oldVideoState != newVideoState) { 734 setVideoState(newVideoState); 735 changed = true; 736 } 737 } 738 739 if (localCallProfile != null) { 740 int callType = localCallProfile.mCallType; 741 742 boolean newLocalVideoCapable = callType == ImsCallProfile.CALL_TYPE_VT; 743 if (isLocalVideoCapable() != newLocalVideoCapable) { 744 setLocalVideoCapable(newLocalVideoCapable); 745 changed = true; 746 } 747 } 748 749 if (remoteCallProfile != null) { 750 boolean newRemoteVideoCapable = remoteCallProfile.mCallType 751 == ImsCallProfile.CALL_TYPE_VT; 752 753 if (isRemoteVideoCapable() != newRemoteVideoCapable) { 754 setRemoteVideoCapable(newRemoteVideoCapable); 755 changed = true; 756 } 757 } 758 759 int newAudioQuality = 760 getAudioQualityFromCallProfile(localCallProfile, remoteCallProfile); 761 if (getAudioQuality() != newAudioQuality) { 762 setAudioQuality(newAudioQuality); 763 changed = true; 764 } 765 } catch (ImsException e) { 766 // No session in place -- no change 767 } 768 769 return changed; 770 } 771 772 /** 773 * Check for a change in the wifi state of the ImsPhoneCallTracker and update the 774 * {@link ImsPhoneConnection} with this information. 775 * 776 * @return Whether the ImsPhoneCallTracker's usage of wifi has been changed. 777 */ 778 public boolean updateWifiState() { 779 Rlog.d(LOG_TAG, "updateWifiState: " + mOwner.isVowifiEnabled()); 780 if (isWifi() != mOwner.isVowifiEnabled()) { 781 setWifi(mOwner.isVowifiEnabled()); 782 return true; 783 } 784 return false; 785 } 786 787 /** 788 * Determines the {@link ImsPhoneConnection} audio quality based on the local and remote 789 * {@link ImsCallProfile}. If indicate a HQ audio call if the local stream profile 790 * indicates AMR_WB or EVRC_WB and there is no remote restrict cause. 791 * 792 * @param localCallProfile The local call profile. 793 * @param remoteCallProfile The remote call profile. 794 * @return The audio quality. 795 */ 796 private int getAudioQualityFromCallProfile( 797 ImsCallProfile localCallProfile, ImsCallProfile remoteCallProfile) { 798 if (localCallProfile == null || remoteCallProfile == null 799 || localCallProfile.mMediaProfile == null) { 800 return AUDIO_QUALITY_STANDARD; 801 } 802 803 boolean isHighDef = (localCallProfile.mMediaProfile.mAudioQuality 804 == ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB 805 || localCallProfile.mMediaProfile.mAudioQuality 806 == ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB) 807 && remoteCallProfile.mRestrictCause == ImsCallProfile.CALL_RESTRICT_CAUSE_NONE; 808 return isHighDef ? AUDIO_QUALITY_HIGH_DEFINITION : AUDIO_QUALITY_STANDARD; 809 } 810 811 /** 812 * Provides a string representation of the {@link ImsPhoneConnection}. Primarily intended for 813 * use in log statements. 814 * 815 * @return String representation of call. 816 */ 817 @Override 818 public String toString() { 819 StringBuilder sb = new StringBuilder(); 820 sb.append("[ImsPhoneConnection objId: "); 821 sb.append(System.identityHashCode(this)); 822 sb.append(" address:"); 823 sb.append(Log.pii(getAddress())); 824 sb.append(" ImsCall:"); 825 if (mImsCall == null) { 826 sb.append("null"); 827 } else { 828 sb.append(mImsCall); 829 } 830 sb.append("]"); 831 return sb.toString(); 832 } 833} 834 835