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

Commit 93581e6f authored by Jonggeon Kim's avatar Jonggeon Kim Committed by Automerger Merge Worker
Browse files

ImsService Subscription Notifications am: 9da546b4 am: a8fdf52c am: 1e7da7bb

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

Change-Id: I73abc49041edc58c4bbdeed811416eba40331013
parents aebf15f5 1e7da7bb
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