1 /*
2  * Copyright (C) 2018 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.feature;
18 
19 import android.annotation.CallbackExecutor;
20 import android.annotation.FlaggedApi;
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.SystemApi;
25 import android.os.Binder;
26 import android.os.Bundle;
27 import android.os.Message;
28 import android.os.RemoteException;
29 import android.os.ServiceSpecificException;
30 import android.telecom.TelecomManager;
31 import android.telephony.AccessNetworkConstants;
32 import android.telephony.ims.ImsCallProfile;
33 import android.telephony.ims.ImsCallSession;
34 import android.telephony.ims.ImsCallSessionListener;
35 import android.telephony.ims.ImsException;
36 import android.telephony.ims.ImsReasonInfo;
37 import android.telephony.ims.ImsService;
38 import android.telephony.ims.MediaQualityStatus;
39 import android.telephony.ims.MediaThreshold;
40 import android.telephony.ims.RtpHeaderExtensionType;
41 import android.telephony.ims.SrvccCall;
42 import android.telephony.ims.aidl.IImsCallSessionListener;
43 import android.telephony.ims.aidl.IImsCapabilityCallback;
44 import android.telephony.ims.aidl.IImsMmTelFeature;
45 import android.telephony.ims.aidl.IImsMmTelListener;
46 import android.telephony.ims.aidl.IImsSmsListener;
47 import android.telephony.ims.aidl.IImsTrafficSessionCallback;
48 import android.telephony.ims.aidl.ISrvccStartedCallback;
49 import android.telephony.ims.stub.ImsCallSessionImplBase;
50 import android.telephony.ims.stub.ImsEcbmImplBase;
51 import android.telephony.ims.stub.ImsMultiEndpointImplBase;
52 import android.telephony.ims.stub.ImsRegistrationImplBase;
53 import android.telephony.ims.stub.ImsSmsImplBase;
54 import android.telephony.ims.stub.ImsUtImplBase;
55 import android.util.ArraySet;
56 import android.util.Log;
57 
58 import com.android.ims.internal.IImsCallSession;
59 import com.android.ims.internal.IImsEcbm;
60 import com.android.ims.internal.IImsMultiEndpoint;
61 import com.android.ims.internal.IImsUt;
62 import com.android.internal.telephony.util.TelephonyUtils;
63 import com.android.server.telecom.flags.Flags;
64 
65 import java.lang.annotation.Retention;
66 import java.lang.annotation.RetentionPolicy;
67 import java.lang.ref.WeakReference;
68 import java.util.HashMap;
69 import java.util.List;
70 import java.util.Set;
71 import java.util.concurrent.CancellationException;
72 import java.util.concurrent.CompletableFuture;
73 import java.util.concurrent.CompletionException;
74 import java.util.concurrent.ExecutionException;
75 import java.util.concurrent.Executor;
76 import java.util.concurrent.atomic.AtomicInteger;
77 import java.util.concurrent.atomic.AtomicReference;
78 import java.util.function.Consumer;
79 import java.util.function.Supplier;
80 
81 /**
82  * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
83  *
84  * Any class wishing to use MmTelFeature should extend this class and implement all methods that the
85  * service supports.
86  */
87 public class MmTelFeature extends ImsFeature {
88 
89     private static final String LOG_TAG = "MmTelFeature";
90     private Executor mExecutor;
91     private ImsSmsImplBase mSmsImpl;
92 
93     private HashMap<ImsTrafficSessionCallback, ImsTrafficSessionCallbackWrapper> mTrafficCallbacks =
94             new HashMap<>();
95     /**
96      * Creates a new MmTelFeature using the Executor set in {@link ImsService#getExecutor}
97      * @hide
98      */
99     @SystemApi
MmTelFeature()100     public MmTelFeature() {
101     }
102 
103     /**
104      * Create a new MmTelFeature using the Executor specified for methods being called by the
105      * framework.
106      * @param executor The executor for the framework to use when executing the methods overridden
107      * by the implementation of MmTelFeature.
108      * @hide
109      */
110     @SystemApi
MmTelFeature(@onNull Executor executor)111     public MmTelFeature(@NonNull Executor executor) {
112         super();
113         mExecutor = executor;
114     }
115 
116     private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
117 
118         @Override
119         public void setListener(IImsMmTelListener l) {
120             executeMethodAsyncNoException(() -> MmTelFeature.this.setListener(l), "setListener");
121         }
122 
123         @Override
124         public int getFeatureState() throws RemoteException {
125             return executeMethodAsyncForResult(() -> MmTelFeature.this.getFeatureState(),
126                     "getFeatureState");
127         }
128 
129         @Override
130         public ImsCallProfile createCallProfile(int callSessionType, int callType)
131                 throws RemoteException {
132             return executeMethodAsyncForResult(() -> MmTelFeature.this.createCallProfile(
133                     callSessionType, callType), "createCallProfile");
134         }
135 
136         @Override
137         public void changeOfferedRtpHeaderExtensionTypes(List<RtpHeaderExtensionType> types)
138                 throws RemoteException {
139             executeMethodAsync(() -> MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes(
140                     new ArraySet<>(types)), "changeOfferedRtpHeaderExtensionTypes");
141         }
142 
143         @Override
144         public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
145             AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
146             IImsCallSession result = executeMethodAsyncForResult(() -> {
147                 try {
148                     return createCallSessionInterface(profile);
149                 } catch (RemoteException e) {
150                     exceptionRef.set(e);
151                     return null;
152                 }
153             }, "createCallSession");
154 
155             if (exceptionRef.get() != null) {
156                 throw exceptionRef.get();
157             }
158 
159             return result;
160         }
161 
162         @Override
163         public int shouldProcessCall(String[] numbers) {
164             Integer result = executeMethodAsyncForResultNoException(() ->
165                     MmTelFeature.this.shouldProcessCall(numbers), "shouldProcessCall");
166             if (result != null) {
167                 return result.intValue();
168             } else {
169                 return PROCESS_CALL_CSFB;
170             }
171         }
172 
173         @Override
174         public IImsUt getUtInterface() throws RemoteException {
175             AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
176             IImsUt result = executeMethodAsyncForResult(() -> {
177                 try {
178                     return MmTelFeature.this.getUtInterface();
179                 } catch (RemoteException e) {
180                     exceptionRef.set(e);
181                     return null;
182                 }
183             }, "getUtInterface");
184 
185             if (exceptionRef.get() != null) {
186                 throw exceptionRef.get();
187             }
188 
189             return result;
190         }
191 
192         @Override
193         public IImsEcbm getEcbmInterface() throws RemoteException {
194             AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
195             IImsEcbm result = executeMethodAsyncForResult(() -> {
196                 try {
197                     return MmTelFeature.this.getEcbmInterface();
198                 } catch (RemoteException e) {
199                     exceptionRef.set(e);
200                     return null;
201                 }
202             }, "getEcbmInterface");
203 
204             if (exceptionRef.get() != null) {
205                 throw exceptionRef.get();
206             }
207 
208             return result;
209         }
210 
211         @Override
212         public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
213             executeMethodAsync(() -> MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage),
214                     "setUiTtyMode");
215         }
216 
217         @Override
218         public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
219             AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
220             IImsMultiEndpoint result = executeMethodAsyncForResult(() -> {
221                 try {
222                     return MmTelFeature.this.getMultiEndpointInterface();
223                 } catch (RemoteException e) {
224                     exceptionRef.set(e);
225                     return null;
226                 }
227             }, "getMultiEndpointInterface");
228 
229             if (exceptionRef.get() != null) {
230                 throw exceptionRef.get();
231             }
232 
233             return result;
234         }
235 
236         @Override
237         public int queryCapabilityStatus() {
238             Integer result = executeMethodAsyncForResultNoException(() -> MmTelFeature.this
239                     .queryCapabilityStatus().mCapabilities, "queryCapabilityStatus");
240 
241             if (result != null) {
242                 return result.intValue();
243             } else {
244                 return 0;
245             }
246         }
247 
248         @Override
249         public void addCapabilityCallback(IImsCapabilityCallback c) {
250             executeMethodAsyncNoException(() -> MmTelFeature.this
251                     .addCapabilityCallback(c), "addCapabilityCallback");
252         }
253 
254         @Override
255         public void removeCapabilityCallback(IImsCapabilityCallback c) {
256             executeMethodAsyncNoException(() -> MmTelFeature.this
257                     .removeCapabilityCallback(c), "removeCapabilityCallback");
258         }
259 
260         @Override
261         public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
262                 IImsCapabilityCallback c) {
263             executeMethodAsyncNoException(() -> MmTelFeature.this
264                             .requestChangeEnabledCapabilities(request, c),
265                     "changeCapabilitiesConfiguration");
266         }
267 
268         @Override
269         public void queryCapabilityConfiguration(int capability, int radioTech,
270                 IImsCapabilityCallback c) {
271             executeMethodAsyncNoException(() -> queryCapabilityConfigurationInternal(
272                     capability, radioTech, c), "queryCapabilityConfiguration");
273         }
274 
275         @Override
276         public void setMediaQualityThreshold(@MediaQualityStatus.MediaSessionType int sessionType,
277                 MediaThreshold mediaThreshold) {
278             if (mediaThreshold != null) {
279                 executeMethodAsyncNoException(() -> setMediaThreshold(sessionType, mediaThreshold),
280                         "setMediaQualityThreshold");
281             } else {
282                 executeMethodAsyncNoException(() -> clearMediaThreshold(sessionType),
283                         "clearMediaQualityThreshold");
284             }
285         }
286 
287         @Override
288         public MediaQualityStatus queryMediaQualityStatus(
289                 @MediaQualityStatus.MediaSessionType int sessionType)
290                 throws RemoteException {
291             return executeMethodAsyncForResult(() -> MmTelFeature.this.queryMediaQualityStatus(
292                     sessionType), "queryMediaQualityStatus");
293         }
294 
295         @Override
296         public void setSmsListener(IImsSmsListener l) {
297             executeMethodAsyncNoException(() -> MmTelFeature.this.setSmsListener(l),
298                     "setSmsListener", getImsSmsImpl().getExecutor());
299         }
300 
301         @Override
302         public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
303                 byte[] pdu) {
304             executeMethodAsyncNoException(() -> MmTelFeature.this
305                     .sendSms(token, messageRef, format, smsc, retry, pdu), "sendSms",
306                     getImsSmsImpl().getExecutor());
307         }
308 
309         @Override
310         public void onMemoryAvailable(int token) {
311             executeMethodAsyncNoException(() -> MmTelFeature.this
312                     .onMemoryAvailable(token), "onMemoryAvailable", getImsSmsImpl().getExecutor());
313         }
314 
315         @Override
316         public void acknowledgeSms(int token, int messageRef, int result) {
317             executeMethodAsyncNoException(() -> MmTelFeature.this
318                     .acknowledgeSms(token, messageRef, result), "acknowledgeSms",
319                     getImsSmsImpl().getExecutor());
320         }
321 
322         @Override
323         public void acknowledgeSmsWithPdu(int token, int messageRef, int result, byte[] pdu) {
324             executeMethodAsyncNoException(() -> MmTelFeature.this
325                     .acknowledgeSms(token, messageRef, result, pdu), "acknowledgeSms",
326                     getImsSmsImpl().getExecutor());
327         }
328 
329         @Override
330         public void acknowledgeSmsReport(int token, int messageRef, int result) {
331             executeMethodAsyncNoException(() -> MmTelFeature.this
332                     .acknowledgeSmsReport(token, messageRef, result), "acknowledgeSmsReport",
333                     getImsSmsImpl().getExecutor());
334         }
335 
336         @Override
337         public String getSmsFormat() {
338             return executeMethodAsyncForResultNoException(() -> MmTelFeature.this
339                     .getSmsFormat(), "getSmsFormat", getImsSmsImpl().getExecutor());
340         }
341 
342         @Override
343         public void onSmsReady() {
344             executeMethodAsyncNoException(() -> MmTelFeature.this.onSmsReady(),
345                     "onSmsReady", getImsSmsImpl().getExecutor());
346         }
347 
348         @Override
349         public void notifySrvccStarted(final ISrvccStartedCallback cb) {
350             executeMethodAsyncNoException(
351                     () -> MmTelFeature.this.notifySrvccStarted(
352                             (profiles) -> {
353                                 try {
354                                     cb.onSrvccCallNotified(profiles);
355                                 } catch (Exception e) {
356                                     Log.e(LOG_TAG, "onSrvccCallNotified e=" + e);
357                                 }
358                             }),
359                     "notifySrvccStarted");
360         }
361 
362         @Override
363         public void notifySrvccCompleted() {
364             executeMethodAsyncNoException(
365                     () -> MmTelFeature.this.notifySrvccCompleted(), "notifySrvccCompleted");
366         }
367 
368         @Override
369         public void notifySrvccFailed() {
370             executeMethodAsyncNoException(
371                     () -> MmTelFeature.this.notifySrvccFailed(), "notifySrvccFailed");
372         }
373 
374         @Override
375         public void notifySrvccCanceled() {
376             executeMethodAsyncNoException(
377                     () -> MmTelFeature.this.notifySrvccCanceled(), "notifySrvccCanceled");
378         }
379 
380         @Override
381         public void setTerminalBasedCallWaitingStatus(boolean enabled) throws RemoteException {
382             synchronized (mLock) {
383                 try {
384                     MmTelFeature.this.setTerminalBasedCallWaitingStatus(enabled);
385                 } catch (ServiceSpecificException se) {
386                     throw new ServiceSpecificException(se.errorCode, se.getMessage());
387                 } catch (Exception e) {
388                     throw new RemoteException(e.getMessage());
389                 }
390             }
391         }
392 
393         // Call the methods with a clean calling identity on the executor and wait indefinitely for
394         // the future to return.
395         private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
396             try {
397                 CompletableFuture.runAsync(
398                         () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
399             } catch (CancellationException | CompletionException e) {
400                 Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
401                         + e.getMessage());
402                 throw new RemoteException(e.getMessage());
403             }
404         }
405 
406         private void executeMethodAsyncNoException(Runnable r, String errorLogName) {
407             try {
408                 CompletableFuture.runAsync(
409                         () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
410             } catch (CancellationException | CompletionException e) {
411                 Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
412                         + e.getMessage());
413             }
414         }
415 
416         private void executeMethodAsyncNoException(Runnable r, String errorLogName,
417                 Executor executor) {
418             try {
419                 CompletableFuture.runAsync(
420                         () -> TelephonyUtils.runWithCleanCallingIdentity(r), executor).join();
421             } catch (CancellationException | CompletionException e) {
422                 Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
423                         + e.getMessage());
424             }
425         }
426 
427         private <T> T executeMethodAsyncForResult(Supplier<T> r,
428                 String errorLogName) throws RemoteException {
429             CompletableFuture<T> future = CompletableFuture.supplyAsync(
430                     () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
431             try {
432                 return future.get();
433             } catch (ExecutionException | InterruptedException e) {
434                 Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
435                         + e.getMessage());
436                 throw new RemoteException(e.getMessage());
437             }
438         }
439 
440         private <T> T executeMethodAsyncForResultNoException(Supplier<T> r,
441                 String errorLogName) {
442             CompletableFuture<T> future = CompletableFuture.supplyAsync(
443                     () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
444             try {
445                 return future.get();
446             } catch (ExecutionException | InterruptedException e) {
447                 Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
448                         + e.getMessage());
449                 return null;
450             }
451         }
452 
453         private <T> T executeMethodAsyncForResultNoException(Supplier<T> r,
454                 String errorLogName, Executor executor) {
455             CompletableFuture<T> future = CompletableFuture.supplyAsync(
456                     () -> TelephonyUtils.runWithCleanCallingIdentity(r), executor);
457             try {
458                 return future.get();
459             } catch (ExecutionException | InterruptedException e) {
460                 Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
461                         + e.getMessage());
462                 return null;
463             }
464         }
465     };
466 
467     /**
468      * Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask.
469      * The capabilities that are used in MmTelFeature are defined as
470      * {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE},
471      * {@link MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
472      * {@link MmTelCapabilities#CAPABILITY_TYPE_UT},
473      * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}, and
474      * {@link MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER}.
475      *
476      * The capabilities of this MmTelFeature will be set by the framework.
477      */
478     public static class MmTelCapabilities extends Capabilities {
479 
480         /**
481          * Create a new empty {@link MmTelCapabilities} instance.
482          * @see #addCapabilities(int)
483          * @see #removeCapabilities(int)
484          * @hide
485          */
486         @SystemApi
MmTelCapabilities()487         public MmTelCapabilities() {
488             super();
489         }
490 
491         /**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead.
492          * @hide
493          */
494         @Deprecated
495         @SystemApi
MmTelCapabilities(Capabilities c)496         public MmTelCapabilities(Capabilities c) {
497             mCapabilities = c.mCapabilities;
498         }
499 
500         /**
501          * Create a new {link @MmTelCapabilities} instance with the provided capabilities.
502          * @param capabilities The capabilities that are supported for MmTel in the form of a
503          *                     bitfield.
504          * @hide
505          */
506         @SystemApi
MmTelCapabilities(@mTelCapability int capabilities)507         public MmTelCapabilities(@MmTelCapability int capabilities) {
508             super(capabilities);
509         }
510 
511         /** @hide */
512         @IntDef(flag = true,
513                 value = {
514                         CAPABILITY_TYPE_VOICE,
515                         CAPABILITY_TYPE_VIDEO,
516                         CAPABILITY_TYPE_UT,
517                         CAPABILITY_TYPE_SMS,
518                         CAPABILITY_TYPE_CALL_COMPOSER,
519                         CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY
520                 })
521         @Retention(RetentionPolicy.SOURCE)
522         public @interface MmTelCapability {}
523 
524         /**
525          * Undefined capability type for initialization
526          * This is used to check the upper range of MmTel capability
527          * @hide
528          */
529         public static final int CAPABILITY_TYPE_NONE = 0;
530 
531         /**
532          * This MmTelFeature supports Voice calling (IR.92)
533          */
534         public static final int CAPABILITY_TYPE_VOICE = 1 << 0;
535 
536         /**
537          * This MmTelFeature supports Video (IR.94)
538          */
539         public static final int CAPABILITY_TYPE_VIDEO = 1 << 1;
540 
541         /**
542          * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92)
543          */
544         public static final int CAPABILITY_TYPE_UT = 1 << 2;
545 
546         /**
547          * This MmTelFeature supports SMS (IR.92)
548          */
549         public static final int CAPABILITY_TYPE_SMS = 1 << 3;
550 
551         /**
552          * This MmTelFeature supports Call Composer (section 2.4 of RC.20). This is the superset
553          * Call Composer, meaning that all subset types of Call Composers must be enabled when this
554          * capability is enabled
555          */
556         public static final int CAPABILITY_TYPE_CALL_COMPOSER = 1 << 4;
557 
558 
559         /**
560          * This MmTelFeature supports Business-only Call Composer. This is a subset of
561          * {@code CAPABILITY_TYPE_CALL_COMPOSER} that only supports business related
562          * information for calling (e.g. information to signal if the call is a business call) in
563          * the SIP header.  When enabling {@code CAPABILITY_TYPE_CALL_COMPOSER}, the
564          * {@code CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY} capability must also be enabled.
565          */
566         @FlaggedApi(Flags.FLAG_BUSINESS_CALL_COMPOSER)
567         public static final int CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY = 1 << 5;
568 
569         /**
570          * This is used to check the upper range of MmTel capability
571          * @hide
572          */
573         public static final int CAPABILITY_TYPE_MAX =
574                 CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY + 1;
575 
576         /**
577          * @hide
578          */
579         @Override
580         @SystemApi
addCapabilities(@mTelCapability int capabilities)581         public final void addCapabilities(@MmTelCapability int capabilities) {
582             super.addCapabilities(capabilities);
583         }
584 
585         /**
586          * @hide
587          */
588         @Override
589         @SystemApi
removeCapabilities(@mTelCapability int capability)590         public final void removeCapabilities(@MmTelCapability int capability) {
591             super.removeCapabilities(capability);
592         }
593 
594         /**
595          * @param capabilities a bitmask of one or more capabilities.
596          *
597          * @return true if all queried capabilities are true, otherwise false.
598          */
599         @Override
isCapable(@mTelCapability int capabilities)600         public final boolean isCapable(@MmTelCapability int capabilities) {
601             return super.isCapable(capabilities);
602         }
603 
604         /**
605          * @hide
606          */
607         @NonNull
608         @Override
toString()609         public String toString() {
610             StringBuilder builder = new StringBuilder("MmTel Capabilities - [");
611             builder.append("Voice: ");
612             builder.append(isCapable(CAPABILITY_TYPE_VOICE));
613             builder.append(" Video: ");
614             builder.append(isCapable(CAPABILITY_TYPE_VIDEO));
615             builder.append(" UT: ");
616             builder.append(isCapable(CAPABILITY_TYPE_UT));
617             builder.append(" SMS: ");
618             builder.append(isCapable(CAPABILITY_TYPE_SMS));
619             builder.append(" CALL_COMPOSER: ");
620             builder.append(isCapable(CAPABILITY_TYPE_CALL_COMPOSER));
621             builder.append(" BUSINESS_COMPOSER_ONLY: ");
622             builder.append(isCapable(CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY));
623             builder.append("]");
624             return builder.toString();
625         }
626     }
627 
628     /**
629      * Listener that the framework implements for communication from the MmTelFeature.
630      * @hide
631      */
632     public static class Listener extends IImsMmTelListener.Stub {
633 
634         /**
635          * Called when the IMS provider receives an incoming call.
636          * @param c The {@link ImsCallSession} associated with the new call.
637          * @param callId The call ID of the session of the new incoming call.
638          * @param extras A bundle containing extra parameters related to the call. See
639          * {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above.
640          * @return the listener to listen to the session events. An {@link ImsCallSession} can only
641          *         hold one listener at a time. see {@link ImsCallSessionListener}.
642          *         If this method returns {@code null}, then the call could not be placed.
643          * @hide
644          */
645         @Override
646         @Nullable
onIncomingCall(IImsCallSession c, String callId, Bundle extras)647         public IImsCallSessionListener onIncomingCall(IImsCallSession c,
648                 String callId, Bundle extras) {
649             return null;
650         }
651 
652         /**
653          * Called when the IMS provider implicitly rejects an incoming call during setup.
654          * @param callProfile An {@link ImsCallProfile} with the call details.
655          * @param reason The {@link ImsReasonInfo} reason for call rejection.
656          * @hide
657          */
658         @Override
onRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason)659         public void onRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason) {
660 
661         }
662 
663         /**
664          * Updates the Listener when the voice message count for IMS has changed.
665          * @param count an integer representing the new message count.
666          * @hide
667          */
668         @Override
onVoiceMessageCountUpdate(int count)669         public void onVoiceMessageCountUpdate(int count) {
670 
671         }
672 
673         /**
674          * Called to set the audio handler for this connection.
675          * @param imsAudioHandler an {@link ImsAudioHandler} used to handle the audio
676          *        for this IMS call.
677          * @hide
678          */
679         @Override
onAudioModeIsVoipChanged(int imsAudioHandler)680         public void onAudioModeIsVoipChanged(int imsAudioHandler) {
681 
682         }
683 
684         /**
685          * Called when the IMS triggers EPS fallback procedure.
686          *
687          * @param reason specifies the reason that causes EPS fallback.
688          * @hide
689          */
690         @Override
onTriggerEpsFallback(@psFallbackReason int reason)691         public void onTriggerEpsFallback(@EpsFallbackReason int reason) {
692 
693         }
694 
695         /**
696          * Called when the IMS notifies the upcoming traffic type to the radio.
697          *
698          * @param token A nonce to identify the request
699          * @param trafficType The {@link ImsTrafficType} type for IMS traffic.
700          * @param accessNetworkType The {@link AccessNetworkConstants#RadioAccessNetworkType}
701          *        type of the radio access network.
702          * @param trafficDirection Indicates whether traffic is originated by mobile originated or
703          *        mobile terminated use case eg. MO/MT call/SMS etc.
704          * @param callback The callback to receive the result.
705          * @hide
706          */
707         @Override
onStartImsTrafficSession(int token, @ImsTrafficType int trafficType, @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType, @ImsTrafficDirection int trafficDirection, IImsTrafficSessionCallback callback)708         public void onStartImsTrafficSession(int token,
709                 @ImsTrafficType int trafficType,
710                 @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
711                 @ImsTrafficDirection int trafficDirection,
712                 IImsTrafficSessionCallback callback) {
713 
714         }
715 
716         /**
717          * Called when the IMS notifies the traffic type has been stopped.
718          *
719          * @param token A nonce registered with {@link #onStartImsTrafficSession}.
720          * @param accessNetworkType The {@link AccessNetworkConstants#RadioAccessNetworkType}
721          *        type of the radio access network.
722          * @hide
723          */
724         @Override
onModifyImsTrafficSession(int token, @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType)725         public void onModifyImsTrafficSession(int token,
726                 @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType) {
727 
728         }
729 
730         /**
731          * Called when the IMS notifies the traffic type has been stopped.
732          *
733          * @param token A nonce registered with {@link #onStartImsTrafficSession}.
734          * @hide
735          */
736         @Override
onStopImsTrafficSession(int token)737         public void onStopImsTrafficSession(int token) {
738 
739         }
740 
741         /**
742          * Called when the IMS provider notifies {@link MediaQualityStatus}.
743          *
744          * @param status media quality status currently measured.
745          * @hide
746          */
747         @Override
onMediaQualityStatusChanged(MediaQualityStatus status)748         public void onMediaQualityStatusChanged(MediaQualityStatus status) {
749 
750         }
751     }
752 
753     /**
754      * A wrapper class of {@link ImsTrafficSessionCallback}.
755      * @hide
756      */
757     public static class ImsTrafficSessionCallbackWrapper {
758         public static final int INVALID_TOKEN = -1;
759 
760         private static final int MAX_TOKEN = 0x10000;
761 
762         private static final AtomicInteger sTokenGenerator = new AtomicInteger();
763 
764         /** Callback to receive the response */
765         private IImsTrafficSessionCallbackStub mCallback = null;
766         /** Identifier to distinguish each IMS traffic request */
767         private int mToken = INVALID_TOKEN;
768 
769         private ImsTrafficSessionCallback mImsTrafficSessionCallback;
770 
ImsTrafficSessionCallbackWrapper(ImsTrafficSessionCallback callback)771         private ImsTrafficSessionCallbackWrapper(ImsTrafficSessionCallback callback) {
772             mImsTrafficSessionCallback = callback;
773         }
774 
775         /**
776          * Updates the callback.
777          *
778          * The mToken should be kept since it is used to identify the traffic notified to the modem
779          * until calling {@link MmtelFEature#stopImsTrafficSession}.
780          */
update(@onNull @allbackExecutor Executor executor)781         final void update(@NonNull @CallbackExecutor Executor executor) {
782             if (executor == null) {
783                 throw new IllegalArgumentException(
784                         "ImsTrafficSessionCallback Executor must be non-null");
785             }
786 
787             if (mCallback == null) {
788                 // initial start of Ims traffic.
789                 mCallback = new IImsTrafficSessionCallbackStub(
790                         mImsTrafficSessionCallback, executor);
791                 mToken = generateToken();
792             } else {
793                 // handover between cellular and Wi-Fi
794                 mCallback.update(executor);
795             }
796         }
797 
798         /**
799          * Using a static class and weak reference here to avoid memory leak caused by the
800          * {@link IImsTrafficSessionCallback.Stub} callback retaining references to the outside
801          * {@link ImsTrafficSessionCallback}.
802          */
803         private static class IImsTrafficSessionCallbackStub
804                 extends IImsTrafficSessionCallback.Stub {
805             private WeakReference<ImsTrafficSessionCallback> mImsTrafficSessionCallbackWeakRef;
806             private Executor mExecutor;
807 
IImsTrafficSessionCallbackStub(ImsTrafficSessionCallback imsTrafficCallback, Executor executor)808             IImsTrafficSessionCallbackStub(ImsTrafficSessionCallback imsTrafficCallback,
809                     Executor executor) {
810                 mImsTrafficSessionCallbackWeakRef =
811                         new WeakReference<ImsTrafficSessionCallback>(imsTrafficCallback);
812                 mExecutor = executor;
813             }
814 
update(Executor executor)815             void update(Executor executor) {
816                 mExecutor = executor;
817             }
818 
819             @Override
onReady()820             public void onReady() {
821                 ImsTrafficSessionCallback callback = mImsTrafficSessionCallbackWeakRef.get();
822                 if (callback == null) return;
823 
824                 Binder.withCleanCallingIdentity(
825                         () -> mExecutor.execute(() -> callback.onReady()));
826             }
827 
828             @Override
onError(ConnectionFailureInfo info)829             public void onError(ConnectionFailureInfo info) {
830                 ImsTrafficSessionCallback callback = mImsTrafficSessionCallbackWeakRef.get();
831                 if (callback == null) return;
832 
833                 Binder.withCleanCallingIdentity(
834                         () -> mExecutor.execute(() -> callback.onError(info)));
835             }
836         }
837 
838         /**
839          * Returns the callback binder.
840          */
getCallbackBinder()841         final IImsTrafficSessionCallbackStub getCallbackBinder() {
842             return mCallback;
843         }
844 
845         /**
846          * Returns the token.
847          */
getToken()848         final int getToken() {
849             return mToken;
850         }
851 
852         /**
853          * Resets the members.
854          * It's called by {@link MmTelFeature#stopImsTrafficSession}.
855          */
reset()856         final void reset() {
857             mCallback = null;
858             mToken = INVALID_TOKEN;
859         }
860 
generateToken()861         private static int generateToken() {
862             int token = sTokenGenerator.incrementAndGet();
863             if (token == MAX_TOKEN) sTokenGenerator.set(0);
864             return token;
865         }
866     }
867 
868     /**
869      * To be returned by {@link #shouldProcessCall(String[])} when the ImsService should process the
870      * outgoing call as IMS.
871      * @hide
872      */
873     @SystemApi
874     public static final int PROCESS_CALL_IMS = 0;
875     /**
876      * To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should
877      * not process the outgoing call as IMS and should instead use circuit switch.
878      * @hide
879      */
880     @SystemApi
881     public static final int PROCESS_CALL_CSFB = 1;
882 
883     /** @hide */
884     @IntDef(flag = true,
885             value = {
886                     PROCESS_CALL_IMS,
887                     PROCESS_CALL_CSFB
888             })
889     @Retention(RetentionPolicy.SOURCE)
890     public @interface ProcessCallResult {}
891 
892     /**
893      * If the flag is present and true, it indicates that the incoming call is for USSD.
894      * <p>
895      * This is an optional boolean flag.
896      * @hide
897      */
898     @SystemApi
899     public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
900 
901     /**
902      * If this flag is present and true, this call is marked as an unknown dialing call instead
903      * of an incoming call. An example of such a call is a call that is originated by sending
904      * commands (like AT commands) directly to the modem without Android involvement or dialing
905      * calls appearing over IMS when the modem does a silent redial from circuit-switched to IMS in
906      * certain situations.
907      * <p>
908      * This is an optional boolean flag.
909      * @hide
910      */
911     @SystemApi
912     public static final String EXTRA_IS_UNKNOWN_CALL =
913             "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
914 
915     /** @hide */
916     @IntDef(
917         prefix = "AUDIO_HANDLER_",
918         value = {
919             AUDIO_HANDLER_ANDROID,
920             AUDIO_HANDLER_BASEBAND
921         })
922     @Retention(RetentionPolicy.SOURCE)
923     public @interface ImsAudioHandler {}
924 
925     /**
926     * Audio Handler - Android
927     * @hide
928     */
929     @SystemApi
930     public static final int AUDIO_HANDLER_ANDROID = 0;
931 
932     /**
933     * Audio Handler - Baseband
934     * @hide
935     */
936     @SystemApi
937     public static final int AUDIO_HANDLER_BASEBAND = 1;
938 
939     /** @hide */
940     @Retention(RetentionPolicy.SOURCE)
941     @IntDef(
942         prefix = "EPS_FALLBACK_REASON_",
943         value = {
944             EPS_FALLBACK_REASON_INVALID,
945             EPS_FALLBACK_REASON_NO_NETWORK_TRIGGER,
946             EPS_FALLBACK_REASON_NO_NETWORK_RESPONSE,
947         })
948     public @interface EpsFallbackReason {}
949 
950     /**
951      * Default value. Internal use only.
952      * This value should not be used to trigger EPS fallback.
953      * @hide
954      */
955     public static final int EPS_FALLBACK_REASON_INVALID = -1;
956 
957     /**
958      * If the network only supports the EPS fallback in 5G NR SA for voice calling and the EPS
959      * Fallback procedure by the network during the call setup is not triggered, UE initiated
960      * fallback will be triggered with this reason. The modem shall locally release the 5G NR
961      * SA RRC connection and acquire the LTE network and perform a tracking area update
962      * procedure. After the EPS fallback procedure is completed, the call setup for voice will
963      * be established if there is no problem.
964      *
965      * @hide
966      */
967     public static final int EPS_FALLBACK_REASON_NO_NETWORK_TRIGGER = 1;
968 
969     /**
970      * If the UE doesn't receive any response for SIP INVITE within a certain timeout in 5G NR
971      * SA for MO voice calling, the device determines that voice call is not available in 5G and
972      * terminates all active SIP dialogs and SIP requests and enters IMS non-registered state.
973      * In that case, UE initiated fallback will be triggered with this reason. The modem shall
974      * reset modem's data buffer of IMS PDU to prevent the ghost call. After the EPS fallback
975      * procedure is completed, VoLTE call could be tried if there is no problem.
976      *
977      * @hide
978      */
979     public static final int EPS_FALLBACK_REASON_NO_NETWORK_RESPONSE = 2;
980 
981     /** @hide */
982     @Retention(RetentionPolicy.SOURCE)
983     @IntDef(
984         prefix = "IMS_TRAFFIC_TYPE_",
985         value = {
986             IMS_TRAFFIC_TYPE_NONE,
987             IMS_TRAFFIC_TYPE_EMERGENCY,
988             IMS_TRAFFIC_TYPE_EMERGENCY_SMS,
989             IMS_TRAFFIC_TYPE_VOICE,
990             IMS_TRAFFIC_TYPE_VIDEO,
991             IMS_TRAFFIC_TYPE_SMS,
992             IMS_TRAFFIC_TYPE_REGISTRATION,
993             IMS_TRAFFIC_TYPE_UT_XCAP
994         })
995     public @interface ImsTrafficType {}
996 
997     /**
998      * Default value for initialization. Internal use only.
999      * @hide
1000      */
1001     public static final int IMS_TRAFFIC_TYPE_NONE = -1;
1002     /**
1003      * Emergency call
1004      * @hide
1005      */
1006     public static final int IMS_TRAFFIC_TYPE_EMERGENCY = 0;
1007     /**
1008      * Emergency SMS
1009      * @hide
1010      */
1011     public static final int IMS_TRAFFIC_TYPE_EMERGENCY_SMS = 1;
1012     /**
1013      * Voice call
1014      * @hide
1015      */
1016     public static final int IMS_TRAFFIC_TYPE_VOICE = 2;
1017     /**
1018      * Video call
1019      * @hide
1020      */
1021     public static final int IMS_TRAFFIC_TYPE_VIDEO = 3;
1022     /**
1023      * SMS over IMS
1024      * @hide
1025      */
1026     public static final int IMS_TRAFFIC_TYPE_SMS = 4;
1027     /**
1028      * IMS registration and subscription for reg event package (signaling)
1029      * @hide
1030      */
1031     public static final int IMS_TRAFFIC_TYPE_REGISTRATION = 5;
1032     /**
1033      * Ut/XCAP (XML Configuration Access Protocol)
1034      * @hide
1035      */
1036     public static final int IMS_TRAFFIC_TYPE_UT_XCAP = 6;
1037 
1038     /** @hide */
1039     @Retention(RetentionPolicy.SOURCE)
1040     @IntDef(
1041             prefix = { "IMS_TRAFFIC_DIRECTION_" },
1042             value = {IMS_TRAFFIC_DIRECTION_INCOMING, IMS_TRAFFIC_DIRECTION_OUTGOING})
1043     public @interface ImsTrafficDirection {}
1044 
1045     /**
1046      * Indicates that the traffic is an incoming traffic.
1047      * @hide
1048      */
1049     public static final int IMS_TRAFFIC_DIRECTION_INCOMING = 0;
1050     /**
1051      * Indicates that the traffic is an outgoing traffic.
1052      * @hide
1053      */
1054     public static final int IMS_TRAFFIC_DIRECTION_OUTGOING = 1;
1055 
1056     private IImsMmTelListener mListener;
1057 
1058     /**
1059      * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and
1060      *     notifies the framework.
1061      */
setListener(IImsMmTelListener listener)1062     private void setListener(IImsMmTelListener listener) {
1063         synchronized (mLock) {
1064             mListener = listener;
1065             if (mListener != null) {
1066                 onFeatureReady();
1067             }
1068         }
1069     }
1070 
1071     /**
1072      * @return the listener associated with this MmTelFeature. May be null if it has not been set
1073      * by the framework yet.
1074      */
getListener()1075     private IImsMmTelListener getListener() {
1076         synchronized (mLock) {
1077             return mListener;
1078         }
1079     }
1080 
1081     /**
1082      * The current capability status that this MmTelFeature has defined is available. This
1083      * configuration will be used by the platform to figure out which capabilities are CURRENTLY
1084      * available to be used.
1085      *
1086      * Should be a subset of the capabilities that are enabled by the framework in
1087      * {@link #changeEnabledCapabilities}.
1088      * @return A copy of the current MmTelFeature capability status.
1089      * @hide
1090      */
1091     @Override
1092     @SystemApi
queryCapabilityStatus()1093     public @NonNull final MmTelCapabilities queryCapabilityStatus() {
1094         return new MmTelCapabilities(super.queryCapabilityStatus());
1095     }
1096 
1097     /**
1098      * Notify the framework that the status of the Capabilities has changed. Even though the
1099      * MmTelFeature capability may be enabled by the framework, the status may be disabled due to
1100      * the feature being unavailable from the network.
1101      * @param c The current capability status of the MmTelFeature. If a capability is disabled, then
1102      * the status of that capability is disabled. This can happen if the network does not currently
1103      * support the capability that is enabled. A capability that is disabled by the framework (via
1104      * {@link #changeEnabledCapabilities}) should also show the status as disabled.
1105      * @hide
1106      */
1107     @SystemApi
notifyCapabilitiesStatusChanged(@onNull MmTelCapabilities c)1108     public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) {
1109         if (c == null) {
1110             throw new IllegalArgumentException("MmTelCapabilities must be non-null!");
1111         }
1112         super.notifyCapabilitiesStatusChanged(c);
1113     }
1114 
1115     /**
1116      * Notify the framework that the measured media quality has crossed a threshold set by {@link
1117      * MmTelFeature#setMediaThreshold}
1118      *
1119      * @param status current media quality status measured.
1120      * @hide
1121      */
1122     @SystemApi
notifyMediaQualityStatusChanged( @onNull MediaQualityStatus status)1123     public final void notifyMediaQualityStatusChanged(
1124             @NonNull MediaQualityStatus status) {
1125         if (status == null) {
1126             throw new IllegalArgumentException(
1127                     "MediaQualityStatus must be non-null!");
1128         }
1129         Log.i(LOG_TAG, "notifyMediaQualityStatusChanged " + status);
1130         IImsMmTelListener listener = getListener();
1131         if (listener == null) {
1132             throw new IllegalStateException("Session is not available.");
1133         }
1134         try {
1135             listener.onMediaQualityStatusChanged(status);
1136         } catch (RemoteException e) {
1137             throw new RuntimeException(e);
1138         }
1139     }
1140 
1141     /**
1142      * Notify the framework of an incoming call.
1143      * @param c The {@link ImsCallSessionImplBase} of the new incoming call.
1144      * @param extras A bundle containing extra parameters related to the call. See
1145      * {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above.
1146      * @hide
1147      *
1148      * @deprecated use {@link #notifyIncomingCall(ImsCallSessionImplBase, String, Bundle)} instead
1149      */
1150     @Deprecated
1151     @SystemApi
notifyIncomingCall(@onNull ImsCallSessionImplBase c, @NonNull Bundle extras)1152     public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c,
1153             @NonNull Bundle extras) {
1154         if (c == null || extras == null) {
1155             throw new IllegalArgumentException("ImsCallSessionImplBase and Bundle can not be "
1156                     + "null.");
1157         }
1158         IImsMmTelListener listener = getListener();
1159         if (listener == null) {
1160             throw new IllegalStateException("Session is not available.");
1161         }
1162         try {
1163             c.setDefaultExecutor(MmTelFeature.this.mExecutor);
1164             listener.onIncomingCall(c.getServiceImpl(), null, extras);
1165         } catch (RemoteException e) {
1166             throw new RuntimeException(e);
1167         }
1168     }
1169 
1170     /**
1171      * Notify the framework of an incoming call.
1172      * @param c The {@link ImsCallSessionImplBase} of the new incoming call.
1173      * @param callId The call ID of the session of the new incoming call.
1174      * @param extras A bundle containing extra parameters related to the call. See
1175      * {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above.
1176      * @return The listener used by the framework to listen to call session events created
1177      *         from the ImsService.
1178      *         If this method returns {@code null}, then the call could not be placed.
1179      * @hide
1180      */
1181     @SystemApi
1182     @Nullable
notifyIncomingCall( @onNull ImsCallSessionImplBase c, @NonNull String callId, @NonNull Bundle extras)1183     public final ImsCallSessionListener notifyIncomingCall(
1184             @NonNull ImsCallSessionImplBase c, @NonNull String callId, @NonNull Bundle extras) {
1185         if (c == null || callId == null || extras == null) {
1186             throw new IllegalArgumentException("ImsCallSessionImplBase, callId, and Bundle can "
1187                     + "not be null.");
1188         }
1189         IImsMmTelListener listener = getListener();
1190         if (listener == null) {
1191             throw new IllegalStateException("Session is not available.");
1192         }
1193         try {
1194             c.setDefaultExecutor(MmTelFeature.this.mExecutor);
1195             IImsCallSessionListener isl =
1196                     listener.onIncomingCall(c.getServiceImpl(), callId, extras);
1197             if (isl != null) {
1198                 ImsCallSessionListener iCSL = new ImsCallSessionListener(isl);
1199                 iCSL.setDefaultExecutor(MmTelFeature.this.mExecutor);
1200                 return iCSL;
1201             } else {
1202                 return null;
1203             }
1204         } catch (RemoteException e) {
1205             throw new RuntimeException(e);
1206         }
1207     }
1208 
1209     /**
1210      * Notify the framework that a call has been implicitly rejected by this MmTelFeature
1211      * during call setup.
1212      * @param callProfile The {@link ImsCallProfile} IMS call profile with details.
1213      *        This can be null if no call information is available for the rejected call.
1214      * @param reason The {@link ImsReasonInfo} call rejection reason.
1215      * @hide
1216      */
1217     @SystemApi
notifyRejectedCall(@onNull ImsCallProfile callProfile, @NonNull ImsReasonInfo reason)1218     public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile,
1219             @NonNull ImsReasonInfo reason) {
1220         if (callProfile == null || reason == null) {
1221             throw new IllegalArgumentException("ImsCallProfile and ImsReasonInfo must not be "
1222                     + "null.");
1223         }
1224         IImsMmTelListener listener = getListener();
1225         if (listener == null) {
1226             throw new IllegalStateException("Session is not available.");
1227         }
1228         try {
1229             listener.onRejectedCall(callProfile, reason);
1230         } catch (RemoteException e) {
1231             throw new RuntimeException(e);
1232         }
1233     }
1234 
1235     /**
1236      *
1237      * @hide
1238      */
notifyIncomingCallSession(IImsCallSession c, Bundle extras)1239     public final void notifyIncomingCallSession(IImsCallSession c, Bundle extras) {
1240         IImsMmTelListener listener = getListener();
1241         if (listener == null) {
1242             throw new IllegalStateException("Session is not available.");
1243         }
1244         try {
1245             listener.onIncomingCall(c, null, extras);
1246         } catch (RemoteException e) {
1247             throw new RuntimeException(e);
1248         }
1249     }
1250 
1251     /**
1252      * Notify the framework of a change in the Voice Message count.
1253      * @link count the new Voice Message count.
1254      * @hide
1255      */
1256     @SystemApi
notifyVoiceMessageCountUpdate(int count)1257     public final void notifyVoiceMessageCountUpdate(int count) {
1258         IImsMmTelListener listener = getListener();
1259         if (listener == null) {
1260             throw new IllegalStateException("Session is not available.");
1261         }
1262         try {
1263             listener.onVoiceMessageCountUpdate(count);
1264         } catch (RemoteException e) {
1265             throw new RuntimeException(e);
1266         }
1267     }
1268 
1269     /**
1270      * Sets the audio handler for this connection. The vendor IMS stack will invoke this API
1271      * to inform Telephony/Telecom layers about which audio handlers i.e. either Android or Modem
1272      * shall be used for handling the IMS call audio.
1273      *
1274      * @param imsAudioHandler {@link MmTelFeature#ImsAudioHandler} used to handle the audio
1275      *        for this IMS call.
1276      * @hide
1277      */
1278     @SystemApi
setCallAudioHandler(@msAudioHandler int imsAudioHandler)1279     public final void setCallAudioHandler(@ImsAudioHandler int imsAudioHandler) {
1280         IImsMmTelListener listener = getListener();
1281         if (listener == null) {
1282             throw new IllegalStateException("Session is not available.");
1283         }
1284         try {
1285             listener.onAudioModeIsVoipChanged(imsAudioHandler);
1286         } catch (RemoteException e) {
1287             throw new RuntimeException(e);
1288         }
1289     }
1290 
1291     /**
1292      * Triggers the EPS fallback procedure.
1293      *
1294      * @param reason specifies the reason that causes EPS fallback.
1295      * @hide
1296      */
triggerEpsFallback(@psFallbackReason int reason)1297     public final void triggerEpsFallback(@EpsFallbackReason int reason) {
1298         IImsMmTelListener listener = getListener();
1299         if (listener == null) {
1300             throw new IllegalStateException("Session is not available.");
1301         }
1302         try {
1303             listener.onTriggerEpsFallback(reason);
1304         } catch (RemoteException e) {
1305             throw new RuntimeException(e);
1306         }
1307     }
1308 
1309     /**
1310      * Starts a new IMS traffic session with the framework.
1311      *
1312      * This API notifies the NAS and RRC layers of the modem that IMS traffic of type
1313      * {@link ImsTrafficType} is starting for the IMS session represented by a
1314      * {@link ImsTrafficSessionCallback}. The {@link ImsTrafficSessionCallback}
1315      * will notify the caller when IMS traffic is ready to start via the
1316      * {@link ImsTrafficSessionCallback#onReady()} callback. If there was an error starting
1317      * IMS traffic for the specified traffic type, {@link ImsTrafficSessionCallback#onError()} will
1318      * be called, which will also notify the caller of the reason of the failure.
1319      *
1320      * If there is a handover that changes the {@link AccessNetworkConstants#RadioAccessNetworkType}
1321      * of this IMS traffic session, then {@link #modifyImsTrafficSession} should be called. This is
1322      * used, for example, when a WiFi <-> cellular handover occurs.
1323      *
1324      * Once the IMS traffic session is finished, {@link #stopImsTrafficSession} must be called.
1325      *
1326      * Note: This API will be used to prioritize RF resources in case of DSDS. The service priority
1327      * is EMERGENCY > EMERGENCY SMS > VOICE > VIDEO > SMS > REGISTRATION > Ut/XCAP. RF
1328      * shall be prioritized to the subscription which handles the higher priority service.
1329      * When both subscriptions are handling the same type of service, then RF shall be
1330      * prioritized to the voice preferred sub.
1331      *
1332      * @param trafficType The {@link ImsTrafficType} type for IMS traffic.
1333      * @param accessNetworkType The {@link AccessNetworkConstants#RadioAccessNetworkType} type of
1334      *        the radio access network.
1335      * @param trafficDirection Indicates whether traffic is originated by mobile originated or
1336      *        mobile terminated use case eg. MO/MT call/SMS etc.
1337      * @param executor The Executor that will be used to call the {@link ImsTrafficSessionCallback}.
1338      * @param callback The session representing the IMS Session associated with a specific
1339      *        trafficType. This callback instance should only be used for the specified traffic type
1340      *        until {@link #stopImsTrafficSession} is called.
1341      *
1342      * @see modifyImsTrafficSession
1343      * @see stopImsTrafficSession
1344      *
1345      * @hide
1346      */
startImsTrafficSession(@msTrafficType int trafficType, @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType, @ImsTrafficDirection int trafficDirection, @NonNull Executor executor, @NonNull ImsTrafficSessionCallback callback)1347     public final void startImsTrafficSession(@ImsTrafficType int trafficType,
1348             @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
1349             @ImsTrafficDirection int trafficDirection,
1350             @NonNull Executor executor, @NonNull ImsTrafficSessionCallback callback) {
1351         IImsMmTelListener listener = getListener();
1352         if (listener == null) {
1353             throw new IllegalStateException("Session is not available.");
1354         }
1355         // TODO: retrieve from the callback list
1356         ImsTrafficSessionCallbackWrapper callbackWrapper = mTrafficCallbacks.get(callback);
1357         if (callbackWrapper == null) {
1358             callbackWrapper = new ImsTrafficSessionCallbackWrapper(callback);
1359             mTrafficCallbacks.put(callback, callbackWrapper);
1360         }
1361         try {
1362             callbackWrapper.update(executor);
1363             listener.onStartImsTrafficSession(callbackWrapper.getToken(),
1364                     trafficType, accessNetworkType, trafficDirection,
1365                     callbackWrapper.getCallbackBinder());
1366         } catch (RemoteException e) {
1367             throw new RuntimeException(e);
1368         }
1369     }
1370 
1371     /**
1372      * Modifies an existing IMS traffic session represented by the associated
1373      * {@link ImsTrafficSessionCallback}.
1374      *
1375      * The {@link ImsTrafficSessionCallback} will notify the caller when IMS traffic is ready to
1376      * start after modification using the {@link ImsTrafficSessionCallback#onReady()} callback.
1377      * If there was an error modifying IMS traffic for the new radio access network type type,
1378      * {@link ImsTrafficSessionCallback#onError()} will be called, which will also notify the
1379      * caller of the reason of the failure.
1380      *
1381      * @param accessNetworkType The {@link AccessNetworkConstants#RadioAccessNetworkType} type of
1382      *        the radio access network.
1383      * @param callback The callback registered with {@link #startImsTrafficSession}.
1384      *
1385      * @see startImsTrafficSession
1386      * @see stopImsTrafficSession
1387      *
1388      * @hide
1389      */
modifyImsTrafficSession( @ccessNetworkConstants.RadioAccessNetworkType int accessNetworkType, @NonNull ImsTrafficSessionCallback callback)1390     public final void modifyImsTrafficSession(
1391             @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
1392             @NonNull ImsTrafficSessionCallback callback) {
1393         IImsMmTelListener listener = getListener();
1394         if (listener == null) {
1395             throw new IllegalStateException("Session is not available.");
1396         }
1397         ImsTrafficSessionCallbackWrapper callbackWrapper = mTrafficCallbacks.get(callback);
1398         if (callbackWrapper == null) {
1399             // should not reach here.
1400             throw new IllegalStateException("Unknown ImsTrafficSessionCallback instance.");
1401         }
1402         try {
1403             listener.onModifyImsTrafficSession(callbackWrapper.getToken(), accessNetworkType);
1404         } catch (RemoteException e) {
1405             throw new RuntimeException(e);
1406         }
1407     }
1408 
1409     /**
1410      * Notifies the framework that the IMS traffic session represented by the associated
1411      * {@link ImsTrafficSessionCallback} has ended.
1412      *
1413      * @param callback The callback registered with {@link #startImsTrafficSession}.
1414      *
1415      * @see startImsTrafficSession
1416      * @see modifyImsTrafficSession
1417      *
1418      * @hide
1419      */
stopImsTrafficSession(@onNull ImsTrafficSessionCallback callback)1420     public final void stopImsTrafficSession(@NonNull ImsTrafficSessionCallback callback) {
1421         IImsMmTelListener listener = getListener();
1422         if (listener == null) {
1423             throw new IllegalStateException("Session is not available.");
1424         }
1425         ImsTrafficSessionCallbackWrapper callbackWrapper = mTrafficCallbacks.get(callback);
1426         if (callbackWrapper == null) {
1427             // should not reach here.
1428             throw new IllegalStateException("Unknown ImsTrafficSessionCallback instance.");
1429         }
1430         try {
1431             listener.onStopImsTrafficSession(callbackWrapper.getToken());
1432             callbackWrapper.reset();
1433             mTrafficCallbacks.remove(callback);
1434         } catch (RemoteException e) {
1435             throw new RuntimeException(e);
1436         }
1437     }
1438 
1439     /**
1440      * Provides the MmTelFeature with the ability to return the framework Capability Configuration
1441      * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
1442      * includes a capability A to enable or disable, this method should return the correct enabled
1443      * status for capability A.
1444      * @param capability The capability that we are querying the configuration for.
1445      * @return true if the capability is enabled, false otherwise.
1446      * @hide
1447      */
1448     @Override
1449     @SystemApi
queryCapabilityConfiguration(@mTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)1450     public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
1451             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
1452         // Base implementation - Override to provide functionality
1453         return false;
1454     }
1455 
1456     /**
1457      * The MmTelFeature should override this method to handle the enabling/disabling of
1458      * MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes
1459      * the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities
1460      * could not be set to their new values,
1461      * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called
1462      * individually for each capability whose processing resulted in an error.
1463      *
1464      * Enabling/Disabling a capability here indicates that the capability should be registered or
1465      * deregistered (depending on the capability change) and become available or unavailable to
1466      * the framework.
1467      * @hide
1468      */
1469     @Override
1470     @SystemApi
changeEnabledCapabilities(@onNull CapabilityChangeRequest request, @NonNull CapabilityCallbackProxy c)1471     public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
1472             @NonNull CapabilityCallbackProxy c) {
1473         // Base implementation, no-op
1474     }
1475 
1476     /**
1477      * Called by the framework to pass {@link MediaThreshold}. The MmTelFeature should override this
1478      * method to get Media quality threshold. This will pass the consolidated threshold values from
1479      * Telephony framework. IMS provider needs to monitor media quality of active call and notify
1480      * media quality {@link #notifyMediaQualityStatusChanged(MediaQualityStatus)} when the measured
1481      * media quality crosses at least one of {@link MediaThreshold} set by this.
1482      *
1483      * @param mediaSessionType media session type for this Threshold info.
1484      * @param mediaThreshold media threshold information
1485      * @hide
1486      */
1487     @SystemApi
setMediaThreshold( @ediaQualityStatus.MediaSessionType int mediaSessionType, @NonNull MediaThreshold mediaThreshold)1488     public void setMediaThreshold(
1489             @MediaQualityStatus.MediaSessionType int mediaSessionType,
1490             @NonNull MediaThreshold mediaThreshold) {
1491         // Base Implementation - Should be overridden.
1492         Log.d(LOG_TAG, "setMediaThreshold is not supported." + mediaThreshold);
1493     }
1494 
1495     /**
1496      * The MmTelFeature should override this method to clear Media quality thresholds that were
1497      * registered and stop media quality status updates.
1498      *
1499      * @param mediaSessionType media session type
1500      * @hide
1501      */
1502     @SystemApi
clearMediaThreshold(@ediaQualityStatus.MediaSessionType int mediaSessionType)1503     public void clearMediaThreshold(@MediaQualityStatus.MediaSessionType int mediaSessionType) {
1504         // Base Implementation - Should be overridden.
1505         Log.d(LOG_TAG, "clearMediaThreshold is not supported." + mediaSessionType);
1506     }
1507 
1508     /**
1509      * IMS provider should override this method to return currently measured media quality status.
1510      *
1511      * <p/>
1512      * If media quality status is not yet measured after call is active, it needs to notify media
1513      * quality status {@link #notifyMediaQualityStatusChanged(MediaQualityStatus)} when the first
1514      * measurement is done.
1515      *
1516      * @param mediaSessionType media session type
1517      * @return Current media quality status. It could be null if media quality status is not
1518      *         measured yet or {@link MediaThreshold} was not set corresponding to the media session
1519      *         type.
1520      *
1521      * @hide
1522      */
1523     @SystemApi
1524     @Nullable
queryMediaQualityStatus( @ediaQualityStatus.MediaSessionType int mediaSessionType)1525     public MediaQualityStatus queryMediaQualityStatus(
1526             @MediaQualityStatus.MediaSessionType int mediaSessionType) {
1527         // Base Implementation - Should be overridden.
1528         Log.d(LOG_TAG, "queryMediaQualityStatus is not supported." + mediaSessionType);
1529         return null;
1530     }
1531 
1532     /**
1533      * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
1534      *
1535      * @param callSessionType a service type that is specified in {@link ImsCallProfile}
1536      *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
1537      *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
1538      *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
1539      * @param callType a call type that is specified in {@link ImsCallProfile}
1540      *        {@link ImsCallProfile#CALL_TYPE_VOICE}
1541      *        {@link ImsCallProfile#CALL_TYPE_VT}
1542      *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
1543      *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
1544      *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
1545      *        {@link ImsCallProfile#CALL_TYPE_VS}
1546      *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
1547      *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
1548      * @return a {@link ImsCallProfile} object
1549      * @hide
1550      */
1551     @SystemApi
createCallProfile(int callSessionType, int callType)1552     public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) {
1553         // Base Implementation - Should be overridden
1554         return null;
1555     }
1556 
1557     /**
1558      * Called by the framework to report a change to the RTP header extension types which should be
1559      * offered during SDP negotiation (see RFC8285 for more information).
1560      * <p>
1561      * The {@link ImsService} should report the RTP header extensions which were accepted during
1562      * SDP negotiation using {@link ImsCallProfile#setAcceptedRtpHeaderExtensionTypes(Set)}.
1563      *
1564      * @param extensionTypes The RTP header extensions the framework wishes to offer during
1565      *                       outgoing and incoming call setup.  An empty list indicates that there
1566      *                       are no framework defined RTP header extension types to offer.
1567      * @hide
1568      */
1569     @SystemApi
changeOfferedRtpHeaderExtensionTypes( @onNull Set<RtpHeaderExtensionType> extensionTypes)1570     public void changeOfferedRtpHeaderExtensionTypes(
1571             @NonNull Set<RtpHeaderExtensionType> extensionTypes) {
1572         // Base implementation - should be overridden if RTP header extension handling is supported.
1573     }
1574 
1575     /**
1576      * @hide
1577      */
createCallSessionInterface(ImsCallProfile profile)1578     public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
1579             throws RemoteException {
1580         ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile);
1581         if (s != null) {
1582             s.setDefaultExecutor(mExecutor);
1583             return s.getServiceImpl();
1584         } else {
1585             return null;
1586         }
1587     }
1588 
1589     /**
1590      * Creates an {@link ImsCallSession} with the specified call profile.
1591      * Use other methods, if applicable, instead of interacting with
1592      * {@link ImsCallSession} directly.
1593      *
1594      * @param profile a call profile to make the call
1595      * @hide
1596      */
1597     @SystemApi
createCallSession(@onNull ImsCallProfile profile)1598     public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) {
1599         // Base Implementation - Should be overridden
1600         return null;
1601     }
1602 
1603     /**
1604      * Called by the framework to determine if the outgoing call, designated by the outgoing
1605      * {@link String}s, should be processed as an IMS call or CSFB call. If this method's
1606      * functionality is not overridden, the platform will process every call as IMS as long as the
1607      * MmTelFeature reports that the {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE} capability is
1608      * available.
1609      * @param numbers An array of {@link String}s that will be used for placing the call. There can
1610      *         be multiple {@link String}s listed in the case when we want to place an outgoing
1611      *         call as a conference.
1612      * @return a {@link ProcessCallResult} to the framework, which will be used to determine if the
1613      *        call will be placed over IMS or via CSFB.
1614      * @hide
1615      */
1616     @SystemApi
shouldProcessCall(@onNull String[] numbers)1617     public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) {
1618         return PROCESS_CALL_IMS;
1619     }
1620 
1621     /**
1622      *
1623      * @hide
1624      */
getUtInterface()1625     protected IImsUt getUtInterface() throws RemoteException {
1626         ImsUtImplBase utImpl = getUt();
1627         if (utImpl != null) {
1628             utImpl.setDefaultExecutor(mExecutor);
1629             return utImpl.getInterface();
1630         } else {
1631             return null;
1632         }
1633     }
1634 
1635     /**
1636      * @hide
1637      */
getEcbmInterface()1638     protected IImsEcbm getEcbmInterface() throws RemoteException {
1639         ImsEcbmImplBase ecbmImpl = getEcbm();
1640         if (ecbmImpl != null) {
1641             ecbmImpl.setDefaultExecutor(mExecutor);
1642             return ecbmImpl.getImsEcbm();
1643         } else {
1644             return null;
1645         }
1646     }
1647 
1648     /**
1649      * @hide
1650      */
getMultiEndpointInterface()1651     public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
1652         ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint();
1653         if (multiendpointImpl != null) {
1654             multiendpointImpl.setDefaultExecutor(mExecutor);
1655             return multiendpointImpl.getIImsMultiEndpoint();
1656         } else {
1657             return null;
1658         }
1659     }
1660 
1661     /**
1662      * @hide
1663      */
getImsSmsImpl()1664     public @NonNull ImsSmsImplBase getImsSmsImpl() {
1665         synchronized (mLock) {
1666             if (mSmsImpl == null) {
1667                 mSmsImpl = getSmsImplementation();
1668                 mSmsImpl.setDefaultExecutor(mExecutor);
1669             }
1670             return mSmsImpl;
1671         }
1672     }
1673 
1674     /**
1675      * @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service
1676      * configuration.
1677      * @hide
1678      */
1679     @SystemApi
getUt()1680     public @NonNull ImsUtImplBase getUt() {
1681         // Base Implementation - Should be overridden
1682         return new ImsUtImplBase();
1683     }
1684 
1685     /**
1686      * @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE
1687      * calls that support it.
1688      * @hide
1689      */
1690     @SystemApi
getEcbm()1691     public @NonNull ImsEcbmImplBase getEcbm() {
1692         // Base Implementation - Should be overridden
1693         return new ImsEcbmImplBase();
1694     }
1695 
1696     /**
1697      * @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event
1698      * package processing for multi-endpoint.
1699      * @hide
1700      */
1701     @SystemApi
getMultiEndpoint()1702     public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() {
1703         // Base Implementation - Should be overridden
1704         return new ImsMultiEndpointImplBase();
1705     }
1706 
1707     /**
1708      * Sets the current UI TTY mode for the MmTelFeature.
1709      * @param mode An integer containing the new UI TTY Mode, can consist of
1710      *         {@link TelecomManager#TTY_MODE_OFF},
1711      *         {@link TelecomManager#TTY_MODE_FULL},
1712      *         {@link TelecomManager#TTY_MODE_HCO},
1713      *         {@link TelecomManager#TTY_MODE_VCO}
1714      * @param onCompleteMessage If non-null, this MmTelFeature should call this {@link Message} when
1715      *         the operation is complete by using the associated {@link android.os.Messenger} in
1716      *         {@link Message#replyTo}. For example:
1717      * {@code
1718      *     // Set UI TTY Mode and other operations...
1719      *     try {
1720      *         // Notify framework that the mode was changed.
1721      *         Messenger uiMessenger = onCompleteMessage.replyTo;
1722      *         uiMessenger.send(onCompleteMessage);
1723      *     } catch (RemoteException e) {
1724      *         // Remote side is dead
1725      *     }
1726      * }
1727      * @hide
1728      */
1729     @SystemApi
setUiTtyMode(int mode, @Nullable Message onCompleteMessage)1730     public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) {
1731         // Base Implementation - Should be overridden
1732     }
1733 
1734     /**
1735      * Notifies the MmTelFeature of the enablement status of terminal based call waiting
1736      *
1737      * If the terminal based call waiting is provisioned,
1738      * IMS controls the enablement of terminal based call waiting which is defined
1739      * in 3GPP TS 24.615.
1740      *
1741      * @param enabled user setting controlling whether or not call waiting is enabled.
1742      *
1743      * @hide
1744      */
1745     @SystemApi
setTerminalBasedCallWaitingStatus(boolean enabled)1746     public void setTerminalBasedCallWaitingStatus(boolean enabled) {
1747         // Base Implementation - Should be overridden by IMS service
1748         throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
1749                 "Not implemented on device.");
1750     }
1751 
1752     /**
1753      * Notifies the MmTelFeature that the network has initiated an SRVCC (Single radio voice
1754      * call continuity) for all IMS calls. When the network initiates an SRVCC, calls from
1755      * the LTE domain are handed over to the legacy circuit switched domain. The modem requires
1756      * knowledge of ongoing calls in the IMS domain in order to complete the SRVCC operation.
1757      * <p>
1758      * @param consumer The callback used to notify the framework of the list of IMS calls and their
1759      * state at the time of the SRVCC.
1760      *
1761      * @hide
1762      */
1763     @SystemApi
notifySrvccStarted(@onNull Consumer<List<SrvccCall>> consumer)1764     public void notifySrvccStarted(@NonNull Consumer<List<SrvccCall>> consumer) {
1765         // Base Implementation - Should be overridden by IMS service
1766     }
1767 
1768     /**
1769      * Notifies the MmTelFeature that the SRVCC is completed and the calls have been moved
1770      * over to the circuit-switched domain.
1771      * {@link android.telephony.CarrierConfigManager.ImsVoice#KEY_SRVCC_TYPE_INT_ARRAY}
1772      * specifies the calls can be moved. Other calls will be disconnected.
1773      * <p>
1774      * The MmTelFeature may now release all resources related to the IMS calls.
1775      *
1776      * @hide
1777      */
1778     @SystemApi
notifySrvccCompleted()1779     public void notifySrvccCompleted() {
1780         // Base Implementation - Should be overridden by IMS service
1781     }
1782 
1783     /**
1784      * Notifies the MmTelFeature that the SRVCC has failed.
1785      *
1786      * The handover can fail by encountering a failure at the radio level
1787      * or temporary MSC server internal errors in handover procedure.
1788      * Refer to 3GPP TS 23.216 section 8 Handover Failure.
1789      * <p>
1790      * IMS service will recover and continue calls over IMS.
1791      * Per TS 24.237 12.2.4.2, UE shall send SIP UPDATE request containing the reason-text
1792      * set to "failure to transition to CS domain".
1793      *
1794      * @hide
1795      */
1796     @SystemApi
notifySrvccFailed()1797     public void notifySrvccFailed() {
1798         // Base Implementation - Should be overridden by IMS service
1799     }
1800 
1801     /**
1802      * Notifies the MmTelFeature that the SRVCC has been canceled.
1803      *
1804      * Since the state of network can be changed, the network can decide to terminate
1805      * the handover procedure before its completion and to return to its state before the handover
1806      * procedure was triggered.
1807      * Refer to 3GPP TS 23.216 section 8.1.3 Handover Cancellation.
1808      *
1809      * <p>
1810      * IMS service will recover and continue calls over IMS.
1811      * Per TS 24.237 12.2.4.2, UE shall send SIP UPDATE request containing the reason-text
1812      * set to "handover canceled".
1813      *
1814      * @hide
1815      */
1816     @SystemApi
notifySrvccCanceled()1817     public void notifySrvccCanceled() {
1818         // Base Implementation - Should be overridden by IMS service
1819     }
1820 
setSmsListener(IImsSmsListener listener)1821     private void setSmsListener(IImsSmsListener listener) {
1822         getImsSmsImpl().registerSmsListener(listener);
1823     }
1824 
sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, byte[] pdu)1825     private void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
1826             byte[] pdu) {
1827         getImsSmsImpl().sendSms(token, messageRef, format, smsc, isRetry, pdu);
1828     }
1829 
onMemoryAvailable(int token)1830     private void onMemoryAvailable(int token) {
1831         getImsSmsImpl().onMemoryAvailable(token);
1832     }
1833 
acknowledgeSms(int token, int messageRef, @ImsSmsImplBase.DeliverStatusResult int result)1834     private void acknowledgeSms(int token, int messageRef,
1835             @ImsSmsImplBase.DeliverStatusResult int result) {
1836         getImsSmsImpl().acknowledgeSms(token, messageRef, result);
1837     }
1838 
acknowledgeSms(int token, int messageRef, @ImsSmsImplBase.DeliverStatusResult int result, byte[] pdu)1839     private void acknowledgeSms(int token, int messageRef,
1840             @ImsSmsImplBase.DeliverStatusResult int result, byte[] pdu) {
1841         getImsSmsImpl().acknowledgeSms(token, messageRef, result, pdu);
1842     }
1843 
acknowledgeSmsReport(int token, int messageRef, @ImsSmsImplBase.StatusReportResult int result)1844     private void acknowledgeSmsReport(int token, int messageRef,
1845             @ImsSmsImplBase.StatusReportResult int result) {
1846         getImsSmsImpl().acknowledgeSmsReport(token, messageRef, result);
1847     }
1848 
onSmsReady()1849     private void onSmsReady() {
1850         getImsSmsImpl().onReady();
1851     }
1852 
1853     /**
1854      * Must be overridden by IMS Provider to be able to support SMS over IMS. Otherwise a default
1855      * non-functional implementation is returned.
1856      *
1857      * @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS
1858      * Provider.
1859      * @hide
1860      */
1861     @SystemApi
getSmsImplementation()1862     public @NonNull ImsSmsImplBase getSmsImplementation() {
1863         return new ImsSmsImplBase();
1864     }
1865 
getSmsFormat()1866     private String getSmsFormat() {
1867         return getImsSmsImpl().getSmsFormat();
1868     }
1869 
1870     /**
1871      * {@inheritDoc}
1872      * @hide
1873      */
1874     @Override
1875     @SystemApi
onFeatureRemoved()1876     public void onFeatureRemoved() {
1877         // Base Implementation - Should be overridden
1878     }
1879 
1880     /**
1881      * {@inheritDoc}
1882      * @hide
1883      */
1884     @Override
1885     @SystemApi
onFeatureReady()1886     public void onFeatureReady() {
1887         // Base Implementation - Should be overridden
1888     }
1889 
1890     /**
1891      * @hide
1892      */
1893     @Override
getBinder()1894     public final IImsMmTelFeature getBinder() {
1895         return mImsMMTelBinder;
1896     }
1897 
1898     /**
1899      * Set default Executor from ImsService.
1900      * @param executor The default executor for the framework to use when executing the methods
1901      * overridden by the implementation of MmTelFeature.
1902      * @hide
1903      */
setDefaultExecutor(@onNull Executor executor)1904     public final void setDefaultExecutor(@NonNull Executor executor) {
1905         if (mExecutor == null) {
1906             mExecutor = executor;
1907         }
1908     }
1909 }
1910