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