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