All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.android.telephony.ims.feature.ImsFeature Maven / Gradle / Ivy

/*
 * 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.SystemApi;
import android.content.Context;
import android.os.IInterface;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.telephony.SubscriptionManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;

import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.internal.annotations.VisibleForTesting;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
import java.util.WeakHashMap;

/**
 * Base class for all IMS features that are supported by the framework. Use a concrete subclass
 * of {@link ImsFeature}, such as {@link MmTelFeature} or {@link RcsFeature}.
 *
 * @hide
 */
@SystemApi
public abstract class ImsFeature {

    private static final String LOG_TAG = "ImsFeature";

    /**
     * Action to broadcast when ImsService is up.
     * Internal use only.
     * Only defined here separately for compatibility purposes with the old ImsService.
     *
     * @hide
     */
    public static final String ACTION_IMS_SERVICE_UP =
            "com.android.ims.IMS_SERVICE_UP";

    /**
     * Action to broadcast when ImsService is down.
     * Internal use only.
     * Only defined here separately for compatibility purposes with the old ImsService.
     *
     * @hide
     */
    public static final String ACTION_IMS_SERVICE_DOWN =
            "com.android.ims.IMS_SERVICE_DOWN";

    /**
     * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
     * A long value; the phone ID corresponding to the IMS service coming up or down.
     * Only defined here separately for compatibility purposes with the old ImsService.
     *
     * @hide
     */
    public static final String EXTRA_PHONE_ID = "android:phone_id";

    /**
     * Invalid feature value
     * @hide
     */
    public static final int FEATURE_INVALID = -1;
    // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
    // defined values in ImsServiceClass for compatibility purposes.
    /**
     * This feature supports emergency calling over MMTEL. If defined, the framework will try to
     * place an emergency call over IMS first. If it is not defined, the framework will only use
     * CSFB for emergency calling.
     */
    public static final int FEATURE_EMERGENCY_MMTEL = 0;
    /**
     * This feature supports the MMTEL feature.
     */
    public static final int FEATURE_MMTEL = 1;
    /**
     * This feature supports the RCS feature.
     */
    public static final int FEATURE_RCS = 2;
    /**
     * Total number of features defined
     * @hide
     */
    public static final int FEATURE_MAX = 3;

    /**
     * Integer values defining IMS features that are supported in ImsFeature.
     * @hide
     */
    @IntDef(flag = true,
            value = {
                    FEATURE_EMERGENCY_MMTEL,
                    FEATURE_MMTEL,
                    FEATURE_RCS
            })
    @Retention(RetentionPolicy.SOURCE)
    public @interface FeatureType {}

    /**
     * Integer values defining the state of the ImsFeature at any time.
     * @hide
     */
    @IntDef(flag = true,
            value = {
                    STATE_UNAVAILABLE,
                    STATE_INITIALIZING,
                    STATE_READY,
            })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ImsState {}

    /**
     * This {@link ImsFeature}'s state is unavailable and should not be communicated with.
     */
    public static final int STATE_UNAVAILABLE = 0;
    /**
     * This {@link ImsFeature} state is initializing and should not be communicated with.
     */
    public static final int STATE_INITIALIZING = 1;
    /**
     * This {@link ImsFeature} is ready for communication.
     */
    public static final int STATE_READY = 2;

    /**
     * Integer values defining the result codes that should be returned from
     * {@link #changeEnabledCapabilities} when the framework tries to set a feature's capability.
     * @hide
     */
    @IntDef(flag = true,
            value = {
                    CAPABILITY_ERROR_GENERIC,
                    CAPABILITY_SUCCESS
            })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ImsCapabilityError {}

    /**
     * The capability was unable to be changed.
     */
    public static final int CAPABILITY_ERROR_GENERIC = -1;
    /**
     * The capability was able to be changed.
     */
    public static final int CAPABILITY_SUCCESS = 0;

    /**
     * Used by the ImsFeature to call back to the CapabilityCallback that the framework has
     * provided.
     */
    protected static class CapabilityCallbackProxy {
        private final IImsCapabilityCallback mCallback;

