1 /*
2  * Copyright (C) 2017 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 android.telephony.ims.stub;
18 
19 import android.annotation.IntDef;
20 import android.annotation.IntRange;
21 import android.annotation.NonNull;
22 import android.annotation.SystemApi;
23 import android.os.Bundle;
24 import android.os.Message;
25 import android.os.RemoteException;
26 import android.telephony.ims.ImsCallProfile;
27 import android.telephony.ims.ImsCallSession;
28 import android.telephony.ims.ImsCallSessionListener;
29 import android.telephony.ims.ImsReasonInfo;
30 import android.telephony.ims.ImsStreamMediaProfile;
31 import android.telephony.ims.ImsVideoCallProvider;
32 import android.telephony.ims.RtpHeaderExtension;
33 import android.telephony.ims.RtpHeaderExtensionType;
34 import android.telephony.ims.aidl.IImsCallSessionListener;
35 import android.util.ArraySet;
36 import android.util.Log;
37 
38 import com.android.ims.internal.IImsCallSession;
39 import com.android.ims.internal.IImsVideoCallProvider;
40 import com.android.internal.telephony.util.TelephonyUtils;
41 
42 import java.lang.annotation.Retention;
43 import java.lang.annotation.RetentionPolicy;
44 import java.util.List;
45 import java.util.Set;
46 import java.util.concurrent.CancellationException;
47 import java.util.concurrent.CompletableFuture;
48 import java.util.concurrent.CompletionException;
49 import java.util.concurrent.ExecutionException;
50 import java.util.concurrent.Executor;
51 import java.util.function.Supplier;
52 
53 /**
54  * Base implementation of IImsCallSession, which implements stub versions of the methods available.
55  *
56  * Override the methods that your implementation of ImsCallSession supports.
57  *
58  * @hide
59  */
60 @SystemApi
61 // DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
62 // will break other implementations of ImsCallSession maintained by other ImsServices.
63 public class ImsCallSessionImplBase implements AutoCloseable {
64 
65     private static final String LOG_TAG = "ImsCallSessionImplBase";
66     /**
67      * Notify USSD Mode.
68      */
69     public static final int USSD_MODE_NOTIFY = 0;
70     /**
71      * Request USSD Mode
72      */
73     public static final int USSD_MODE_REQUEST = 1;
74 
75     /** @hide */
76     @IntDef(
77         prefix = "MEDIA_STREAM_TYPE_",
78         value = {
79             MEDIA_STREAM_TYPE_AUDIO,
80             MEDIA_STREAM_TYPE_VIDEO
81         })
82     @Retention(RetentionPolicy.SOURCE)
83     public @interface MediaStreamType {}
84 
85     /**
86      * Media Stream Type - Audio
87      * @hide
88      */
89     public static final int MEDIA_STREAM_TYPE_AUDIO = 1;
90     /**
91      * Media Stream Type - Video
92      * @hide
93      */
94     public static final int MEDIA_STREAM_TYPE_VIDEO = 2;
95 
96     /** @hide */
97     @IntDef(
98         prefix = "MEDIA_STREAM_DIRECTION_",
99         value = {
100             MEDIA_STREAM_DIRECTION_UPLINK,
101             MEDIA_STREAM_DIRECTION_DOWNLINK
102     })
103     @Retention(RetentionPolicy.SOURCE)
104     public @interface MediaStreamDirection {}
105 
106     /**
107      * Media Stream Direction - Uplink
108      * @hide
109      */
110     public static final int MEDIA_STREAM_DIRECTION_UPLINK = 1;
111     /**
112      * Media Stream Direction - Downlink
113      * @hide
114      */
115     public static final int MEDIA_STREAM_DIRECTION_DOWNLINK = 2;
116 
117     /**
118      * Defines IMS call session state.
119      */
120     public static class State {
121         public static final int IDLE = 0;
122         public static final int INITIATED = 1;
123         public static final int NEGOTIATING = 2;
124         public static final int ESTABLISHING = 3;
125         public static final int ESTABLISHED = 4;
126 
127         public static final int RENEGOTIATING = 5;
128         public static final int REESTABLISHING = 6;
129 
130         public static final int TERMINATING = 7;
131         public static final int TERMINATED = 8;
132 
133         public static final int INVALID = (-1);
134 
135         /**
136          * Converts the state to string.
137          */
toString(int state)138         public static String toString(int state) {
139             switch (state) {
140                 case IDLE:
141                     return "IDLE";
142                 case INITIATED:
143                     return "INITIATED";
144                 case NEGOTIATING:
145                     return "NEGOTIATING";
146                 case ESTABLISHING:
147                     return "ESTABLISHING";
148                 case ESTABLISHED:
149                     return "ESTABLISHED";
150                 case RENEGOTIATING:
151                     return "RENEGOTIATING";
152                 case REESTABLISHING:
153                     return "REESTABLISHING";
154                 case TERMINATING:
155                     return "TERMINATING";
156                 case TERMINATED:
157                     return "TERMINATED";
158                 default:
159                     return "UNKNOWN";
160             }
161         }
162 
163         /**
164          * @hide
165          */
State()166         private State() {
167         }
168     }
169 
170     private Executor mExecutor = Runnable::run;
171 
172     // Non-final for injection by tests
173     private IImsCallSession mServiceImpl = new IImsCallSession.Stub() {
174         @Override
175         public void close() {
176             executeMethodAsync(() -> ImsCallSessionImplBase.this.close(), "close");
177         }
178 
179         @Override
180         public String getCallId() {
181             return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getCallId(),
182                     "getCallId");
183         }
184 
185         @Override
186         public ImsCallProfile getCallProfile() {
187             return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getCallProfile(),
188                     "getCallProfile");
189         }
190 
191         @Override
192         public ImsCallProfile getLocalCallProfile() {
193             return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
194                     .getLocalCallProfile(), "getLocalCallProfile");
195         }
196 
197         @Override
198         public ImsCallProfile getRemoteCallProfile() {
199             return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
200                     .getRemoteCallProfile(), "getRemoteCallProfile");
201         }
202 
203         @Override
204         public String getProperty(String name) {
205             return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getProperty(name),
206                     "getProperty");
207         }
208 
209         @Override
210         public int getState() {
211             return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getState(),
212                     "getState");
213         }
214 
215         @Override
216         public boolean isInCall() {
217             return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.isInCall(),
218                     "isInCall");
219         }
220 
221         @Override
222         public void setListener(IImsCallSessionListener listener) {
223             ImsCallSessionListener iCSL = new ImsCallSessionListener(listener);
224             iCSL.setDefaultExecutor(mExecutor);
225             executeMethodAsync(() -> ImsCallSessionImplBase.this.setListener(iCSL), "setListener");
226         }
227 
228         @Override
229         public void setMute(boolean muted) {
230             executeMethodAsync(() -> ImsCallSessionImplBase.this.setMute(muted), "setMute");
231         }
232 
233         @Override
234         public void start(String callee, ImsCallProfile profile) {
235             executeMethodAsync(() -> ImsCallSessionImplBase.this.start(callee, profile), "start");
236         }
237 
238         @Override
239         public void startConference(String[] participants, ImsCallProfile profile) throws
240                 RemoteException {
241             executeMethodAsync(() -> ImsCallSessionImplBase.this.startConference(participants,
242                     profile), "startConference");
243         }
244 
245         @Override
246         public void accept(int callType, ImsStreamMediaProfile profile) {
247             executeMethodAsync(() -> ImsCallSessionImplBase.this.accept(callType, profile),
248                     "accept");
249         }
250 
251         @Override
252         public void deflect(String deflectNumber) {
253             executeMethodAsync(() -> ImsCallSessionImplBase.this.deflect(deflectNumber),
254                     "deflect");
255         }
256 
257         @Override
258         public void reject(int reason) {
259             executeMethodAsync(() -> ImsCallSessionImplBase.this.reject(reason), "reject");
260         }
261 
262         @Override
263         public void transfer(@NonNull String number, boolean isConfirmationRequired) {
264             executeMethodAsync(() -> ImsCallSessionImplBase.this.transfer(number,
265                     isConfirmationRequired), "transfer");
266         }
267 
268         @Override
269         public void consultativeTransfer(@NonNull IImsCallSession transferToSession) {
270             executeMethodAsync(() -> {
271                 ImsCallSessionImplBase otherSession = new ImsCallSessionImplBase();
272                 otherSession.setServiceImpl(transferToSession);
273                 ImsCallSessionImplBase.this.transfer(otherSession);
274             }, "consultativeTransfer");
275         }
276 
277         @Override
278         public void terminate(int reason) {
279             executeMethodAsync(() -> ImsCallSessionImplBase.this.terminate(reason), "terminate");
280         }
281 
282         @Override
283         public void hold(ImsStreamMediaProfile profile) {
284             executeMethodAsync(() -> ImsCallSessionImplBase.this.hold(profile), "hold");
285         }
286 
287         @Override
288         public void resume(ImsStreamMediaProfile profile) {
289             executeMethodAsync(() -> ImsCallSessionImplBase.this.resume(profile), "resume");
290         }
291 
292         @Override
293         public void merge() {
294             executeMethodAsync(() -> ImsCallSessionImplBase.this.merge(), "merge");
295         }
296 
297         @Override
298         public void update(int callType, ImsStreamMediaProfile profile) {
299             executeMethodAsync(() -> ImsCallSessionImplBase.this.update(callType, profile),
300                     "update");
301         }
302 
303         @Override
304         public void extendToConference(String[] participants) {
305             executeMethodAsync(() -> ImsCallSessionImplBase.this.extendToConference(participants),
306                     "extendToConference");
307         }
308 
309         @Override
310         public void inviteParticipants(String[] participants) {
311             executeMethodAsync(() -> ImsCallSessionImplBase.this.inviteParticipants(participants),
312                     "inviteParticipants");
313         }
314 
315         @Override
316         public void removeParticipants(String[] participants) {
317             executeMethodAsync(() -> ImsCallSessionImplBase.this.removeParticipants(participants),
318                     "removeParticipants");
319         }
320 
321         @Override
322         public void sendDtmf(char c, Message result) {
323             executeMethodAsync(() -> ImsCallSessionImplBase.this.sendDtmf(c, result), "sendDtmf");
324         }
325 
326         @Override
327         public void startDtmf(char c) {
328             executeMethodAsync(() -> ImsCallSessionImplBase.this.startDtmf(c), "startDtmf");
329         }
330 
331         @Override
332         public void stopDtmf() {
333             executeMethodAsync(() -> ImsCallSessionImplBase.this.stopDtmf(), "stopDtmf");
334         }
335 
336         @Override
337         public void sendUssd(String ussdMessage) {
338             executeMethodAsync(() -> ImsCallSessionImplBase.this.sendUssd(ussdMessage), "sendUssd");
339         }
340 
341         @Override
342         public IImsVideoCallProvider getVideoCallProvider() {
343             return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
344                     .getVideoCallProvider(), "getVideoCallProvider");
345         }
346 
347         @Override
348         public boolean isMultiparty() {
349             return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.isMultiparty(),
350                     "isMultiparty");
351         }
352 
353         @Override
354         public void sendRttModifyRequest(ImsCallProfile toProfile) {
355             executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttModifyRequest(toProfile),
356                     "sendRttModifyRequest");
357         }
358 
359         @Override
360         public void sendRttModifyResponse(boolean status) {
361             executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttModifyResponse(status),
362                     "sendRttModifyResponse");
363         }
364 
365         @Override
366         public void sendRttMessage(String rttMessage) {
367             executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttMessage(rttMessage),
368                     "sendRttMessage");
369         }
370 
371         @Override
372         public void sendRtpHeaderExtensions(@NonNull List<RtpHeaderExtension> extensions) {
373             executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRtpHeaderExtensions(
374                     new ArraySet<RtpHeaderExtension>(extensions)), "sendRtpHeaderExtensions");
375         }
376 
377         @Override
378         public void callSessionNotifyAnbr(int mediaType, int direction, int bitsPerSecond) {
379             executeMethodAsync(() -> ImsCallSessionImplBase.this.callSessionNotifyAnbr(
380                     mediaType, direction, bitsPerSecond), "callSessionNotifyAnbr");
381         }
382 
383         // Call the methods with a clean calling identity on the executor and wait indefinitely for
384         // the future to return.
385         private void executeMethodAsync(Runnable r, String errorLogName) {
386             try {
387                 CompletableFuture.runAsync(
388                         () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
389             } catch (CancellationException | CompletionException e) {
390                 Log.w(LOG_TAG, "ImsCallSessionImplBase Binder - " + errorLogName + " exception: "
391                         + e.getMessage());
392             }
393         }
394 
395         private <T> T executeMethodAsyncForResult(Supplier<T> r,
396                 String errorLogName) {
397             CompletableFuture<T> future = CompletableFuture.supplyAsync(
398                     () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
399             try {
400                 return future.get();
401             } catch (ExecutionException | InterruptedException e) {
402                 Log.w(LOG_TAG, "ImsCallSessionImplBase Binder - " + errorLogName + " exception: "
403                         + e.getMessage());
404                 return null;
405             }
406         }
407     };
408 
409     /**
410      * @hide
411      */
setListener(IImsCallSessionListener listener)412     public final void setListener(IImsCallSessionListener listener) throws RemoteException {
413         setListener(new ImsCallSessionListener(listener));
414     }
415 
416     /**
417      * Sets the listener to listen to the session events. An {@link ImsCallSession}
418      * can only hold one listener at a time. Subsequent calls to this method
419      * override the previous listener.
420      *
421      * @param listener {@link ImsCallSessionListener} used to notify the framework of updates
422      * to the ImsCallSession
423 
424      * @deprecated use {@link android.telephony.ims.feature.MmTelFeature#notifyIncomingCall(
425      * ImsCallSessionImplBase, String, Bundle)} to get the listener instead
426      */
427     @Deprecated
setListener(ImsCallSessionListener listener)428     public void setListener(ImsCallSessionListener listener) {
429     }
430 
431     /**
432      * Closes the object. This {@link ImsCallSessionImplBase} is not usable after being closed.
433      */
434     @Override
close()435     public void close() {
436 
437     }
438 
439     /**
440      * @return A String containing the unique call ID of this {@link ImsCallSessionImplBase}.
441      */
getCallId()442     public String getCallId() {
443         return null;
444     }
445 
446     /**
447      * @return The {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is associated
448      * with.
449      */
getCallProfile()450     public ImsCallProfile getCallProfile() {
451         return null;
452     }
453 
454     /**
455      * @return The local {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
456      * associated with.
457      */
getLocalCallProfile()458     public ImsCallProfile getLocalCallProfile() {
459         return null;
460     }
461 
462     /**
463      * @return The remote {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
464      * associated with.
465      */
getRemoteCallProfile()466     public ImsCallProfile getRemoteCallProfile() {
467         return null;
468     }
469 
470     /**
471      * @param name The String extra key.
472      * @return The string extra value associated with the specified property.
473      */
getProperty(String name)474     public String getProperty(String name) {
475         return null;
476     }
477 
478     /**
479      * @return The {@link ImsCallSessionImplBase} state, defined in
480      * {@link ImsCallSessionImplBase.State}.
481      */
getState()482     public int getState() {
483         return ImsCallSessionImplBase.State.INVALID;
484     }
485 
486     /**
487      * @return true if the {@link ImsCallSessionImplBase} is in a call, false otherwise.
488      */
isInCall()489     public boolean isInCall() {
490         return false;
491     }
492 
493     /**
494      * Mutes or unmutes the mic for the active call.
495      *
496      * @param muted true if the call should be muted, false otherwise.
497      */
setMute(boolean muted)498     public void setMute(boolean muted) {
499     }
500 
501     /**
502      * Initiates an IMS call with the specified number and call profile.
503      * The session listener set in {@link #setListener(ImsCallSessionListener)} is called back upon
504      * defined session events.
505      * Only valid to call when the session state is in
506      * {@link ImsCallSession.State#IDLE}.
507      *
508      * @param callee dialed string to make the call to
509      * @param profile call profile to make the call with the specified service type,
510      *      call type and media information
511      * @see {@link ImsCallSession.Listener#callSessionStarted},
512      * {@link ImsCallSession.Listener#callSessionStartFailed}
513      */
start(String callee, ImsCallProfile profile)514     public void start(String callee, ImsCallProfile profile) {
515     }
516 
517     /**
518      * Initiates an IMS call with the specified participants and call profile.
519      * The session listener set in {@link #setListener(ImsCallSessionListener)} is called back upon
520      * defined session events.
521      * The method is only valid to call when the session state is in
522      * {@link ImsCallSession.State#IDLE}.
523      *
524      * @param participants participant list to initiate an IMS conference call
525      * @param profile call profile to make the call with the specified service type,
526      *      call type and media information
527      * @see {@link ImsCallSession.Listener#callSessionStarted},
528      * {@link ImsCallSession.Listener#callSessionStartFailed}
529      */
startConference(String[] participants, ImsCallProfile profile)530     public void startConference(String[] participants, ImsCallProfile profile) {
531     }
532 
533     /**
534      * Accepts an incoming call or session update.
535      *
536      * @param callType call type specified in {@link ImsCallProfile} to be answered
537      * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
538      * @see {@link ImsCallSession.Listener#callSessionStarted}
539      */
accept(int callType, ImsStreamMediaProfile profile)540     public void accept(int callType, ImsStreamMediaProfile profile) {
541     }
542 
543     /**
544      * Deflects an incoming call.
545      *
546      * @param deflectNumber number to deflect the call
547      */
deflect(String deflectNumber)548     public void deflect(String deflectNumber) {
549     }
550 
551     /**
552      * Rejects an incoming call or session update.
553      *
554      * @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
555      *               The {@link android.telecom.InCallService} (dialer app) can use the
556      *               {@link android.telecom.Call#reject(int)} API to reject a call while specifying
557      *               a user-indicated reason for rejecting the call.
558      *               Normal call declines ({@link android.telecom.Call#REJECT_REASON_DECLINED}) will
559      *               map to {@link ImsReasonInfo#CODE_USER_DECLINE}.
560      *               Unwanted calls ({@link android.telecom.Call#REJECT_REASON_UNWANTED}) will map
561      *               to {@link ImsReasonInfo#CODE_SIP_USER_MARKED_UNWANTED}.
562      * {@link ImsCallSession.Listener#callSessionStartFailed}
563      */
reject(int reason)564     public void reject(int reason) {
565     }
566 
567     /**
568      * Transfer an established call to given number
569      *
570      * @param number number to transfer the call
571      * @param isConfirmationRequired if {@code True}, indicates a confirmed transfer,
572      * if {@code False} it indicates an unconfirmed transfer.
573      * @hide
574      */
transfer(@onNull String number, boolean isConfirmationRequired)575     public void transfer(@NonNull String number, boolean isConfirmationRequired) {
576     }
577 
578     /**
579      * Transfer an established call to another call session
580      *
581      * @param otherSession The other ImsCallSession to transfer the ongoing session to.
582      * @hide
583      */
transfer(@onNull ImsCallSessionImplBase otherSession)584     public void transfer(@NonNull ImsCallSessionImplBase otherSession) {
585     }
586 
587     /**
588      * Terminates a call.
589      *
590      * @param reason reason code to terminate a call, defined in {@link ImsReasonInfo}.
591      *
592      * @see {@link ImsCallSession.Listener#callSessionTerminated}
593      */
terminate(int reason)594     public void terminate(int reason) {
595     }
596 
597     /**
598      * Puts a call on hold. When it succeeds, {@link ImsCallSession.Listener#callSessionHeld} is
599      * called.
600      *
601      * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
602      * @see {@link ImsCallSession.Listener#callSessionHeld},
603      * {@link ImsCallSession.Listener#callSessionHoldFailed}
604      */
hold(ImsStreamMediaProfile profile)605     public void hold(ImsStreamMediaProfile profile) {
606     }
607 
608     /**
609      * Continues a call that's on hold. When it succeeds,
610      * {@link ImsCallSession.Listener#callSessionResumed} is called.
611      *
612      * @param profile stream media profile with {@link ImsStreamMediaProfile} to resume the call
613      * @see {@link ImsCallSession.Listener#callSessionResumed},
614      * {@link ImsCallSession.Listener#callSessionResumeFailed}
615      */
resume(ImsStreamMediaProfile profile)616     public void resume(ImsStreamMediaProfile profile) {
617     }
618 
619     /**
620      * Merges the active and held call. When the merge starts,
621      * {@link ImsCallSession.Listener#callSessionMergeStarted} is called.
622      * {@link ImsCallSession.Listener#callSessionMergeComplete} is called if the merge is
623      * successful, and {@link ImsCallSession.Listener#callSessionMergeFailed} is called if the merge
624      * fails.
625      *
626      * @see {@link ImsCallSession.Listener#callSessionMergeStarted},
627      * {@link ImsCallSession.Listener#callSessionMergeComplete},
628      *      {@link ImsCallSession.Listener#callSessionMergeFailed}
629      */
merge()630     public void merge() {
631     }
632 
633     /**
634      * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
635      *
636      * @param callType call type specified in {@link ImsCallProfile} to be updated
637      * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
638      * @see {@link ImsCallSession.Listener#callSessionUpdated},
639      * {@link ImsCallSession.Listener#callSessionUpdateFailed}
640      */
update(int callType, ImsStreamMediaProfile profile)641     public void update(int callType, ImsStreamMediaProfile profile) {
642     }
643 
644     /**
645      * Extends this call to the conference call with the specified recipients.
646      *
647      * @param participants participant list to be invited to the conference call after extending the
648      * call
649      * @see {@link ImsCallSession.Listener#callSessionConferenceExtended},
650      * {@link ImsCallSession.Listener#callSessionConferenceExtendFailed}
651      */
extendToConference(String[] participants)652     public void extendToConference(String[] participants) {
653     }
654 
655     /**
656      * Requests the conference server to invite an additional participants to the conference.
657      *
658      * @param participants participant list to be invited to the conference call
659      * @see {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestDelivered},
660      *      {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestFailed}
661      */
inviteParticipants(String[] participants)662     public void inviteParticipants(String[] participants) {
663     }
664 
665     /**
666      * Requests the conference server to remove the specified participants from the conference.
667      *
668      * @param participants participant list to be removed from the conference call
669      * @see {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestDelivered},
670      *      {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestFailed}
671      */
removeParticipants(String[] participants)672     public void removeParticipants(String[] participants) {
673     }
674 
675     /**
676      * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
677      * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
678      * and event flash to 16. Currently, event flash is not supported.
679      *
680      * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
681      * @param result If non-null, the {@link Message} to send when the operation is complete. This
682      *         is done by using the associated {@link android.os.Messenger} in
683      *         {@link Message#replyTo}. For example:
684      * {@code
685      *     // Send DTMF and other operations...
686      *     try {
687      *         // Notify framework that the DTMF was sent.
688      *         Messenger dtmfMessenger = result.replyTo;
689      *         if (dtmfMessenger != null) {
690      *             dtmfMessenger.send(result);
691      *         }
692      *     } catch (RemoteException e) {
693      *         // Remote side is dead
694      *     }
695      * }
696      */
sendDtmf(char c, Message result)697     public void sendDtmf(char c, Message result) {
698     }
699 
700     /**
701      * Start a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
702      * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
703      * and event flash to 16. Currently, event flash is not supported.
704      *
705      * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
706      */
startDtmf(char c)707     public void startDtmf(char c) {
708     }
709 
710     /**
711      * Stop a DTMF code.
712      */
stopDtmf()713     public void stopDtmf() {
714     }
715 
716     /**
717      * Sends an USSD message.
718      *
719      * @param ussdMessage USSD message to send
720      */
sendUssd(String ussdMessage)721     public void sendUssd(String ussdMessage) {
722     }
723 
724     /**
725      * See {@link #getImsVideoCallProvider()}, used directly in older ImsService implementations.
726      * @hide
727      */
getVideoCallProvider()728     public IImsVideoCallProvider getVideoCallProvider() {
729         ImsVideoCallProvider provider = getImsVideoCallProvider();
730         return provider != null ? provider.getInterface() : null;
731     }
732 
733     /**
734      * @return The {@link ImsVideoCallProvider} implementation contained within the IMS service
735      * process.
736      */
getImsVideoCallProvider()737     public ImsVideoCallProvider getImsVideoCallProvider() {
738         return null;
739     }
740 
741     /**
742      * Determines if the current session is multiparty.
743      * @return {@code True} if the session is multiparty.
744      */
isMultiparty()745     public boolean isMultiparty() {
746         return false;
747     }
748 
749     /**
750      * Device issues RTT modify request
751      * @param toProfile The profile with requested changes made
752      */
sendRttModifyRequest(ImsCallProfile toProfile)753     public void sendRttModifyRequest(ImsCallProfile toProfile) {
754     }
755 
756     /**
757      * Device responds to Remote RTT modify request
758      * @param status true if the the request was accepted or false of the request is defined.
759      */
sendRttModifyResponse(boolean status)760     public void sendRttModifyResponse(boolean status) {
761     }
762 
763     /**
764      * Device sends RTT message
765      * @param rttMessage RTT message to be sent
766      */
sendRttMessage(String rttMessage)767     public void sendRttMessage(String rttMessage) {
768     }
769 
770     /**
771      * Device requests that {@code rtpHeaderExtensions} are sent as a header extension with the next
772      * RTP packet sent by the IMS stack.
773      * <p>
774      * The {@link RtpHeaderExtensionType}s negotiated during SDP (Session Description Protocol)
775      * signalling determine the {@link RtpHeaderExtension}s which can be sent using this method.
776      * See RFC8285 for more information.
777      * <p>
778      * By specification, the RTP header extension is an unacknowledged transmission and there is no
779      * guarantee that the header extension will be delivered by the network to the other end of the
780      * call.
781      * @param rtpHeaderExtensions The RTP header extensions to be included in the next RTP header.
782      */
sendRtpHeaderExtensions(@onNull Set<RtpHeaderExtension> rtpHeaderExtensions)783     public void sendRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> rtpHeaderExtensions) {
784     }
785 
786     /**
787      * Deliver the bitrate for the indicated media type, direction and bitrate to the upper layer.
788      *
789      * @param mediaType MediaType is used to identify media stream such as audio or video.
790      * @param direction Direction of this packet stream (e.g. uplink or downlink).
791      * @param bitsPerSecond This value is the bitrate received from the NW through the Recommended
792      *        bitrate MAC Control Element message and ImsStack converts this value from MAC bitrate
793      *        to audio/video codec bitrate (defined in TS26.114).
794      * @hide
795      */
callSessionNotifyAnbr(@ediaStreamType int mediaType, @MediaStreamDirection int direction, @IntRange(from = 0) int bitsPerSecond)796     public void callSessionNotifyAnbr(@MediaStreamType int mediaType,
797             @MediaStreamDirection int direction, @IntRange(from = 0) int bitsPerSecond) {
798         // Base Implementation - Should be overridden by IMS service
799         Log.i(LOG_TAG, "ImsCallSessionImplBase callSessionNotifyAnbr - mediaType: " + mediaType);
800     }
801 
802     /** @hide */
getServiceImpl()803     public IImsCallSession getServiceImpl() {
804         return mServiceImpl;
805     }
806 
807     /** @hide */
setServiceImpl(IImsCallSession serviceImpl)808     public void setServiceImpl(IImsCallSession serviceImpl) {
809         mServiceImpl = serviceImpl;
810     }
811 
812     /**
813      * Set default Executor from MmTelFeature.
814      * @param executor The default executor for the framework to use when executing the methods
815      * overridden by the implementation of ImsCallSession.
816      * @hide
817      */
setDefaultExecutor(@onNull Executor executor)818     public final void setDefaultExecutor(@NonNull Executor executor) {
819         mExecutor = executor;
820     }
821 }
822