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