        /** @hide */
        public CapabilityCallbackProxy(IImsCapabilityCallback c) {
            mCallback = c;
        }

        /**
         * This method notifies the provided framework callback that the request to change the
         * indicated capability has failed and has not changed.
         *
         * @param capability The Capability that will be notified to the framework, defined as
         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}.
         * @param radioTech The radio tech that this capability failed for, defined as
         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}.
         * @param reason The reason this capability was unable to be changed, defined as
         * {@link #CAPABILITY_ERROR_GENERIC} or {@link #CAPABILITY_SUCCESS}.
         */
        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
                @ImsCapabilityError int reason) {
            if (mCallback == null) {
                return;
            }
            try {
                mCallback.onChangeCapabilityConfigurationError(capability, radioTech, reason);
            } catch (RemoteException e) {
                Log.e(LOG_TAG, "onChangeCapabilityConfigurationError called on dead binder.");
            }
        }
    }

    /**
     * Contains the capabilities defined and supported by an ImsFeature in the form of a bit mask.
     * @hide
     * @deprecated Use {@link MmTelFeature.MmTelCapabilities} instead.
     */
    @SystemApi  // SystemApi only because it was leaked through type usage in a previous release.
    public static class Capabilities {
        protected int mCapabilities = 0;

        /**
         * @hide
         */
        public Capabilities() {
        }

        /**
         * @hide
         */
        protected Capabilities(int capabilities) {
            mCapabilities = capabilities;
        }

        /**
         * @param capabilities Capabilities to be added to the configuration in the form of a
         *     bit mask.
         * @hide
         */
        public void addCapabilities(int capabilities) {
            mCapabilities |= capabilities;
        }

        /**
         * @param capabilities Capabilities to be removed to the configuration in the form of a
         *     bit mask.
         * @hide
         */
        public void removeCapabilities(int capabilities) {
            mCapabilities &= ~capabilities;
        }

        /**
         * @return true if all of the capabilities specified are capable.
         * @hide
         */
        public boolean isCapable(int capabilities) {
            return (mCapabilities & capabilities) == capabilities;
        }

        /**
         * @return a deep copy of the Capabilites.
         * @hide
         */
        public Capabilities copy() {
            return new Capabilities(mCapabilities);
        }

        /**
         * @return a bitmask containing the capability flags directly.
         * @hide
         */
        public int getMask() {
            return mCapabilities;
        }

        /**
         * @hide
         */
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof Capabilities)) return false;

            Capabilities that = (Capabilities) o;

            return mCapabilities == that.mCapabilities;
        }

        /**
         * @hide
         */
        @Override
        public int hashCode() {
            return mCapabilities;
        }

        /**
         * @hide
         */
        @Override
        public String toString() {
            return "Capabilities: " + Integer.toBinaryString(mCapabilities);
        }
    }

    /** @hide */
    protected Context mContext;
    /** @hide */
    protected final Object mLock = new Object();

    private final Set mStatusCallbacks = Collections.newSetFromMap(
            new WeakHashMap());
    private @ImsState int mState = STATE_UNAVAILABLE;
    private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
    private final RemoteCallbackList mCapabilityCallbacks
            = new RemoteCallbackList<>();
    private Capabilities mCapabilityStatus = new Capabilities();

    /**
     * @hide
     */
    public final void initialize(Context context, int slotId) {
        mContext = context;
        mSlotId = slotId;
    }

    /**
     * @return The current state of the feature, defined as {@link #STATE_UNAVAILABLE},
     * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}.
     * @hide
     */
    public int getFeatureState() {
        synchronized (mLock) {
            return mState;
        }
    }

    /**
     * Set the state of the ImsFeature. The state is used as a signal to the framework to start or
     * stop communication, depending on the state sent.
     * @param state The ImsFeature's state, defined as {@link #STATE_UNAVAILABLE},
     * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}.
     */
    public final void setFeatureState(@ImsState int state) {
        synchronized (mLock) {
            if (mState != state) {
                mState = state;
                notifyFeatureState(state);
            }
        }
    }

    /**
     * Not final for testing, but shouldn't be extended!
     * @hide
     */
    @VisibleForTesting
    public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
        try {
            // If we have just connected, send queued status.
            c.notifyImsFeatureStatus(getFeatureState());
            // Add the callback if the callback completes successfully without a RemoteException.
            synchronized (mLock) {
                mStatusCallbacks.add(c);
            }
        } catch (RemoteException e) {
            Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
        }
    }

    /**
     * Not final for testing, but shouldn't be extended!
     * @hide
     */
    @VisibleForTesting
    public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
        synchronized (mLock) {
            mStatusCallbacks.remove(c);
        }
    }

    /**
     * Internal method called by ImsFeature when setFeatureState has changed.
     */
    private void notifyFeatureState(@ImsState int state) {
        synchronized (mLock) {
            for (Iterator iter = mStatusCallbacks.iterator();
                    iter.hasNext(); ) {
                IImsFeatureStatusCallback callback = iter.next();
                try {
                    Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
                    callback.notifyImsFeatureStatus(state);
                } catch (RemoteException e) {
                    // remove if the callback is no longer alive.
                    iter.remove();
                    Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
                }
            }
        }
    }

    /**
     * @hide
     */
    public final void addCapabilityCallback(IImsCapabilityCallback c) {
        mCapabilityCallbacks.register(c);
        try {
            // Notify the Capability callback that was just registered of the current capabilities.
            c.onCapabilitiesStatusChanged(queryCapabilityStatus().mCapabilities);
        } catch (RemoteException e) {
            Log.w(LOG_TAG, "addCapabilityCallback: error accessing callback: " + e.getMessage());
        }
    }

    /**
     * @hide
     */
    public final void removeCapabilityCallback(IImsCapabilityCallback c) {
        mCapabilityCallbacks.unregister(c);
    }

    /**
     * @return the cached capabilities status for this feature.
     * @hide
     */
    @VisibleForTesting
    public Capabilities queryCapabilityStatus() {
        synchronized (mLock) {
            return mCapabilityStatus.copy();
        }
    }

    /**
     * Called internally to request the change of enabled capabilities.
     * @hide
     */
    @VisibleForTesting
    public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request,
            IImsCapabilityCallback c) {
        if (request == null) {
            throw new IllegalArgumentException(
                    "ImsFeature#requestChangeEnabledCapabilities called with invalid params.");
        }
        changeEnabledCapabilities(request, new CapabilityCallbackProxy(c));
    }

    /**
     * Called by the ImsFeature when the capabilities status has changed.
     *
     * @param c A {@link Capabilities} containing the new Capabilities status.
     *
     * @hide
     */
    protected final void notifyCapabilitiesStatusChanged(Capabilities c) {
        synchronized (mLock) {
            mCapabilityStatus = c.copy();
        }
        int count = mCapabilityCallbacks.beginBroadcast();
        try {
            for (int i = 0; i < count; i++) {
                try {
                    mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged(
                            c.mCapabilities);
                } catch (RemoteException e) {
                    Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " +
                            "callback.");
                }
            }
        } finally {
            mCapabilityCallbacks.finishBroadcast();
        }
    }

    /**
     * Features should override this method to receive Capability preference change requests from
     * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities
     * in the {@link CapabilityChangeRequest} are not able to be completed due to an error,
     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for
     * each failed capability.
     *
     * @param request A {@link CapabilityChangeRequest} containing requested capabilities to
     *     enable/disable.
     * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework
     * setting a subset of these capabilities fail, using
     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}.
     */
    public abstract void changeEnabledCapabilities(CapabilityChangeRequest request,
            CapabilityCallbackProxy c);

    /**
     * Called when the framework is removing this feature and it needs to be cleaned up.
     */
    public abstract void onFeatureRemoved();

    /**
     * Called when the feature has been initialized and communication with the framework is set up.
     * Any attempt by this feature to access the framework before this method is called will return
     * with an {@link IllegalStateException}.
     * The IMS provider should use this method to trigger registration for this feature on the IMS
     * network, if needed.
     */
    public abstract void onFeatureReady();

    /**
     * @return Binder instance that the framework will use to communicate with this feature.
     * @hide
     */
    protected abstract IInterface getBinder();
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy