ImsCall.java revision ef36ef67e009449300b0150c60c9f637e205d79e
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.ims; 18 19import java.util.ArrayList; 20import java.util.HashMap; 21import java.util.Iterator; 22import java.util.Map.Entry; 23import java.util.Set; 24 25import android.content.Context; 26import android.os.Bundle; 27import android.os.Message; 28import android.telephony.Rlog; 29 30import com.android.ims.internal.CallGroup; 31import com.android.ims.internal.CallGroupManager; 32import com.android.ims.internal.ICall; 33import com.android.ims.internal.ImsCallSession; 34import com.android.ims.internal.ImsStreamMediaSession; 35 36/** 37 * Handles an IMS voice / video call over LTE. You can instantiate this class with 38 * {@link ImsManager}. 39 * 40 * @hide 41 */ 42public class ImsCall implements ICall { 43 public static final int CALL_STATE_ACTIVE_TO_HOLD = 1; 44 public static final int CALL_STATE_HOLD_TO_ACTIVE = 2; 45 46 // Mode of USSD message 47 public static final int USSD_MODE_NOTIFY = 0; 48 public static final int USSD_MODE_REQUEST = 1; 49 50 private static final String TAG = "ImsCall"; 51 private static final boolean DBG = true; 52 53 /** 54 * Listener for events relating to an IMS call, such as when a call is being 55 * recieved ("on ringing") or a call is outgoing ("on calling"). 56 * <p>Many of these events are also received by {@link ImsCallSession.Listener}.</p> 57 */ 58 public static class Listener { 59 /** 60 * Called when a request is sent out to initiate a new call 61 * and 1xx response is received from the network. 62 * The default implementation calls {@link #onCallStateChanged}. 63 * 64 * @param call the call object that carries out the IMS call 65 */ 66 public void onCallProgressing(ImsCall call) { 67 onCallStateChanged(call); 68 } 69 70 /** 71 * Called when the call is established. 72 * The default implementation calls {@link #onCallStateChanged}. 73 * 74 * @param call the call object that carries out the IMS call 75 */ 76 public void onCallStarted(ImsCall call) { 77 onCallStateChanged(call); 78 } 79 80 /** 81 * Called when the call setup is failed. 82 * The default implementation calls {@link #onCallError}. 83 * 84 * @param call the call object that carries out the IMS call 85 * @param reasonInfo detailed reason of the call setup failure 86 */ 87 public void onCallStartFailed(ImsCall call, ImsReasonInfo reasonInfo) { 88 onCallError(call, reasonInfo); 89 } 90 91 /** 92 * Called when the call is terminated. 93 * The default implementation calls {@link #onCallStateChanged}. 94 * 95 * @param call the call object that carries out the IMS call 96 * @param reasonInfo detailed reason of the call termination 97 */ 98 public void onCallTerminated(ImsCall call, ImsReasonInfo reasonInfo) { 99 // Store the call termination reason 100 101 onCallStateChanged(call); 102 } 103 104 /** 105 * Called when the call is in hold. 106 * The default implementation calls {@link #onCallStateChanged}. 107 * 108 * @param call the call object that carries out the IMS call 109 */ 110 public void onCallHeld(ImsCall call) { 111 onCallStateChanged(call); 112 } 113 114 /** 115 * Called when the call hold is failed. 116 * The default implementation calls {@link #onCallError}. 117 * 118 * @param call the call object that carries out the IMS call 119 * @param reasonInfo detailed reason of the call hold failure 120 */ 121 public void onCallHoldFailed(ImsCall call, ImsReasonInfo reasonInfo) { 122 onCallError(call, reasonInfo); 123 } 124 125 /** 126 * Called when the call hold is received from the remote user. 127 * The default implementation calls {@link #onCallStateChanged}. 128 * 129 * @param call the call object that carries out the IMS call 130 */ 131 public void onCallHoldReceived(ImsCall call) { 132 onCallStateChanged(call); 133 } 134 135 /** 136 * Called when the call is in call. 137 * The default implementation calls {@link #onCallStateChanged}. 138 * 139 * @param call the call object that carries out the IMS call 140 */ 141 public void onCallResumed(ImsCall call) { 142 onCallStateChanged(call); 143 } 144 145 /** 146 * Called when the call resume is failed. 147 * The default implementation calls {@link #onCallError}. 148 * 149 * @param call the call object that carries out the IMS call 150 * @param reasonInfo detailed reason of the call resume failure 151 */ 152 public void onCallResumeFailed(ImsCall call, ImsReasonInfo reasonInfo) { 153 onCallError(call, reasonInfo); 154 } 155 156 /** 157 * Called when the call resume is received from the remote user. 158 * The default implementation calls {@link #onCallStateChanged}. 159 * 160 * @param call the call object that carries out the IMS call 161 */ 162 public void onCallResumeReceived(ImsCall call) { 163 onCallStateChanged(call); 164 } 165 166 /** 167 * Called when the call is in call. 168 * The default implementation calls {@link #onCallStateChanged}. 169 * 170 * @param call the call object that carries out the IMS call 171 * @param newCall the call object that is merged with an active & hold call 172 */ 173 public void onCallMerged(ImsCall call, ImsCall newCall) { 174 onCallStateChanged(call, newCall); 175 } 176 177 /** 178 * Called when the call merge is failed. 179 * The default implementation calls {@link #onCallError}. 180 * 181 * @param call the call object that carries out the IMS call 182 * @param reasonInfo detailed reason of the call merge failure 183 */ 184 public void onCallMergeFailed(ImsCall call, ImsReasonInfo reasonInfo) { 185 onCallError(call, reasonInfo); 186 } 187 188 /** 189 * Called when the call is updated (except for hold/unhold). 190 * The default implementation calls {@link #onCallStateChanged}. 191 * 192 * @param call the call object that carries out the IMS call 193 */ 194 public void onCallUpdated(ImsCall call) { 195 onCallStateChanged(call); 196 } 197 198 /** 199 * Called when the call update is failed. 200 * The default implementation calls {@link #onCallError}. 201 * 202 * @param call the call object that carries out the IMS call 203 * @param reasonInfo detailed reason of the call update failure 204 */ 205 public void onCallUpdateFailed(ImsCall call, ImsReasonInfo reasonInfo) { 206 onCallError(call, reasonInfo); 207 } 208 209 /** 210 * Called when the call update is received from the remote user. 211 * 212 * @param call the call object that carries out the IMS call 213 */ 214 public void onCallUpdateReceived(ImsCall call) { 215 // no-op 216 } 217 218 /** 219 * Called when the call is extended to the conference call. 220 * The default implementation calls {@link #onCallStateChanged}. 221 * 222 * @param call the call object that carries out the IMS call 223 * @param newCall the call object that is extended to the conference from the active call 224 */ 225 public void onCallConferenceExtended(ImsCall call, ImsCall newCall) { 226 onCallStateChanged(call, newCall); 227 } 228 229 /** 230 * Called when the conference extension is failed. 231 * The default implementation calls {@link #onCallError}. 232 * 233 * @param call the call object that carries out the IMS call 234 * @param reasonInfo detailed reason of the conference extension failure 235 */ 236 public void onCallConferenceExtendFailed(ImsCall call, 237 ImsReasonInfo reasonInfo) { 238 onCallError(call, reasonInfo); 239 } 240 241 /** 242 * Called when the conference extension is received from the remote user. 243 * 244 * @param call the call object that carries out the IMS call 245 * @param newCall the call object that is extended to the conference from the active call 246 */ 247 public void onCallConferenceExtendReceived(ImsCall call, ImsCall newCall) { 248 onCallStateChanged(call, newCall); 249 } 250 251 /** 252 * Called when the invitation request of the participants is delivered to 253 * the conference server. 254 * 255 * @param call the call object that carries out the IMS call 256 */ 257 public void onCallInviteParticipantsRequestDelivered(ImsCall call) { 258 // no-op 259 } 260 261 /** 262 * Called when the invitation request of the participants is failed. 263 * 264 * @param call the call object that carries out the IMS call 265 * @param reasonInfo detailed reason of the conference invitation failure 266 */ 267 public void onCallInviteParticipantsRequestFailed(ImsCall call, 268 ImsReasonInfo reasonInfo) { 269 // no-op 270 } 271 272 /** 273 * Called when the removal request of the participants is delivered to 274 * the conference server. 275 * 276 * @param call the call object that carries out the IMS call 277 */ 278 public void onCallRemoveParticipantsRequestDelivered(ImsCall call) { 279 // no-op 280 } 281 282 /** 283 * Called when the removal request of the participants is failed. 284 * 285 * @param call the call object that carries out the IMS call 286 * @param reasonInfo detailed reason of the conference removal failure 287 */ 288 public void onCallRemoveParticipantsRequestFailed(ImsCall call, 289 ImsReasonInfo reasonInfo) { 290 // no-op 291 } 292 293 /** 294 * Called when the conference state is updated. 295 * 296 * @param call the call object that carries out the IMS call 297 * @param state state of the participant who is participated in the conference call 298 */ 299 public void onCallConferenceStateUpdated(ImsCall call, ImsConferenceState state) { 300 // no-op 301 } 302 303 /** 304 * Called when the USSD message is received from the network. 305 * 306 * @param mode mode of the USSD message (REQUEST / NOTIFY) 307 * @param ussdMessage USSD message 308 */ 309 public void onCallUssdMessageReceived(ImsCall call, 310 int mode, String ussdMessage) { 311 // no-op 312 } 313 314 /** 315 * Called when an error occurs. The default implementation is no op. 316 * overridden. The default implementation is no op. Error events are 317 * not re-directed to this callback and are handled in {@link #onCallError}. 318 * 319 * @param call the call object that carries out the IMS call 320 * @param reasonInfo detailed reason of this error 321 * @see ImsReasonInfo 322 */ 323 public void onCallError(ImsCall call, ImsReasonInfo reasonInfo) { 324 // no-op 325 } 326 327 /** 328 * Called when an event occurs and the corresponding callback is not 329 * overridden. The default implementation is no op. Error events are 330 * not re-directed to this callback and are handled in {@link #onCallError}. 331 * 332 * @param call the call object that carries out the IMS call 333 */ 334 public void onCallStateChanged(ImsCall call) { 335 // no-op 336 } 337 338 /** 339 * Called when an event occurs and the corresponding callback is not 340 * overridden. The default implementation is no op. Error events are 341 * not re-directed to this callback and are handled in {@link #onCallError}. 342 * 343 * @param call the call object that carries out the IMS call 344 * @param newCall the call object that will be replaced by the previous call 345 */ 346 public void onCallStateChanged(ImsCall call, ImsCall newCall) { 347 // no-op 348 } 349 350 /** 351 * Called when the call moves the hold state to the conversation state. 352 * For example, when merging the active & hold call, the state of all the hold call 353 * will be changed from hold state to conversation state. 354 * This callback method can be invoked even though the application does not trigger 355 * any operations. 356 * 357 * @param call the call object that carries out the IMS call 358 * @param state the detailed state of call state changes; 359 * Refer to CALL_STATE_* in {@link ImsCall} 360 */ 361 public void onCallStateChanged(ImsCall call, int state) { 362 // no-op 363 } 364 } 365 366 367 368 // List of update operation for IMS call control 369 private static final int UPDATE_NONE = 0; 370 private static final int UPDATE_HOLD = 1; 371 private static final int UPDATE_HOLD_MERGE = 2; 372 private static final int UPDATE_RESUME = 3; 373 private static final int UPDATE_MERGE = 4; 374 private static final int UPDATE_EXTEND_TO_CONFERENCE = 5; 375 private static final int UPDATE_UNSPECIFIED = 6; 376 377 // For synchronization of private variables 378 private Object mLockObj = new Object(); 379 private Context mContext; 380 381 // true if the call is established & in the conversation state 382 private boolean mInCall = false; 383 // true if the call is on hold 384 // If it is triggered by the local, mute the call. Otherwise, play local hold tone 385 // or network generated media. 386 private boolean mHold = false; 387 // true if the call is on mute 388 private boolean mMute = false; 389 // It contains the exclusive call update request. Refer to UPDATE_*. 390 private int mUpdateRequest = UPDATE_NONE; 391 392 private ImsCall.Listener mListener = null; 393 // It is for managing the multiple calls 394 // when the multiparty call is extended to the conference. 395 private CallGroup mCallGroup = null; 396 397 // Wrapper call session to interworking the IMS service (server). 398 private ImsCallSession mSession = null; 399 // Call profile of the current session. 400 // It can be changed at anytime when the call is updated. 401 private ImsCallProfile mCallProfile = null; 402 // Call profile to be updated after the application's action (accept/reject) 403 // to the call update. After the application's action (accept/reject) is done, 404 // it will be set to null. 405 private ImsCallProfile mProposedCallProfile = null; 406 private ImsReasonInfo mLastReasonInfo = null; 407 408 // Media session to control media (audio/video) operations for an IMS call 409 private ImsStreamMediaSession mMediaSession = null; 410 411 /** 412 * Create an IMS call object. 413 * 414 * @param context the context for accessing system services 415 * @param profile the call profile to make/take a call 416 */ 417 public ImsCall(Context context, ImsCallProfile profile) { 418 mContext = context; 419 mCallProfile = profile; 420 } 421 422 /** 423 * Closes this object. This object is not usable after being closed. 424 */ 425 @Override 426 public void close() { 427 synchronized(mLockObj) { 428 destroyCallGroup(); 429 430 if (mSession != null) { 431 mSession.close(); 432 mSession = null; 433 } 434 435 mCallProfile = null; 436 mProposedCallProfile = null; 437 mLastReasonInfo = null; 438 mMediaSession = null; 439 } 440 } 441 442 /** 443 * Checks if the call has a same remote user identity or not. 444 * 445 * @param userId the remote user identity 446 * @return true if the remote user identity is equal; otherwise, false 447 */ 448 @Override 449 public boolean checkIfRemoteUserIsSame(String userId) { 450 if (userId == null) { 451 return false; 452 } 453 454 return userId.equals(mCallProfile.getCallExtra(ImsCallProfile.EXTRA_REMOTE_URI, "")); 455 } 456 457 /** 458 * Checks if the call is equal or not. 459 * 460 * @param call the call to be compared 461 * @return true if the call is equal; otherwise, false 462 */ 463 @Override 464 public boolean equalsTo(ICall call) { 465 if (call == null) { 466 return false; 467 } 468 469 if (call instanceof ImsCall) { 470 return this.equals((ImsCall)call); 471 } 472 473 return false; 474 } 475 476 /** 477 * Gets the negotiated (local & remote) call profile. 478 * 479 * @return a {@link ImsCallProfile} object that has the negotiated call profile 480 */ 481 public ImsCallProfile getCallProfile() { 482 synchronized(mLockObj) { 483 return mCallProfile; 484 } 485 } 486 487 /** 488 * Gets the local call profile (local capabilities). 489 * 490 * @return a {@link ImsCallProfile} object that has the local call profile 491 */ 492 public ImsCallProfile getLocalCallProfile() throws ImsException { 493 synchronized(mLockObj) { 494 if (mSession == null) { 495 throw new ImsException("No call session", 496 ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED); 497 } 498 499 try { 500 return mSession.getLocalCallProfile(); 501 } catch (Throwable t) { 502 loge("getLocalCallProfile :: ", t); 503 throw new ImsException("getLocalCallProfile()", t, 0); 504 } 505 } 506 } 507 508 /** 509 * Gets the call profile proposed by the local/remote user. 510 * 511 * @return a {@link ImsCallProfile} object that has the proposed call profile 512 */ 513 public ImsCallProfile getProposedCallProfile() { 514 synchronized(mLockObj) { 515 if (!isInCall()) { 516 return null; 517 } 518 519 return mProposedCallProfile; 520 } 521 } 522 523 /** 524 * Gets the state of the {@link ImsCallSession} that carries this call. 525 * The value returned must be one of the states in {@link ImsCallSession#State}. 526 * 527 * @return the session state 528 */ 529 public int getState() { 530 synchronized(mLockObj) { 531 if (mSession == null) { 532 return ImsCallSession.State.IDLE; 533 } 534 535 return mSession.getState(); 536 } 537 } 538 539 /** 540 * Gets the {@link ImsCallSession} that carries this call. 541 * 542 * @return the session object that carries this call 543 * @hide 544 */ 545 public ImsCallSession getCallSession() { 546 synchronized(mLockObj) { 547 return mSession; 548 } 549 } 550 551 /** 552 * Gets the {@link ImsStreamMediaSession} that handles the media operation of this call. 553 * Almost interface APIs are for the VT (Video Telephony). 554 * 555 * @return the media session object that handles the media operation of this call 556 * @hide 557 */ 558 public ImsStreamMediaSession getMediaSession() { 559 synchronized(mLockObj) { 560 return mMediaSession; 561 } 562 } 563 564 /** 565 * Gets the specified property of this call. 566 * 567 * @param name key to get the extra call information defined in {@link ImsCallProfile} 568 * @return the extra call information as string 569 */ 570 public String getCallExtra(String name) throws ImsException { 571 // Lookup the cache 572 573 synchronized(mLockObj) { 574 // If not found, try to get the property from the remote 575 if (mSession == null) { 576 throw new ImsException("No call session", 577 ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED); 578 } 579 580 try { 581 return mSession.getProperty(name); 582 } catch (Throwable t) { 583 loge("getCallExtra :: ", t); 584 throw new ImsException("getCallExtra()", t, 0); 585 } 586 } 587 } 588 589 /** 590 * Gets the last reason information when the call is not established, cancelled or terminated. 591 * 592 * @return the last reason information 593 */ 594 public ImsReasonInfo getLastReasonInfo() { 595 synchronized(mLockObj) { 596 return mLastReasonInfo; 597 } 598 } 599 600 /** 601 * Checks if the call has a pending update operation. 602 * 603 * @return true if the call has a pending update operation 604 */ 605 public boolean hasPendingUpdate() { 606 synchronized(mLockObj) { 607 return (mUpdateRequest != UPDATE_NONE); 608 } 609 } 610 611 /** 612 * Checks if the call is established. 613 * 614 * @return true if the call is established 615 */ 616 public boolean isInCall() { 617 synchronized(mLockObj) { 618 return mInCall; 619 } 620 } 621 622 /** 623 * Checks if the call is muted. 624 * 625 * @return true if the call is muted 626 */ 627 public boolean isMuted() { 628 synchronized(mLockObj) { 629 return mMute; 630 } 631 } 632 633 /** 634 * Checks if the call is on hold. 635 * 636 * @return true if the call is on hold 637 */ 638 public boolean isOnHold() { 639 synchronized(mLockObj) { 640 return mHold; 641 } 642 } 643 644 /** 645 * Sets the listener to listen to the IMS call events. 646 * The method calls {@link #setListener setListener(listener, false)}. 647 * 648 * @param listener to listen to the IMS call events of this object; null to remove listener 649 * @see #setListener(Listener, boolean) 650 */ 651 public void setListener(ImsCall.Listener listener) { 652 setListener(listener, false); 653 } 654 655 /** 656 * Sets the listener to listen to the IMS call events. 657 * A {@link ImsCall} can only hold one listener at a time. Subsequent calls 658 * to this method override the previous listener. 659 * 660 * @param listener to listen to the IMS call events of this object; null to remove listener 661 * @param callbackImmediately set to true if the caller wants to be called 662 * back immediately on the current state 663 */ 664 public void setListener(ImsCall.Listener listener, boolean callbackImmediately) { 665 boolean inCall; 666 boolean onHold; 667 int state; 668 ImsReasonInfo lastReasonInfo; 669 670 synchronized(mLockObj) { 671 mListener = listener; 672 673 if ((listener == null) || !callbackImmediately) { 674 return; 675 } 676 677 inCall = mInCall; 678 onHold = mHold; 679 state = getState(); 680 lastReasonInfo = mLastReasonInfo; 681 } 682 683 try { 684 if (lastReasonInfo != null) { 685 listener.onCallError(this, lastReasonInfo); 686 } else if (inCall) { 687 if (onHold) { 688 listener.onCallHeld(this); 689 } else { 690 listener.onCallStarted(this); 691 } 692 } else { 693 switch (state) { 694 case ImsCallSession.State.ESTABLISHING: 695 listener.onCallProgressing(this); 696 break; 697 case ImsCallSession.State.TERMINATED: 698 listener.onCallTerminated(this, lastReasonInfo); 699 break; 700 default: 701 // Ignore it. There is no action in the other state. 702 break; 703 } 704 } 705 } catch (Throwable t) { 706 loge("setListener()", t); 707 } 708 } 709 710 /** 711 * Mutes or unmutes the mic for the active call. 712 * 713 * @param muted true if the call is muted, false otherwise 714 */ 715 public void setMute(boolean muted) throws ImsException { 716 synchronized(mLockObj) { 717 if (mMute != muted) { 718 mMute = muted; 719 720 try { 721 mSession.setMute(muted); 722 } catch (Throwable t) { 723 loge("setMute :: ", t); 724 throwImsException(t, 0); 725 } 726 } 727 } 728 } 729 730 /** 731 * Attaches an incoming call to this call object. 732 * 733 * @param session the session that receives the incoming call 734 * @throws ImsException if the IMS service fails to attach this object to the session 735 */ 736 public void attachSession(ImsCallSession session) throws ImsException { 737 if (DBG) { 738 log("attachSession :: session=" + session); 739 } 740 741 synchronized(mLockObj) { 742 mSession = session; 743 744 try { 745 mSession.setListener(createCallSessionListener()); 746 } catch (Throwable t) { 747 loge("attachSession :: ", t); 748 throwImsException(t, 0); 749 } 750 } 751 } 752 753 /** 754 * Initiates an IMS call with the call profile which is provided 755 * when creating a {@link ImsCall}. 756 * 757 * @param session the {@link ImsCallSession} for carrying out the call 758 * @param callee callee information to initiate an IMS call 759 * @throws ImsException if the IMS service fails to initiate the call 760 */ 761 public void start(ImsCallSession session, String callee) 762 throws ImsException { 763 if (DBG) { 764 log("start(1) :: session=" + session + ", callee=" + callee); 765 } 766 767 synchronized(mLockObj) { 768 mSession = session; 769 770 try { 771 session.setListener(createCallSessionListener()); 772 session.start(callee, mCallProfile); 773 } catch (Throwable t) { 774 loge("start(1) :: ", t); 775 throw new ImsException("start(1)", t, 0); 776 } 777 } 778 } 779 780 /** 781 * Initiates an IMS conferenca call with the call profile which is provided 782 * when creating a {@link ImsCall}. 783 * 784 * @param session the {@link ImsCallSession} for carrying out the call 785 * @param participants participant list to initiate an IMS conference call 786 * @throws ImsException if the IMS service fails to initiate the call 787 */ 788 public void start(ImsCallSession session, String[] participants) 789 throws ImsException { 790 if (DBG) { 791 log("start(n) :: session=" + session + ", callee=" + participants); 792 } 793 794 synchronized(mLockObj) { 795 mSession = session; 796 797 try { 798 session.setListener(createCallSessionListener()); 799 session.start(participants, mCallProfile); 800 } catch (Throwable t) { 801 loge("start(n) :: ", t); 802 throw new ImsException("start(n)", t, 0); 803 } 804 } 805 } 806 807 /** 808 * Accepts a call. 809 * 810 * @see Listener#onCallStarted 811 * @throws ImsException if the IMS service fails to accept the call 812 */ 813 public void accept() throws ImsException { 814 if (DBG) { 815 log("accept :: session=" + mSession); 816 } 817 818 accept(ImsCallProfile.CALL_TYPE_VOICE, new ImsStreamMediaProfile()); 819 } 820 821 /** 822 * Accepts a call. 823 * 824 * @param callType call type to be answered in {@link ImsCallProfile} 825 * @param profile a media profile to be answered (audio/audio & video, direction, ...) 826 * @see Listener#onCallStarted 827 * @throws ImsException if the IMS service fails to accept the call 828 */ 829 public void accept(int callType, ImsStreamMediaProfile profile) throws ImsException { 830 if (DBG) { 831 log("accept :: session=" + mSession 832 + ", callType=" + callType + ", profile=" + profile); 833 } 834 835 synchronized(mLockObj) { 836 if (mSession == null) { 837 throw new ImsException("No call to answer", 838 ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED); 839 } 840 841 try { 842 mSession.accept(callType, profile); 843 } catch (Throwable t) { 844 loge("accept :: ", t); 845 throw new ImsException("accept()", t, 0); 846 } 847 848 if (mInCall && (mProposedCallProfile != null)) { 849 if (DBG) { 850 log("accept :: call profile will be updated"); 851 } 852 853 mCallProfile = mProposedCallProfile; 854 mProposedCallProfile = null; 855 } 856 857 // Other call update received 858 if (mInCall && (mUpdateRequest == UPDATE_UNSPECIFIED)) { 859 mUpdateRequest = UPDATE_NONE; 860 } 861 } 862 } 863 864 /** 865 * Rejects a call. 866 * 867 * @param reason reason code to reject an incoming call 868 * @see Listener#onCallStartFailed 869 * @throws ImsException if the IMS service fails to accept the call 870 */ 871 public void reject(int reason) throws ImsException { 872 if (DBG) { 873 log("reject :: session=" + mSession + ", reason=" + reason); 874 } 875 876 synchronized(mLockObj) { 877 if (mSession != null) { 878 mSession.reject(reason); 879 } 880 881 if (mInCall && (mProposedCallProfile != null)) { 882 if (DBG) { 883 log("reject :: call profile is not updated; destroy it..."); 884 } 885 886 mProposedCallProfile = null; 887 } 888 889 // Other call update received 890 if (mInCall && (mUpdateRequest == UPDATE_UNSPECIFIED)) { 891 mUpdateRequest = UPDATE_NONE; 892 } 893 } 894 } 895 896 /** 897 * Terminates an IMS call. 898 * 899 * @param reason reason code to terminate a call 900 * @throws ImsException if the IMS service fails to terminate the call 901 */ 902 public void terminate(int reason) throws ImsException { 903 if (DBG) { 904 log("terminate :: session=" + mSession + ", reason=" + reason); 905 } 906 907 synchronized(mLockObj) { 908 mHold = false; 909 mInCall = false; 910 911 if (mSession != null) { 912 mSession.terminate(reason); 913 } 914 } 915 } 916 917 /** 918 * Puts a call on hold. When succeeds, {@link Listener#onCallHeld} is called. 919 * 920 * @see Listener#onCallHeld, Listener#onCallHoldFailed 921 * @throws ImsException if the IMS service fails to hold the call 922 */ 923 public void hold() throws ImsException { 924 if (DBG) { 925 log("hold :: session=" + mSession); 926 } 927 928 if (isOnHold()) { 929 if (DBG) { 930 log("hold :: call is already on hold"); 931 } 932 return; 933 } 934 935 synchronized(mLockObj) { 936 if (mUpdateRequest != UPDATE_NONE) { 937 loge("hold :: update is in progress; request=" + mUpdateRequest); 938 throw new ImsException("Call update is in progress", 939 ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE); 940 } 941 942 if (mSession == null) { 943 loge("hold :: "); 944 throw new ImsException("No call session", 945 ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED); 946 } 947 948 mSession.hold(createHoldMediaProfile()); 949 // FIXME: update the state on the callback? 950 mHold = true; 951 mUpdateRequest = UPDATE_HOLD; 952 } 953 } 954 955 /** 956 * Continues a call that's on hold. When succeeds, {@link Listener#onCallResumed} is called. 957 * 958 * @see Listener#onCallResumed, Listener#onCallResumeFailed 959 * @throws ImsException if the IMS service fails to resume the call 960 */ 961 public void resume() throws ImsException { 962 if (DBG) { 963 log("resume :: session=" + mSession); 964 } 965 966 if (!isOnHold()) { 967 if (DBG) { 968 log("resume :: call is in conversation"); 969 } 970 return; 971 } 972 973 synchronized(mLockObj) { 974 if (mUpdateRequest != UPDATE_NONE) { 975 loge("resume :: update is in progress; request=" + mUpdateRequest); 976 throw new ImsException("Call update is in progress", 977 ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE); 978 } 979 980 if (mSession == null) { 981 loge("resume :: "); 982 throw new ImsException("No call session", 983 ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED); 984 } 985 986 mSession.resume(createResumeMediaProfile()); 987 // FIXME: update the state on the callback? 988 mHold = false; 989 mUpdateRequest = UPDATE_RESUME; 990 } 991 } 992 993 /** 994 * Merges the active & hold call. 995 * 996 * @see Listener#onCallMerged, Listener#onCallMergeFailed 997 * @throws ImsException if the IMS service fails to merge the call 998 */ 999 public void merge() throws ImsException { 1000 if (DBG) { 1001 log("merge :: session=" + mSession); 1002 } 1003 1004 synchronized(mLockObj) { 1005 if (mUpdateRequest != UPDATE_NONE) { 1006 loge("merge :: update is in progress; request=" + mUpdateRequest); 1007 throw new ImsException("Call update is in progress", 1008 ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE); 1009 } 1010 1011 if (mSession == null) { 1012 loge("merge :: "); 1013 throw new ImsException("No call session", 1014 ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED); 1015 } 1016 1017 if (mHold) { 1018 mSession.merge(); 1019 mUpdateRequest = UPDATE_MERGE; 1020 } else { 1021 mSession.hold(createHoldMediaProfile()); 1022 // FIXME: ? 1023 mHold = true; 1024 mUpdateRequest = UPDATE_HOLD_MERGE; 1025 } 1026 } 1027 } 1028 1029 /** 1030 * Merges the active & hold call. 1031 * 1032 * @param bgCall the background (holding) call 1033 * @see Listener#onCallMerged, Listener#onCallMergeFailed 1034 * @throws ImsException if the IMS service fails to merge the call 1035 */ 1036 public void merge(ImsCall bgCall) throws ImsException { 1037 if (DBG) { 1038 log("merge(1) :: session=" + mSession); 1039 } 1040 1041 if (bgCall == null) { 1042 throw new ImsException("No background call", 1043 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT); 1044 } 1045 1046 synchronized(mLockObj) { 1047 createCallGroup(bgCall); 1048 } 1049 1050 merge(); 1051 } 1052 1053 /** 1054 * Updates the current call's properties (ex. call mode change: video upgrade / downgrade). 1055 */ 1056 public void update(int callType, ImsStreamMediaProfile mediaProfile) throws ImsException { 1057 if (DBG) { 1058 log("update :: session=" + mSession); 1059 } 1060 1061 if (isOnHold()) { 1062 if (DBG) { 1063 log("update :: call is on hold"); 1064 } 1065 throw new ImsException("Not in a call to update call", 1066 ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE); 1067 } 1068 1069 synchronized(mLockObj) { 1070 if (mUpdateRequest != UPDATE_NONE) { 1071 if (DBG) { 1072 log("update :: update is in progress; request=" + mUpdateRequest); 1073 } 1074 throw new ImsException("Call update is in progress", 1075 ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE); 1076 } 1077 1078 if (mSession == null) { 1079 loge("update :: "); 1080 throw new ImsException("No call session", 1081 ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED); 1082 } 1083 1084 mSession.update(callType, mediaProfile); 1085 mUpdateRequest = UPDATE_UNSPECIFIED; 1086 } 1087 } 1088 1089 /** 1090 * Extends this call (1-to-1 call) to the conference call 1091 * inviting the specified participants to. 1092 * 1093 */ 1094 public void extendToConference(String[] participants) throws ImsException { 1095 if (DBG) { 1096 log("extendToConference :: session=" + mSession); 1097 } 1098 1099 if (isOnHold()) { 1100 if (DBG) { 1101 log("extendToConference :: call is on hold"); 1102 } 1103 throw new ImsException("Not in a call to extend a call to conference", 1104 ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE); 1105 } 1106 1107 synchronized(mLockObj) { 1108 if (mUpdateRequest != UPDATE_NONE) { 1109 if (DBG) { 1110 log("extendToConference :: update is in progress; request=" + mUpdateRequest); 1111 } 1112 throw new ImsException("Call update is in progress", 1113 ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE); 1114 } 1115 1116 if (mSession == null) { 1117 loge("extendToConference :: "); 1118 throw new ImsException("No call session", 1119 ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED); 1120 } 1121 1122 mSession.extendToConference(participants); 1123 mUpdateRequest = UPDATE_EXTEND_TO_CONFERENCE; 1124 } 1125 } 1126 1127 /** 1128 * Requests the conference server to invite an additional participants to the conference. 1129 * 1130 */ 1131 public void inviteParticipants(String[] participants) throws ImsException { 1132 if (DBG) { 1133 log("inviteParticipants :: session=" + mSession); 1134 } 1135 1136 synchronized(mLockObj) { 1137 if (mSession == null) { 1138 loge("inviteParticipants :: "); 1139 throw new ImsException("No call session", 1140 ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED); 1141 } 1142 1143 mSession.inviteParticipants(participants); 1144 } 1145 } 1146 1147 /** 1148 * Requests the conference server to remove the specified participants from the conference. 1149 * 1150 */ 1151 public void removeParticipants(String[] participants) throws ImsException { 1152 if (DBG) { 1153 log("removeParticipants :: session=" + mSession); 1154 } 1155 1156 synchronized(mLockObj) { 1157 if (mSession == null) { 1158 loge("removeParticipants :: "); 1159 throw new ImsException("No call session", 1160 ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED); 1161 } 1162 1163 mSession.removeParticipants(participants); 1164 } 1165 } 1166 1167 1168 /** 1169 * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>, 1170 * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15, 1171 * and event flash to 16. Currently, event flash is not supported. 1172 * 1173 * @param code the DTMF to send. Value 0 to 15 (inclusive) are valid inputs. 1174 */ 1175 public void sendDtmf(int code) { 1176 sendDtmf(code, null); 1177 } 1178 1179 /** 1180 * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>, 1181 * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15, 1182 * and event flash to 16. Currently, event flash is not supported. 1183 * 1184 * @param code the DTMF to send. Value 0 to 15 (inclusive) are valid inputs. 1185 * @param result the result message to send when done 1186 */ 1187 public void sendDtmf(int code, Message result) { 1188 if (DBG) { 1189 log("sendDtmf :: session=" + mSession + ", code=" + code); 1190 } 1191 1192 synchronized(mLockObj) { 1193 if (mSession != null) { 1194 mSession.sendDtmf(code); 1195 } 1196 } 1197 1198 if (result != null) { 1199 result.sendToTarget(); 1200 } 1201 } 1202 1203 /** 1204 * Sends an USSD message. 1205 * 1206 * @param ussdMessage USSD message to send 1207 */ 1208 public void sendUssd(String ussdMessage) throws ImsException { 1209 if (DBG) { 1210 log("sendUssd :: session=" + mSession + ", ussdMessage=" + ussdMessage); 1211 } 1212 1213 synchronized(mLockObj) { 1214 if (mSession == null) { 1215 loge("sendUssd :: "); 1216 throw new ImsException("No call session", 1217 ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED); 1218 } 1219 1220 mSession.sendUssd(ussdMessage); 1221 } 1222 } 1223 1224 private void clear(ImsReasonInfo lastReasonInfo) { 1225 mInCall = false; 1226 mHold = false; 1227 mUpdateRequest = UPDATE_NONE; 1228 mLastReasonInfo = lastReasonInfo; 1229 destroyCallGroup(); 1230 } 1231 1232 private void createCallGroup(ImsCall neutralReferrer) { 1233 CallGroup referrerCallGroup = neutralReferrer.getCallGroup(); 1234 1235 if (mCallGroup == null) { 1236 if (referrerCallGroup == null) { 1237 mCallGroup = CallGroupManager.getInstance().createCallGroup(new ImsCallGroup()); 1238 } else { 1239 mCallGroup = referrerCallGroup; 1240 } 1241 1242 if (mCallGroup != null) { 1243 mCallGroup.setNeutralReferrer(neutralReferrer); 1244 } 1245 } else { 1246 mCallGroup.setNeutralReferrer(neutralReferrer); 1247 1248 if ((referrerCallGroup != null) 1249 && (mCallGroup != referrerCallGroup)) { 1250 loge("fatal :: call group is mismatched; call is corrupted..."); 1251 } 1252 } 1253 } 1254 1255 private void updateCallGroup(ImsCall owner) { 1256 if (mCallGroup == null) { 1257 return; 1258 } 1259 1260 ImsCall neutralReferrer = (ImsCall)mCallGroup.getNeutralReferrer(); 1261 1262 mCallGroup.setNeutralReferrer(null); 1263 1264 if (owner == null) { 1265 // Maintain the call group if the current call has been merged in the past. 1266 if (!mCallGroup.hasReferrer()) { 1267 CallGroupManager.getInstance().destroyCallGroup(mCallGroup); 1268 mCallGroup = null; 1269 } 1270 } else { 1271 mCallGroup.addReferrer(this); 1272 1273 if (neutralReferrer != null) { 1274 if (neutralReferrer.isInCall() && (neutralReferrer.getCallGroup() == null)) { 1275 neutralReferrer.setCallGroup(mCallGroup); 1276 mCallGroup.addReferrer(neutralReferrer); 1277 } 1278 1279 neutralReferrer.enforceConversationMode(); 1280 } 1281 1282 // Close the existing owner call if present 1283 ImsCall exOwner = (ImsCall)mCallGroup.getOwner(); 1284 1285 mCallGroup.setOwner(owner); 1286 1287 if (exOwner != null) { 1288 exOwner.close(); 1289 } 1290 } 1291 } 1292 1293 private void destroyCallGroup() { 1294 if (mCallGroup == null) { 1295 return; 1296 } 1297 1298 mCallGroup.removeReferrer(this); 1299 1300 if (!mCallGroup.hasReferrer()) { 1301 CallGroupManager.getInstance().destroyCallGroup(mCallGroup); 1302 } 1303 1304 mCallGroup = null; 1305 } 1306 1307 private CallGroup getCallGroup() { 1308 synchronized(mLockObj) { 1309 return mCallGroup; 1310 } 1311 } 1312 1313 private void setCallGroup(CallGroup callGroup) { 1314 synchronized(mLockObj) { 1315 mCallGroup = callGroup; 1316 } 1317 } 1318 1319 /** 1320 * Creates an IMS call session listener. 1321 */ 1322 private ImsCallSession.Listener createCallSessionListener() { 1323 return new ImsCallSessionListenerProxy(); 1324 } 1325 1326 private ImsCall createNewCall(ImsCallSession session, ImsCallProfile profile) { 1327 ImsCall call = new ImsCall(mContext, profile); 1328 1329 try { 1330 call.attachSession(session); 1331 } catch (ImsException e) { 1332 if (call != null) { 1333 call.close(); 1334 call = null; 1335 } 1336 } 1337 1338 // Do additional operations... 1339 1340 return call; 1341 } 1342 1343 private ImsStreamMediaProfile createHoldMediaProfile() { 1344 ImsStreamMediaProfile mediaProfile = new ImsStreamMediaProfile(); 1345 1346 if (mCallProfile == null) { 1347 return mediaProfile; 1348 } 1349 1350 mediaProfile.mAudioQuality = mCallProfile.mMediaProfile.mAudioQuality; 1351 mediaProfile.mVideoQuality = mCallProfile.mMediaProfile.mVideoQuality; 1352 mediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND; 1353 1354 if (mediaProfile.mVideoQuality != ImsStreamMediaProfile.VIDEO_QUALITY_NONE) { 1355 mediaProfile.mVideoDirection = ImsStreamMediaProfile.DIRECTION_SEND; 1356 } 1357 1358 return mediaProfile; 1359 } 1360 1361 private ImsStreamMediaProfile createResumeMediaProfile() { 1362 ImsStreamMediaProfile mediaProfile = new ImsStreamMediaProfile(); 1363 1364 if (mCallProfile == null) { 1365 return mediaProfile; 1366 } 1367 1368 mediaProfile.mAudioQuality = mCallProfile.mMediaProfile.mAudioQuality; 1369 mediaProfile.mVideoQuality = mCallProfile.mMediaProfile.mVideoQuality; 1370 mediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE; 1371 1372 if (mediaProfile.mVideoQuality != ImsStreamMediaProfile.VIDEO_QUALITY_NONE) { 1373 mediaProfile.mVideoDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE; 1374 } 1375 1376 return mediaProfile; 1377 } 1378 1379 private void enforceConversationMode() { 1380 if (mInCall) { 1381 mHold = false; 1382 mUpdateRequest = UPDATE_NONE; 1383 } 1384 } 1385 1386 private void mergeInternal() { 1387 if (DBG) { 1388 log("mergeInternal :: session=" + mSession); 1389 } 1390 1391 mSession.merge(); 1392 mUpdateRequest = UPDATE_MERGE; 1393 } 1394 1395 private void notifyCallStateChanged() { 1396 int state = 0; 1397 1398 if (mInCall && (mUpdateRequest == UPDATE_HOLD_MERGE)) { 1399 state = CALL_STATE_ACTIVE_TO_HOLD; 1400 mHold = true; 1401 } else if (mInCall && ((mUpdateRequest == UPDATE_MERGE) 1402 || (mUpdateRequest == UPDATE_EXTEND_TO_CONFERENCE))) { 1403 state = CALL_STATE_HOLD_TO_ACTIVE; 1404 mHold = false; 1405 mMute = false; 1406 } 1407 1408 if (state != 0) { 1409 if (mListener != null) { 1410 try { 1411 mListener.onCallStateChanged(ImsCall.this, state); 1412 } catch (Throwable t) { 1413 loge("notifyCallStateChanged :: ", t); 1414 } 1415 } 1416 } 1417 } 1418 1419 private void notifyConferenceSessionTerminated(ImsReasonInfo reasonInfo) { 1420 ImsCall.Listener listener; 1421 1422 if (mCallGroup.isOwner(ImsCall.this)) { 1423 ArrayList<ICall> referrers = mCallGroup.getReferrers(); 1424 1425 if (referrers != null) { 1426 for (int i = 0; i < referrers.size(); ++i) { 1427 ImsCall call = (ImsCall)referrers.get(i); 1428 1429 if (call == null) { 1430 continue; 1431 } 1432 1433 listener = call.mListener; 1434 call.clear(reasonInfo); 1435 1436 if (listener != null) { 1437 try { 1438 listener.onCallTerminated(call, reasonInfo); 1439 } catch (Throwable t) { 1440 loge("notifyConferenceSessionTerminated :: ", t); 1441 } 1442 } 1443 } 1444 } 1445 } else if (!mCallGroup.isReferrer(ImsCall.this)) { 1446 return; 1447 } 1448 1449 listener = mListener; 1450 clear(reasonInfo); 1451 1452 if (listener != null) { 1453 try { 1454 listener.onCallTerminated(this, reasonInfo); 1455 } catch (Throwable t) { 1456 loge("notifyConferenceSessionTerminated :: ", t); 1457 } 1458 } 1459 } 1460 1461 private void notifyConferenceStateUpdated(ImsConferenceState state) { 1462 Set<Entry<String, Bundle>> paticipants = state.mParticipants.entrySet(); 1463 1464 if (paticipants == null) { 1465 return; 1466 } 1467 1468 Iterator<Entry<String, Bundle>> iterator = paticipants.iterator(); 1469 1470 while (iterator.hasNext()) { 1471 Entry<String, Bundle> entry = iterator.next(); 1472 1473 String key = entry.getKey(); 1474 Bundle confInfo = entry.getValue(); 1475 String status = confInfo.getString(ImsConferenceState.STATUS); 1476 String user = confInfo.getString(ImsConferenceState.USER); 1477 String endpoint = confInfo.getString(ImsConferenceState.ENDPOINT); 1478 1479 if (DBG) { 1480 log("notifyConferenceStateUpdated :: key=" + key + 1481 ", status=" + status + 1482 ", user=" + user + 1483 ", endpoint=" + endpoint); 1484 } 1485 1486 if ((mCallGroup != null) && (!mCallGroup.isOwner(ImsCall.this))) { 1487 continue; 1488 } 1489 1490 ImsCall referrer = (ImsCall)mCallGroup.getReferrer(endpoint); 1491 1492 if (referrer == null) { 1493 continue; 1494 } 1495 1496 if (referrer.mListener == null) { 1497 continue; 1498 } 1499 1500 try { 1501 if (status.equals(ImsConferenceState.STATUS_ALERTING)) { 1502 referrer.mListener.onCallProgressing(referrer); 1503 } 1504 else if (status.equals(ImsConferenceState.STATUS_CONNECT_FAIL)) { 1505 referrer.mListener.onCallStartFailed(referrer, new ImsReasonInfo()); 1506 } 1507 else if (status.equals(ImsConferenceState.STATUS_ON_HOLD)) { 1508 referrer.mListener.onCallHoldReceived(referrer); 1509 } 1510 else if (status.equals(ImsConferenceState.STATUS_CONNECTED)) { 1511 referrer.mListener.onCallStarted(referrer); 1512 } 1513 else if (status.equals(ImsConferenceState.STATUS_DISCONNECTED)) { 1514 referrer.clear(new ImsReasonInfo()); 1515 referrer.mListener.onCallTerminated(referrer, referrer.mLastReasonInfo); 1516 } 1517 } catch (Throwable t) { 1518 loge("notifyConferenceStateUpdated :: ", t); 1519 } 1520 } 1521 } 1522 1523 private void notifyError(int reason, int statusCode, String message) { 1524 } 1525 1526 private void throwImsException(Throwable t, int code) throws ImsException { 1527 if (t instanceof ImsException) { 1528 throw (ImsException) t; 1529 } else { 1530 throw new ImsException(String.valueOf(code), t, code); 1531 } 1532 } 1533 1534 private void log(String s) { 1535 Rlog.d(TAG, s); 1536 } 1537 1538 private void loge(String s) { 1539 Rlog.e(TAG, s); 1540 } 1541 1542 private void loge(String s, Throwable t) { 1543 Rlog.e(TAG, s, t); 1544 } 1545 1546 private class ImsCallSessionListenerProxy extends ImsCallSession.Listener { 1547 @Override 1548 public void callSessionProgressing(ImsCallSession session, 1549 ImsStreamMediaProfile profile) { 1550 if (DBG) { 1551 log("callSessionProgressing :: session=" + session + ", profile=" + profile); 1552 } 1553 1554 ImsCall.Listener listener; 1555 1556 synchronized(ImsCall.this) { 1557 listener = mListener; 1558 mCallProfile.mMediaProfile.copyFrom(profile); 1559 } 1560 1561 if (listener != null) { 1562 try { 1563 listener.onCallProgressing(ImsCall.this); 1564 } catch (Throwable t) { 1565 loge("callSessionProgressing :: ", t); 1566 } 1567 } 1568 } 1569 1570 @Override 1571 public void callSessionStarted(ImsCallSession session, 1572 ImsCallProfile profile) { 1573 if (DBG) { 1574 log("callSessionStarted :: session=" + session + ", profile=" + profile); 1575 } 1576 1577 ImsCall.Listener listener; 1578 1579 synchronized(ImsCall.this) { 1580 listener = mListener; 1581 mCallProfile = profile; 1582 } 1583 1584 if (listener != null) { 1585 try { 1586 listener.onCallStarted(ImsCall.this); 1587 } catch (Throwable t) { 1588 loge("callSessionStarted :: ", t); 1589 } 1590 } 1591 } 1592 1593 @Override 1594 public void callSessionStartFailed(ImsCallSession session, 1595 ImsReasonInfo reasonInfo) { 1596 if (DBG) { 1597 log("callSessionStartFailed :: session=" + session + 1598 ", reasonInfo=" + reasonInfo); 1599 } 1600 1601 ImsCall.Listener listener; 1602 1603 synchronized(ImsCall.this) { 1604 listener = mListener; 1605 mLastReasonInfo = reasonInfo; 1606 } 1607 1608 if (listener != null) { 1609 try { 1610 listener.onCallStartFailed(ImsCall.this, reasonInfo); 1611 } catch (Throwable t) { 1612 loge("callSessionStarted :: ", t); 1613 } 1614 } 1615 } 1616 1617 @Override 1618 public void callSessionTerminated(ImsCallSession session, 1619 ImsReasonInfo reasonInfo) { 1620 if (DBG) { 1621 log("callSessionTerminated :: session=" + session + 1622 ", reasonInfo=" + reasonInfo); 1623 } 1624 1625 ImsCall.Listener listener = null; 1626 1627 synchronized(ImsCall.this) { 1628 if (mCallGroup != null) { 1629 notifyConferenceSessionTerminated(reasonInfo); 1630 } else { 1631 listener = mListener; 1632 clear(reasonInfo); 1633 } 1634 } 1635 1636 if (listener != null) { 1637 try { 1638 listener.onCallTerminated(ImsCall.this, reasonInfo); 1639 } catch (Throwable t) { 1640 loge("callSessionTerminated :: ", t); 1641 } 1642 } 1643 } 1644 1645 @Override 1646 public void callSessionHeld(ImsCallSession session, 1647 ImsCallProfile profile) { 1648 if (DBG) { 1649 log("callSessionHeld :: session=" + session + ", profile=" + profile); 1650 } 1651 1652 ImsCall.Listener listener; 1653 1654 synchronized(ImsCall.this) { 1655 mCallProfile = profile; 1656 1657 if (mUpdateRequest == UPDATE_HOLD_MERGE) { 1658 mergeInternal(); 1659 return; 1660 } 1661 1662 mUpdateRequest = UPDATE_NONE; 1663 listener = mListener; 1664 } 1665 1666 if (listener != null) { 1667 try { 1668 listener.onCallHeld(ImsCall.this); 1669 } catch (Throwable t) { 1670 loge("callSessionHeld :: ", t); 1671 } 1672 } 1673 } 1674 1675 @Override 1676 public void callSessionHoldFailed(ImsCallSession session, 1677 ImsReasonInfo reasonInfo) { 1678 if (DBG) { 1679 log("callSessionHoldFailed :: session=" + session + 1680 ", reasonInfo=" + reasonInfo); 1681 } 1682 1683 boolean isHoldForMerge = false; 1684 ImsCall.Listener listener; 1685 1686 synchronized(ImsCall.this) { 1687 if (mUpdateRequest == UPDATE_HOLD_MERGE) { 1688 isHoldForMerge = true; 1689 } 1690 1691 mUpdateRequest = UPDATE_NONE; 1692 listener = mListener; 1693 } 1694 1695 if (isHoldForMerge) { 1696 callSessionMergeFailed(session, reasonInfo); 1697 return; 1698 } 1699 1700 if (listener != null) { 1701 try { 1702 listener.onCallHoldFailed(ImsCall.this, reasonInfo); 1703 } catch (Throwable t) { 1704 loge("callSessionHoldFailed :: ", t); 1705 } 1706 } 1707 } 1708 1709 @Override 1710 public void callSessionHoldReceived(ImsCallSession session, 1711 ImsCallProfile profile) { 1712 if (DBG) { 1713 log("callSessionHoldReceived :: session=" + session + ", profile=" + profile); 1714 } 1715 1716 ImsCall.Listener listener; 1717 1718 synchronized(ImsCall.this) { 1719 listener = mListener; 1720 mCallProfile = profile; 1721 } 1722 1723 if (listener != null) { 1724 try { 1725 listener.onCallHoldReceived(ImsCall.this); 1726 } catch (Throwable t) { 1727 loge("callSessionHoldReceived :: ", t); 1728 } 1729 } 1730 } 1731 1732 @Override 1733 public void callSessionResumed(ImsCallSession session, 1734 ImsCallProfile profile) { 1735 if (DBG) { 1736 log("callSessionResumed :: session=" + session + ", profile=" + profile); 1737 } 1738 1739 ImsCall.Listener listener; 1740 1741 synchronized(ImsCall.this) { 1742 listener = mListener; 1743 mCallProfile = profile; 1744 mUpdateRequest = UPDATE_NONE; 1745 } 1746 1747 if (listener != null) { 1748 try { 1749 listener.onCallResumed(ImsCall.this); 1750 } catch (Throwable t) { 1751 loge("callSessionResumed :: ", t); 1752 } 1753 } 1754 } 1755 1756 @Override 1757 public void callSessionResumeFailed(ImsCallSession session, 1758 ImsReasonInfo reasonInfo) { 1759 if (DBG) { 1760 log("callSessionResumeFailed :: session=" + session + 1761 ", reasonInfo=" + reasonInfo); 1762 } 1763 1764 ImsCall.Listener listener; 1765 1766 synchronized(ImsCall.this) { 1767 listener = mListener; 1768 mUpdateRequest = UPDATE_NONE; 1769 } 1770 1771 if (listener != null) { 1772 try { 1773 listener.onCallResumeFailed(ImsCall.this, reasonInfo); 1774 } catch (Throwable t) { 1775 loge("callSessionResumeFailed :: ", t); 1776 } 1777 } 1778 } 1779 1780 @Override 1781 public void callSessionResumeReceived(ImsCallSession session, 1782 ImsCallProfile profile) { 1783 if (DBG) { 1784 log("callSessionResumeReceived :: session=" + session + 1785 ", profile=" + profile); 1786 } 1787 1788 ImsCall.Listener listener; 1789 1790 synchronized(ImsCall.this) { 1791 listener = mListener; 1792 mCallProfile = profile; 1793 } 1794 1795 if (listener != null) { 1796 try { 1797 listener.onCallResumeReceived(ImsCall.this); 1798 } catch (Throwable t) { 1799 loge("callSessionResumeReceived :: ", t); 1800 } 1801 } 1802 } 1803 1804 @Override 1805 public void callSessionMerged(ImsCallSession session, 1806 ImsCallSession newSession, ImsCallProfile profile) { 1807 if (DBG) { 1808 log("callSessionMerged :: session=" + session 1809 + ", newSession=" + newSession + ", profile=" + profile); 1810 } 1811 1812 ImsCall newCall = createNewCall(newSession, profile); 1813 1814 if (newCall == null) { 1815 callSessionMergeFailed(session, new ImsReasonInfo()); 1816 return; 1817 } 1818 1819 ImsCall.Listener listener; 1820 1821 synchronized(ImsCall.this) { 1822 listener = mListener; 1823 updateCallGroup(newCall); 1824 mUpdateRequest = UPDATE_NONE; 1825 } 1826 1827 if (listener != null) { 1828 try { 1829 listener.onCallMerged(ImsCall.this, newCall); 1830 } catch (Throwable t) { 1831 loge("callSessionMerged :: ", t); 1832 } 1833 } 1834 } 1835 1836 @Override 1837 public void callSessionMergeFailed(ImsCallSession session, 1838 ImsReasonInfo reasonInfo) { 1839 if (DBG) { 1840 log("callSessionMergeFailed :: session=" + session + 1841 ", reasonInfo=" + reasonInfo); 1842 } 1843 1844 ImsCall.Listener listener; 1845 1846 synchronized(ImsCall.this) { 1847 listener = mListener; 1848 updateCallGroup(null); 1849 mUpdateRequest = UPDATE_NONE; 1850 } 1851 1852 if (listener != null) { 1853 try { 1854 listener.onCallMergeFailed(ImsCall.this, reasonInfo); 1855 } catch (Throwable t) { 1856 loge("callSessionMergeFailed :: ", t); 1857 } 1858 } 1859 } 1860 1861 @Override 1862 public void callSessionUpdated(ImsCallSession session, 1863 ImsCallProfile profile) { 1864 if (DBG) { 1865 log("callSessionUpdated :: session=" + session + ", profile=" + profile); 1866 } 1867 1868 ImsCall.Listener listener; 1869 1870 synchronized(ImsCall.this) { 1871 listener = mListener; 1872 mCallProfile = profile; 1873 mUpdateRequest = UPDATE_NONE; 1874 } 1875 1876 if (listener != null) { 1877 try { 1878 listener.onCallUpdated(ImsCall.this); 1879 } catch (Throwable t) { 1880 loge("callSessionUpdated :: ", t); 1881 } 1882 } 1883 } 1884 1885 @Override 1886 public void callSessionUpdateFailed(ImsCallSession session, 1887 ImsReasonInfo reasonInfo) { 1888 if (DBG) { 1889 log("callSessionUpdateFailed :: session=" + session + 1890 ", reasonInfo=" + reasonInfo); 1891 } 1892 1893 ImsCall.Listener listener; 1894 1895 synchronized(ImsCall.this) { 1896 listener = mListener; 1897 mUpdateRequest = UPDATE_NONE; 1898 } 1899 1900 if (listener != null) { 1901 try { 1902 listener.onCallUpdateFailed(ImsCall.this, reasonInfo); 1903 } catch (Throwable t) { 1904 loge("callSessionUpdateFailed :: ", t); 1905 } 1906 } 1907 } 1908 1909 @Override 1910 public void callSessionUpdateReceived(ImsCallSession session, 1911 ImsCallProfile profile) { 1912 if (DBG) { 1913 log("callSessionUpdateReceived :: session=" + session + 1914 ", profile=" + profile); 1915 } 1916 1917 ImsCall.Listener listener; 1918 1919 synchronized(ImsCall.this) { 1920 listener = mListener; 1921 mProposedCallProfile = profile; 1922 mUpdateRequest = UPDATE_UNSPECIFIED; 1923 } 1924 1925 if (listener != null) { 1926 try { 1927 listener.onCallUpdateReceived(ImsCall.this); 1928 } catch (Throwable t) { 1929 loge("callSessionUpdateReceived :: ", t); 1930 } 1931 } 1932 } 1933 1934 @Override 1935 public void callSessionConferenceExtended(ImsCallSession session, 1936 ImsCallSession newSession, ImsCallProfile profile) { 1937 if (DBG) { 1938 log("callSessionConferenceExtended :: session=" + session 1939 + ", newSession=" + newSession + ", profile=" + profile); 1940 } 1941 1942 ImsCall newCall = createNewCall(newSession, profile); 1943 1944 if (newCall == null) { 1945 callSessionConferenceExtendFailed(session, new ImsReasonInfo()); 1946 return; 1947 } 1948 1949 ImsCall.Listener listener; 1950 1951 synchronized(ImsCall.this) { 1952 listener = mListener; 1953 mUpdateRequest = UPDATE_NONE; 1954 } 1955 1956 if (listener != null) { 1957 try { 1958 listener.onCallConferenceExtended(ImsCall.this, newCall); 1959 } catch (Throwable t) { 1960 loge("callSessionConferenceExtended :: ", t); 1961 } 1962 } 1963 } 1964 1965 @Override 1966 public void callSessionConferenceExtendFailed(ImsCallSession session, 1967 ImsReasonInfo reasonInfo) { 1968 if (DBG) { 1969 log("callSessionConferenceExtendFailed :: session=" + session + 1970 ", reasonInfo=" + reasonInfo); 1971 } 1972 1973 ImsCall.Listener listener; 1974 1975 synchronized(ImsCall.this) { 1976 listener = mListener; 1977 mUpdateRequest = UPDATE_NONE; 1978 } 1979 1980 if (listener != null) { 1981 try { 1982 listener.onCallConferenceExtendFailed(ImsCall.this, reasonInfo); 1983 } catch (Throwable t) { 1984 loge("callSessionConferenceExtendFailed :: ", t); 1985 } 1986 } 1987 } 1988 1989 @Override 1990 public void callSessionConferenceExtendReceived(ImsCallSession session, 1991 ImsCallSession newSession, ImsCallProfile profile) { 1992 if (DBG) { 1993 log("callSessionConferenceExtendReceived :: session=" + session 1994 + ", newSession=" + newSession + ", profile=" + profile); 1995 } 1996 1997 ImsCall newCall = createNewCall(newSession, profile); 1998 1999 if (newCall == null) { 2000 // Should all the calls be terminated...??? 2001 return; 2002 } 2003 2004 ImsCall.Listener listener; 2005 2006 synchronized(ImsCall.this) { 2007 listener = mListener; 2008 } 2009 2010 if (listener != null) { 2011 try { 2012 listener.onCallConferenceExtendReceived(ImsCall.this, newCall); 2013 } catch (Throwable t) { 2014 loge("callSessionConferenceExtendReceived :: ", t); 2015 } 2016 } 2017 } 2018 2019 @Override 2020 public void callSessionInviteParticipantsRequestDelivered(ImsCallSession session) { 2021 if (DBG) { 2022 log("callSessionInviteParticipantsRequestDelivered :: session=" + session); 2023 } 2024 2025 ImsCall.Listener listener; 2026 2027 synchronized(ImsCall.this) { 2028 listener = mListener; 2029 } 2030 2031 if (listener != null) { 2032 try { 2033 listener.onCallInviteParticipantsRequestDelivered(ImsCall.this); 2034 } catch (Throwable t) { 2035 loge("callSessionInviteParticipantsRequestDelivered :: ", t); 2036 } 2037 } 2038 } 2039 2040 @Override 2041 public void callSessionInviteParticipantsRequestFailed(ImsCallSession session, 2042 ImsReasonInfo reasonInfo) { 2043 if (DBG) { 2044 log("callSessionInviteParticipantsRequestFailed :: session=" + session 2045 + ", reasonInfo=" + reasonInfo); 2046 } 2047 2048 ImsCall.Listener listener; 2049 2050 synchronized(ImsCall.this) { 2051 listener = mListener; 2052 } 2053 2054 if (listener != null) { 2055 try { 2056 listener.onCallInviteParticipantsRequestFailed(ImsCall.this, reasonInfo); 2057 } catch (Throwable t) { 2058 loge("callSessionInviteParticipantsRequestFailed :: ", t); 2059 } 2060 } 2061 } 2062 2063 @Override 2064 public void callSessionRemoveParticipantsRequestDelivered(ImsCallSession session) { 2065 if (DBG) { 2066 log("callSessionRemoveParticipantsRequestDelivered :: session=" + session); 2067 } 2068 2069 ImsCall.Listener listener; 2070 2071 synchronized(ImsCall.this) { 2072 listener = mListener; 2073 } 2074 2075 if (listener != null) { 2076 try { 2077 listener.onCallRemoveParticipantsRequestDelivered(ImsCall.this); 2078 } catch (Throwable t) { 2079 loge("callSessionRemoveParticipantsRequestDelivered :: ", t); 2080 } 2081 } 2082 } 2083 2084 @Override 2085 public void callSessionRemoveParticipantsRequestFailed(ImsCallSession session, 2086 ImsReasonInfo reasonInfo) { 2087 if (DBG) { 2088 log("callSessionRemoveParticipantsRequestFailed :: session=" + session 2089 + ", reasonInfo=" + reasonInfo); 2090 } 2091 2092 ImsCall.Listener listener; 2093 2094 synchronized(ImsCall.this) { 2095 listener = mListener; 2096 } 2097 2098 if (listener != null) { 2099 try { 2100 listener.onCallRemoveParticipantsRequestFailed(ImsCall.this, reasonInfo); 2101 } catch (Throwable t) { 2102 loge("callSessionRemoveParticipantsRequestFailed :: ", t); 2103 } 2104 } 2105 } 2106 2107 @Override 2108 public void callSessionConferenceStateUpdated(ImsCallSession session, 2109 ImsConferenceState state) { 2110 if (DBG) { 2111 log("callSessionConferenceStateUpdated :: session=" + session 2112 + ", state=" + state); 2113 } 2114 2115 ImsCall.Listener listener; 2116 2117 synchronized(ImsCall.this) { 2118 notifyConferenceStateUpdated(state); 2119 listener = mListener; 2120 } 2121 2122 if (listener != null) { 2123 try { 2124 listener.onCallConferenceStateUpdated(ImsCall.this, state); 2125 } catch (Throwable t) { 2126 loge("callSessionConferenceStateUpdated :: ", t); 2127 } 2128 } 2129 } 2130 2131 @Override 2132 public void callSessionUssdMessageReceived(ImsCallSession session, 2133 int mode, String ussdMessage) { 2134 if (DBG) { 2135 log("callSessionUssdMessageReceived :: session=" + session 2136 + ", mode=" + mode + ", ussdMessage=" + ussdMessage); 2137 } 2138 2139 ImsCall.Listener listener; 2140 2141 synchronized(ImsCall.this) { 2142 listener = mListener; 2143 } 2144 2145 if (listener != null) { 2146 try { 2147 listener.onCallUssdMessageReceived(ImsCall.this, mode, ussdMessage); 2148 } catch (Throwable t) { 2149 loge("callSessionUssdMessageReceived :: ", t); 2150 } 2151 } 2152 } 2153 } 2154} 2155