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