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.aidl.IImsCapabilityCallback;
31 import android.telephony.ims.aidl.IImsMmTelFeature;
32 import android.telephony.ims.aidl.IImsMmTelListener;
33 import android.telephony.ims.aidl.IImsSmsListener;
34 import android.telephony.ims.stub.ImsCallSessionImplBase;
35 import android.telephony.ims.stub.ImsEcbmImplBase;
36 import android.telephony.ims.stub.ImsMultiEndpointImplBase;
37 import android.telephony.ims.stub.ImsRegistrationImplBase;
38 import android.telephony.ims.stub.ImsSmsImplBase;
39 import android.telephony.ims.stub.ImsUtImplBase;
40 import android.util.Log;
41 
42 import com.android.ims.internal.IImsCallSession;
43 import com.android.ims.internal.IImsEcbm;
44 import com.android.ims.internal.IImsMultiEndpoint;
45 import com.android.ims.internal.IImsUt;
46 
47 import java.lang.annotation.Retention;
48 import java.lang.annotation.RetentionPolicy;
49 
50 /**
51  * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
52  *
53  * Any class wishing to use MmTelFeature should extend this class and implement all methods that the
54  * service supports.
55  * @hide
56  */
57 @SystemApi
58 public class MmTelFeature extends ImsFeature {
59 
60     private static final String LOG_TAG = "MmTelFeature";
61 
62     private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
63 
64         @Override
65         public void setListener(IImsMmTelListener l) {
66             MmTelFeature.this.setListener(l);
67         }
68 
69         @Override
70         public int getFeatureState() throws RemoteException {
71             try {
72                 return MmTelFeature.this.getFeatureState();
73             } catch (Exception e) {
74                 throw new RemoteException(e.getMessage());
75             }
76         }
77 
78 
79         @Override
80         public ImsCallProfile createCallProfile(int callSessionType, int callType)
81                 throws RemoteException {
82             synchronized (mLock) {
83                 try {
84                     return MmTelFeature.this.createCallProfile(callSessionType, callType);
85                 } catch (Exception e) {
86                     throw new RemoteException(e.getMessage());
87                 }
88             }
89         }
90 
91         @Override
92         public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
93             synchronized (mLock) {
94                 return createCallSessionInterface(profile);
95             }
96         }
97 
98         @Override
99         public int shouldProcessCall(String[] numbers) {
100             synchronized (mLock) {
101                 return MmTelFeature.this.shouldProcessCall(numbers);
102             }
103         }
104 
105         @Override
106         public IImsUt getUtInterface() throws RemoteException {
107             synchronized (mLock) {
108                 return MmTelFeature.this.getUtInterface();
109             }
110         }
111 
112         @Override
113         public IImsEcbm getEcbmInterface() throws RemoteException {
114             synchronized (mLock) {
115                 return MmTelFeature.this.getEcbmInterface();
116             }
117         }
118 
119         @Override
120         public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
121             synchronized (mLock) {
122                 try {
123                     MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
124                 } catch (Exception e) {
125                     throw new RemoteException(e.getMessage());
126                 }
127             }
128         }
129 
130         @Override
131         public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
132             synchronized (mLock) {
133                 return MmTelFeature.this.getMultiEndpointInterface();
134             }
135         }
136 
137         @Override
138         public int queryCapabilityStatus() {
139             return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
140         }
141 
142         @Override
143         public void addCapabilityCallback(IImsCapabilityCallback c) {
144             // no need to lock, structure already handles multithreading.
145             MmTelFeature.this.addCapabilityCallback(c);
146         }
147 
148         @Override
149         public void removeCapabilityCallback(IImsCapabilityCallback c) {
150             // no need to lock, structure already handles multithreading.
151             MmTelFeature.this.removeCapabilityCallback(c);
152         }
153 
154         @Override
155         public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
156                 IImsCapabilityCallback c) {
157             synchronized (mLock) {
158                 MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
159             }
160         }
161 
162         @Override
163         public void queryCapabilityConfiguration(int capability, int radioTech,
164                 IImsCapabilityCallback c) {
165             synchronized (mLock) {
166                 queryCapabilityConfigurationInternal(capability, radioTech, c);
167             }
168         }
169 
170         @Override
171         public void setSmsListener(IImsSmsListener l) {
172             MmTelFeature.this.setSmsListener(l);
173         }
174 
175         @Override
176         public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
177                 byte[] pdu) {
178             synchronized (mLock) {
179                 MmTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu);
180             }
181         }
182 
183         @Override
184         public void acknowledgeSms(int token, int messageRef, int result) {
185             synchronized (mLock) {
186                 MmTelFeature.this.acknowledgeSms(token, messageRef, result);
187             }
188         }
189 
190         @Override
191         public void acknowledgeSmsReport(int token, int messageRef, int result) {
192             synchronized (mLock) {
193                 MmTelFeature.this.acknowledgeSmsReport(token, messageRef, result);
194             }
195         }
196 
197         @Override
198         public String getSmsFormat() {
199             synchronized (mLock) {
200                 return MmTelFeature.this.getSmsFormat();
201             }
202         }
203 
204         @Override
205         public void onSmsReady() {
206             synchronized (mLock) {
207                 MmTelFeature.this.onSmsReady();
208             }
209         }
210     };
211 
212     /**
213      * Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask.
214      * The capabilities that are used in MmTelFeature are defined as
215      * {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE},
216      * {@link MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
217      * {@link MmTelCapabilities#CAPABILITY_TYPE_UT}, and
218      * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}.
219      *
220      * The capabilities of this MmTelFeature will be set by the framework and can be queried with
221      * {@link #queryCapabilityStatus()}.
222      *
223      * This MmTelFeature can then return the status of each of these capabilities (enabled or not)
224      * by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current
225      * status can also be queried using {@link #queryCapabilityStatus()}.
226      * @see #isCapable(int)
227      */
228     public static class MmTelCapabilities extends Capabilities {
229 
230         /**
231          * Create a new empty {@link MmTelCapabilities} instance.
232          * @see #addCapabilities(int)
233          * @see #removeCapabilities(int)
234          */
MmTelCapabilities()235         public MmTelCapabilities() {
236             super();
237         }
238 
239         /**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead.*/
240         @Deprecated
MmTelCapabilities(Capabilities c)241         public MmTelCapabilities(Capabilities c) {
242             mCapabilities = c.mCapabilities;
243         }
244 
245         /**
246          * Create a new {link @MmTelCapabilities} instance with the provided capabilities.
247          * @param capabilities The capabilities that are supported for MmTel in the form of a
248          *                     bitfield.
249          */
MmTelCapabilities(int capabilities)250         public MmTelCapabilities(int capabilities) {
251             mCapabilities = capabilities;
252         }
253 
254         @IntDef(flag = true,
255                 value = {
256                         CAPABILITY_TYPE_VOICE,
257                         CAPABILITY_TYPE_VIDEO,
258                         CAPABILITY_TYPE_UT,
259                         CAPABILITY_TYPE_SMS
260                 })
261         @Retention(RetentionPolicy.SOURCE)
262         public @interface MmTelCapability {}
263 
264         /**
265          * This MmTelFeature supports Voice calling (IR.92)
266          */
267         public static final int CAPABILITY_TYPE_VOICE = 1 << 0;
268 
269         /**
270          * This MmTelFeature supports Video (IR.94)
271          */
272         public static final int CAPABILITY_TYPE_VIDEO = 1 << 1;
273 
274         /**
275          * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92)
276          */
277         public static final int CAPABILITY_TYPE_UT = 1 << 2;
278 
279         /**
280          * This MmTelFeature supports SMS (IR.92)
281          */
282         public static final int CAPABILITY_TYPE_SMS = 1 << 3;
283 
284         @Override
addCapabilities(@mTelCapability int capabilities)285         public final void addCapabilities(@MmTelCapability int capabilities) {
286             super.addCapabilities(capabilities);
287         }
288 
289         @Override
removeCapabilities(@mTelCapability int capability)290         public final void removeCapabilities(@MmTelCapability int capability) {
291             super.removeCapabilities(capability);
292         }
293 
294         @Override
isCapable(@mTelCapability int capabilities)295         public final boolean isCapable(@MmTelCapability int capabilities) {
296             return super.isCapable(capabilities);
297         }
298 
299         @Override
toString()300         public String toString() {
301             StringBuilder builder = new StringBuilder("MmTel Capabilities - [");
302             builder.append("Voice: ");
303             builder.append(isCapable(CAPABILITY_TYPE_VOICE));
304             builder.append(" Video: ");
305             builder.append(isCapable(CAPABILITY_TYPE_VIDEO));
306             builder.append(" UT: ");
307             builder.append(isCapable(CAPABILITY_TYPE_UT));
308             builder.append(" SMS: ");
309             builder.append(isCapable(CAPABILITY_TYPE_SMS));
310             builder.append("]");
311             return builder.toString();
312         }
313     }
314 
315     /**
316      * Listener that the framework implements for communication from the MmTelFeature.
317      * @hide
318      */
319     public static class Listener extends IImsMmTelListener.Stub {
320 
321         /**
322          * Called when the IMS provider receives an incoming call.
323          * @param c The {@link ImsCallSession} associated with the new call.
324          */
325         @Override
onIncomingCall(IImsCallSession c, Bundle extras)326         public void onIncomingCall(IImsCallSession c, Bundle extras) {
327 
328         }
329 
330         /**
331          * Called when the IMS provider implicitly rejects an incoming call during setup.
332          * @param callProfile An {@link ImsCallProfile} with the call details.
333          * @param reason The {@link ImsReasonInfo} reason for call rejection.
334          */
335         @Override
onRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason)336         public void onRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason) {
337 
338         }
339 
340         /**
341          * Updates the Listener when the voice message count for IMS has changed.
342          * @param count an integer representing the new message count.
343          */
344         @Override
onVoiceMessageCountUpdate(int count)345         public void onVoiceMessageCountUpdate(int count) {
346 
347         }
348     }
349 
350     /**
351      * To be returned by {@link #shouldProcessCall(String[])} when the ImsService should process the
352      * outgoing call as IMS.
353      */
354     public static final int PROCESS_CALL_IMS = 0;
355     /**
356      * To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should
357      * not process the outgoing call as IMS and should instead use circuit switch.
358      */
359     public static final int PROCESS_CALL_CSFB = 1;
360 
361     @IntDef(flag = true,
362             value = {
363                     PROCESS_CALL_IMS,
364                     PROCESS_CALL_CSFB
365             })
366     @Retention(RetentionPolicy.SOURCE)
367     public @interface ProcessCallResult {}
368 
369     private IImsMmTelListener mListener;
370 
371     /**
372      * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and
373      *     notifies the framework.
374      */
setListener(IImsMmTelListener listener)375     private void setListener(IImsMmTelListener listener) {
376         synchronized (mLock) {
377             mListener = listener;
378             if (mListener != null) {
379                 onFeatureReady();
380             }
381         }
382     }
383 
queryCapabilityConfigurationInternal(int capability, int radioTech, IImsCapabilityCallback c)384     private void queryCapabilityConfigurationInternal(int capability, int radioTech,
385             IImsCapabilityCallback c) {
386         boolean enabled = queryCapabilityConfiguration(capability, radioTech);
387         try {
388             if (c != null) {
389                 c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
390             }
391         } catch (RemoteException e) {
392             Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
393         }
394     }
395 
396     /**
397      * The current capability status that this MmTelFeature has defined is available. This
398      * configuration will be used by the platform to figure out which capabilities are CURRENTLY
399      * available to be used.
400      *
401      * Should be a subset of the capabilities that are enabled by the framework in
402      * {@link #changeEnabledCapabilities}.
403      * @return A copy of the current MmTelFeature capability status.
404      */
405     @Override
queryCapabilityStatus()406     public final MmTelCapabilities queryCapabilityStatus() {
407         return new MmTelCapabilities(super.queryCapabilityStatus());
408     }
409 
410     /**
411      * Notify the framework that the status of the Capabilities has changed. Even though the
412      * MmTelFeature capability may be enabled by the framework, the status may be disabled due to
413      * the feature being unavailable from the network.
414      * @param c The current capability status of the MmTelFeature. If a capability is disabled, then
415      * the status of that capability is disabled. This can happen if the network does not currently
416      * support the capability that is enabled. A capability that is disabled by the framework (via
417      * {@link #changeEnabledCapabilities}) should also show the status as disabled.
418      */
notifyCapabilitiesStatusChanged(@onNull MmTelCapabilities c)419     public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) {
420         if (c == null) {
421             throw new IllegalArgumentException("MmTelCapabilities must be non-null!");
422         }
423         super.notifyCapabilitiesStatusChanged(c);
424     }
425 
426     /**
427      * Notify the framework of an incoming call.
428      * @param c The {@link ImsCallSessionImplBase} of the new incoming call.
429      */
notifyIncomingCall(@onNull ImsCallSessionImplBase c, @NonNull Bundle extras)430     public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c,
431             @NonNull Bundle extras) {
432         if (c == null || extras == null) {
433             throw new IllegalArgumentException("ImsCallSessionImplBase and Bundle can not be "
434                     + "null.");
435         }
436         synchronized (mLock) {
437             if (mListener == null) {
438                 throw new IllegalStateException("Session is not available.");
439             }
440             try {
441                 mListener.onIncomingCall(c.getServiceImpl(), extras);
442             } catch (RemoteException e) {
443                 throw new RuntimeException(e);
444             }
445         }
446     }
447 
448     /**
449      * Notify the framework that a call has been implicitly rejected by this MmTelFeature
450      * during call setup.
451      * @param callProfile The {@link ImsCallProfile} IMS call profile with details.
452      *        This can be null if no call information is available for the rejected call.
453      * @param reason The {@link ImsReasonInfo} call rejection reason.
454      */
notifyRejectedCall(@onNull ImsCallProfile callProfile, @NonNull ImsReasonInfo reason)455     public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile,
456             @NonNull ImsReasonInfo reason) {
457         if (callProfile == null || reason == null) {
458             throw new IllegalArgumentException("ImsCallProfile and ImsReasonInfo must not be "
459                     + "null.");
460         }
461         synchronized (mLock) {
462             if (mListener == null) {
463                 throw new IllegalStateException("Session is not available.");
464             }
465             try {
466                 mListener.onRejectedCall(callProfile, reason);
467             } catch (RemoteException e) {
468                 throw new RuntimeException(e);
469             }
470         }
471     }
472 
473     /**
474      *
475      * @hide
476      */
notifyIncomingCallSession(IImsCallSession c, Bundle extras)477     public final void notifyIncomingCallSession(IImsCallSession c, Bundle extras) {
478         synchronized (mLock) {
479             if (mListener == null) {
480                 throw new IllegalStateException("Session is not available.");
481             }
482             try {
483                 mListener.onIncomingCall(c, extras);
484             } catch (RemoteException e) {
485                 throw new RuntimeException(e);
486             }
487         }
488     }
489 
490     /**
491      * Notify the framework of a change in the Voice Message count.
492      * @link count the new Voice Message count.
493      */
notifyVoiceMessageCountUpdate(int count)494     public final void notifyVoiceMessageCountUpdate(int count) {
495         synchronized (mLock) {
496             if (mListener == null) {
497                 throw new IllegalStateException("Session is not available.");
498             }
499             try {
500                 mListener.onVoiceMessageCountUpdate(count);
501             } catch (RemoteException e) {
502                 throw new RuntimeException(e);
503             }
504         }
505     }
506 
507     /**
508      * Provides the MmTelFeature with the ability to return the framework Capability Configuration
509      * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
510      * includes a capability A to enable or disable, this method should return the correct enabled
511      * status for capability A.
512      * @param capability The capability that we are querying the configuration for.
513      * @return true if the capability is enabled, false otherwise.
514      */
queryCapabilityConfiguration(@mTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)515     public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
516             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
517         // Base implementation - Override to provide functionality
518         return false;
519     }
520 
521     /**
522      * The MmTelFeature should override this method to handle the enabling/disabling of
523      * MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes
524      * the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities
525      * could not be set to their new values,
526      * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called
527      * individually for each capability whose processing resulted in an error.
528      *
529      * Enabling/Disabling a capability here indicates that the capability should be registered or
530      * deregistered (depending on the capability change) and become available or unavailable to
531      * the framework.
532      */
533     @Override
changeEnabledCapabilities(@onNull CapabilityChangeRequest request, @NonNull CapabilityCallbackProxy c)534     public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
535             @NonNull CapabilityCallbackProxy c) {
536         // Base implementation, no-op
537     }
538 
539     /**
540      * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
541      *
542      * @param callSessionType a service type that is specified in {@link ImsCallProfile}
543      *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
544      *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
545      *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
546      * @param callType a call type that is specified in {@link ImsCallProfile}
547      *        {@link ImsCallProfile#CALL_TYPE_VOICE}
548      *        {@link ImsCallProfile#CALL_TYPE_VT}
549      *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
550      *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
551      *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
552      *        {@link ImsCallProfile#CALL_TYPE_VS}
553      *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
554      *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
555      * @return a {@link ImsCallProfile} object
556      */
createCallProfile(int callSessionType, int callType)557     public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) {
558         // Base Implementation - Should be overridden
559         return null;
560     }
561 
562     /**
563      * @hide
564      */
createCallSessionInterface(ImsCallProfile profile)565     public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
566             throws RemoteException {
567         ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile);
568         return s != null ? s.getServiceImpl() : null;
569     }
570 
571     /**
572      * Creates an {@link ImsCallSession} with the specified call profile.
573      * Use other methods, if applicable, instead of interacting with
574      * {@link ImsCallSession} directly.
575      *
576      * @param profile a call profile to make the call
577      */
createCallSession(@onNull ImsCallProfile profile)578     public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) {
579         // Base Implementation - Should be overridden
580         return null;
581     }
582 
583     /**
584      * Called by the framework to determine if the outgoing call, designated by the outgoing
585      * {@link String}s, should be processed as an IMS call or CSFB call. If this method's
586      * functionality is not overridden, the platform will process every call as IMS as long as the
587      * MmTelFeature reports that the {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE} capability is
588      * available.
589      * @param numbers An array of {@link String}s that will be used for placing the call. There can
590      *         be multiple {@link String}s listed in the case when we want to place an outgoing
591      *         call as a conference.
592      * @return a {@link ProcessCallResult} to the framework, which will be used to determine if the
593      *        call will be placed over IMS or via CSFB.
594      */
shouldProcessCall(@onNull String[] numbers)595     public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) {
596         return PROCESS_CALL_IMS;
597     }
598 
599     /**
600      *
601      * @hide
602      */
getUtInterface()603     protected IImsUt getUtInterface() throws RemoteException {
604         ImsUtImplBase utImpl = getUt();
605         return utImpl != null ? utImpl.getInterface() : null;
606     }
607 
608     /**
609      * @hide
610      */
getEcbmInterface()611     protected IImsEcbm getEcbmInterface() throws RemoteException {
612         ImsEcbmImplBase ecbmImpl = getEcbm();
613         return ecbmImpl != null ? ecbmImpl.getImsEcbm() : null;
614     }
615 
616     /**
617      * @hide
618      */
getMultiEndpointInterface()619     public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
620         ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint();
621         return multiendpointImpl != null ? multiendpointImpl.getIImsMultiEndpoint() : null;
622     }
623 
624     /**
625      * @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service
626      * configuration.
627      */
getUt()628     public @NonNull ImsUtImplBase getUt() {
629         // Base Implementation - Should be overridden
630         return new ImsUtImplBase();
631     }
632 
633     /**
634      * @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE
635      * calls that support it.
636      */
getEcbm()637     public @NonNull ImsEcbmImplBase getEcbm() {
638         // Base Implementation - Should be overridden
639         return new ImsEcbmImplBase();
640     }
641 
642     /**
643      * @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event
644      * package processing for multi-endpoint.
645      */
getMultiEndpoint()646     public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() {
647         // Base Implementation - Should be overridden
648         return new ImsMultiEndpointImplBase();
649     }
650 
651     /**
652      * Sets the current UI TTY mode for the MmTelFeature.
653      * @param mode An integer containing the new UI TTY Mode, can consist of
654      *         {@link TelecomManager#TTY_MODE_OFF},
655      *         {@link TelecomManager#TTY_MODE_FULL},
656      *         {@link TelecomManager#TTY_MODE_HCO},
657      *         {@link TelecomManager#TTY_MODE_VCO}
658      * @param onCompleteMessage If non-null, this MmTelFeature should call this {@link Message} when
659      *         the operation is complete by using the associated {@link android.os.Messenger} in
660      *         {@link Message#replyTo}. For example:
661      * {@code
662      *     // Set UI TTY Mode and other operations...
663      *     try {
664      *         // Notify framework that the mode was changed.
665      *         Messenger uiMessenger = onCompleteMessage.replyTo;
666      *         uiMessenger.send(onCompleteMessage);
667      *     } catch (RemoteException e) {
668      *         // Remote side is dead
669      *     }
670      * }
671      */
setUiTtyMode(int mode, @Nullable Message onCompleteMessage)672     public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) {
673         // Base Implementation - Should be overridden
674     }
675 
setSmsListener(IImsSmsListener listener)676     private void setSmsListener(IImsSmsListener listener) {
677         getSmsImplementation().registerSmsListener(listener);
678     }
679 
sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, byte[] pdu)680     private void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
681             byte[] pdu) {
682         getSmsImplementation().sendSms(token, messageRef, format, smsc, isRetry, pdu);
683     }
684 
acknowledgeSms(int token, int messageRef, @ImsSmsImplBase.DeliverStatusResult int result)685     private void acknowledgeSms(int token, int messageRef,
686             @ImsSmsImplBase.DeliverStatusResult int result) {
687         getSmsImplementation().acknowledgeSms(token, messageRef, result);
688     }
689 
acknowledgeSmsReport(int token, int messageRef, @ImsSmsImplBase.StatusReportResult int result)690     private void acknowledgeSmsReport(int token, int messageRef,
691             @ImsSmsImplBase.StatusReportResult int result) {
692         getSmsImplementation().acknowledgeSmsReport(token, messageRef, result);
693     }
694 
onSmsReady()695     private void onSmsReady() {
696         getSmsImplementation().onReady();
697     }
698 
699     /**
700      * Must be overridden by IMS Provider to be able to support SMS over IMS. Otherwise a default
701      * non-functional implementation is returned.
702      *
703      * @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS
704      * Provider.
705      */
getSmsImplementation()706     public @NonNull ImsSmsImplBase getSmsImplementation() {
707         return new ImsSmsImplBase();
708     }
709 
getSmsFormat()710     private String getSmsFormat() {
711         return getSmsImplementation().getSmsFormat();
712     }
713 
714     /**{@inheritDoc}*/
715     @Override
onFeatureRemoved()716     public void onFeatureRemoved() {
717         // Base Implementation - Should be overridden
718     }
719 
720     /**{@inheritDoc}*/
721     @Override
onFeatureReady()722     public void onFeatureReady() {
723         // Base Implementation - Should be overridden
724     }
725 
726     /**
727      * @hide
728      */
729     @Override
getBinder()730     public final IImsMmTelFeature getBinder() {
731         return mImsMMTelBinder;
732     }
733 }
734