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