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 17 package com.android.ims.internal; 18 19 import android.os.Message; 20 import android.os.RemoteException; 21 import android.telecom.Connection; 22 23 import java.util.Objects; 24 import android.util.Log; 25 import com.android.ims.ImsCallProfile; 26 import com.android.ims.ImsConferenceState; 27 import com.android.ims.ImsReasonInfo; 28 import com.android.ims.ImsStreamMediaProfile; 29 30 /** 31 * Provides the call initiation/termination, and media exchange between two IMS endpoints. 32 * It directly communicates with IMS service which implements the IMS protocol behavior. 33 * 34 * @hide 35 */ 36 public class ImsCallSession { 37 private static final String TAG = "ImsCallSession"; 38 39 /** 40 * Defines IMS call session state. 41 */ 42 public static class State { 43 public static final int IDLE = 0; 44 public static final int INITIATED = 1; 45 public static final int NEGOTIATING = 2; 46 public static final int ESTABLISHING = 3; 47 public static final int ESTABLISHED = 4; 48 49 public static final int RENEGOTIATING = 5; 50 public static final int REESTABLISHING = 6; 51 52 public static final int TERMINATING = 7; 53 public static final int TERMINATED = 8; 54 55 public static final int INVALID = (-1); 56 57 /** 58 * Converts the state to string. 59 */ toString(int state)60 public static String toString(int state) { 61 switch (state) { 62 case IDLE: 63 return "IDLE"; 64 case INITIATED: 65 return "INITIATED"; 66 case NEGOTIATING: 67 return "NEGOTIATING"; 68 case ESTABLISHING: 69 return "ESTABLISHING"; 70 case ESTABLISHED: 71 return "ESTABLISHED"; 72 case RENEGOTIATING: 73 return "RENEGOTIATING"; 74 case REESTABLISHING: 75 return "REESTABLISHING"; 76 case TERMINATING: 77 return "TERMINATING"; 78 case TERMINATED: 79 return "TERMINATED"; 80 default: 81 return "UNKNOWN"; 82 } 83 } 84 State()85 private State() { 86 } 87 } 88 89 /** 90 * Listener for events relating to an IMS session, such as when a session is being 91 * recieved ("on ringing") or a call is outgoing ("on calling"). 92 * <p>Many of these events are also received by {@link ImsCall.Listener}.</p> 93 */ 94 public static class Listener { 95 /** 96 * Called when a request is sent out to initiate a new session 97 * and 1xx response is received from the network. 98 * 99 * @param session the session object that carries out the IMS session 100 */ callSessionProgressing(ImsCallSession session, ImsStreamMediaProfile profile)101 public void callSessionProgressing(ImsCallSession session, 102 ImsStreamMediaProfile profile) { 103 // no-op 104 } 105 106 /** 107 * Called when the session is established. 108 * 109 * @param session the session object that carries out the IMS session 110 */ callSessionStarted(ImsCallSession session, ImsCallProfile profile)111 public void callSessionStarted(ImsCallSession session, 112 ImsCallProfile profile) { 113 // no-op 114 } 115 116 /** 117 * Called when the session establishment is failed. 118 * 119 * @param session the session object that carries out the IMS session 120 * @param reasonInfo detailed reason of the session establishment failure 121 */ callSessionStartFailed(ImsCallSession session, ImsReasonInfo reasonInfo)122 public void callSessionStartFailed(ImsCallSession session, 123 ImsReasonInfo reasonInfo) { 124 } 125 126 /** 127 * Called when the session is terminated. 128 * 129 * @param session the session object that carries out the IMS session 130 * @param reasonInfo detailed reason of the session termination 131 */ callSessionTerminated(ImsCallSession session, ImsReasonInfo reasonInfo)132 public void callSessionTerminated(ImsCallSession session, 133 ImsReasonInfo reasonInfo) { 134 } 135 136 /** 137 * Called when the session is in hold. 138 * 139 * @param session the session object that carries out the IMS session 140 */ callSessionHeld(ImsCallSession session, ImsCallProfile profile)141 public void callSessionHeld(ImsCallSession session, 142 ImsCallProfile profile) { 143 } 144 145 /** 146 * Called when the session hold is failed. 147 * 148 * @param session the session object that carries out the IMS session 149 * @param reasonInfo detailed reason of the session hold failure 150 */ callSessionHoldFailed(ImsCallSession session, ImsReasonInfo reasonInfo)151 public void callSessionHoldFailed(ImsCallSession session, 152 ImsReasonInfo reasonInfo) { 153 } 154 155 /** 156 * Called when the session hold is received from the remote user. 157 * 158 * @param session the session object that carries out the IMS session 159 */ callSessionHoldReceived(ImsCallSession session, ImsCallProfile profile)160 public void callSessionHoldReceived(ImsCallSession session, 161 ImsCallProfile profile) { 162 } 163 164 /** 165 * Called when the session resume is done. 166 * 167 * @param session the session object that carries out the IMS session 168 */ callSessionResumed(ImsCallSession session, ImsCallProfile profile)169 public void callSessionResumed(ImsCallSession session, 170 ImsCallProfile profile) { 171 } 172 173 /** 174 * Called when the session resume is failed. 175 * 176 * @param session the session object that carries out the IMS session 177 * @param reasonInfo detailed reason of the session resume failure 178 */ callSessionResumeFailed(ImsCallSession session, ImsReasonInfo reasonInfo)179 public void callSessionResumeFailed(ImsCallSession session, 180 ImsReasonInfo reasonInfo) { 181 } 182 183 /** 184 * Called when the session resume is received from the remote user. 185 * 186 * @param session the session object that carries out the IMS session 187 */ callSessionResumeReceived(ImsCallSession session, ImsCallProfile profile)188 public void callSessionResumeReceived(ImsCallSession session, 189 ImsCallProfile profile) { 190 } 191 192 /** 193 * Called when the session merge has been started. At this point, the {@code newSession} 194 * represents the session which has been initiated to the IMS conference server for the 195 * new merged conference. 196 * 197 * @param session the session object that carries out the IMS session 198 * @param newSession the session object that is merged with an active & hold session 199 */ callSessionMergeStarted(ImsCallSession session, ImsCallSession newSession, ImsCallProfile profile)200 public void callSessionMergeStarted(ImsCallSession session, 201 ImsCallSession newSession, ImsCallProfile profile) { 202 } 203 204 /** 205 * Called when the session merge is successful and the merged session is active. 206 * 207 * @param session the session object that carries out the IMS session 208 */ callSessionMergeComplete(ImsCallSession session)209 public void callSessionMergeComplete(ImsCallSession session) { 210 } 211 212 /** 213 * Called when the session merge has failed. 214 * 215 * @param session the session object that carries out the IMS session 216 * @param reasonInfo detailed reason of the call merge failure 217 */ callSessionMergeFailed(ImsCallSession session, ImsReasonInfo reasonInfo)218 public void callSessionMergeFailed(ImsCallSession session, 219 ImsReasonInfo reasonInfo) { 220 } 221 222 /** 223 * Called when the session is updated (except for hold/unhold). 224 * 225 * @param call the call object that carries out the IMS call 226 */ callSessionUpdated(ImsCallSession session, ImsCallProfile profile)227 public void callSessionUpdated(ImsCallSession session, 228 ImsCallProfile profile) { 229 } 230 231 /** 232 * Called when the session update is failed. 233 * 234 * @param session the session object that carries out the IMS session 235 * @param reasonInfo detailed reason of the session update failure 236 */ callSessionUpdateFailed(ImsCallSession session, ImsReasonInfo reasonInfo)237 public void callSessionUpdateFailed(ImsCallSession session, 238 ImsReasonInfo reasonInfo) { 239 } 240 241 /** 242 * Called when the session update is received from the remote user. 243 * 244 * @param session the session object that carries out the IMS session 245 */ callSessionUpdateReceived(ImsCallSession session, ImsCallProfile profile)246 public void callSessionUpdateReceived(ImsCallSession session, 247 ImsCallProfile profile) { 248 // no-op 249 } 250 251 /** 252 * Called when the session is extended to the conference session. 253 * 254 * @param session the session object that carries out the IMS session 255 * @param newSession the session object that is extended to the conference 256 * from the active session 257 */ callSessionConferenceExtended(ImsCallSession session, ImsCallSession newSession, ImsCallProfile profile)258 public void callSessionConferenceExtended(ImsCallSession session, 259 ImsCallSession newSession, ImsCallProfile profile) { 260 } 261 262 /** 263 * Called when the conference extension is failed. 264 * 265 * @param session the session object that carries out the IMS session 266 * @param reasonInfo detailed reason of the conference extension failure 267 */ callSessionConferenceExtendFailed(ImsCallSession session, ImsReasonInfo reasonInfo)268 public void callSessionConferenceExtendFailed(ImsCallSession session, 269 ImsReasonInfo reasonInfo) { 270 } 271 272 /** 273 * Called when the conference extension is received from the remote user. 274 * 275 * @param session the session object that carries out the IMS session 276 */ callSessionConferenceExtendReceived(ImsCallSession session, ImsCallSession newSession, ImsCallProfile profile)277 public void callSessionConferenceExtendReceived(ImsCallSession session, 278 ImsCallSession newSession, ImsCallProfile profile) { 279 // no-op 280 } 281 282 /** 283 * Called when the invitation request of the participants is delivered to the conference 284 * server. 285 * 286 * @param session the session object that carries out the IMS session 287 */ callSessionInviteParticipantsRequestDelivered(ImsCallSession session)288 public void callSessionInviteParticipantsRequestDelivered(ImsCallSession session) { 289 // no-op 290 } 291 292 /** 293 * Called when the invitation request of the participants is failed. 294 * 295 * @param session the session object that carries out the IMS session 296 * @param reasonInfo detailed reason of the conference invitation failure 297 */ callSessionInviteParticipantsRequestFailed(ImsCallSession session, ImsReasonInfo reasonInfo)298 public void callSessionInviteParticipantsRequestFailed(ImsCallSession session, 299 ImsReasonInfo reasonInfo) { 300 // no-op 301 } 302 303 /** 304 * Called when the removal request of the participants is delivered to the conference 305 * server. 306 * 307 * @param session the session object that carries out the IMS session 308 */ callSessionRemoveParticipantsRequestDelivered(ImsCallSession session)309 public void callSessionRemoveParticipantsRequestDelivered(ImsCallSession session) { 310 // no-op 311 } 312 313 /** 314 * Called when the removal request of the participants is failed. 315 * 316 * @param session the session object that carries out the IMS session 317 * @param reasonInfo detailed reason of the conference removal failure 318 */ callSessionRemoveParticipantsRequestFailed(ImsCallSession session, ImsReasonInfo reasonInfo)319 public void callSessionRemoveParticipantsRequestFailed(ImsCallSession session, 320 ImsReasonInfo reasonInfo) { 321 // no-op 322 } 323 324 /** 325 * Called when the conference state is updated. 326 * 327 * @param session the session object that carries out the IMS session 328 */ callSessionConferenceStateUpdated(ImsCallSession session, ImsConferenceState state)329 public void callSessionConferenceStateUpdated(ImsCallSession session, 330 ImsConferenceState state) { 331 // no-op 332 } 333 334 /** 335 * Called when the USSD message is received from the network. 336 * 337 * @param mode mode of the USSD message (REQUEST / NOTIFY) 338 * @param ussdMessage USSD message 339 */ callSessionUssdMessageReceived(ImsCallSession session, int mode, String ussdMessage)340 public void callSessionUssdMessageReceived(ImsCallSession session, 341 int mode, String ussdMessage) { 342 // no-op 343 } 344 345 /** 346 * Called when session access technology changes 347 * 348 * @param session IMS session object 349 * @param srcAccessTech original access technology 350 * @param targetAccessTech new access technology 351 * @param reasonInfo 352 */ callSessionHandover(ImsCallSession session, int srcAccessTech, int targetAccessTech, ImsReasonInfo reasonInfo)353 public void callSessionHandover(ImsCallSession session, 354 int srcAccessTech, int targetAccessTech, 355 ImsReasonInfo reasonInfo) { 356 // no-op 357 } 358 359 /** 360 * Called when session access technology change fails 361 * 362 * @param session IMS session object 363 * @param srcAccessTech original access technology 364 * @param targetAccessTech new access technology 365 * @param reasonInfo handover failure reason 366 */ callSessionHandoverFailed(ImsCallSession session, int srcAccessTech, int targetAccessTech, ImsReasonInfo reasonInfo)367 public void callSessionHandoverFailed(ImsCallSession session, 368 int srcAccessTech, int targetAccessTech, 369 ImsReasonInfo reasonInfo) { 370 // no-op 371 } 372 373 /** 374 * Called when TTY mode of remote party changed 375 * 376 * @param session IMS session object 377 * @param mode TTY mode of remote party 378 */ callSessionTtyModeReceived(ImsCallSession session, int mode)379 public void callSessionTtyModeReceived(ImsCallSession session, 380 int mode) { 381 // no-op 382 } 383 384 /** 385 * Notifies of a change to the multiparty state for this {@code ImsCallSession}. 386 * 387 * @param session The call session. 388 * @param isMultiParty {@code true} if the session became multiparty, {@code false} 389 * otherwise. 390 */ callSessionMultipartyStateChanged(ImsCallSession session, boolean isMultiParty)391 public void callSessionMultipartyStateChanged(ImsCallSession session, 392 boolean isMultiParty) { 393 // no-op 394 } 395 } 396 397 private final IImsCallSession miSession; 398 private boolean mClosed = false; 399 private Listener mListener; 400 ImsCallSession(IImsCallSession iSession)401 public ImsCallSession(IImsCallSession iSession) { 402 miSession = iSession; 403 404 if (iSession != null) { 405 try { 406 iSession.setListener(new IImsCallSessionListenerProxy()); 407 } catch (RemoteException e) { 408 } 409 } else { 410 mClosed = true; 411 } 412 } 413 ImsCallSession(IImsCallSession iSession, Listener listener)414 public ImsCallSession(IImsCallSession iSession, Listener listener) { 415 this(iSession); 416 setListener(listener); 417 } 418 419 /** 420 * Closes this object. This object is not usable after being closed. 421 */ close()422 public synchronized void close() { 423 if (mClosed) { 424 return; 425 } 426 427 try { 428 miSession.close(); 429 mClosed = true; 430 } catch (RemoteException e) { 431 } 432 } 433 434 /** 435 * Gets the call ID of the session. 436 * 437 * @return the call ID 438 */ getCallId()439 public String getCallId() { 440 if (mClosed) { 441 return null; 442 } 443 444 try { 445 return miSession.getCallId(); 446 } catch (RemoteException e) { 447 return null; 448 } 449 } 450 451 /** 452 * Gets the call profile that this session is associated with 453 * 454 * @return the call profile that this session is associated with 455 */ getCallProfile()456 public ImsCallProfile getCallProfile() { 457 if (mClosed) { 458 return null; 459 } 460 461 try { 462 return miSession.getCallProfile(); 463 } catch (RemoteException e) { 464 return null; 465 } 466 } 467 468 /** 469 * Gets the local call profile that this session is associated with 470 * 471 * @return the local call profile that this session is associated with 472 */ getLocalCallProfile()473 public ImsCallProfile getLocalCallProfile() { 474 if (mClosed) { 475 return null; 476 } 477 478 try { 479 return miSession.getLocalCallProfile(); 480 } catch (RemoteException e) { 481 return null; 482 } 483 } 484 485 /** 486 * Gets the remote call profile that this session is associated with 487 * 488 * @return the remote call profile that this session is associated with 489 */ getRemoteCallProfile()490 public ImsCallProfile getRemoteCallProfile() { 491 if (mClosed) { 492 return null; 493 } 494 495 try { 496 return miSession.getRemoteCallProfile(); 497 } catch (RemoteException e) { 498 return null; 499 } 500 } 501 502 /** 503 * Gets the video call provider for the session. 504 * 505 * @return The video call provider. 506 */ getVideoCallProvider()507 public IImsVideoCallProvider getVideoCallProvider() { 508 if (mClosed) { 509 return null; 510 } 511 512 try { 513 return miSession.getVideoCallProvider(); 514 } catch (RemoteException e) { 515 return null; 516 } 517 } 518 519 /** 520 * Gets the value associated with the specified property of this session. 521 * 522 * @return the string value associated with the specified property 523 */ getProperty(String name)524 public String getProperty(String name) { 525 if (mClosed) { 526 return null; 527 } 528 529 try { 530 return miSession.getProperty(name); 531 } catch (RemoteException e) { 532 return null; 533 } 534 } 535 536 /** 537 * Gets the session state. 538 * The value returned must be one of the states in {@link State}. 539 * 540 * @return the session state 541 */ getState()542 public int getState() { 543 if (mClosed) { 544 return State.INVALID; 545 } 546 547 try { 548 return miSession.getState(); 549 } catch (RemoteException e) { 550 return State.INVALID; 551 } 552 } 553 554 /** 555 * Determines if the {@link ImsCallSession} is currently alive (e.g. not in a terminated or 556 * closed state). 557 * 558 * @return {@code True} if the session is alive. 559 */ isAlive()560 public boolean isAlive() { 561 if (mClosed) { 562 return false; 563 } 564 565 int state = getState(); 566 switch (state) { 567 case State.IDLE: 568 case State.INITIATED: 569 case State.NEGOTIATING: 570 case State.ESTABLISHING: 571 case State.ESTABLISHED: 572 case State.RENEGOTIATING: 573 case State.REESTABLISHING: 574 return true; 575 default: 576 return false; 577 } 578 } 579 580 /** 581 * Gets the native IMS call session. 582 * @hide 583 */ getSession()584 public IImsCallSession getSession() { 585 return miSession; 586 } 587 588 /** 589 * Checks if the session is in call. 590 * 591 * @return true if the session is in call 592 */ isInCall()593 public boolean isInCall() { 594 if (mClosed) { 595 return false; 596 } 597 598 try { 599 return miSession.isInCall(); 600 } catch (RemoteException e) { 601 return false; 602 } 603 } 604 605 /** 606 * Sets the listener to listen to the session events. A {@link ImsCallSession} 607 * can only hold one listener at a time. Subsequent calls to this method 608 * override the previous listener. 609 * 610 * @param listener to listen to the session events of this object 611 */ setListener(Listener listener)612 public void setListener(Listener listener) { 613 mListener = listener; 614 } 615 616 /** 617 * Mutes or unmutes the mic for the active call. 618 * 619 * @param muted true if the call is muted, false otherwise 620 */ setMute(boolean muted)621 public void setMute(boolean muted) { 622 if (mClosed) { 623 return; 624 } 625 626 try { 627 miSession.setMute(muted); 628 } catch (RemoteException e) { 629 } 630 } 631 632 /** 633 * Initiates an IMS call with the specified target and call profile. 634 * The session listener is called back upon defined session events. 635 * The method is only valid to call when the session state is in 636 * {@link ImsCallSession#State#IDLE}. 637 * 638 * @param callee dialed string to make the call to 639 * @param profile call profile to make the call with the specified service type, 640 * call type and media information 641 * @see Listener#callSessionStarted, Listener#callSessionStartFailed 642 */ start(String callee, ImsCallProfile profile)643 public void start(String callee, ImsCallProfile profile) { 644 if (mClosed) { 645 return; 646 } 647 648 try { 649 miSession.start(callee, profile); 650 } catch (RemoteException e) { 651 } 652 } 653 654 /** 655 * Initiates an IMS conference call with the specified target and call profile. 656 * The session listener is called back upon defined session events. 657 * The method is only valid to call when the session state is in 658 * {@link ImsCallSession#State#IDLE}. 659 * 660 * @param participants participant list to initiate an IMS conference call 661 * @param profile call profile to make the call with the specified service type, 662 * call type and media information 663 * @see Listener#callSessionStarted, Listener#callSessionStartFailed 664 */ start(String[] participants, ImsCallProfile profile)665 public void start(String[] participants, ImsCallProfile profile) { 666 if (mClosed) { 667 return; 668 } 669 670 try { 671 miSession.startConference(participants, profile); 672 } catch (RemoteException e) { 673 } 674 } 675 676 /** 677 * Accepts an incoming call or session update. 678 * 679 * @param callType call type specified in {@link ImsCallProfile} to be answered 680 * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered 681 * @see Listener#callSessionStarted 682 */ accept(int callType, ImsStreamMediaProfile profile)683 public void accept(int callType, ImsStreamMediaProfile profile) { 684 if (mClosed) { 685 return; 686 } 687 688 try { 689 miSession.accept(callType, profile); 690 } catch (RemoteException e) { 691 } 692 } 693 694 /** 695 * Rejects an incoming call or session update. 696 * 697 * @param reason reason code to reject an incoming call 698 * @see Listener#callSessionStartFailed 699 */ reject(int reason)700 public void reject(int reason) { 701 if (mClosed) { 702 return; 703 } 704 705 try { 706 miSession.reject(reason); 707 } catch (RemoteException e) { 708 } 709 } 710 711 /** 712 * Terminates a call. 713 * 714 * @see Listener#callSessionTerminated 715 */ terminate(int reason)716 public void terminate(int reason) { 717 if (mClosed) { 718 return; 719 } 720 721 try { 722 miSession.terminate(reason); 723 } catch (RemoteException e) { 724 } 725 } 726 727 /** 728 * Puts a call on hold. When it succeeds, {@link Listener#callSessionHeld} is called. 729 * 730 * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call 731 * @see Listener#callSessionHeld, Listener#callSessionHoldFailed 732 */ hold(ImsStreamMediaProfile profile)733 public void hold(ImsStreamMediaProfile profile) { 734 if (mClosed) { 735 return; 736 } 737 738 try { 739 miSession.hold(profile); 740 } catch (RemoteException e) { 741 } 742 } 743 744 /** 745 * Continues a call that's on hold. When it succeeds, 746 * {@link Listener#callSessionResumed} is called. 747 * 748 * @param profile stream media profile {@link ImsStreamMediaProfile} to resume the call 749 * @see Listener#callSessionResumed, Listener#callSessionResumeFailed 750 */ resume(ImsStreamMediaProfile profile)751 public void resume(ImsStreamMediaProfile profile) { 752 if (mClosed) { 753 return; 754 } 755 756 try { 757 miSession.resume(profile); 758 } catch (RemoteException e) { 759 } 760 } 761 762 /** 763 * Merges the active & hold call. When it succeeds, 764 * {@link Listener#callSessionMergeStarted} is called. 765 * 766 * @see Listener#callSessionMergeStarted , Listener#callSessionMergeFailed 767 */ merge()768 public void merge() { 769 if (mClosed) { 770 return; 771 } 772 773 try { 774 miSession.merge(); 775 } catch (RemoteException e) { 776 } 777 } 778 779 /** 780 * Updates the current call's properties (ex. call mode change: video upgrade / downgrade). 781 * 782 * @param callType call type specified in {@link ImsCallProfile} to be updated 783 * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated 784 * @see Listener#callSessionUpdated, Listener#callSessionUpdateFailed 785 */ update(int callType, ImsStreamMediaProfile profile)786 public void update(int callType, ImsStreamMediaProfile profile) { 787 if (mClosed) { 788 return; 789 } 790 791 try { 792 miSession.update(callType, profile); 793 } catch (RemoteException e) { 794 } 795 } 796 797 /** 798 * Extends this call to the conference call with the specified recipients. 799 * 800 * @participants participant list to be invited to the conference call after extending the call 801 * @see Listener#sessionConferenceExtened, Listener#sessionConferenceExtendFailed 802 */ extendToConference(String[] participants)803 public void extendToConference(String[] participants) { 804 if (mClosed) { 805 return; 806 } 807 808 try { 809 miSession.extendToConference(participants); 810 } catch (RemoteException e) { 811 } 812 } 813 814 /** 815 * Requests the conference server to invite an additional participants to the conference. 816 * 817 * @participants participant list to be invited to the conference call 818 * @see Listener#sessionInviteParticipantsRequestDelivered, 819 * Listener#sessionInviteParticipantsRequestFailed 820 */ inviteParticipants(String[] participants)821 public void inviteParticipants(String[] participants) { 822 if (mClosed) { 823 return; 824 } 825 826 try { 827 miSession.inviteParticipants(participants); 828 } catch (RemoteException e) { 829 } 830 } 831 832 /** 833 * Requests the conference server to remove the specified participants from the conference. 834 * 835 * @param participants participant list to be removed from the conference call 836 * @see Listener#sessionRemoveParticipantsRequestDelivered, 837 * Listener#sessionRemoveParticipantsRequestFailed 838 */ removeParticipants(String[] participants)839 public void removeParticipants(String[] participants) { 840 if (mClosed) { 841 return; 842 } 843 844 try { 845 miSession.removeParticipants(participants); 846 } catch (RemoteException e) { 847 } 848 } 849 850 851 /** 852 * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>, 853 * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15, 854 * and event flash to 16. Currently, event flash is not supported. 855 * 856 * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs. 857 */ sendDtmf(char c, Message result)858 public void sendDtmf(char c, Message result) { 859 if (mClosed) { 860 return; 861 } 862 863 try { 864 miSession.sendDtmf(c, result); 865 } catch (RemoteException e) { 866 } 867 } 868 869 /** 870 * Starts a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>, 871 * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15, 872 * and event flash to 16. Currently, event flash is not supported. 873 * 874 * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs. 875 */ startDtmf(char c)876 public void startDtmf(char c) { 877 if (mClosed) { 878 return; 879 } 880 881 try { 882 miSession.startDtmf(c); 883 } catch (RemoteException e) { 884 } 885 } 886 887 /** 888 * Stops a DTMF code. 889 */ stopDtmf()890 public void stopDtmf() { 891 if (mClosed) { 892 return; 893 } 894 895 try { 896 miSession.stopDtmf(); 897 } catch (RemoteException e) { 898 } 899 } 900 901 /** 902 * Sends an USSD message. 903 * 904 * @param ussdMessage USSD message to send 905 */ sendUssd(String ussdMessage)906 public void sendUssd(String ussdMessage) { 907 if (mClosed) { 908 return; 909 } 910 911 try { 912 miSession.sendUssd(ussdMessage); 913 } catch (RemoteException e) { 914 } 915 } 916 917 /** 918 * Determines if the session is multiparty. 919 * 920 * @return {@code True} if the session is multiparty. 921 */ isMultiparty()922 public boolean isMultiparty() { 923 if (mClosed) { 924 return false; 925 } 926 927 try { 928 return miSession.isMultiparty(); 929 } catch (RemoteException e) { 930 return false; 931 } 932 } 933 934 /** 935 * A listener type for receiving notification on IMS call session events. 936 * When an event is generated for an {@link IImsCallSession}, 937 * the application is notified by having one of the methods called on 938 * the {@link IImsCallSessionListener}. 939 */ 940 private class IImsCallSessionListenerProxy extends IImsCallSessionListener.Stub { 941 /** 942 * Notifies the result of the basic session operation (setup / terminate). 943 */ 944 @Override callSessionProgressing(IImsCallSession session, ImsStreamMediaProfile profile)945 public void callSessionProgressing(IImsCallSession session, 946 ImsStreamMediaProfile profile) { 947 if (mListener != null) { 948 mListener.callSessionProgressing(ImsCallSession.this, profile); 949 } 950 } 951 952 @Override callSessionStarted(IImsCallSession session, ImsCallProfile profile)953 public void callSessionStarted(IImsCallSession session, 954 ImsCallProfile profile) { 955 if (mListener != null) { 956 mListener.callSessionStarted(ImsCallSession.this, profile); 957 } 958 } 959 960 @Override callSessionStartFailed(IImsCallSession session, ImsReasonInfo reasonInfo)961 public void callSessionStartFailed(IImsCallSession session, 962 ImsReasonInfo reasonInfo) { 963 if (mListener != null) { 964 mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo); 965 } 966 } 967 968 @Override callSessionTerminated(IImsCallSession session, ImsReasonInfo reasonInfo)969 public void callSessionTerminated(IImsCallSession session, 970 ImsReasonInfo reasonInfo) { 971 if (mListener != null) { 972 mListener.callSessionTerminated(ImsCallSession.this, reasonInfo); 973 } 974 } 975 976 /** 977 * Notifies the result of the call hold/resume operation. 978 */ 979 @Override callSessionHeld(IImsCallSession session, ImsCallProfile profile)980 public void callSessionHeld(IImsCallSession session, 981 ImsCallProfile profile) { 982 if (mListener != null) { 983 mListener.callSessionHeld(ImsCallSession.this, profile); 984 } 985 } 986 987 @Override callSessionHoldFailed(IImsCallSession session, ImsReasonInfo reasonInfo)988 public void callSessionHoldFailed(IImsCallSession session, 989 ImsReasonInfo reasonInfo) { 990 if (mListener != null) { 991 mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo); 992 } 993 } 994 995 @Override callSessionHoldReceived(IImsCallSession session, ImsCallProfile profile)996 public void callSessionHoldReceived(IImsCallSession session, 997 ImsCallProfile profile) { 998 if (mListener != null) { 999 mListener.callSessionHoldReceived(ImsCallSession.this, profile); 1000 } 1001 } 1002 1003 @Override callSessionResumed(IImsCallSession session, ImsCallProfile profile)1004 public void callSessionResumed(IImsCallSession session, 1005 ImsCallProfile profile) { 1006 if (mListener != null) { 1007 mListener.callSessionResumed(ImsCallSession.this, profile); 1008 } 1009 } 1010 1011 @Override callSessionResumeFailed(IImsCallSession session, ImsReasonInfo reasonInfo)1012 public void callSessionResumeFailed(IImsCallSession session, 1013 ImsReasonInfo reasonInfo) { 1014 if (mListener != null) { 1015 mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo); 1016 } 1017 } 1018 1019 @Override callSessionResumeReceived(IImsCallSession session, ImsCallProfile profile)1020 public void callSessionResumeReceived(IImsCallSession session, 1021 ImsCallProfile profile) { 1022 if (mListener != null) { 1023 mListener.callSessionResumeReceived(ImsCallSession.this, profile); 1024 } 1025 } 1026 1027 /** 1028 * Notifies the start of a call merge operation. 1029 * 1030 * @param session The call session. 1031 * @param newSession The merged call session. 1032 * @param profile The call profile. 1033 */ 1034 @Override callSessionMergeStarted(IImsCallSession session, IImsCallSession newSession, ImsCallProfile profile)1035 public void callSessionMergeStarted(IImsCallSession session, 1036 IImsCallSession newSession, ImsCallProfile profile) { 1037 // This callback can be used for future use to add additional 1038 // functionality that may be needed between conference start and complete 1039 Log.d(TAG, "callSessionMergeStarted"); 1040 } 1041 1042 /** 1043 * Notifies the successful completion of a call merge operation. 1044 * 1045 * @param session The call session. 1046 */ 1047 @Override callSessionMergeComplete(IImsCallSession newSession)1048 public void callSessionMergeComplete(IImsCallSession newSession) { 1049 if (mListener != null) { 1050 if (newSession != null) { 1051 // Check if the active session is the same session that was 1052 // active before the merge request was sent. 1053 ImsCallSession validActiveSession = ImsCallSession.this; 1054 try { 1055 if (!Objects.equals(miSession.getCallId(), newSession.getCallId())) { 1056 // New session created after conference 1057 validActiveSession = new ImsCallSession(newSession); 1058 } 1059 } catch (RemoteException rex) { 1060 Log.e(TAG, "callSessionMergeComplete: exception for getCallId!"); 1061 } 1062 mListener.callSessionMergeComplete(validActiveSession); 1063 } else { 1064 // Session already exists. Hence no need to pass 1065 mListener.callSessionMergeComplete(null); 1066 } 1067 } 1068 } 1069 1070 /** 1071 * Notifies of a failure to perform a call merge operation. 1072 * 1073 * @param session The call session. 1074 * @param reasonInfo The merge failure reason. 1075 */ 1076 @Override callSessionMergeFailed(IImsCallSession session, ImsReasonInfo reasonInfo)1077 public void callSessionMergeFailed(IImsCallSession session, 1078 ImsReasonInfo reasonInfo) { 1079 if (mListener != null) { 1080 mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo); 1081 } 1082 } 1083 1084 /** 1085 * Notifies the result of call upgrade / downgrade or any other call updates. 1086 */ 1087 @Override callSessionUpdated(IImsCallSession session, ImsCallProfile profile)1088 public void callSessionUpdated(IImsCallSession session, 1089 ImsCallProfile profile) { 1090 if (mListener != null) { 1091 mListener.callSessionUpdated(ImsCallSession.this, profile); 1092 } 1093 } 1094 1095 @Override callSessionUpdateFailed(IImsCallSession session, ImsReasonInfo reasonInfo)1096 public void callSessionUpdateFailed(IImsCallSession session, 1097 ImsReasonInfo reasonInfo) { 1098 if (mListener != null) { 1099 mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo); 1100 } 1101 } 1102 1103 @Override callSessionUpdateReceived(IImsCallSession session, ImsCallProfile profile)1104 public void callSessionUpdateReceived(IImsCallSession session, 1105 ImsCallProfile profile) { 1106 if (mListener != null) { 1107 mListener.callSessionUpdateReceived(ImsCallSession.this, profile); 1108 } 1109 } 1110 1111 /** 1112 * Notifies the result of conference extension. 1113 */ 1114 @Override callSessionConferenceExtended(IImsCallSession session, IImsCallSession newSession, ImsCallProfile profile)1115 public void callSessionConferenceExtended(IImsCallSession session, 1116 IImsCallSession newSession, ImsCallProfile profile) { 1117 if (mListener != null) { 1118 mListener.callSessionConferenceExtended(ImsCallSession.this, 1119 new ImsCallSession(newSession), profile); 1120 } 1121 } 1122 1123 @Override callSessionConferenceExtendFailed(IImsCallSession session, ImsReasonInfo reasonInfo)1124 public void callSessionConferenceExtendFailed(IImsCallSession session, 1125 ImsReasonInfo reasonInfo) { 1126 if (mListener != null) { 1127 mListener.callSessionConferenceExtendFailed(ImsCallSession.this, reasonInfo); 1128 } 1129 } 1130 1131 @Override callSessionConferenceExtendReceived(IImsCallSession session, IImsCallSession newSession, ImsCallProfile profile)1132 public void callSessionConferenceExtendReceived(IImsCallSession session, 1133 IImsCallSession newSession, ImsCallProfile profile) { 1134 if (mListener != null) { 1135 mListener.callSessionConferenceExtendReceived(ImsCallSession.this, 1136 new ImsCallSession(newSession), profile); 1137 } 1138 } 1139 1140 /** 1141 * Notifies the result of the participant invitation / removal to/from 1142 * the conference session. 1143 */ 1144 @Override callSessionInviteParticipantsRequestDelivered(IImsCallSession session)1145 public void callSessionInviteParticipantsRequestDelivered(IImsCallSession session) { 1146 if (mListener != null) { 1147 mListener.callSessionInviteParticipantsRequestDelivered(ImsCallSession.this); 1148 } 1149 } 1150 1151 @Override callSessionInviteParticipantsRequestFailed(IImsCallSession session, ImsReasonInfo reasonInfo)1152 public void callSessionInviteParticipantsRequestFailed(IImsCallSession session, 1153 ImsReasonInfo reasonInfo) { 1154 if (mListener != null) { 1155 mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this, 1156 reasonInfo); 1157 } 1158 } 1159 1160 @Override callSessionRemoveParticipantsRequestDelivered(IImsCallSession session)1161 public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) { 1162 if (mListener != null) { 1163 mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this); 1164 } 1165 } 1166 1167 @Override callSessionRemoveParticipantsRequestFailed(IImsCallSession session, ImsReasonInfo reasonInfo)1168 public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session, 1169 ImsReasonInfo reasonInfo) { 1170 if (mListener != null) { 1171 mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this, 1172 reasonInfo); 1173 } 1174 } 1175 1176 /** 1177 * Notifies the changes of the conference info. in the conference session. 1178 */ 1179 @Override callSessionConferenceStateUpdated(IImsCallSession session, ImsConferenceState state)1180 public void callSessionConferenceStateUpdated(IImsCallSession session, 1181 ImsConferenceState state) { 1182 if (mListener != null) { 1183 mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state); 1184 } 1185 } 1186 1187 /** 1188 * Notifies the incoming USSD message. 1189 */ 1190 @Override callSessionUssdMessageReceived(IImsCallSession session, int mode, String ussdMessage)1191 public void callSessionUssdMessageReceived(IImsCallSession session, 1192 int mode, String ussdMessage) { 1193 if (mListener != null) { 1194 mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode, ussdMessage); 1195 } 1196 } 1197 1198 /** 1199 * Notifies of handover information for this call 1200 */ 1201 @Override callSessionHandover(IImsCallSession session, int srcAccessTech, int targetAccessTech, ImsReasonInfo reasonInfo)1202 public void callSessionHandover(IImsCallSession session, 1203 int srcAccessTech, int targetAccessTech, 1204 ImsReasonInfo reasonInfo) { 1205 if (mListener != null) { 1206 mListener.callSessionHandover(ImsCallSession.this, srcAccessTech, 1207 targetAccessTech, reasonInfo); 1208 } 1209 } 1210 1211 /** 1212 * Notifies of handover failure info for this call 1213 */ 1214 @Override callSessionHandoverFailed(IImsCallSession session, int srcAccessTech, int targetAccessTech, ImsReasonInfo reasonInfo)1215 public void callSessionHandoverFailed(IImsCallSession session, 1216 int srcAccessTech, int targetAccessTech, 1217 ImsReasonInfo reasonInfo) { 1218 if (mListener != null) { 1219 mListener.callSessionHandoverFailed(ImsCallSession.this, srcAccessTech, 1220 targetAccessTech, reasonInfo); 1221 } 1222 } 1223 1224 /** 1225 * Notifies the TTY mode received from remote party. 1226 */ 1227 @Override callSessionTtyModeReceived(IImsCallSession session, int mode)1228 public void callSessionTtyModeReceived(IImsCallSession session, 1229 int mode) { 1230 if (mListener != null) { 1231 mListener.callSessionTtyModeReceived(ImsCallSession.this, mode); 1232 } 1233 } 1234 1235 /** 1236 * Notifies of a change to the multiparty state for this {@code ImsCallSession}. 1237 * 1238 * @param session The call session. 1239 * @param isMultiParty {@code true} if the session became multiparty, {@code false} 1240 * otherwise. 1241 */ callSessionMultipartyStateChanged(IImsCallSession session, boolean isMultiParty)1242 public void callSessionMultipartyStateChanged(IImsCallSession session, 1243 boolean isMultiParty) { 1244 1245 if (mListener != null) { 1246 mListener.callSessionMultipartyStateChanged(ImsCallSession.this, isMultiParty); 1247 } 1248 } 1249 } 1250 1251 /** 1252 * Provides a string representation of the {@link ImsCallSession}. Primarily intended for 1253 * use in log statements. 1254 * 1255 * @return String representation of session. 1256 */ 1257 @Override toString()1258 public String toString() { 1259 StringBuilder sb = new StringBuilder(); 1260 sb.append("[ImsCallSession objId:"); 1261 sb.append(System.identityHashCode(this)); 1262 sb.append(" state:"); 1263 sb.append(State.toString(getState())); 1264 sb.append(" callId:"); 1265 sb.append(getCallId()); 1266 sb.append("]"); 1267 return sb.toString(); 1268 } 1269 } 1270