/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package android.telephony.ims.feature;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Bundle;
import android.os.Message;
import android.os.RemoteException;
import android.telecom.TelecomManager;
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsCallSession;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsMmTelListener;
import android.telephony.ims.aidl.IImsSmsListener;
import android.telephony.ims.stub.ImsCallSessionImplBase;
import android.telephony.ims.stub.ImsEcbmImplBase;
import android.telephony.ims.stub.ImsMultiEndpointImplBase;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsSmsImplBase;
import android.telephony.ims.stub.ImsUtImplBase;

import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsUt;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
 *
 * Any class wishing to use MmTelFeature should extend this class and implement all methods that the
 * service supports.
 */
public class MmTelFeature extends ImsFeature {

    private static final String LOG_TAG = "MmTelFeature";

    /**
     * @hide
     */
    @SystemApi @TestApi
    public MmTelFeature() {
    }

    private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {

        @Override
        public void setListener(IImsMmTelListener l) {
            MmTelFeature.this.setListener(l);
        }

        @Override
        public int getFeatureState() throws RemoteException {
            try {
                return MmTelFeature.this.getFeatureState();
            } catch (Exception e) {
                throw new RemoteException(e.getMessage());
            }
        }


        @Override
        public ImsCallProfile createCallProfile(int callSessionType, int callType)
                throws RemoteException {
            synchronized (mLock) {
                try {
                    return MmTelFeature.this.createCallProfile(callSessionType, callType);
                } catch (Exception e) {
                    throw new RemoteException(e.getMessage());
                }
            }
        }

        @Override
        public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
            synchronized (mLock) {
                return createCallSessionInterface(profile);
            }
        }

        @Override
        public int shouldProcessCall(String[] numbers) {
            synchronized (mLock) {
                return MmTelFeature.this.shouldProcessCall(numbers);
            }
        }

        @Override
        public IImsUt getUtInterface() throws RemoteException {
            synchronized (mLock) {
                return MmTelFeature.this.getUtInterface();
            }
        }

        @Override
        public IImsEcbm getEcbmInterface() throws RemoteException {
            synchronized (mLock) {
                return MmTelFeature.this.getEcbmInterface();
            }
        }

