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