ImsPhoneCallTracker.java revision bd4d6093314c0d2afc4b4f69f352957d01d9bc58
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.app.PendingIntent; 20import android.content.BroadcastReceiver; 21import android.content.Context; 22import android.content.Intent; 23import android.content.IntentFilter; 24import android.content.SharedPreferences; 25import android.os.AsyncResult; 26import android.os.Handler; 27import android.os.Message; 28import android.os.Registrant; 29import android.os.RegistrantList; 30import android.os.SystemProperties; 31import android.preference.PreferenceManager; 32import android.telephony.DisconnectCause; 33import android.telephony.PhoneNumberUtils; 34import android.telephony.ServiceState; 35import android.telephony.Rlog; 36 37import com.android.internal.telephony.Call; 38import com.android.internal.telephony.CallStateException; 39import com.android.internal.telephony.CallTracker; 40import com.android.internal.telephony.CommandException; 41import com.android.internal.telephony.CommandsInterface; 42import com.android.internal.telephony.Connection; 43import com.android.internal.telephony.EventLogTags; 44import com.android.internal.telephony.Phone; 45import com.android.internal.telephony.PhoneBase; 46import com.android.internal.telephony.PhoneConstants; 47import com.android.internal.telephony.TelephonyProperties; 48 49import com.android.ims.ImsCall; 50import com.android.ims.ImsCallProfile; 51import com.android.ims.ImsConnectionStateListener; 52import com.android.ims.ImsException; 53import com.android.ims.ImsManager; 54import com.android.ims.ImsReasonInfo; 55import com.android.ims.ImsServiceClass; 56import com.android.ims.ImsUtInterface; 57 58 59import java.io.FileDescriptor; 60import java.io.PrintWriter; 61import java.util.List; 62import java.util.ArrayList; 63 64/** 65 * {@hide} 66 */ 67public final class ImsPhoneCallTracker extends CallTracker { 68 static final String LOG_TAG = "ImsPhoneCallTracker"; 69 70 private static final boolean DBG = true; 71 72 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 73 @Override 74 public void onReceive(Context context, Intent intent) { 75 if (intent.getAction().equals(ImsManager.ACTION_IMS_INCOMING_CALL)) { 76 if (DBG) log("onReceive : incoming call intent"); 77 78 if (mImsManager == null) return; 79 80 if (mServiceId < 0) return; 81 82 try { 83 // Network initiated USSD will be treated by mImsUssdListener 84 boolean isUssd = intent.getBooleanExtra(ImsManager.EXTRA_USSD, false); 85 if (isUssd) { 86 if (DBG) log("onReceive : USSD"); 87 mUssdSession = mImsManager.takeCall(mServiceId, intent, mImsUssdListener); 88 if (mUssdSession != null) { 89 mUssdSession.accept(); 90 } 91 return; 92 } 93 94 // Normal MT call 95 ImsCall imsCall = mImsManager.takeCall(mServiceId, intent, mImsCallListener); 96 97 ImsPhoneConnection conn = new ImsPhoneConnection(mPhone.getContext(), imsCall, 98 ImsPhoneCallTracker.this, mRingingCall); 99 addConnection(conn); 100 101 if ((mForegroundCall.getState() != ImsPhoneCall.State.IDLE) || 102 (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE)) { 103 conn.update(imsCall, ImsPhoneCall.State.WAITING); 104 } 105 106 mPhone.notifyNewRingingConnection(conn); 107 mPhone.notifyIncomingRing(); 108 109 updatePhoneState(); 110 mPhone.notifyPreciseCallStateChanged(); 111 } catch (ImsException e) { 112 loge("onReceive : exception " + e); 113 } 114 } 115 } 116 }; 117 118 //***** Constants 119 120 static final int MAX_CONNECTIONS = 7; 121 static final int MAX_CONNECTIONS_PER_CALL = 5; 122 123 private static final int EVENT_HANGUP_PENDINGMO = 18; 124 private static final int EVENT_RESUME_BACKGROUND = 19; 125 private static final int EVENT_DIAL_PENDINGMO = 20; 126 127 private static final int TIMEOUT_HANGUP_PENDINGMO = 500; 128 129 //***** Instance Variables 130 private ArrayList<ImsPhoneConnection> mConnections = new ArrayList<ImsPhoneConnection>(); 131 private RegistrantList mVoiceCallEndedRegistrants = new RegistrantList(); 132 private RegistrantList mVoiceCallStartedRegistrants = new RegistrantList(); 133 134 ImsPhoneCall mRingingCall = new ImsPhoneCall(this); 135 ImsPhoneCall mForegroundCall = new ImsPhoneCall(this); 136 ImsPhoneCall mBackgroundCall = new ImsPhoneCall(this); 137 ImsPhoneCall mHandoverCall = new ImsPhoneCall(this); 138 139 private ImsPhoneConnection mPendingMO; 140 private int mClirMode = CommandsInterface.CLIR_DEFAULT; 141 private Object mSyncHold = new Object(); 142 143 private ImsCall mUssdSession = null; 144 private Message mPendingUssd = null; 145 146 ImsPhone mPhone; 147 148 private boolean mDesiredMute = false; // false = mute off 149 private boolean mOnHoldToneStarted = false; 150 151 PhoneConstants.State mState = PhoneConstants.State.IDLE; 152 153 private ImsManager mImsManager; 154 private int mServiceId = -1; 155 156 private Call.SrvccState mSrvccState = Call.SrvccState.NONE; 157 158 //***** Events 159 160 161 //***** Constructors 162 163 ImsPhoneCallTracker(ImsPhone phone) { 164 this.mPhone = phone; 165 166 IntentFilter intentfilter = new IntentFilter(); 167 intentfilter.addAction(ImsManager.ACTION_IMS_INCOMING_CALL); 168 mPhone.getContext().registerReceiver(mReceiver, intentfilter); 169 170 Thread t = new Thread() { 171 public void run() { 172 getImsService(); 173 } 174 }; 175 t.start(); 176 } 177 178 private PendingIntent createIncomingCallPendingIntent() { 179 Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL); 180 return PendingIntent.getBroadcast(mPhone.getContext(), 0, intent, 181 PendingIntent.FLAG_UPDATE_CURRENT); 182 } 183 184 private void getImsService() { 185 if (DBG) log("getImsService"); 186 mImsManager = ImsManager.getInstance(mPhone.getContext()); 187 try { 188 // If there are multiple GSM phones, 189 // each GSM phone creates IMS phone for itself. 190 // IMS service needs something to determine appropriate IMS phone. 191 // 192 // TODO: add additional parameter to identity the IMS phone's 193 // subscriber's information. 194 mServiceId = mImsManager.open(ImsServiceClass.MMTEL, 195 createIncomingCallPendingIntent(), 196 mImsConnectionStateListener); 197 } catch (ImsException e) { 198 loge("getImsService: " + e); 199 //Leave mImsManager as null, then CallStateException will be thrown when dialing 200 mImsManager = null; 201 } 202 } 203 204 public void dispose() { 205 if (DBG) log("dispose"); 206 mRingingCall.dispose(); 207 mBackgroundCall.dispose(); 208 mForegroundCall.dispose(); 209 mHandoverCall.dispose(); 210 211 clearDisconnected(); 212 mPhone.getContext().unregisterReceiver(mReceiver); 213 } 214 215 @Override 216 protected void finalize() { 217 log("ImsPhoneCallTracker finalized"); 218 } 219 220 //***** Instance Methods 221 222 //***** Public Methods 223 @Override 224 public void registerForVoiceCallStarted(Handler h, int what, Object obj) { 225 Registrant r = new Registrant(h, what, obj); 226 mVoiceCallStartedRegistrants.add(r); 227 } 228 229 @Override 230 public void unregisterForVoiceCallStarted(Handler h) { 231 mVoiceCallStartedRegistrants.remove(h); 232 } 233 234 @Override 235 public void registerForVoiceCallEnded(Handler h, int what, Object obj) { 236 Registrant r = new Registrant(h, what, obj); 237 mVoiceCallEndedRegistrants.add(r); 238 } 239 240 @Override 241 public void unregisterForVoiceCallEnded(Handler h) { 242 mVoiceCallEndedRegistrants.remove(h); 243 } 244 245 Connection 246 dial(String dialString) throws CallStateException { 247 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext()); 248 int oirMode = sp.getInt(PhoneBase.CLIR_KEY, CommandsInterface.CLIR_DEFAULT); 249 return dial(dialString, oirMode); 250 } 251 252 /** 253 * oirMode is one of the CLIR_ constants 254 */ 255 synchronized Connection 256 dial(String dialString, int clirMode) throws CallStateException { 257 if (DBG) log("dial clirMode=" + clirMode); 258 259 // note that this triggers call state changed notif 260 clearDisconnected(); 261 262 if (mImsManager == null) { 263 throw new CallStateException("service not available"); 264 } 265 266 if (!canDial()) { 267 throw new CallStateException("cannot dial in current state"); 268 } 269 270 boolean holdBeforeDial = false; 271 272 // The new call must be assigned to the foreground call. 273 // That call must be idle, so place anything that's 274 // there on hold 275 if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) { 276 if (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE) { 277 //we should have failed in !canDial() above before we get here 278 throw new CallStateException("cannot dial in current state"); 279 } 280 // foreground call is empty for the newly dialed connection 281 holdBeforeDial = true; 282 switchWaitingOrHoldingAndActive(); 283 } 284 285 ImsPhoneCall.State fgState = ImsPhoneCall.State.IDLE; 286 ImsPhoneCall.State bgState = ImsPhoneCall.State.IDLE; 287 288 mClirMode = clirMode; 289 290 synchronized (mSyncHold) { 291 if (holdBeforeDial) { 292 fgState = mForegroundCall.getState(); 293 bgState = mBackgroundCall.getState(); 294 295 //holding foreground call failed 296 if (fgState == ImsPhoneCall.State.ACTIVE) { 297 throw new CallStateException("cannot dial in current state"); 298 } 299 300 //holding foreground call succeeded 301 if (bgState == ImsPhoneCall.State.HOLDING) { 302 holdBeforeDial = false; 303 } 304 } 305 306 mPendingMO = new ImsPhoneConnection(mPhone.getContext(), 307 checkForTestEmergencyNumber(dialString), this, mForegroundCall); 308 } 309 addConnection(mPendingMO); 310 311 if (!holdBeforeDial) { 312 dialInternal(mPendingMO, clirMode); 313 } 314 315 updatePhoneState(); 316 mPhone.notifyPreciseCallStateChanged(); 317 318 return mPendingMO; 319 } 320 321 private void dialInternal(ImsPhoneConnection conn, int clirMode) { 322 if (conn == null) { 323 return; 324 } 325 326 if (conn.getAddress()== null || conn.getAddress().length() == 0 327 || conn.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) { 328 // Phone number is invalid 329 conn.setDisconnectCause(DisconnectCause.INVALID_NUMBER); 330 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 331 return; 332 } 333 334 // Always unmute when initiating a new call 335 setMute(false); 336 int serviceType = PhoneNumberUtils.isEmergencyNumber(conn.getAddress()) ? 337 ImsCallProfile.SERVICE_TYPE_EMERGENCY : ImsCallProfile.SERVICE_TYPE_NORMAL; 338 339 try { 340 String[] callees = new String[] { conn.getAddress() }; 341 ImsCallProfile profile = mImsManager.createCallProfile(mServiceId, 342 serviceType, ImsCallProfile.CALL_TYPE_VOICE); 343 profile.setCallExtraInt(ImsCallProfile.EXTRA_OIR, clirMode); 344 345 ImsCall imsCall = mImsManager.makeCall(mServiceId, profile, 346 callees, mImsCallListener); 347 conn.setImsCall(imsCall); 348 } catch (ImsException e) { 349 loge("dialInternal : " + e); 350 conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); 351 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 352 } 353 } 354 355 void 356 acceptCall () throws CallStateException { 357 if (DBG) log("acceptCall"); 358 359 if (mForegroundCall.getState().isAlive() 360 && mBackgroundCall.getState().isAlive()) { 361 throw new CallStateException("cannot accept call"); 362 } 363 364 if ((mRingingCall.getState() == ImsPhoneCall.State.WAITING) 365 && mForegroundCall.getState().isAlive()) { 366 setMute(false); 367 switchWaitingOrHoldingAndActive(); 368 } else if (mRingingCall.getState().isRinging()) { 369 if (DBG) log("acceptCall: incoming..."); 370 // Always unmute when answering a new call 371 setMute(false); 372 try { 373 ImsCall imsCall = mRingingCall.getImsCall(); 374 if (imsCall != null) { 375 imsCall.accept(); 376 } else { 377 throw new CallStateException("no valid ims call"); 378 } 379 } catch (ImsException e) { 380 throw new CallStateException("cannot accept call"); 381 } 382 } else { 383 throw new CallStateException("phone not ringing"); 384 } 385 } 386 387 void 388 rejectCall () throws CallStateException { 389 if (DBG) log("rejectCall"); 390 391 if (mRingingCall.getState().isRinging()) { 392 hangup(mRingingCall); 393 } else { 394 throw new CallStateException("phone not ringing"); 395 } 396 } 397 398 void 399 switchWaitingOrHoldingAndActive() throws CallStateException { 400 if (DBG) log("switchWaitingOrHoldingAndActive"); 401 402 if (mRingingCall.getState() == ImsPhoneCall.State.INCOMING) { 403 throw new CallStateException("cannot be in the incoming state"); 404 } 405 406 if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) { 407 ImsCall imsCall = mForegroundCall.getImsCall(); 408 if (imsCall == null) { 409 throw new CallStateException("no ims call"); 410 } 411 412 mForegroundCall.switchWith(mBackgroundCall); 413 414 try { 415 imsCall.hold(); 416 } catch (ImsException e) { 417 mForegroundCall.switchWith(mBackgroundCall); 418 throw new CallStateException(e.getMessage()); 419 } 420 } else if (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING) { 421 resumeWaitingOrHolding(); 422 } 423 } 424 425 void 426 conference() { 427 if (DBG) log("conference"); 428 429 ImsCall fgImsCall = mForegroundCall.getImsCall(); 430 if (fgImsCall == null) { 431 log("conference no foreground ims call"); 432 return; 433 } 434 435 ImsCall bgImsCall = mBackgroundCall.getImsCall(); 436 if (bgImsCall == null) { 437 log("conference no background ims call"); 438 return; 439 } 440 441 try { 442 fgImsCall.merge(bgImsCall); 443 } catch (ImsException e) { 444 log("conference " + e.getMessage()); 445 } 446 } 447 448 void 449 explicitCallTransfer() { 450 //TODO : implement 451 } 452 453 void 454 clearDisconnected() { 455 if (DBG) log("clearDisconnected"); 456 457 internalClearDisconnected(); 458 459 updatePhoneState(); 460 mPhone.notifyPreciseCallStateChanged(); 461 } 462 463 boolean 464 canConference() { 465 return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE 466 && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING 467 && !mBackgroundCall.isFull() 468 && !mForegroundCall.isFull(); 469 } 470 471 boolean 472 canDial() { 473 boolean ret; 474 int serviceState = mPhone.getServiceState().getState(); 475 String disableCall = SystemProperties.get( 476 TelephonyProperties.PROPERTY_DISABLE_CALL, "false"); 477 478 ret = (serviceState != ServiceState.STATE_POWER_OFF) 479 && mPendingMO == null 480 && !mRingingCall.isRinging() 481 && !disableCall.equals("true") 482 && (!mForegroundCall.getState().isAlive() 483 || !mBackgroundCall.getState().isAlive()); 484 485 return ret; 486 } 487 488 boolean 489 canTransfer() { 490 return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE 491 && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING; 492 } 493 494 //***** Private Instance Methods 495 496 private void 497 internalClearDisconnected() { 498 mRingingCall.clearDisconnected(); 499 mForegroundCall.clearDisconnected(); 500 mBackgroundCall.clearDisconnected(); 501 mHandoverCall.clearDisconnected(); 502 } 503 504 private void 505 updatePhoneState() { 506 PhoneConstants.State oldState = mState; 507 508 if (mRingingCall.isRinging()) { 509 mState = PhoneConstants.State.RINGING; 510 } else if (mPendingMO != null || 511 !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) { 512 mState = PhoneConstants.State.OFFHOOK; 513 } else { 514 mState = PhoneConstants.State.IDLE; 515 } 516 517 if (mState == PhoneConstants.State.IDLE && oldState != mState) { 518 mVoiceCallEndedRegistrants.notifyRegistrants( 519 new AsyncResult(null, null, null)); 520 } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) { 521 mVoiceCallStartedRegistrants.notifyRegistrants ( 522 new AsyncResult(null, null, null)); 523 } 524 525 if (DBG) log("updatePhoneState oldState=" + oldState + ", newState=" + mState); 526 527 if (mState != oldState) { 528 mPhone.notifyPhoneStateChanged(); 529 } 530 } 531 532 private void 533 handleRadioNotAvailable() { 534 // handlePollCalls will clear out its 535 // call list when it gets the CommandException 536 // error result from this 537 pollCallsWhenSafe(); 538 } 539 540 private void 541 dumpState() { 542 List l; 543 544 log("Phone State:" + mState); 545 546 log("Ringing call: " + mRingingCall.toString()); 547 548 l = mRingingCall.getConnections(); 549 for (int i = 0, s = l.size(); i < s; i++) { 550 log(l.get(i).toString()); 551 } 552 553 log("Foreground call: " + mForegroundCall.toString()); 554 555 l = mForegroundCall.getConnections(); 556 for (int i = 0, s = l.size(); i < s; i++) { 557 log(l.get(i).toString()); 558 } 559 560 log("Background call: " + mBackgroundCall.toString()); 561 562 l = mBackgroundCall.getConnections(); 563 for (int i = 0, s = l.size(); i < s; i++) { 564 log(l.get(i).toString()); 565 } 566 567 } 568 569 //***** Called from ImsPhone 570 571 /*package*/ void 572 setMute(boolean mute) { 573 mDesiredMute = mute; 574 mForegroundCall.setMute(mute); 575 } 576 577 /*package*/ boolean 578 getMute() { 579 return mDesiredMute; 580 } 581 582 /*package*/ void 583 sendDtmf(char c) { 584 if (DBG) log("sendDtmf"); 585 586 ImsCall imscall = mForegroundCall.getImsCall(); 587 if (imscall != null) { 588 imscall.sendDtmf(convertDtmf(c)); 589 } 590 } 591 592 private int convertDtmf(char c) { 593 int code = c - '0'; 594 if ((code < 0) || (code > 9)) { 595 switch (c) { 596 case '*': return 10; 597 case '#': return 11; 598 case 'A': return 12; 599 case 'B': return 13; 600 case 'C': return 14; 601 case 'D': return 15; 602 default: 603 throw new IllegalArgumentException( 604 "invalid DTMF char: " + (int) c); 605 } 606 } 607 return code; 608 } 609 610 //***** Called from ImsPhoneConnection 611 612 /*package*/ void 613 hangup (ImsPhoneConnection conn) throws CallStateException { 614 if (DBG) log("hangup connection"); 615 616 if (conn.getOwner() != this) { 617 throw new CallStateException ("ImsPhoneConnection " + conn 618 + "does not belong to ImsPhoneCallTracker " + this); 619 } 620 621 hangup(conn.getCall()); 622 } 623 624 //***** Called from ImsPhoneCall 625 626 /* package */ void 627 hangup (ImsPhoneCall call) throws CallStateException { 628 if (DBG) log("hangup call"); 629 630 if (call.getConnections().size() == 0) { 631 throw new CallStateException("no connections"); 632 } 633 634 ImsCall imsCall = call.getImsCall(); 635 boolean rejectCall = false; 636 637 if (call == mRingingCall) { 638 if (Phone.DEBUG_PHONE) log("(ringing) hangup incoming"); 639 rejectCall = true; 640 } else if (call == mForegroundCall) { 641 if (call.isDialingOrAlerting()) { 642 if (Phone.DEBUG_PHONE) { 643 log("(foregnd) hangup dialing or alerting..."); 644 } 645 } else { 646 if (Phone.DEBUG_PHONE) { 647 log("(foregnd) hangup foreground"); 648 } 649 //held call will be resumed by onCallTerminated 650 } 651 } else if (call == mBackgroundCall) { 652 if (Phone.DEBUG_PHONE) { 653 log("(backgnd) hangup waiting or background"); 654 } 655 } else { 656 throw new RuntimeException ("ImsPhoneCall " + call + 657 "does not belong to ImsPhoneCallTracker " + this); 658 } 659 660 call.onHangupLocal(); 661 662 try { 663 if (imsCall != null) { 664 if (rejectCall) imsCall.reject(ImsReasonInfo.CODE_USER_DECLINE); 665 else imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED); 666 } else if (mPendingMO != null && call == mForegroundCall) { 667 // is holding a foreground call 668 mPendingMO.update(null, ImsPhoneCall.State.DISCONNECTED); 669 mPendingMO.onDisconnect(); 670 removeConnection(mPendingMO); 671 mPendingMO = null; 672 updatePhoneState(); 673 removeMessages(EVENT_DIAL_PENDINGMO); 674 } 675 } catch (ImsException e) { 676 throw new CallStateException(e.getMessage()); 677 } 678 679 mPhone.notifyPreciseCallStateChanged(); 680 } 681 682 /* package */ 683 void resumeWaitingOrHolding() throws CallStateException { 684 if (DBG) log("resumeWaitingOrHolding"); 685 686 try { 687 if (mForegroundCall.getState().isAlive()) { 688 //resume foreground call after holding background call 689 //they were switched before holding 690 ImsCall imsCall = mForegroundCall.getImsCall(); 691 if (imsCall != null) imsCall.resume(); 692 } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING) { 693 //accept waiting call after holding background call 694 ImsCall imsCall = mRingingCall.getImsCall(); 695 if (imsCall != null) imsCall.accept(); 696 } else { 697 //Just resume background call. 698 //To distinguish resuming call with swapping calls 699 //we do not switch calls.here 700 //ImsPhoneConnection.update will chnage the parent when completed 701 ImsCall imsCall = mBackgroundCall.getImsCall(); 702 if (imsCall != null) imsCall.resume(); 703 } 704 } catch (ImsException e) { 705 throw new CallStateException(e.getMessage()); 706 } 707 } 708 709 /* package */ 710 void sendUSSD (String ussdString, Message response) { 711 if (DBG) log("sendUSSD"); 712 713 try { 714 if (mUssdSession != null) { 715 mUssdSession.sendUssd(ussdString); 716 AsyncResult.forMessage(response, null, null); 717 response.sendToTarget(); 718 return; 719 } 720 721 String[] callees = new String[] { ussdString }; 722 ImsCallProfile profile = mImsManager.createCallProfile(mServiceId, 723 ImsCallProfile.SERVICE_TYPE_NORMAL, ImsCallProfile.CALL_TYPE_VOICE); 724 profile.setCallExtraInt(ImsCallProfile.EXTRA_DIALSTRING, 725 ImsCallProfile.DIALSTRING_USSD); 726 727 mUssdSession = mImsManager.makeCall(mServiceId, profile, 728 callees, mImsUssdListener); 729 } catch (ImsException e) { 730 loge("sendUSSD : " + e); 731 mPhone.sendErrorResponse(response, e); 732 } 733 } 734 735 /* package */ 736 void cancelUSSD() { 737 if (mUssdSession == null) return; 738 739 try { 740 mUssdSession.terminate(ImsReasonInfo.CODE_USER_TERMINATED); 741 } catch (ImsException e) { 742 } 743 744 } 745 746 private synchronized ImsPhoneConnection findConnection(ImsCall imsCall) { 747 for (ImsPhoneConnection conn : mConnections) { 748 if (conn.getImsCall() == imsCall) { 749 return conn; 750 } 751 } 752 return null; 753 } 754 755 private synchronized void removeConnection(ImsPhoneConnection conn) { 756 mConnections.remove(conn); 757 } 758 759 private synchronized void addConnection(ImsPhoneConnection conn) { 760 mConnections.add(conn); 761 } 762 763 private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause) { 764 if (DBG) log("processCallStateChange state=" + state + " cause=" + cause); 765 766 if (imsCall == null) return; 767 768 boolean changed = false; 769 ImsPhoneConnection conn = findConnection(imsCall); 770 771 if (conn == null) { 772 // TODO : what should be done? 773 return; 774 } 775 776 changed = conn.update(imsCall, state); 777 778 if (state == ImsPhoneCall.State.DISCONNECTED) { 779 changed = conn.onDisconnect(cause) || changed; 780 removeConnection(conn); 781 } 782 783 if (changed) { 784 if (conn.getCall() == mHandoverCall) return; 785 updatePhoneState(); 786 mPhone.notifyPreciseCallStateChanged(); 787 } 788 } 789 790 private int getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo) { 791 int cause = DisconnectCause.ERROR_UNSPECIFIED; 792 793 //int type = reasonInfo.getReasonType(); 794 int code = reasonInfo.getCode(); 795 switch (code) { 796 case ImsReasonInfo.CODE_SIP_BAD_ADDRESS: 797 case ImsReasonInfo.CODE_SIP_NOT_REACHABLE: 798 return DisconnectCause.NUMBER_UNREACHABLE; 799 800 case ImsReasonInfo.CODE_SIP_BUSY: 801 return DisconnectCause.BUSY; 802 803 case ImsReasonInfo.CODE_USER_TERMINATED: 804 return DisconnectCause.LOCAL; 805 806 case ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE: 807 return DisconnectCause.NORMAL; 808 809 case ImsReasonInfo.CODE_SIP_REDIRECTED: 810 case ImsReasonInfo.CODE_SIP_BAD_REQUEST: 811 case ImsReasonInfo.CODE_SIP_FORBIDDEN: 812 case ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE: 813 case ImsReasonInfo.CODE_SIP_USER_REJECTED: 814 case ImsReasonInfo.CODE_SIP_GLOBAL_ERROR: 815 return DisconnectCause.SERVER_ERROR; 816 817 case ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE: 818 case ImsReasonInfo.CODE_SIP_NOT_FOUND: 819 case ImsReasonInfo.CODE_SIP_SERVER_ERROR: 820 return DisconnectCause.SERVER_UNREACHABLE; 821 822 case ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING: 823 case ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED: 824 case ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN: 825 case ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE: 826 case ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED: 827 case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE: 828 case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE: 829 case ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING: 830 return DisconnectCause.OUT_OF_SERVICE; 831 832 case ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT: 833 case ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING: 834 case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER: 835 case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE: 836 return DisconnectCause.TIMED_OUT; 837 838 case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY: 839 case ImsReasonInfo.CODE_LOCAL_POWER_OFF: 840 return DisconnectCause.POWER_OFF; 841 842 default: 843 } 844 845 return cause; 846 } 847 848 /** 849 * Listen to the IMS call state change 850 */ 851 private ImsCall.Listener mImsCallListener = new ImsCall.Listener() { 852 @Override 853 public void onCallProgressing(ImsCall imsCall) { 854 if (DBG) log("onCallProgressing"); 855 856 mPendingMO = null; 857 processCallStateChange(imsCall, ImsPhoneCall.State.ALERTING, 858 DisconnectCause.NOT_DISCONNECTED); 859 } 860 861 @Override 862 public void onCallStarted(ImsCall imsCall) { 863 if (DBG) log("onCallStarted"); 864 865 mPendingMO = null; 866 processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE, 867 DisconnectCause.NOT_DISCONNECTED); 868 } 869 870 /** 871 * onCallStartFailed will be invoked when: 872 * case 1) Dialing fails 873 * case 2) Ringing call is disconnected by local or remote user 874 */ 875 @Override 876 public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 877 if (DBG) log("onCallStartFailed reasonCode=" + reasonInfo.getCode()); 878 879 if (mPendingMO != null) { 880 // To initiate dialing circuit-switched call 881 if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED 882 && mBackgroundCall.getState() == ImsPhoneCall.State.IDLE 883 && mRingingCall.getState() == ImsPhoneCall.State.IDLE) { 884 mForegroundCall.detach(mPendingMO); 885 removeConnection(mPendingMO); 886 mPendingMO.finalize(); 887 mPendingMO = null; 888 mPhone.initiateSilentRedial(); 889 return; 890 } 891 mPendingMO = null; 892 } 893 onCallTerminated(imsCall, reasonInfo); 894 } 895 896 @Override 897 public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) { 898 if (DBG) log("onCallTerminated reasonCode=" + reasonInfo.getCode()); 899 900 ImsPhoneCall.State oldState = mForegroundCall.getState(); 901 902 processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, 903 getDisconnectCauseFromReasonInfo(reasonInfo)); 904 905 if (reasonInfo.getCode() == ImsReasonInfo.CODE_USER_TERMINATED) { 906 if ((oldState == ImsPhoneCall.State.DISCONNECTING) 907 && (mForegroundCall.getState() == ImsPhoneCall.State.DISCONNECTED) 908 && (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING)) { 909 sendEmptyMessage(EVENT_RESUME_BACKGROUND); 910 } 911 } 912 } 913 914 @Override 915 public void onCallHeld(ImsCall imsCall) { 916 if (DBG) log("onCallHeld"); 917 918 synchronized (mSyncHold) { 919 ImsPhoneCall.State oldState = mBackgroundCall.getState(); 920 processCallStateChange(imsCall, ImsPhoneCall.State.HOLDING, 921 DisconnectCause.NOT_DISCONNECTED); 922 923 if (oldState == ImsPhoneCall.State.ACTIVE) { 924 if ((mForegroundCall.getState() == ImsPhoneCall.State.HOLDING) 925 || (mRingingCall.getState() == ImsPhoneCall.State.WAITING)) { 926 sendEmptyMessage(EVENT_RESUME_BACKGROUND); 927 } else { 928 //when multiple connections belong to background call, 929 //only the first callback reaches here 930 //otherwise the oldState is already HOLDING 931 if (mPendingMO != null) { 932 sendEmptyMessage(EVENT_DIAL_PENDINGMO); 933 } 934 } 935 } 936 } 937 } 938 939 @Override 940 public void onCallHoldFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 941 if (DBG) log("onCallHoldFailed reasonCode=" + reasonInfo.getCode()); 942 943 synchronized (mSyncHold) { 944 ImsPhoneCall.State bgState = mBackgroundCall.getState(); 945 if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED) { 946 // disconnected while processing hold 947 if (mPendingMO != null) { 948 sendEmptyMessage(EVENT_DIAL_PENDINGMO); 949 } 950 } else if (bgState == ImsPhoneCall.State.ACTIVE) { 951 mForegroundCall.switchWith(mBackgroundCall); 952 953 if (mPendingMO != null) { 954 mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); 955 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 956 } 957 } 958 } 959 } 960 961 @Override 962 public void onCallResumed(ImsCall imsCall) { 963 if (DBG) log("onCallResumed"); 964 965 processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE, 966 DisconnectCause.NOT_DISCONNECTED); 967 } 968 969 @Override 970 public void onCallResumeFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 971 // TODO : What should be done? 972 } 973 974 @Override 975 public void onCallResumeReceived(ImsCall imsCall) { 976 if (DBG) log("onCallResumeReceived"); 977 978 if (mOnHoldToneStarted) { 979 mPhone.stopOnHoldTone(); 980 mOnHoldToneStarted = false; 981 } 982 } 983 984 @Override 985 public void onCallHoldReceived(ImsCall imsCall) { 986 if (DBG) log("onCallHoldReceived"); 987 988 ImsPhoneConnection conn = findConnection(imsCall); 989 if (conn != null && conn.getState() == ImsPhoneCall.State.ACTIVE) { 990 if (!mOnHoldToneStarted && ImsPhoneCall.isLocalTone(imsCall)) { 991 mPhone.startOnHoldTone(); 992 mOnHoldToneStarted = true; 993 } 994 } 995 } 996 997 @Override 998 public void onCallMerged(ImsCall call, ImsCall newCall) { 999 if (DBG) log("onCallMerged"); 1000 1001 mForegroundCall.merge(mBackgroundCall, mForegroundCall.getState()); 1002 updatePhoneState(); 1003 mPhone.notifyPreciseCallStateChanged(); 1004 } 1005 1006 @Override 1007 public void onCallMergeFailed(ImsCall call, ImsReasonInfo reasonInfo) { 1008 if (DBG) log("onCallMergeFailed reasonCode=" + reasonInfo.getCode()); 1009 mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE); 1010 } 1011 }; 1012 1013 /** 1014 * Listen to the IMS call state change 1015 */ 1016 private ImsCall.Listener mImsUssdListener = new ImsCall.Listener() { 1017 @Override 1018 public void onCallStarted(ImsCall imsCall) { 1019 if (DBG) log("mImsUssdListener onCallStarted"); 1020 1021 if (imsCall == mUssdSession) { 1022 if (mPendingUssd != null) { 1023 AsyncResult.forMessage(mPendingUssd); 1024 mPendingUssd.sendToTarget(); 1025 mPendingUssd = null; 1026 } 1027 } 1028 } 1029 1030 @Override 1031 public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1032 if (DBG) log("mImsUssdListener onCallStartFailed reasonCode=" + reasonInfo.getCode()); 1033 1034 onCallTerminated(imsCall, reasonInfo); 1035 } 1036 1037 @Override 1038 public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1039 if (DBG) log("mImsUssdListener onCallTerminated reasonCode=" + reasonInfo.getCode()); 1040 1041 if (imsCall == mUssdSession) { 1042 mUssdSession = null; 1043 if (mPendingUssd != null) { 1044 CommandException ex = 1045 new CommandException(CommandException.Error.GENERIC_FAILURE); 1046 AsyncResult.forMessage(mPendingUssd, null, ex); 1047 mPendingUssd.sendToTarget(); 1048 mPendingUssd = null; 1049 } 1050 } 1051 imsCall.close(); 1052 } 1053 1054 @Override 1055 public void onCallUssdMessageReceived(ImsCall call, 1056 int mode, String ussdMessage) { 1057 if (DBG) log("mImsUssdListener onCallUssdMessageReceived mode=" + mode); 1058 1059 int ussdMode = -1; 1060 1061 switch(mode) { 1062 case ImsCall.USSD_MODE_REQUEST: 1063 ussdMode = CommandsInterface.USSD_MODE_REQUEST; 1064 break; 1065 1066 case ImsCall.USSD_MODE_NOTIFY: 1067 ussdMode = CommandsInterface.USSD_MODE_NOTIFY; 1068 break; 1069 } 1070 1071 mPhone.onIncomingUSSD(ussdMode, ussdMessage); 1072 } 1073 }; 1074 1075 /** 1076 * Listen to the IMS service state change 1077 * 1078 */ 1079 private ImsConnectionStateListener mImsConnectionStateListener = 1080 new ImsConnectionStateListener() { 1081 @Override 1082 public void onImsConnected() { 1083 if (DBG) log("onImsConnected"); 1084 mPhone.setServiceState(ServiceState.STATE_IN_SERVICE); 1085 } 1086 1087 @Override 1088 public void onImsDisconnected() { 1089 if (DBG) log("onImsDisconnected"); 1090 mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE); 1091 } 1092 1093 @Override 1094 public void onImsResumed() { 1095 if (DBG) log("onImsResumed"); 1096 mPhone.setServiceState(ServiceState.STATE_IN_SERVICE); 1097 } 1098 1099 @Override 1100 public void onImsSuspended() { 1101 if (DBG) log("onImsSuspended"); 1102 mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE); 1103 } 1104 }; 1105 1106 /* package */ 1107 ImsUtInterface getUtInterface() throws ImsException { 1108 if (mImsManager == null) { 1109 throw new ImsException("no ims manager", ImsReasonInfo.CODE_UNSPECIFIED); 1110 } 1111 1112 ImsUtInterface ut = mImsManager.getSupplementaryServiceConfiguration(mServiceId); 1113 return ut; 1114 } 1115 1116 /* package */ 1117 void notifySrvccState(Call.SrvccState state) { 1118 if (DBG) log("notifySrvccState state=" + state); 1119 1120 mSrvccState = state; 1121 1122 if (mSrvccState == Call.SrvccState.COMPLETED) { 1123 if (mForegroundCall.getConnections().size() > 0) { 1124 mHandoverCall.switchWith(mForegroundCall); 1125 } else if (mBackgroundCall.getConnections().size() > 0) { 1126 mHandoverCall.switchWith(mBackgroundCall); 1127 } 1128 } 1129 } 1130 1131 //****** Overridden from Handler 1132 1133 @Override 1134 public void 1135 handleMessage (Message msg) { 1136 AsyncResult ar; 1137 if (DBG) log("handleMessage what=" + msg.what); 1138 1139 switch (msg.what) { 1140 case EVENT_HANGUP_PENDINGMO: 1141 if (mPendingMO != null) { 1142 mPendingMO.onDisconnect(); 1143 removeConnection(mPendingMO); 1144 mPendingMO = null; 1145 } 1146 1147 updatePhoneState(); 1148 mPhone.notifyPreciseCallStateChanged(); 1149 break; 1150 case EVENT_RESUME_BACKGROUND: 1151 try { 1152 resumeWaitingOrHolding(); 1153 } catch (CallStateException e) { 1154 if (Phone.DEBUG_PHONE) { 1155 loge("handleMessage EVENT_RESUME_BACKGROUND exception=" + e); 1156 } 1157 } 1158 break; 1159 case EVENT_DIAL_PENDINGMO: 1160 dialInternal(mPendingMO, mClirMode); 1161 break; 1162 } 1163 } 1164 1165 @Override 1166 protected void log(String msg) { 1167 Rlog.d(LOG_TAG, "[ImsPhoneCallTracker] " + msg); 1168 } 1169 1170 protected void loge(String msg) { 1171 Rlog.e(LOG_TAG, "[ImsPhoneCallTracker] " + msg); 1172 } 1173 1174 @Override 1175 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1176 pw.println("ImsPhoneCallTracker extends:"); 1177 super.dump(fd, pw, args); 1178 pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants); 1179 pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants); 1180 pw.println(" mRingingCall=" + mRingingCall); 1181 pw.println(" mForegroundCall=" + mForegroundCall); 1182 pw.println(" mBackgroundCall=" + mBackgroundCall); 1183 pw.println(" mHandoverCall=" + mHandoverCall); 1184 pw.println(" mPendingMO=" + mPendingMO); 1185 //pw.println(" mHangupPendingMO=" + mHangupPendingMO); 1186 pw.println(" mPhone=" + mPhone); 1187 pw.println(" mDesiredMute=" + mDesiredMute); 1188 pw.println(" mState=" + mState); 1189 } 1190 1191 @Override 1192 protected void handlePollCalls(AsyncResult ar) { 1193 } 1194} 1195