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