        @Override
        public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
            synchronized (mLock) {
                try {
                    MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
                } catch (Exception e) {
                    throw new RemoteException(e.getMessage());
                }
            }
        }

        @Override
        public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
            synchronized (mLock) {
                return MmTelFeature.this.getMultiEndpointInterface();
            }
        }

        @Override
        public int queryCapabilityStatus() {
            return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
        }

        @Override
        public void addCapabilityCallback(IImsCapabilityCallback c) {
            // no need to lock, structure already handles multithreading.
            MmTelFeature.this.addCapabilityCallback(c);
        }

        @Override
        public void removeCapabilityCallback(IImsCapabilityCallback c) {
            // no need to lock, structure already handles multithreading.
            MmTelFeature.this.removeCapabilityCallback(c);
        }

        @Override
        public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
                IImsCapabilityCallback c) {
            MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
        }

        @Override
        public void queryCapabilityConfiguration(int capability, int radioTech,
                IImsCapabilityCallback c) {
            queryCapabilityConfigurationInternal(capability, radioTech, c);
        }

        @Override
        public void setSmsListener(IImsSmsListener l) {
            MmTelFeature.this.setSmsListener(l);
        }

        @Override
        public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
                byte[] pdu) {
            synchronized (mLock) {
                MmTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu);
            }
        }

        @Override
        public void acknowledgeSms(int token, int messageRef, int result) {
            synchronized (mLock) {
                MmTelFeature.this.acknowledgeSms(token, messageRef, result);
            }
        }

        @Override
        public void acknowledgeSmsReport(int token, int messageRef, int result) {
            synchronized (mLock) {
                MmTelFeature.this.acknowledgeSmsReport(token, messageRef, result);
            }
        }

        @Override
        public String getSmsFormat() {
            synchronized (mLock) {
                return MmTelFeature.this.getSmsFormat();
            }
        }

        @Override
        public void onSmsReady() {
            synchronized (mLock) {
                MmTelFeature.this.onSmsReady();
            }
        }
    };

    /**
     * Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask.
     * The capabilities that are used in MmTelFeature are defined as
     * {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE},
     * {@link MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
     * {@link MmTelCapabilities#CAPABILITY_TYPE_UT}, and
     * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}.
     *
     * The capabilities of this MmTelFeature will be set by the framework.
     */
    public static class MmTelCapabilities extends Capabilities {

        /**
         * Create a new empty {@link MmTelCapabilities} instance.
         * @see #addCapabilities(int)
         * @see #removeCapabilities(int)
         * @hide
         */
        @SystemApi @TestApi
        public MmTelCapabilities() {
            super();
        }

        /**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead.
         * @hide
         */
        @Deprecated
        @SystemApi @TestApi
        public MmTelCapabilities(Capabilities c) {
            mCapabilities = c.mCapabilities;
        }

        /**
         * Create a new {link @MmTelCapabilities} instance with the provided capabilities.
         * @param capabilities The capabilities that are supported for MmTel in the form of a
         *                     bitfield.
         * @hide
         */
        @SystemApi @TestApi
        public MmTelCapabilities(@MmTelCapability int capabilities) {
            super(capabilities);
        }

        /** @hide */
        @IntDef(flag = true,
                value = {
                        CAPABILITY_TYPE_VOICE,
                        CAPABILITY_TYPE_VIDEO,
                        CAPABILITY_TYPE_UT,
                        CAPABILITY_TYPE_SMS
                })
        @Retention(RetentionPolicy.SOURCE)
        public @interface MmTelCapability {}

        /**
         * This MmTelFeature supports Voice calling (IR.92)
         */
        public static final int CAPABILITY_TYPE_VOICE = 1 << 0;

        /**
         * This MmTelFeature supports Video (IR.94)
         */
        public static final int CAPABILITY_TYPE_VIDEO = 1 << 1;

        /**
         * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92)
         */
        public static final int CAPABILITY_TYPE_UT = 1 << 2;

        /**
         * This MmTelFeature supports SMS (IR.92)
         */
        public static final int CAPABILITY_TYPE_SMS = 1 << 3;

        /**
        * @hide
        */
        @Override
        @SystemApi @TestApi
        public final void addCapabilities(@MmTelCapability int capabilities) {
            super.addCapabilities(capabilities);
        }

        /**
        * @hide
        */
        @Override
        @SystemApi @TestApi
        public final void removeCapabilities(@MmTelCapability int capability) {
            super.removeCapabilities(capability);
        }

        /**
        * @hide
        */
        @Override
        @SystemApi @TestApi
        public final boolean isCapable(@MmTelCapability int capabilities) {
            return super.isCapable(capabilities);
        }

        /**
        * @hide
        */
        @NonNull
        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder("MmTel Capabilities - [");
            builder.append("Voice: ");
            builder.append(isCapable(CAPABILITY_TYPE_VOICE));
            builder.append(" Video: ");
            builder.append(isCapable(CAPABILITY_TYPE_VIDEO));
            builder.append(" UT: ");
            builder.append(isCapable(CAPABILITY_TYPE_UT));
            builder.append(" SMS: ");
            builder.append(isCapable(CAPABILITY_TYPE_SMS));
            builder.append("]");
            return builder.toString();
        }
    }

    /**
     * Listener that the framework implements for communication from the MmTelFeature.
     * @hide
     */
    public static class Listener extends IImsMmTelListener.Stub {

        /**
         * Called when the IMS provider receives an incoming call.
         * @param c The {@link ImsCallSession} associated with the new call.
         * @hide
         */
        @Override
        @SystemApi @TestApi
        public void onIncomingCall(IImsCallSession c, Bundle extras) {

        }

        /**
         * Called when the IMS provider implicitly rejects an incoming call during setup.
         * @param callProfile An {@link ImsCallProfile} with the call details.
         * @param reason The {@link ImsReasonInfo} reason for call rejection.
         * @hide
         */
        @Override
        @SystemApi @TestApi
        public void onRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason) {

        }

        /**
         * Updates the Listener when the voice message count for IMS has changed.
         * @param count an integer representing the new message count.
         * @hide
         */
        @Override
        @SystemApi @TestApi
        public void onVoiceMessageCountUpdate(int count) {

        }
    }

    /**
     * To be returned by {@link #shouldProcessCall(String[])} when the ImsService should process the
     * outgoing call as IMS.
     * @hide
     */
    @SystemApi @TestApi
    public static final int PROCESS_CALL_IMS = 0;
    /**
     * To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should
     * not process the outgoing call as IMS and should instead use circuit switch.
     * @hide
     */
    @SystemApi @TestApi
    public static final int PROCESS_CALL_CSFB = 1;

    /** @hide */
    @IntDef(flag = true,
            value = {
                    PROCESS_CALL_IMS,
                    PROCESS_CALL_CSFB
            })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ProcessCallResult {}

    /**
     * If the flag is present and true, it indicates that the incoming call is for USSD.
     * <p>
     * This is an optional boolean flag.
     * @hide
     */
    @SystemApi @TestApi
    public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";

    /**
     * If this flag is present and true, this call is marked as an unknown dialing call instead
     * of an incoming call. An example of such a call is a call that is originated by sending
     * commands (like AT commands) directly to the modem without Android involvement or dialing
     * calls appearing over IMS when the modem does a silent redial from circuit-switched to IMS in
     * certain situations.
     * <p>
     * This is an optional boolean flag.
     * @hide
     */
    @SystemApi @TestApi
    public static final String EXTRA_IS_UNKNOWN_CALL =
            "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";

    private IImsMmTelListener mListener;

    /**
     * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and
     *     notifies the framework.
     */
    private void setListener(IImsMmTelListener listener) {
        synchronized (mLock) {
            mListener = listener;
            if (mListener != null) {
                onFeatureReady();
            }
        }
    }

    /**
     * @return the listener associated with this MmTelFeature. May be null if it has not been set
     * by the framework yet.
     */
    private IImsMmTelListener getListener() {
        synchronized (mLock) {
            return mListener;
        }
    }

    /**
     * The current capability status that this MmTelFeature has defined is available. This
     * configuration will be used by the platform to figure out which capabilities are CURRENTLY
     * available to be used.
     *
     * Should be a subset of the capabilities that are enabled by the framework in
     * {@link #changeEnabledCapabilities}.
     * @return A copy of the current MmTelFeature capability status.
     * @hide
     */
    @Override
    @SystemApi @TestApi
    public @NonNull final MmTelCapabilities queryCapabilityStatus() {
        return new MmTelCapabilities(super.queryCapabilityStatus());
    }

    /**
     * Notify the framework that the status of the Capabilities has changed. Even though the
     * MmTelFeature capability may be enabled by the framework, the status may be disabled due to
     * the feature being unavailable from the network.
     * @param c The current capability status of the MmTelFeature. If a capability is disabled, then
     * the status of that capability is disabled. This can happen if the network does not currently
     * support the capability that is enabled. A capability that is disabled by the framework (via
     * {@link #changeEnabledCapabilities}) should also show the status as disabled.
     * @hide
     */
    @SystemApi @TestApi
    public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) {
        if (c == null) {
            throw new IllegalArgumentException("MmTelCapabilities must be non-null!");
        }
        super.notifyCapabilitiesStatusChanged(c);
    }

    /**
     * Notify the framework of an incoming call.
     * @param c The {@link ImsCallSessionImplBase} of the new incoming call.
     * @param extras A bundle containing extra parameters related to the call. See
     * {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above.
      * @hide
     */
    @SystemApi @TestApi
    public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c,
            @NonNull Bundle extras) {
        if (c == null || extras == null) {
            throw new IllegalArgumentException("ImsCallSessionImplBase and Bundle can not be "
                    + "null.");
        }
        IImsMmTelListener listener = getListener();
        if (listener == null) {
            throw new IllegalStateException("Session is not available.");
        }
        try {
            listener.onIncomingCall(c.getServiceImpl(), extras);
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Notify the framework that a call has been implicitly rejected by this MmTelFeature
     * during call setup.
     * @param callProfile The {@link ImsCallProfile} IMS call profile with details.
     *        This can be null if no call information is available for the rejected call.
     * @param reason The {@link ImsReasonInfo} call rejection reason.
     * @hide
     */
    @SystemApi @TestApi
    public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile,
            @NonNull ImsReasonInfo reason) {
        if (callProfile == null || reason == null) {
            throw new IllegalArgumentException("ImsCallProfile and ImsReasonInfo must not be "
                    + "null.");
        }
        IImsMmTelListener listener = getListener();
        if (listener == null) {
            throw new IllegalStateException("Session is not available.");
        }
        try {
            listener.onRejectedCall(callProfile, reason);
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     *
     * @hide
     */
    public final void notifyIncomingCallSession(IImsCallSession c, Bundle extras) {
        IImsMmTelListener listener = getListener();
        if (listener == null) {
            throw new IllegalStateException("Session is not available.");
        }
        try {
            listener.onIncomingCall(c, extras);
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Notify the framework of a change in the Voice Message count.
     * @link count the new Voice Message count.
     * @hide
     */
    @SystemApi @TestApi
    public final void notifyVoiceMessageCountUpdate(int count) {
        IImsMmTelListener listener = getListener();
        if (listener == null) {
            throw new IllegalStateException("Session is not available.");
        }
        try {
            listener.onVoiceMessageCountUpdate(count);
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Provides the MmTelFeature with the ability to return the framework Capability Configuration
     * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
     * includes a capability A to enable or disable, this method should return the correct enabled
     * status for capability A.
     * @param capability The capability that we are querying the configuration for.
     * @return true if the capability is enabled, false otherwise.
     * @hide
     */
    @Override
    @SystemApi @TestApi
    public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
        // Base implementation - Override to provide functionality
        return false;
    }

    /**
     * The MmTelFeature should override this method to handle the enabling/disabling of
     * MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes
     * the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities
     * could not be set to their new values,
     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called
     * individually for each capability whose processing resulted in an error.
     *
     * Enabling/Disabling a capability here indicates that the capability should be registered or
     * deregistered (depending on the capability change) and become available or unavailable to
     * the framework.
     * * @hide
     */
    @Override
    @SystemApi @TestApi
    public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
            @NonNull CapabilityCallbackProxy c) {
        // Base implementation, no-op
    }

    /**
     * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
     *
     * @param callSessionType a service type that is specified in {@link ImsCallProfile}
     *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
     *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
     *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
     * @param callType a call type that is specified in {@link ImsCallProfile}
     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
     *        {@link ImsCallProfile#CALL_TYPE_VT}
     *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
     *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
     *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
     *        {@link ImsCallProfile#CALL_TYPE_VS}
     *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
     *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
     * @return a {@link ImsCallProfile} object
     * @hide
     */
    @SystemApi @TestApi
    public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) {
        // Base Implementation - Should be overridden
        return null;
    }

    /**
     * @hide
     */
    public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
            throws RemoteException {
        ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile);
        return s != null ? s.getServiceImpl() : null;
    }

    /**
     * Creates an {@link ImsCallSession} with the specified call profile.
     * Use other methods, if applicable, instead of interacting with
     * {@link ImsCallSession} directly.
     *
     * @param profile a call profile to make the call
     * @hide
     */
    @SystemApi @TestApi
    public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) {
        // Base Implementation - Should be overridden
        return null;
    }

    /**
     * Called by the framework to determine if the outgoing call, designated by the outgoing
     * {@link String}s, should be processed as an IMS call or CSFB call. If this method's
     * functionality is not overridden, the platform will process every call as IMS as long as the
     * MmTelFeature reports that the {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE} capability is
     * available.
     * @param numbers An array of {@link String}s that will be used for placing the call. There can
     *         be multiple {@link String}s listed in the case when we want to place an outgoing
     *         call as a conference.
     * @return a {@link ProcessCallResult} to the framework, which will be used to determine if the
     *        call will be placed over IMS or via CSFB.
     * @hide
     */
    @SystemApi @TestApi
    public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) {
        return PROCESS_CALL_IMS;
    }

    /**
     *
     * @hide
     */
    protected IImsUt getUtInterface() throws RemoteException {
        ImsUtImplBase utImpl = getUt();
        return utImpl != null ? utImpl.getInterface() : null;
    }

    /**
     * @hide
     */
    protected IImsEcbm getEcbmInterface() throws RemoteException {
        ImsEcbmImplBase ecbmImpl = getEcbm();
        return ecbmImpl != null ? ecbmImpl.getImsEcbm() : null;
    }

    /**
     * @hide
     */
    public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
        ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint();
        return multiendpointImpl != null ? multiendpointImpl.getIImsMultiEndpoint() : null;
    }

    /**
     * @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service
     * configuration.
     * @hide
     */
    @SystemApi @TestApi
    public @NonNull ImsUtImplBase getUt() {
        // Base Implementation - Should be overridden
        return new ImsUtImplBase();
    }

    /**
     * @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE
     * calls that support it.
     * @hide
     */
    @SystemApi @TestApi
    public @NonNull ImsEcbmImplBase getEcbm() {
        // Base Implementation - Should be overridden
        return new ImsEcbmImplBase();
    }

    /**
     * @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event
     * package processing for multi-endpoint.
     * @hide
     */
    @SystemApi @TestApi
    public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() {
        // Base Implementation - Should be overridden
        return new ImsMultiEndpointImplBase();
    }

    /**
     * Sets the current UI TTY mode for the MmTelFeature.
     * @param mode An integer containing the new UI TTY Mode, can consist of
     *         {@link TelecomManager#TTY_MODE_OFF},
     *         {@link TelecomManager#TTY_MODE_FULL},
     *         {@link TelecomManager#TTY_MODE_HCO},
     *         {@link TelecomManager#TTY_MODE_VCO}
     * @param onCompleteMessage If non-null, this MmTelFeature should call this {@link Message} when
     *         the operation is complete by using the associated {@link android.os.Messenger} in
     *         {@link Message#replyTo}. For example:
     * {@code
     *     // Set UI TTY Mode and other operations...
     *     try {
     *         // Notify framework that the mode was changed.
     *         Messenger uiMessenger = onCompleteMessage.replyTo;
     *         uiMessenger.send(onCompleteMessage);
     *     } catch (RemoteException e) {
     *         // Remote side is dead
     *     }
     * }
     * @hide
     */
    @SystemApi @TestApi
    public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) {
        // Base Implementation - Should be overridden
    }

    private void setSmsListener(IImsSmsListener listener) {
        getSmsImplementation().registerSmsListener(listener);
    }

    private void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
            byte[] pdu) {
        getSmsImplementation().sendSms(token, messageRef, format, smsc, isRetry, pdu);
    }

    private void acknowledgeSms(int token, int messageRef,
            @ImsSmsImplBase.DeliverStatusResult int result) {
        getSmsImplementation().acknowledgeSms(token, messageRef, result);
    }

    private void acknowledgeSmsReport(int token, int messageRef,
            @ImsSmsImplBase.StatusReportResult int result) {
        getSmsImplementation().acknowledgeSmsReport(token, messageRef, result);
    }

    private void onSmsReady() {
        getSmsImplementation().onReady();
    }

    /**
     * Must be overridden by IMS Provider to be able to support SMS over IMS. Otherwise a default
     * non-functional implementation is returned.
     *
     * @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS
     * Provider.
     * @hide
     */
    @SystemApi @TestApi
    public @NonNull ImsSmsImplBase getSmsImplementation() {
        return new ImsSmsImplBase();
    }

    private String getSmsFormat() {
        return getSmsImplementation().getSmsFormat();
    }

    /**
     * {@inheritDoc}
     * @hide
     */
    @Override
    @SystemApi @TestApi
    public void onFeatureRemoved() {
        // Base Implementation - Should be overridden
    }

    /**
     * {@inheritDoc}
     * @hide
     */
    @Override
    @SystemApi @TestApi
    public void onFeatureReady() {
        // Base Implementation - Should be overridden
    }

    /**
     * @hide
     */
    @Override
    public final IImsMmTelFeature getBinder() {
        return mImsMMTelBinder;
    }
}
