• 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 android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.os.Bundle;
24 import android.os.Message;
25 import android.os.RemoteException;
26 import android.telecom.TelecomManager;
27 import android.telephony.ims.ImsCallProfile;
28 import android.telephony.ims.ImsCallSession;
29 import android.telephony.ims.ImsReasonInfo;
30 import android.telephony.ims.ImsService;
31 import android.telephony.ims.RtpHeaderExtensionType;
32 import android.telephony.ims.aidl.IImsCapabilityCallback;
33 import android.telephony.ims.aidl.IImsMmTelFeature;
34 import android.telephony.ims.aidl.IImsMmTelListener;
35 import android.telephony.ims.aidl.IImsSmsListener;
36 import android.telephony.ims.stub.ImsCallSessionImplBase;
37 import android.telephony.ims.stub.ImsEcbmImplBase;
38 import android.telephony.ims.stub.ImsMultiEndpointImplBase;
39 import android.telephony.ims.stub.ImsRegistrationImplBase;
40 import android.telephony.ims.stub.ImsSmsImplBase;
41 import android.telephony.ims.stub.ImsUtImplBase;
42 import android.util.ArraySet;
43 import android.util.Log;
44 
45 import com.android.ims.internal.IImsCallSession;
46 import com.android.ims.internal.IImsEcbm;
47 import com.android.ims.internal.IImsMultiEndpoint;
48 import com.android.ims.internal.IImsUt;
49 import com.android.internal.telephony.util.TelephonyUtils;
50 
51 import java.lang.annotation.Retention;
52 import java.lang.annotation.RetentionPolicy;
53 import java.util.List;
54 import java.util.Set;
55 import java.util.concurrent.CancellationException;
56 import java.util.concurrent.CompletableFuture;
57 import java.util.concurrent.CompletionException;
58 import java.util.concurrent.ExecutionException;
59 import java.util.concurrent.Executor;
60 import java.util.concurrent.atomic.AtomicReference;
61 import java.util.function.Supplier;
62 
63 /**
64  * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
65  *
66  * Any class wishing to use MmTelFeature should extend this class and implement all methods that the
67  * service supports.
68  */
69 public class MmTelFeature extends ImsFeature {
70 
71     private static final String LOG_TAG = "MmTelFeature";
72     private Executor mExecutor;
73 
74     /**
75      * @hide
76      */
77     @SystemApi
MmTelFeature()78     public MmTelFeature() {
79     }
80 
81     /**
82      * Create a new MmTelFeature using the Executor specified for methods being called by the
83      * framework.
84      * @param executor The executor for the framework to use when executing the methods overridden
85      * by the implementation of MmTelFeature.
86      * @hide
87      */
88     @SystemApi
MmTelFeature(@onNull Executor executor)89     public MmTelFeature(@NonNull Executor executor) {
90         super();
91         mExecutor = executor;
92     }
93 
94     private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
95 
96         @Override
97         public void setListener(IImsMmTelListener l) {
98             executeMethodAsyncNoException(() -> MmTelFeature.this.setListener(l), "setListener");
99         }
100 
101         @Override
102         public int getFeatureState() throws RemoteException {
103             return executeMethodAsyncForResult(() -> MmTelFeature.this.getFeatureState(),
104                     "getFeatureState");
105         }
106 
107         @Override
108         public ImsCallProfile createCallProfile(int callSessionType, int callType)
109                 throws RemoteException {
110             return executeMethodAsyncForResult(() -> MmTelFeature.this.createCallProfile(
111                     callSessionType, callType), "createCallProfile");
112         }
113 
114         @Override
115         public void changeOfferedRtpHeaderExtensionTypes(List<RtpHeaderExtensionType> types)
116                 throws RemoteException {
117             executeMethodAsync(() -> MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes(
118                     new ArraySet<>(types)), "changeOfferedRtpHeaderExtensionTypes");
119         }
120 
121         @Override
122         public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
123             AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
124             IImsCallSession result = executeMethodAsyncForResult(() -> {
125                 try {
126                     return createCallSessionInterface(profile);
127                 } catch (RemoteException e) {
128                     exceptionRef.set(e);
129                     return null;
130                 }
131             }, "createCallSession");
132 
133             if (exceptionRef.get() != null) {
134                 throw exceptionRef.get();
135             }
136 
137             return result;
138         }
139 
140         @Override
141         public int shouldProcessCall(String[] numbers) {
142             Integer result = executeMethodAsyncForResultNoException(() ->
143                     MmTelFeature.this.shouldProcessCall(numbers), "shouldProcessCall");
144             if (result != null) {
145                 return result.intValue();
146             } else {
147                 return PROCESS_CALL_CSFB;
148             }
149         }
150 
151         @Override
152         public IImsUt getUtInterface() throws RemoteException {
153             AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
154             IImsUt result = executeMethodAsyncForResult(() -> {
155                 try {
156                     return MmTelFeature.this.getUtInterface();
157                 } catch (RemoteException e) {
158                     exceptionRef.set(e);
159                     return null;
160                 }
161             }, "getUtInterface");
162 
163             if (exceptionRef.get() != null) {
164                 throw exceptionRef.get();
165             }
166 
167             return result;
168         }
169 
170         @Override
171         public IImsEcbm getEcbmInterface() throws RemoteException {
172             AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
173             IImsEcbm result = executeMethodAsyncForResult(() -> {
174                 try {
175                     return MmTelFeature.this.getEcbmInterface();
176                 } catch (RemoteException e) {
177                     exceptionRef.set(e);
178                     return null;
179                 }
180             }, "getEcbmInterface");
181 
182             if (exceptionRef.get() != null) {
183                 throw exceptionRef.get();
184             }
185 
186             return result;
187         }
188 
189         @Override
190         public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
191             executeMethodAsync(() -> MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage),
192                     "setUiTtyMode");
193         }
194 
195         @Override
196         public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
197             AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
198             IImsMultiEndpoint result = executeMethodAsyncForResult(() -> {
199                 try {
200                     return MmTelFeature.this.getMultiEndpointInterface();
201                 } catch (RemoteException e) {
202                     exceptionRef.set(e);
203                     return null;
204                 }
205             }, "getMultiEndpointInterface");
206 
207             if (exceptionRef.get() != null) {
208                 throw exceptionRef.get();
209             }
210 
211             return result;
212         }
213 
214         @Override
215         public int queryCapabilityStatus() {
216             Integer result = executeMethodAsyncForResultNoException(() -> MmTelFeature.this
217                     .queryCapabilityStatus().mCapabilities, "queryCapabilityStatus");
218 
219             if (result != null) {
220                 return result.intValue();
221             } else {
222                 return 0;
223             }
224         }
225 
226         @Override
227         public void addCapabilityCallback(IImsCapabilityCallback c) {
228             executeMethodAsyncNoException(() -> MmTelFeature.this
229                     .addCapabilityCallback(c), "addCapabilityCallback");
230         }
231 
232         @Override
233         public void removeCapabilityCallback(IImsCapabilityCallback c) {
234             executeMethodAsyncNoException(() -> MmTelFeature.this
235                     .removeCapabilityCallback(c), "removeCapabilityCallback");
236         }
237 
238         @Override
239         public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
240                 IImsCapabilityCallback c) {
241             executeMethodAsyncNoException(() -> MmTelFeature.this
242                     .requestChangeEnabledCapabilities(request, c),
243                     "changeCapabilitiesConfiguration");
244         }
245 
246         @Override
247         public void queryCapabilityConfiguration(int capability, int radioTech,
248                 IImsCapabilityCallback c) {
249             executeMethodAsyncNoException(() -> queryCapabilityConfigurationInternal(
250                     capability, radioTech, c), "queryCapabilityConfiguration");
251         }
252 
253         @Override
254         public void setSmsListener(IImsSmsListener l) {
255             executeMethodAsyncNoException(() -> MmTelFeature.this.setSmsListener(l),
256                     "setSmsListener");
257         }
258 
259         @Override
260         public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
261                 byte[] pdu) {
262             executeMethodAsyncNoException(() -> MmTelFeature.this
263                     .sendSms(token, messageRef, format, smsc, retry, pdu), "sendSms");
264         }
265 
266         @Override
267         public void acknowledgeSms(int token, int messageRef, int result) {
268             executeMethodAsyncNoException(() -> MmTelFeature.this
269                     .acknowledgeSms(token, messageRef, result), "acknowledgeSms");
270         }
271 
272         @Override
273         public void acknowledgeSmsReport(int token, int messageRef, int result) {
274             executeMethodAsyncNoException(() -> MmTelFeature.this
275                     .acknowledgeSmsReport(token, messageRef, result), "acknowledgeSmsReport");
276         }
277 
278         @Override
279         public String getSmsFormat() {
280             return executeMethodAsyncForResultNoException(() -> MmTelFeature.this
281                     .getSmsFormat(), "getSmsFormat");
282         }
283 
284         @Override
285         public void onSmsReady() {
286             executeMethodAsyncNoException(() -> MmTelFeature.this.onSmsReady(),
287                     "onSmsReady");
288         }
289 
290         // Call the methods with a clean calling identity on the executor and wait indefinitely for
291         // the future to return.
292         private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
293             try {
294                 CompletableFuture.runAsync(
295                         () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
296             } catch (CancellationException | CompletionException e) {
297                 Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
298                         + e.getMessage());
299                 throw new RemoteException(e.getMessage());
300             }
301         }
302 
303         private void executeMethodAsyncNoException(Runnable r, String errorLogName) {
304             try {
305                 CompletableFuture.runAsync(
306                         () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
307             } catch (CancellationException | CompletionException e) {
308                 Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
309                         + e.getMessage());
310             }
311         }
312 
313         private <T> T executeMethodAsyncForResult(Supplier<T> r,
314                 String errorLogName) throws RemoteException {
315             CompletableFuture<T> future = CompletableFuture.supplyAsync(
316                     () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
317             try {
318                 return future.get();
319             } catch (ExecutionException | InterruptedException e) {
320                 Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
321                         + e.getMessage());
322                 throw new RemoteException(e.getMessage());
323             }
324         }
325 
326         private <T> T executeMethodAsyncForResultNoException(Supplier<T> r,
327                 String errorLogName) {
328             CompletableFuture<T> future = CompletableFuture.supplyAsync(
329                     () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
330             try {
331                 return future.get();
332             } catch (ExecutionException | InterruptedException e) {
333                 Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
334                         + e.getMessage());
335                 return null;
336             }
337         }
338     };
339 
340     /**
341      * Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask.
342      * The capabilities that are used in MmTelFeature are defined as
343      * {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE},
344      * {@link MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
345      * {@link MmTelCapabilities#CAPABILITY_TYPE_UT},
346      * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}, and
347      * {@link MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER}.
348      *
349      * The capabilities of this MmTelFeature will be set by the framework.
350      */
351     public static class MmTelCapabilities extends Capabilities {
352 
353         /**
354          * Create a new empty {@link MmTelCapabilities} instance.
355          * @see #addCapabilities(int)
356          * @see #removeCapabilities(int)
357          * @hide
358          */
359         @SystemApi
MmTelCapabilities()360         public MmTelCapabilities() {
361             super();
362         }
363 
364         /**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead.
365          * @hide
366          */
367         @Deprecated
368         @SystemApi
MmTelCapabilities(Capabilities c)369         public MmTelCapabilities(Capabilities c) {
370             mCapabilities = c.mCapabilities;
371         }
372 
373         /**
374          * Create a new {link @MmTelCapabilities} instance with the provided capabilities.
375          * @param capabilities The capabilities that are supported for MmTel in the form of a
376          *                     bitfield.
377          * @hide
378          */
379         @SystemApi
MmTelCapabilities(@mTelCapability int capabilities)380         public MmTelCapabilities(@MmTelCapability int capabilities) {
381             super(capabilities);
382         }
383 
384         /** @hide */
385         @IntDef(flag = true,
386                 value = {
387                         CAPABILITY_TYPE_VOICE,
388                         CAPABILITY_TYPE_VIDEO,
389                         CAPABILITY_TYPE_UT,
390                         CAPABILITY_TYPE_SMS,
391                         CAPABILITY_TYPE_CALL_COMPOSER
392                 })
393         @Retention(RetentionPolicy.SOURCE)
394         public @interface MmTelCapability {}
395 
396         /**
397          * Undefined capability type for initialization
398          * This is used to check the upper range of MmTel capability
399          * @hide
400          */
401         public static final int CAPABILITY_TYPE_NONE = 0;
402 
403         /**
404          * This MmTelFeature supports Voice calling (IR.92)
405          */
406         public static final int CAPABILITY_TYPE_VOICE = 1 << 0;
407 
408         /**
409          * This MmTelFeature supports Video (IR.94)
410          */
411         public static final int CAPABILITY_TYPE_VIDEO = 1 << 1;
412 
413         /**
414          * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92)
415          */
416         public static final int CAPABILITY_TYPE_UT = 1 << 2;
417 
418         /**
419          * This MmTelFeature supports SMS (IR.92)
420          */
421         public static final int CAPABILITY_TYPE_SMS = 1 << 3;
422 
423         /**
424          * This MmTelFeature supports Call Composer (section 2.4 of RC.20)
425          */
426         public static final int CAPABILITY_TYPE_CALL_COMPOSER = 1 << 4;
427 
428         /**
429          * This is used to check the upper range of MmTel capability
430          * @hide
431          */
432         public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_CALL_COMPOSER + 1;
433 
434         /**
435          * @hide
436          */
437         @Override
438         @SystemApi
addCapabilities(@mTelCapability int capabilities)439         public final void addCapabilities(@MmTelCapability int capabilities) {
440             super.addCapabilities(capabilities);
441         }
442 
443         /**
444          * @hide
445          */
446         @Override
447         @SystemApi
removeCapabilities(@mTelCapability int capability)448         public final void removeCapabilities(@MmTelCapability int capability) {
449             super.removeCapabilities(capability);
450         }
451 
452         /**
453          * @param capabilities a bitmask of one or more capabilities.
454          *
455          * @return true if all queried capabilities are true, otherwise false.
456          */
457         @Override
isCapable(@mTelCapability int capabilities)458         public final boolean isCapable(@MmTelCapability int capabilities) {
459             return super.isCapable(capabilities);
460         }
461 
462         /**
463          * @hide
464          */
465         @NonNull
466         @Override
toString()467         public String toString() {
468             StringBuilder builder = new StringBuilder("MmTel Capabilities - [");
469             builder.append("Voice: ");
470             builder.append(isCapable(CAPABILITY_TYPE_VOICE));
471             builder.append(" Video: ");
472             builder.append(isCapable(CAPABILITY_TYPE_VIDEO));
473             builder.append(" UT: ");
474             builder.append(isCapable(CAPABILITY_TYPE_UT));
475             builder.append(" SMS: ");
476             builder.append(isCapable(CAPABILITY_TYPE_SMS));
477             builder.append(" CALL_COMPOSER: ");
478             builder.append(isCapable(CAPABILITY_TYPE_CALL_COMPOSER));
479             builder.append("]");
480             return builder.toString();
481         }
482     }
483 
484     /**
485      * Listener that the framework implements for communication from the MmTelFeature.
486      * @hide
487      */
488     public static class Listener extends IImsMmTelListener.Stub {
489 
490         /**
491          * Called when the IMS provider receives an incoming call.
492          * @param c The {@link ImsCallSession} associated with the new call.
493          * @hide
494          */
495         @Override
onIncomingCall(IImsCallSession c, Bundle extras)496         public void onIncomingCall(IImsCallSession c, Bundle extras) {
497 
498         }
499 
500         /**
501          * Called when the IMS provider implicitly rejects an incoming call during setup.
502          * @param callProfile An {@link ImsCallProfile} with the call details.
503          * @param reason The {@link ImsReasonInfo} reason for call rejection.
504          * @hide
505          */
506         @Override
onRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason)507         public void onRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason) {
508 
509         }
510 
511         /**
512          * Updates the Listener when the voice message count for IMS has changed.
513          * @param count an integer representing the new message count.
514          * @hide
515          */
516         @Override
onVoiceMessageCountUpdate(int count)517         public void onVoiceMessageCountUpdate(int count) {
518 
519         }
520     }
521 
522     /**
523      * To be returned by {@link #shouldProcessCall(String[])} when the ImsService should process the
524      * outgoing call as IMS.
525      * @hide
526      */
527     @SystemApi
528     public static final int PROCESS_CALL_IMS = 0;
529     /**
530      * To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should
531      * not process the outgoing call as IMS and should instead use circuit switch.
532      * @hide
533      */
534     @SystemApi
535     public static final int PROCESS_CALL_CSFB = 1;
536 
537     /** @hide */
538     @IntDef(flag = true,
539             value = {
540                     PROCESS_CALL_IMS,
541                     PROCESS_CALL_CSFB
542             })
543     @Retention(RetentionPolicy.SOURCE)
544     public @interface ProcessCallResult {}
545 
546     /**
547      * If the flag is present and true, it indicates that the incoming call is for USSD.
548      * <p>
549      * This is an optional boolean flag.
550      * @hide
551      */
552     @SystemApi
553     public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
554 
555     /**
556      * If this flag is present and true, this call is marked as an unknown dialing call instead
557      * of an incoming call. An example of such a call is a call that is originated by sending
558      * commands (like AT commands) directly to the modem without Android involvement or dialing
559      * calls appearing over IMS when the modem does a silent redial from circuit-switched to IMS in
560      * certain situations.
561      * <p>
562      * This is an optional boolean flag.
563      * @hide
564      */
565     @SystemApi
566     public static final String EXTRA_IS_UNKNOWN_CALL =
567             "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
568 
569     private IImsMmTelListener mListener;
570 
571     /**
572      * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and
573      *     notifies the framework.
574      */
setListener(IImsMmTelListener listener)575     private void setListener(IImsMmTelListener listener) {
576         synchronized (mLock) {
577             mListener = listener;
578             if (mListener != null) {
579                 onFeatureReady();
580             }
581         }
582     }
583 
584     /**
585      * @return the listener associated with this MmTelFeature. May be null if it has not been set
586      * by the framework yet.
587      */
getListener()588     private IImsMmTelListener getListener() {
589         synchronized (mLock) {
590             return mListener;
591         }
592     }
593 
594     /**
595      * The current capability status that this MmTelFeature has defined is available. This
596      * configuration will be used by the platform to figure out which capabilities are CURRENTLY
597      * available to be used.
598      *
599      * Should be a subset of the capabilities that are enabled by the framework in
600      * {@link #changeEnabledCapabilities}.
601      * @return A copy of the current MmTelFeature capability status.
602      * @hide
603      */
604     @Override
605     @SystemApi
queryCapabilityStatus()606     public @NonNull final MmTelCapabilities queryCapabilityStatus() {
607         return new MmTelCapabilities(super.queryCapabilityStatus());
608     }
609 
610     /**
611      * Notify the framework that the status of the Capabilities has changed. Even though the
612      * MmTelFeature capability may be enabled by the framework, the status may be disabled due to
613      * the feature being unavailable from the network.
614      * @param c The current capability status of the MmTelFeature. If a capability is disabled, then
615      * the status of that capability is disabled. This can happen if the network does not currently
616      * support the capability that is enabled. A capability that is disabled by the framework (via
617      * {@link #changeEnabledCapabilities}) should also show the status as disabled.
618      * @hide
619      */
620     @SystemApi
notifyCapabilitiesStatusChanged(@onNull MmTelCapabilities c)621     public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) {
622         if (c == null) {
623             throw new IllegalArgumentException("MmTelCapabilities must be non-null!");
624         }
625         super.notifyCapabilitiesStatusChanged(c);
626     }
627 
628     /**
629      * Notify the framework of an incoming call.
630      * @param c The {@link ImsCallSessionImplBase} of the new incoming call.
631      * @param extras A bundle containing extra parameters related to the call. See
632      * {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above.
633       * @hide
634      */
635     @SystemApi
notifyIncomingCall(@onNull ImsCallSessionImplBase c, @NonNull Bundle extras)636     public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c,
637             @NonNull Bundle extras) {
638         if (c == null || extras == null) {
639             throw new IllegalArgumentException("ImsCallSessionImplBase and Bundle can not be "
640                     + "null.");
641         }
642         IImsMmTelListener listener = getListener();
643         if (listener == null) {
644             throw new IllegalStateException("Session is not available.");
645         }
646         try {
647             listener.onIncomingCall(c.getServiceImpl(), extras);
648         } catch (RemoteException e) {
649             throw new RuntimeException(e);
650         }
651     }
652 
653     /**
654      * Notify the framework that a call has been implicitly rejected by this MmTelFeature
655      * during call setup.
656      * @param callProfile The {@link ImsCallProfile} IMS call profile with details.
657      *        This can be null if no call information is available for the rejected call.
658      * @param reason The {@link ImsReasonInfo} call rejection reason.
659      * @hide
660      */
661     @SystemApi
notifyRejectedCall(@onNull ImsCallProfile callProfile, @NonNull ImsReasonInfo reason)662     public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile,
663             @NonNull ImsReasonInfo reason) {
664         if (callProfile == null || reason == null) {
665             throw new IllegalArgumentException("ImsCallProfile and ImsReasonInfo must not be "
666                     + "null.");
667         }
668         IImsMmTelListener listener = getListener();
669         if (listener == null) {
670             throw new IllegalStateException("Session is not available.");
671         }
672         try {
673             listener.onRejectedCall(callProfile, reason);
674         } catch (RemoteException e) {
675             throw new RuntimeException(e);
676         }
677     }
678 
679     /**
680      *
681      * @hide
682      */
notifyIncomingCallSession(IImsCallSession c, Bundle extras)683     public final void notifyIncomingCallSession(IImsCallSession c, Bundle extras) {
684         IImsMmTelListener listener = getListener();
685         if (listener == null) {
686             throw new IllegalStateException("Session is not available.");
687         }
688         try {
689             listener.onIncomingCall(c, extras);
690         } catch (RemoteException e) {
691             throw new RuntimeException(e);
692         }
693     }
694 
695     /**
696      * Notify the framework of a change in the Voice Message count.
697      * @link count the new Voice Message count.
698      * @hide
699      */
700     @SystemApi
notifyVoiceMessageCountUpdate(int count)701     public final void notifyVoiceMessageCountUpdate(int count) {
702         IImsMmTelListener listener = getListener();
703         if (listener == null) {
704             throw new IllegalStateException("Session is not available.");
705         }
706         try {
707             listener.onVoiceMessageCountUpdate(count);
708         } catch (RemoteException e) {
709             throw new RuntimeException(e);
710         }
711     }
712 
713     /**
714      * Provides the MmTelFeature with the ability to return the framework Capability Configuration
715      * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
716      * includes a capability A to enable or disable, this method should return the correct enabled
717      * status for capability A.
718      * @param capability The capability that we are querying the configuration for.
719      * @return true if the capability is enabled, false otherwise.
720      * @hide
721      */
722     @Override
723     @SystemApi
queryCapabilityConfiguration(@mTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)724     public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
725             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
726         // Base implementation - Override to provide functionality
727         return false;
728     }
729 
730     /**
731      * The MmTelFeature should override this method to handle the enabling/disabling of
732      * MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes
733      * the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities
734      * could not be set to their new values,
735      * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called
736      * individually for each capability whose processing resulted in an error.
737      *
738      * Enabling/Disabling a capability here indicates that the capability should be registered or
739      * deregistered (depending on the capability change) and become available or unavailable to
740      * the framework.
741      * @hide
742      */
743     @Override
744     @SystemApi
changeEnabledCapabilities(@onNull CapabilityChangeRequest request, @NonNull CapabilityCallbackProxy c)745     public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
746             @NonNull CapabilityCallbackProxy c) {
747         // Base implementation, no-op
748     }
749 
750     /**
751      * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
752      *
753      * @param callSessionType a service type that is specified in {@link ImsCallProfile}
754      *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
755      *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
756      *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
757      * @param callType a call type that is specified in {@link ImsCallProfile}
758      *        {@link ImsCallProfile#CALL_TYPE_VOICE}
759      *        {@link ImsCallProfile#CALL_TYPE_VT}
760      *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
761      *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
762      *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
763      *        {@link ImsCallProfile#CALL_TYPE_VS}
764      *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
765      *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
766      * @return a {@link ImsCallProfile} object
767      * @hide
768      */
769     @SystemApi
createCallProfile(int callSessionType, int callType)770     public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) {
771         // Base Implementation - Should be overridden
772         return null;
773     }
774 
775     /**
776      * Called by the framework to report a change to the RTP header extension types which should be
777      * offered during SDP negotiation (see RFC8285 for more information).
778      * <p>
779      * The {@link ImsService} should report the RTP header extensions which were accepted during
780      * SDP negotiation using {@link ImsCallProfile#setAcceptedRtpHeaderExtensionTypes(Set)}.
781      *
782      * @param extensionTypes The RTP header extensions the framework wishes to offer during
783      *                       outgoing and incoming call setup.  An empty list indicates that there
784      *                       are no framework defined RTP header extension types to offer.
785      * @hide
786      */
787     @SystemApi
changeOfferedRtpHeaderExtensionTypes( @onNull Set<RtpHeaderExtensionType> extensionTypes)788     public void changeOfferedRtpHeaderExtensionTypes(
789             @NonNull Set<RtpHeaderExtensionType> extensionTypes) {
790         // Base implementation - should be overridden if RTP header extension handling is supported.
791     }
792 
793     /**
794      * @hide
795      */
createCallSessionInterface(ImsCallProfile profile)796     public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
797             throws RemoteException {
798         ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile);
799         if (s != null) {
800             s.setDefaultExecutor(mExecutor);
801             return s.getServiceImpl();
802         } else {
803             return null;
804         }
805     }
806 
807     /**
808      * Creates an {@link ImsCallSession} with the specified call profile.
809      * Use other methods, if applicable, instead of interacting with
810      * {@link ImsCallSession} directly.
811      *
812      * @param profile a call profile to make the call
813      * @hide
814      */
815     @SystemApi
createCallSession(@onNull ImsCallProfile profile)816     public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) {
817         // Base Implementation - Should be overridden
818         return null;
819     }
820 
821     /**
822      * Called by the framework to determine if the outgoing call, designated by the outgoing
823      * {@link String}s, should be processed as an IMS call or CSFB call. If this method's
824      * functionality is not overridden, the platform will process every call as IMS as long as the
825      * MmTelFeature reports that the {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE} capability is
826      * available.
827      * @param numbers An array of {@link String}s that will be used for placing the call. There can
828      *         be multiple {@link String}s listed in the case when we want to place an outgoing
829      *         call as a conference.
830      * @return a {@link ProcessCallResult} to the framework, which will be used to determine if the
831      *        call will be placed over IMS or via CSFB.
832      * @hide
833      */
834     @SystemApi
shouldProcessCall(@onNull String[] numbers)835     public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) {
836         return PROCESS_CALL_IMS;
837     }
838 
839     /**
840      *
841      * @hide
842      */
getUtInterface()843     protected IImsUt getUtInterface() throws RemoteException {
844         ImsUtImplBase utImpl = getUt();
845         if (utImpl != null) {
846             utImpl.setDefaultExecutor(mExecutor);
847             return utImpl.getInterface();
848         } else {
849             return null;
850         }
851     }
852 
853     /**
854      * @hide
855      */
getEcbmInterface()856     protected IImsEcbm getEcbmInterface() throws RemoteException {
857         ImsEcbmImplBase ecbmImpl = getEcbm();
858         if (ecbmImpl != null) {
859             ecbmImpl.setDefaultExecutor(mExecutor);
860             return ecbmImpl.getImsEcbm();
861         } else {
862             return null;
863         }
864     }
865 
866     /**
867      * @hide
868      */
getMultiEndpointInterface()869     public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
870         ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint();
871         if (multiendpointImpl != null) {
872             multiendpointImpl.setDefaultExecutor(mExecutor);
873             return multiendpointImpl.getIImsMultiEndpoint();
874         } else {
875             return null;
876         }
877     }
878 
879     /**
880      * @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service
881      * configuration.
882      * @hide
883      */
884     @SystemApi
getUt()885     public @NonNull ImsUtImplBase getUt() {
886         // Base Implementation - Should be overridden
887         return new ImsUtImplBase();
888     }
889 
890     /**
891      * @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE
892      * calls that support it.
893      * @hide
894      */
895     @SystemApi
getEcbm()896     public @NonNull ImsEcbmImplBase getEcbm() {
897         // Base Implementation - Should be overridden
898         return new ImsEcbmImplBase();
899     }
900 
901     /**
902      * @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event
903      * package processing for multi-endpoint.
904      * @hide
905      */
906     @SystemApi
getMultiEndpoint()907     public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() {
908         // Base Implementation - Should be overridden
909         return new ImsMultiEndpointImplBase();
910     }
911 
912     /**
913      * Sets the current UI TTY mode for the MmTelFeature.
914      * @param mode An integer containing the new UI TTY Mode, can consist of
915      *         {@link TelecomManager#TTY_MODE_OFF},
916      *         {@link TelecomManager#TTY_MODE_FULL},
917      *         {@link TelecomManager#TTY_MODE_HCO},
918      *         {@link TelecomManager#TTY_MODE_VCO}
919      * @param onCompleteMessage If non-null, this MmTelFeature should call this {@link Message} when
920      *         the operation is complete by using the associated {@link android.os.Messenger} in
921      *         {@link Message#replyTo}. For example:
922      * {@code
923      *     // Set UI TTY Mode and other operations...
924      *     try {
925      *         // Notify framework that the mode was changed.
926      *         Messenger uiMessenger = onCompleteMessage.replyTo;
927      *         uiMessenger.send(onCompleteMessage);
928      *     } catch (RemoteException e) {
929      *         // Remote side is dead
930      *     }
931      * }
932      * @hide
933      */
934     @SystemApi
setUiTtyMode(int mode, @Nullable Message onCompleteMessage)935     public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) {
936         // Base Implementation - Should be overridden
937     }
938 
setSmsListener(IImsSmsListener listener)939     private void setSmsListener(IImsSmsListener listener) {
940         getSmsImplementation().registerSmsListener(listener);
941     }
942 
sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, byte[] pdu)943     private void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
944             byte[] pdu) {
945         getSmsImplementation().sendSms(token, messageRef, format, smsc, isRetry, pdu);
946     }
947 
acknowledgeSms(int token, int messageRef, @ImsSmsImplBase.DeliverStatusResult int result)948     private void acknowledgeSms(int token, int messageRef,
949             @ImsSmsImplBase.DeliverStatusResult int result) {
950         getSmsImplementation().acknowledgeSms(token, messageRef, result);
951     }
952 
acknowledgeSmsReport(int token, int messageRef, @ImsSmsImplBase.StatusReportResult int result)953     private void acknowledgeSmsReport(int token, int messageRef,
954             @ImsSmsImplBase.StatusReportResult int result) {
955         getSmsImplementation().acknowledgeSmsReport(token, messageRef, result);
956     }
957 
onSmsReady()958     private void onSmsReady() {
959         getSmsImplementation().onReady();
960     }
961 
962     /**
963      * Must be overridden by IMS Provider to be able to support SMS over IMS. Otherwise a default
964      * non-functional implementation is returned.
965      *
966      * @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS
967      * Provider.
968      * @hide
969      */
970     @SystemApi
getSmsImplementation()971     public @NonNull ImsSmsImplBase getSmsImplementation() {
972         return new ImsSmsImplBase();
973     }
974 
getSmsFormat()975     private String getSmsFormat() {
976         return getSmsImplementation().getSmsFormat();
977     }
978 
979     /**
980      * {@inheritDoc}
981      * @hide
982      */
983     @Override
984     @SystemApi
onFeatureRemoved()985     public void onFeatureRemoved() {
986         // Base Implementation - Should be overridden
987     }
988 
989     /**
990      * {@inheritDoc}
991      * @hide
992      */
993     @Override
994     @SystemApi
onFeatureReady()995     public void onFeatureReady() {
996         // Base Implementation - Should be overridden
997     }
998 
999     /**
1000      * @hide
1001      */
1002     @Override
getBinder()1003     public final IImsMmTelFeature getBinder() {
1004         return mImsMMTelBinder;
1005     }
1006 
1007     /**
1008      * Set default Executor from ImsService.
1009      * @param executor The default executor for the framework to use when executing the methods
1010      * overridden by the implementation of MmTelFeature.
1011      * @hide
1012      */
setDefaultExecutor(@onNull Executor executor)1013     public final void setDefaultExecutor(@NonNull Executor executor) {
1014         if (mExecutor == null) {
1015             mExecutor = executor;
1016         }
1017     }
1018 }
1019