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