Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0982900f authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Fix MmTelFeatureConnection closing causing bad state" am: 274e0259

Original change: https://android-review.googlesource.com/c/platform/frameworks/opt/net/ims/+/1557392

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Id4dcf568a94a571b085a37429fccc8682d531dcc
parents 5fe759d9 274e0259
Loading
Loading
Loading
Loading
+6 −12
Original line number Diff line number Diff line
@@ -39,9 +39,7 @@ import com.android.ims.internal.IImsEcbmListener;
import com.android.telephony.Rlog;

/**
 * Provides APIs for the supplementary service settings using IMS (Ut interface).
 * It is created from 3GPP TS 24.623 (XCAP(XML Configuration Access Protocol)
 * over the Ut interface for manipulating supplementary services).
 * Provides APIs for the modem to communicate the CDMA Emergency Callback Mode status for IMS.
 *
 * @hide
 */
@@ -56,13 +54,9 @@ public class ImsEcbm {
        miEcbm = iEcbm;
    }

    public void setEcbmStateListener(ImsEcbmStateListener ecbmListener) throws ImsException {
        try {
            miEcbm.setListener(new ImsEcbmListenerProxy(ecbmListener));
        } catch (RemoteException e) {
            throw new ImsException("setEcbmStateListener()", e,
                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
        }
    public void setEcbmStateListener(ImsEcbmStateListener ecbmListener) throws RemoteException {
            miEcbm.setListener(ecbmListener != null ?
                    new ImsEcbmListenerProxy(ecbmListener) : null);
    }

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -82,8 +76,8 @@ public class ImsEcbm {
    /**
     * Adapter class for {@link IImsEcbmListener}.
     */
    private class ImsEcbmListenerProxy extends IImsEcbmListener.Stub {
        private ImsEcbmStateListener mListener;
    private static class ImsEcbmListenerProxy extends IImsEcbmListener.Stub {
        private final ImsEcbmStateListener mListener;

        public ImsEcbmListenerProxy(ImsEcbmStateListener listener) {
            mListener = listener;
+30 −47
Original line number Diff line number Diff line
@@ -1729,18 +1729,23 @@ public class ImsManager implements FeatureUpdates {
    }

    /**
     * Opens the IMS service for making calls and/or receiving generic IMS calls.
     * Opens the IMS service for making calls and/or receiving generic IMS calls as well as
     * register listeners for ECBM, Multiendpoint, and UT if the ImsService supports it.
     * <p>
     * The caller may make subsequent calls through {@link #makeCall}.
     * The IMS service will register the device to the operator's network with the credentials
     * (from ISIM) periodically in order to receive calls from the operator's network.
     * When the IMS service receives a new call, it will call
     * {@link MmTelFeature.Listener#onIncomingCall}
     * @param listener A {@link MmTelFeature.Listener}, which is the interface the
     * {@link MmTelFeature} uses to notify the framework of updates
     * {@link MmTelFeature} uses to notify the framework of updates.
     * @param ecbmListener Listener used for ECBM indications.
     * @param multiEndpointListener Listener used for multiEndpoint indications.
     * @throws NullPointerException if {@code listener} is null
     * @throws ImsException if calling the IMS service results in an error
     */
    public void open(MmTelFeature.Listener listener) throws ImsException {
    public void open(MmTelFeature.Listener listener, ImsEcbmStateListener ecbmListener,
            ImsExternalCallStateListener multiEndpointListener) throws ImsException {
        MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();

        if (listener == null) {
@@ -1748,7 +1753,7 @@ public class ImsManager implements FeatureUpdates {
        }

        try {
            c.openConnection(listener);
            c.openConnection(listener, ecbmListener, multiEndpointListener);
        } catch (RemoteException e) {
            throw new ImsException("open()", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
        }
@@ -1893,16 +1898,18 @@ public class ImsManager implements FeatureUpdates {

    /**
     * Removes a previously registered {@link ImsMmTelManager.CapabilityCallback} callback.
     * @throws ImsException when the ImsService connection is not available.
     */
    public void removeCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback)
            throws ImsException {
    public void removeCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback) {
        if (callback == null) {
            throw new NullPointerException("capabilities callback can't be null");
        }

        try {
            MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
            c.removeCapabilityCallback(callback.getBinder());
        } catch (ImsException e) {
            log("Exception removing Capability , exception=" + e);
        }
    }

    /**
@@ -2007,24 +2014,27 @@ public class ImsManager implements FeatureUpdates {
    }

    /**
     * Closes the connection and removes all active callbacks.
     * All the resources that were allocated to the service are also released.
     * Closes the connection opened in {@link #open} and removes the associated listeners.
     */
    public void close() {
        mMmTelConnectionRef.get().closeConnection();
    }

    /**
     * Gets the configuration interface to provision / withdraw the supplementary service settings.
     * Create or get the existing configuration interface to provision / withdraw the supplementary
     * service settings.
     * <p>
     * There can only be one connection to the UT interface, so this may only be called by one
     * ImsManager instance. Otherwise, an IllegalStateException will be thrown.
     *
     * @return the Ut interface instance
     * @throws ImsException if getting the Ut interface results in an error
     */
    public ImsUtInterface getSupplementaryServiceConfiguration() throws ImsException {
    public ImsUtInterface createOrGetSupplementaryServiceConfiguration() throws ImsException {
        ImsUt iUt;
        MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
        try {
            iUt = c.getUtInterface();
            iUt = c.createOrGetUtInterface();
            if (iUt == null) {
                throw new ImsException("getSupplementaryServiceConfiguration()",
                        ImsReasonInfo.CODE_UT_NOT_SUPPORTED);
@@ -2657,24 +2667,20 @@ public class ImsManager implements FeatureUpdates {

    /**
     * Gets the ECBM interface to request ECBM exit.
     * <p>
     * This should only be called after {@link #open} has been called.
     *
     * @return the ECBM interface instance
     * @throws ImsException if getting the ECBM interface results in an error
     */
    public ImsEcbm getEcbmInterface() throws ImsException {
        ImsEcbm iEcbm = null;
        MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
        try {
            iEcbm = c.getEcbmInterface();
        ImsEcbm iEcbm = c.getEcbmInterface();

        if (iEcbm == null) {
            throw new ImsException("getEcbmInterface()",
                    ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED);
        }
        } catch (RemoteException e) {
            throw new ImsException("getEcbmInterface()", e,
                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
        }
        return iEcbm;
    }

@@ -2754,29 +2760,6 @@ public class ImsManager implements FeatureUpdates {
        }
    }

    /**
     * Gets the Multi-Endpoint interface to subscribe to multi-enpoint notifications..
     *
     * @return the multi-endpoint interface instance
     * @throws ImsException if getting the multi-endpoint interface results in an error
     */
    public ImsMultiEndpoint getMultiEndpointInterface() throws ImsException {
        ImsMultiEndpoint iImsMultiEndpoint;
        try {
            iImsMultiEndpoint = mMmTelConnectionRef.get().getMultiEndpointInterface();

            if (iImsMultiEndpoint == null) {
                throw new ImsException("getMultiEndpointInterface()",
                        ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED);
            }
        } catch (RemoteException e) {
            throw new ImsException("getMultiEndpointInterface()", e,
                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
        }

        return iImsMultiEndpoint;
    }

    /**
     * Resets ImsManager settings back to factory defaults.
     *
+4 −9
Original line number Diff line number Diff line
@@ -70,15 +70,10 @@ public class ImsMultiEndpoint {
    }

    public void setExternalCallStateListener(ImsExternalCallStateListener externalCallStateListener)
            throws ImsException {
        try {
            throws RemoteException {
        if (DBG) Rlog.d(TAG, "setExternalCallStateListener");
            mImsMultiendpoint.setListener(new ImsExternalCallStateListenerProxy(
                    externalCallStateListener));
        } catch (RemoteException e) {
            throw new ImsException("setExternalCallStateListener could not be set.", e,
                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
        }
        mImsMultiendpoint.setListener(externalCallStateListener != null ?
                new ImsExternalCallStateListenerProxy(externalCallStateListener) : null);
    }

    public boolean isBinderAlive() {
+127 −35
Original line number Diff line number Diff line
@@ -16,12 +16,12 @@

package com.android.ims;

import android.annotation.NonNull;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Message;
import android.os.RemoteException;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsService;
import android.telephony.ims.RtpHeaderExtensionType;
@@ -34,8 +34,8 @@ import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.aidl.IImsSmsListener;
import android.telephony.ims.aidl.ISipTransport;
import android.telephony.ims.feature.CapabilityChangeRequest;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsEcbmImplBase;
import android.telephony.ims.stub.ImsSmsImplBase;
import android.util.Log;

@@ -43,9 +43,9 @@ 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 com.android.telephony.Rlog;

import java.util.ArrayList;
import java.util.Optional;
import java.util.Set;

/**
@@ -197,12 +197,54 @@ public class MmTelFeatureConnection extends FeatureConnection {
        }
    }

    private static final class BinderAccessState<T> {
        /**
         * We have not tried to get the interface yet.
         */
        static final int STATE_NOT_SET = 0;
        /**
         * We have tried to get the interface, but it is not supported.
         */
        static final int STATE_NOT_SUPPORTED = 1;
        /**
         * The interface is available from the service.
         */
        static final int STATE_AVAILABLE = 2;

        public static <T> BinderAccessState<T> of(T value) {
            return new BinderAccessState<>(value);
        }

        private final int mState;
        private final T mInterface;

        public BinderAccessState(int state) {
            mState = state;
            mInterface = null;
        }

        public BinderAccessState(T binderInterface) {
            mState = STATE_AVAILABLE;
            mInterface = binderInterface;
        }

        public int getState() {
            return mState;
        }

        public T getInterface() {
            return mInterface;
        }
    }

    // Updated by IImsServiceFeatureCallback when FEATURE_EMERGENCY_MMTEL is sent.
    private boolean mSupportsEmergencyCalling = false;
    // MMTEL specific binder Interfaces
    private BinderAccessState<ImsEcbm> mEcbm =
            new BinderAccessState<>(BinderAccessState.STATE_NOT_SET);
    private BinderAccessState<ImsMultiEndpoint> mMultiEndpoint =
            new BinderAccessState<>(BinderAccessState.STATE_NOT_SET);
    private MmTelFeature.Listener mMmTelFeatureListener;
    private ImsUt mUt;
    private ImsEcbm mEcbm;
    private ImsMultiEndpoint mMultiEndpoint;

    private final ImsRegistrationCallbackAdapter mRegistrationCallbackManager;
    private final CapabilityCallbackManager mCapabilityCallbackManager;
@@ -220,9 +262,23 @@ public class MmTelFeatureConnection extends FeatureConnection {

    @Override
    protected void onRemovedOrDied() {
        // Release all callbacks being tracked and unregister them from the connected MmTelFeature.
        mRegistrationCallbackManager.close();
        mCapabilityCallbackManager.close();
        mProvisioningCallbackManager.close();
        // Close mUt interface separately from other listeners, as it is not tied directly to
        // calling. There is still a limitation currently that only one UT listener can be set
        // (through ImsPhoneCallTracker), but this could be relaxed in the future via the ability
        // to register multiple callbacks.
        synchronized (mLock) {
            if (mUt != null) {
                mUt.close();
                mUt = null;
            }
            closeConnection();
            super.onRemovedOrDied();
        }
    }

    public boolean isEmergencyMmTelAvailable() {
        return mSupportsEmergencyCalling;
@@ -232,37 +288,45 @@ public class MmTelFeatureConnection extends FeatureConnection {
     * Opens the connection to the {@link MmTelFeature} and establishes a listener back to the
     * framework. Calling this method multiple times will reset the listener attached to the
     * {@link MmTelFeature}.
     * @param listener A {@link MmTelFeature.Listener} that will be used by the {@link MmTelFeature}
     * to notify the framework of updates.
     * @param mmTelListener A {@link MmTelFeature.Listener} that will be used by the
     *         {@link MmTelFeature} to notify the framework of mmtel calling updates.
     * @param ecbmListener Listener used to listen for ECBM updates from {@link ImsEcbmImplBase}
     *         implementation.
     */
    public void openConnection(MmTelFeature.Listener listener) throws RemoteException {
    public void openConnection(MmTelFeature.Listener mmTelListener,
            ImsEcbmStateListener ecbmListener,
            ImsExternalCallStateListener multiEndpointListener) throws RemoteException {
        synchronized (mLock) {
            checkServiceIsReady();
            getServiceInterface(mBinder).setListener(listener);
            mMmTelFeatureListener = mmTelListener;
            getServiceInterface(mBinder).setListener(mmTelListener);
            setEcbmInterface(ecbmListener);
            setMultiEndpointInterface(multiEndpointListener);
        }
    }

    /**
     * Clean up all caches as well as any callbacks that are currently associated with the
     * MmTelFeature.
     * Closes the connection to the {@link MmTelFeature} if it was previously opened via
     * {@link #openConnection} by removing all listeners.
     */
    public void closeConnection() {
        mRegistrationCallbackManager.close();
        mCapabilityCallbackManager.close();
        mProvisioningCallbackManager.close();
        synchronized (mLock) {
            if (mUt != null) {
                mUt.close();
                mUt = null;
            }
            mEcbm = null;
            mMultiEndpoint = null;
            if (!isBinderAlive()) return;
            try {
                if (isBinderAlive()) {
                if (mMmTelFeatureListener != null) {
                    mMmTelFeatureListener = null;
                    getServiceInterface(mBinder).setListener(null);
                }
                if (mEcbm.getState() == BinderAccessState.STATE_AVAILABLE) {
                    mEcbm.getInterface().setEcbmStateListener(null);
                    mEcbm = new BinderAccessState<>(BinderAccessState.STATE_NOT_SET);
                }
                if (mMultiEndpoint.getState() == BinderAccessState.STATE_AVAILABLE) {
                    mMultiEndpoint.getInterface().setExternalCallStateListener(null);
                    mMultiEndpoint = new BinderAccessState<>(BinderAccessState.STATE_NOT_SET);
                }
            } catch (RemoteException e) {
                Log.w(TAG + " [" + mSlotId + "]", "closeConnection: couldn't remove listener!");
                Log.w(TAG + " [" + mSlotId + "]", "closeConnection: couldn't remove listeners!");
            }
        }
    }
@@ -363,25 +427,45 @@ public class MmTelFeatureConnection extends FeatureConnection {
        }
    }

    public ImsUt getUtInterface() throws RemoteException {
    public ImsUt createOrGetUtInterface() throws RemoteException {
        synchronized (mLock) {
            if (mUt != null) return mUt;

            checkServiceIsReady();
            IImsUt imsUt = getServiceInterface(mBinder).getUtInterface();
            // This will internally set up a listener on the ImsUtImplBase interface, and there is
            // a limitation that there can only be one. If multiple connections try to create this
            // UT interface, it will throw an IllegalStateException.
            mUt = (imsUt != null) ? new ImsUt(imsUt) : null;
            return mUt;
        }
    }

    public ImsEcbm getEcbmInterface() throws RemoteException {
    private void setEcbmInterface(ImsEcbmStateListener ecbmListener) throws RemoteException {
        synchronized (mLock) {
            if (mEcbm != null) return mEcbm;
            if (mEcbm.getState() != BinderAccessState.STATE_NOT_SET) {
                throw new IllegalStateException("ECBM interface already open");
            }

            checkServiceIsReady();
            IImsEcbm imsEcbm = getServiceInterface(mBinder).getEcbmInterface();
            mEcbm = (imsEcbm != null) ? new ImsEcbm(imsEcbm) : null;
            return mEcbm;
            mEcbm = (imsEcbm != null) ? BinderAccessState.of(new ImsEcbm(imsEcbm)) :
                    new BinderAccessState<>(BinderAccessState.STATE_NOT_SUPPORTED);
            if (mEcbm.getState() == BinderAccessState.STATE_AVAILABLE) {
                // May throw an IllegalStateException if a listener already exists.
                mEcbm.getInterface().setEcbmStateListener(ecbmListener);
            }
        }
    }

    public ImsEcbm getEcbmInterface() {
        synchronized (mLock) {
            if (mEcbm.getState() == BinderAccessState.STATE_NOT_SET) {
                throw new IllegalStateException("ECBM interface has not been opened");
            }

            return mEcbm.getState() == BinderAccessState.STATE_AVAILABLE ?
                    mEcbm.getInterface() : null;
        }
    }

@@ -393,14 +477,22 @@ public class MmTelFeatureConnection extends FeatureConnection {
        }
    }

    public ImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
    private void setMultiEndpointInterface(ImsExternalCallStateListener listener)
            throws RemoteException {
        synchronized (mLock) {
            if(mMultiEndpoint != null) return mMultiEndpoint;
            if (mMultiEndpoint.getState() != BinderAccessState.STATE_NOT_SET) {
                throw new IllegalStateException("multiendpoint interface is already open");
            }

            checkServiceIsReady();
            IImsMultiEndpoint imEndpoint = getServiceInterface(mBinder).getMultiEndpointInterface();
            mMultiEndpoint = (imEndpoint != null) ? new ImsMultiEndpoint(imEndpoint) : null;
            return mMultiEndpoint;
            mMultiEndpoint = (imEndpoint != null)
                    ? BinderAccessState.of(new ImsMultiEndpoint(imEndpoint)) :
                    new BinderAccessState<>(BinderAccessState.STATE_NOT_SUPPORTED);
            if (mMultiEndpoint.getState() == BinderAccessState.STATE_AVAILABLE) {
                // May throw an IllegalStateException if a listener already exists.
                mMultiEndpoint.getInterface().setExternalCallStateListener(listener);
            }
        }
    }