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

Commit 274e0259 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Fix MmTelFeatureConnection closing causing bad state"

parents 6ba4f3fa 6e396b0e
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);
            }
        }
    }