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

Commit 9da546b4 authored by Jonggeon Kim's avatar Jonggeon Kim Committed by joonhunshin
Browse files

ImsService Subscription Notifications

Remove OnSubscriptionsChangedListener implementation from ImsCallbackAdapterManager because the MmTelFeatureConnection lifecycle is on a per-subId basis now.
Propagate changed subscription ID to every related component.

Bug: 197991451
Test: atest ImsCommonTests:MmTelFeatureConnectionTest

Change-Id: Ieb185593497ba6702e8bb19e58430b0c3d80061a
Merged-In: Ieb185593497ba6702e8bb19e58430b0c3d80061a
parent e547592a
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ public abstract class FeatureConnection {
    protected static boolean sImsSupportedOnDevice = true;

    protected final int mSlotId;
    protected final int mSubId;
    protected Context mContext;
    protected IBinder mBinder;

@@ -56,9 +57,10 @@ public abstract class FeatureConnection {
    private final ISipTransport mSipTransportBinder;
    protected final Object mLock = new Object();

    public FeatureConnection(Context context, int slotId, IImsConfig c, IImsRegistration r,
            ISipTransport s) {
    public FeatureConnection(Context context, int slotId, int subId, IImsConfig c,
            IImsRegistration r, ISipTransport s) {
        mSlotId = slotId;
        mSubId = subId;
        mContext = context;
        mRegistrationBinder = r;
        mConfigBinder = c;
@@ -223,6 +225,10 @@ public abstract class FeatureConnection {
        return state;
    }

    public int getSubId() {
        return mSubId;
    }

    /**
     * Internal method used to retrieve the feature status from the corresponding ImsService.
     */
+9 −9
Original line number Diff line number Diff line
@@ -104,7 +104,7 @@ public class FeatureConnector<U extends FeatureUpdates> {
        /**
         * ImsFeature manager is connected to the underlying IMS implementation.
         */
        void connectionReady(U manager) throws ImsException;
        void connectionReady(U manager, int subId) throws ImsException;

        /**
         * The underlying IMS implementation is unavailable and can not be used to communicate.
@@ -115,15 +115,15 @@ public class FeatureConnector<U extends FeatureUpdates> {
    private final IImsServiceFeatureCallback mCallback = new IImsServiceFeatureCallback.Stub() {

        @Override
        public void imsFeatureCreated(ImsFeatureContainer c) {
            log("imsFeatureCreated: " + c);
        public void imsFeatureCreated(ImsFeatureContainer c, int subId) {
            log("imsFeatureCreated: " + c + ", subId: " + subId);
            synchronized (mLock) {
                mManager.associate(c);
                mManager.associate(c, subId);
                mManager.updateFeatureCapabilities(c.getCapabilities());
                mDisconnectedReason = null;
            }
            // Notifies executor, so notify outside of lock
            imsStatusChanged(c.getState());
            imsStatusChanged(c.getState(), subId);
        }

        @Override
@@ -151,7 +151,7 @@ public class FeatureConnector<U extends FeatureUpdates> {
        }

        @Override
        public void imsStatusChanged(int status) {
        public void imsStatusChanged(int status, int subId) {
            log("imsStatusChanged: status=" + ImsFeature.STATE_LOG_MAP.get(status));
            final U manager;
            final boolean isReady;
@@ -173,7 +173,7 @@ public class FeatureConnector<U extends FeatureUpdates> {
            mExecutor.execute(() -> {
                try {
                    if (isReady) {
                        notifyReady(manager);
                        notifyReady(manager, subId);
                    } else {
                        notifyNotReady();
                    }
@@ -282,10 +282,10 @@ public class FeatureConnector<U extends FeatureUpdates> {
    }

    // Should be called on executor
    private void notifyReady(U manager) throws ImsException {
    private void notifyReady(U manager, int subId) throws ImsException {
        try {
            if (DBG) log("notifyReady");
            mListener.connectionReady(manager);
            mListener.connectionReady(manager, subId);
        }
        catch (ImsException e) {
            if(DBG) log("notifyReady exception: " + e.getMessage());
+2 −1
Original line number Diff line number Diff line
@@ -48,8 +48,9 @@ public interface FeatureUpdates {
     * Associate this Manager instance with the IMS Binder interfaces specified. This is usually
     * done by creating a FeatureConnection instance with these interfaces.
     * @param container Contains all of the related interfaces attached to a specific ImsFeature.
     * @param subId The subscription ID that the IMS Feature is being created for.
     */
    void associate(ImsFeatureContainer container);
    void associate(ImsFeatureContainer container, int subId);

    /**
     * Invalidate the previously associated Binder interfaces set in {@link #associate}.
+8 −166
Original line number Diff line number Diff line
@@ -20,19 +20,12 @@ import android.content.Context;
import android.os.IInterface;
import android.os.Looper;
import android.os.RemoteCallbackList;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;

import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public abstract class ImsCallbackAdapterManager<T extends IInterface> {
    private static final String TAG = "ImsCallbackAM";
@@ -40,62 +33,16 @@ public abstract class ImsCallbackAdapterManager<T extends IInterface> {
    private final Context mContext;
    private final Object mLock;
    private final int mSlotId;

    // Map of sub id -> List<callbacks> for sub id linked callbacks.
    private final SparseArray<Set<T>> mCallbackSubscriptionMap = new SparseArray<>();
    private final int mSubId;

    // List of all active callbacks to ImsService
    private final RemoteCallbackList<T> mRemoteCallbacks = new RemoteCallbackList<>();

    @VisibleForTesting
    public SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener;

    public ImsCallbackAdapterManager(Context context, Object lock, int slotId) {
    public ImsCallbackAdapterManager(Context context, Object lock, int slotId, int subId) {
        mContext = context;
        mLock = lock;
        mSlotId = slotId;

        if (Looper.myLooper() == null) {
            Looper.prepare();
        }

        // Must be created after Looper.prepare() is called, or else we will get an exception.
        mSubChangedListener = new SubscriptionManager.OnSubscriptionsChangedListener() {
            @Override
            public void onSubscriptionsChanged() {
                SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class);
                if (manager == null) {
                    Log.w(TAG + " [" + mSlotId + "]", "onSubscriptionsChanged: could not find "
                            + "SubscriptionManager.");
                    return;
                }

                List<SubscriptionInfo> subInfos = manager.getActiveSubscriptionInfoList(false);
                if (subInfos == null) {
                    subInfos = Collections.emptyList();
                }

                Set<Integer> newSubIds = subInfos.stream()
                        .map(SubscriptionInfo::getSubscriptionId)
                        .collect(Collectors.toSet());

                synchronized (mLock) {
                    Set<Integer> storedSubIds = new ArraySet<>(mCallbackSubscriptionMap.size());
                    for (int keyIndex = 0; keyIndex < mCallbackSubscriptionMap.size();
                            keyIndex++) {
                        storedSubIds.add(mCallbackSubscriptionMap.keyAt(keyIndex));
                    }

                    // Get the set of sub ids that are in storedSubIds that are not in newSubIds.
                    // This is the set of sub ids that need to be removed.
                    storedSubIds.removeAll(newSubIds);

                    for (Integer subId : storedSubIds) {
                        removeCallbacksForSubscription(subId);
                    }
                }
            }
        };
        mSubId = subId;
    }

    // Add a callback to the ImsFeature associated with this manager (independent of the
@@ -107,20 +54,20 @@ public abstract class ImsCallbackAdapterManager<T extends IInterface> {
            // Throws a IllegalStateException if this registration fails.
            registerCallback(localCallback);
            Log.i(TAG + " [" + mSlotId + "]", "Local callback added: " + localCallback);

            mRemoteCallbacks.register(localCallback);
        }
    }

    // Add a callback to be associated with a subscription. If that subscription is removed,
    // remove the callback and notify the callback that the subscription has been removed.
    // Add a callback to be associated with a subscription.
    public void addCallbackForSubscription(T localCallback, int subId) {
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            Log.w(TAG + " [" + mSlotId + "]", "add callback: invalid subId " + subId);
        if (!SubscriptionManager.isValidSubscriptionId(subId) || mSubId != subId) {
            Log.w(TAG + " [" + mSlotId + ", " + mSubId + "]", "add callback: invalid subId or"
                    + " inactive subID " + subId);
            return;
        }
        synchronized (mLock) {
            addCallback(localCallback);
            linkCallbackToSubscription(localCallback, subId);
        }
    }

@@ -135,110 +82,6 @@ public abstract class ImsCallbackAdapterManager<T extends IInterface> {
        }
    }

    // Remove an existing callback that has been linked to a subscription.
    public void removeCallbackForSubscription(T localCallback, int subId) {
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            Log.w(TAG + " [" + mSlotId + "]", "remove callback: invalid subId " + subId);
            return;
        }
        synchronized (mLock) {
            removeCallback(localCallback);
            unlinkCallbackFromSubscription(localCallback, subId);
        }
    }

    // Links a callback to be tracked by a subscription. If it goes away, emove.
    private void linkCallbackToSubscription(T callback, int subId) {
        synchronized (mLock) {
            if (mCallbackSubscriptionMap.size() == 0) {
                // we are about to add the first entry to the map, register for subscriptions
                //changed listener.
                registerForSubscriptionsChanged();
            }
            Set<T> callbacksPerSub = mCallbackSubscriptionMap.get(subId);
            if (callbacksPerSub == null) {
                // the callback list has not been created yet for this subscription.
                callbacksPerSub = new ArraySet<>();
                mCallbackSubscriptionMap.put(subId, callbacksPerSub);
            }
            callbacksPerSub.add(callback);
        }
    }

    // Unlink the callback from the associated subscription.
    private void unlinkCallbackFromSubscription(T callback, int subId) {
        synchronized (mLock) {
            Set<T> callbacksPerSub = mCallbackSubscriptionMap.get(subId);
            if (callbacksPerSub != null) {
                callbacksPerSub.remove(callback);
                if (callbacksPerSub.isEmpty()) {
                    mCallbackSubscriptionMap.remove(subId);
                }
            }
            if (mCallbackSubscriptionMap.size() == 0) {
                unregisterForSubscriptionsChanged();
            }
        }
    }

    // Removes all of the callbacks that have been registered to the subscription specified.
    // This happens when Telephony sends an indication that the subscriptions have changed.
    private void removeCallbacksForSubscription(int subId) {
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            Log.w(TAG + " [" + mSlotId + "]", "remove all callbacks: invalid subId " + subId);
            return;
        }
        synchronized (mLock) {
            Set<T> callbacksPerSub = mCallbackSubscriptionMap.get(subId);
            if (callbacksPerSub == null) {
                // no callbacks registered for this subscription.
                return;
            }
            // clear all registered callbacks in the subscription map for this subscription.
            mCallbackSubscriptionMap.remove(subId);
            for (T callback : callbacksPerSub) {
                removeCallback(callback);
            }
            // If there are no more callbacks being tracked, remove subscriptions changed
            // listener.
            if (mCallbackSubscriptionMap.size() == 0) {
                unregisterForSubscriptionsChanged();
            }
        }
    }

    // Clear the Subscription -> Callback map because the ImsService connection is no longer
    // current.
    private void clearCallbacksForAllSubscriptions() {
        synchronized (mLock) {
            List<Integer> keys = new ArrayList<>();
            for (int keyIndex = 0; keyIndex < mCallbackSubscriptionMap.size(); keyIndex++) {
                keys.add(mCallbackSubscriptionMap.keyAt(keyIndex));
            }
            keys.forEach(this::removeCallbacksForSubscription);
        }
    }

    private void registerForSubscriptionsChanged() {
        SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class);
        if (manager != null) {
            manager.addOnSubscriptionsChangedListener(mSubChangedListener);
        } else {
            Log.w(TAG + " [" + mSlotId + "]", "registerForSubscriptionsChanged: could not find"
                    + " SubscriptionManager.");
        }
    }

    private void unregisterForSubscriptionsChanged() {
        SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class);
        if (manager != null) {
        manager.removeOnSubscriptionsChangedListener(mSubChangedListener);
        } else {
            Log.w(TAG + " [" + mSlotId + "]", "unregisterForSubscriptionsChanged: could not"
                    + " find SubscriptionManager.");
        }
    }

    // The ImsService these callbacks are registered to has become unavailable or crashed, or
    // the ImsResolver has switched to a new ImsService. In these cases, clean up all existing
    // callbacks.
@@ -250,7 +93,6 @@ public abstract class ImsCallbackAdapterManager<T extends IInterface> {
                unregisterCallback(callbackItem);
                mRemoteCallbacks.unregister(callbackItem);
            }
            clearCallbacksForAllSubscriptions();
            Log.i(TAG + " [" + mSlotId + "]", "Closing connection and clearing callbacks");
        }
    }
+17 −11
Original line number Diff line number Diff line
@@ -59,7 +59,7 @@ public class ImsFeatureBinderRepository {
            mExecutor = e;
        }

        public void notifyFeatureCreatedOrRemoved(ImsFeatureContainer connector) {
        public void notifyFeatureCreatedOrRemoved(ImsFeatureContainer connector, int subId) {
            if (connector == null) {
                mExecutor.execute(() -> {
                    try {
@@ -73,7 +73,7 @@ public class ImsFeatureBinderRepository {
            else {
                mExecutor.execute(() -> {
                    try {
                        mCallback.imsFeatureCreated(connector);
                        mCallback.imsFeatureCreated(connector, subId);
                    } catch (RemoteException e) {
                        // This listener will eventually be caught and removed during stale checks.
                    }
@@ -81,10 +81,10 @@ public class ImsFeatureBinderRepository {
            }
        }

        public void notifyStateChanged(int state) {
        public void notifyStateChanged(int state, int subId) {
            mExecutor.execute(() -> {
                try {
                    mCallback.imsStatusChanged(state);
                    mCallback.imsStatusChanged(state, subId);
                } catch (RemoteException e) {
                    // This listener will eventually be caught and removed during stale checks.
                }
@@ -132,6 +132,7 @@ public class ImsFeatureBinderRepository {
     */
    private static final class UpdateMapper {
        public final int phoneId;
        public int subId;
        public final @ImsFeature.FeatureType int imsFeatureType;
        private final List<ListenerContainer> mListeners = new ArrayList<>();
        private ImsFeatureContainer mFeatureContainer;
@@ -150,7 +151,7 @@ public class ImsFeatureBinderRepository {
                mFeatureContainer = c;
                listeners = copyListenerList(mListeners);
            }
            listeners.forEach(l -> l.notifyFeatureCreatedOrRemoved(mFeatureContainer));
            listeners.forEach(l -> l.notifyFeatureCreatedOrRemoved(mFeatureContainer, subId));
        }

        public ImsFeatureContainer removeFeatureContainer() {
@@ -162,7 +163,7 @@ public class ImsFeatureBinderRepository {
                mFeatureContainer = null;
                listeners = copyListenerList(mListeners);
            }
            listeners.forEach(l -> l.notifyFeatureCreatedOrRemoved(mFeatureContainer));
            listeners.forEach(l -> l.notifyFeatureCreatedOrRemoved(mFeatureContainer, subId));
            return oldContainer;
        }

@@ -184,7 +185,7 @@ public class ImsFeatureBinderRepository {
            }
            // Do not call back until the feature container has been set.
            if (featureContainer != null) {
                c.notifyFeatureCreatedOrRemoved(featureContainer);
                c.notifyFeatureCreatedOrRemoved(featureContainer, subId);
            }
        }

@@ -213,7 +214,7 @@ public class ImsFeatureBinderRepository {
            }
            // Only update if the feature container is set.
            if (featureContainer != null) {
                listeners.forEach(l -> l.notifyStateChanged(newState));
                listeners.forEach(l -> l.notifyStateChanged(newState, subId));
            }
        }

@@ -236,6 +237,10 @@ public class ImsFeatureBinderRepository {
            }
        }

        public void updateSubId(int newSubId) {
            subId = newSubId;
        }

        @GuardedBy("mLock")
        private void removeStaleListeners() {
            List<ListenerContainer> staleListeners = mListeners.stream().filter(
@@ -337,14 +342,15 @@ public class ImsFeatureBinderRepository {
     * @param newConnection A Container containing the IBinder interface connections associated with
     *                      the ImsFeature type.
     */
    public void addConnection(int phoneId, @ImsFeature.FeatureType int type,
    public void addConnection(int phoneId, int subId, @ImsFeature.FeatureType int type,
            @Nullable ImsFeatureContainer newConnection) {
        if (type < 0 || type >= ImsFeature.FEATURE_MAX) {
            throw new IllegalArgumentException("The type must valid");
        }
        logInfoLineLocked(phoneId, "addConnection, type=" + ImsFeature.FEATURE_LOG_MAP.get(type)
                + ", conn=" + newConnection);
        logInfoLineLocked(phoneId, "addConnection, subId=" + subId + ", type="
                + ImsFeature.FEATURE_LOG_MAP.get(type) + ", conn=" + newConnection);
        UpdateMapper m = getUpdateMapper(phoneId, type);
        m.updateSubId(subId);
        m.addFeatureContainer(newConnection);
    }

Loading