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

Commit 87565514 authored by James.cf Lin's avatar James.cf Lin
Browse files

[RCS] Implement the specific methods of RcsFeatureManager

Bug: 139260826
Test: atest FeatureConnectorTest; atest RcsFeatureManagerTest; atest RcsFeatureConnectionTest
Change-Id: I990813a298c298d6cce046a2547dd95f9b4e0aa0
parent 2fc8022d
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -214,7 +214,8 @@ public abstract class FeatureConnection {
        return mRegistrationBinder;
    }

    protected void checkServiceIsReady() throws RemoteException {
    @VisibleForTesting
    public void checkServiceIsReady() throws RemoteException {
        if (!sImsSupportedOnDevice) {
            throw new RemoteException("IMS is not supported on this device.");
        }
+263 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2019 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 com.android.ims;

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 = "ImsCallbackAdapterManager";

    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<>();

    // 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) {
        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);
                    }
                }
            }
        };
    }

    // Add a callback to the MmTelFeature associated with this manager (independent of the)
    // current subscription.
    public final void addCallback(T localCallback) {
        synchronized (mLock) {
            // Skip registering to callback subscription map here, because we are registering
            // for the slot, independent of subscription (deprecated behavior).
            // 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.
    public void addCallbackForSubscription(T localCallback, int subId) {
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            Log.w(TAG + " [" + mSlotId + "]", "add callback: invalid subId " + subId);
            return;
        }
        synchronized (mLock) {
            addCallback(localCallback);
            linkCallbackToSubscription(localCallback, subId);
        }
    }

    // Removes a callback associated with the MmTelFeature.
    public final void removeCallback(T localCallback) {
        Log.i(TAG + " [" + mSlotId + "]", "Local callback removed: " + localCallback);
        synchronized (mLock) {
            if (mRemoteCallbacks.unregister(localCallback)) {
                // Will only occur if we have record of this callback in mRemoteCallbacks.
                unregisterCallback(localCallback);
            }
        }
    }

    // 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.
    public final void close() {
        synchronized (mLock) {
            final int lastCallbackIndex = mRemoteCallbacks.getRegisteredCallbackCount() - 1;
            for(int ii = lastCallbackIndex; ii >= 0; ii --) {
                T callbackItem = mRemoteCallbacks.getRegisteredCallbackItem(ii);
                unregisterCallback(callbackItem);
                mRemoteCallbacks.unregister(callbackItem);
            }
            clearCallbacksForAllSubscriptions();
            Log.i(TAG + " [" + mSlotId + "]", "Closing connection and clearing callbacks");
        }
    }

    // A callback has been registered. Register that callback with the MmTelFeature.
    public abstract void registerCallback(T localCallback);

    // A callback has been removed, unregister that callback with the MmTelFeature.
    public abstract void unregisterCallback(T localCallback);
}
+3 −222
Original line number Diff line number Diff line
@@ -67,227 +67,8 @@ import java.util.stream.Collectors;
public class MmTelFeatureConnection extends FeatureConnection {
    protected static final String TAG = "MmTelFeatureConnection";

    // Manages callbacks to the associated MmTelFeature in mMmTelFeatureConnection.
    @VisibleForTesting
    public static abstract class CallbackAdapterManager<T extends IInterface> {
        private static final String TAG = "CallbackAdapterManager";

        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<>();
        // List of all active callbacks to ImsService
        private final RemoteCallbackList<T> mRemoteCallbacks = new RemoteCallbackList<>();
        @VisibleForTesting
        public SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener;

        public CallbackAdapterManager(Context context, Object lock, int slotId) {
            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);
                        }
                    }
                }
            };

        }

        // Add a callback to the MmTelFeature associated with this manager (independent of the)
        // current subscription.
        public final void addCallback(T localCallback) {
            synchronized (mLock) {
                // Skip registering to callback subscription map here, because we are registering
                // for the slot, independent of subscription (deprecated behavior).
                // 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.
        public final void addCallbackForSubscription(T localCallback, int subId) {
            if (!SubscriptionManager.isValidSubscriptionId(subId)) {
                return;
            }
            synchronized (mLock) {
                addCallback(localCallback);
                linkCallbackToSubscription(localCallback, subId);
            }
        }

        // Removes a callback associated with the MmTelFeature.
        public final void removeCallback(T localCallback) {
            Log.i(TAG + " [" + mSlotId + "]", "Local callback removed: " + localCallback);
            synchronized (mLock) {
                if (mRemoteCallbacks.unregister(localCallback)) {
                    // Will only occur if we have record of this callback in mRemoteCallbacks.
                    unregisterCallback(localCallback);
                }
            }
        }

        // Remove an existing callback that has been linked to a subscription.
        public final void removeCallbackForSubscription(T localCallback, int subId) {
            if (!SubscriptionManager.isValidSubscriptionId(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)) {
                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.
        public final void close() {
            synchronized (mLock) {
                final int lastCallbackIndex = mRemoteCallbacks.getRegisteredCallbackCount() - 1;
                for(int ii = lastCallbackIndex; ii >= 0; ii --) {
                    T callbackItem = mRemoteCallbacks.getRegisteredCallbackItem(ii);
                    unregisterCallback(callbackItem);
                    mRemoteCallbacks.unregister(callbackItem);
                }
                clearCallbacksForAllSubscriptions();
                Log.i(TAG + " [" + mSlotId + "]", "Closing connection and clearing callbacks");
            }
        }

        // A callback has been registered. Register that callback with the MmTelFeature.
        public abstract void registerCallback(T localCallback);

        // A callback has been removed, unregister that callback with the MmTelFeature.
        public abstract void unregisterCallback(T localCallback);
    }

    private class ImsRegistrationCallbackAdapter extends
            CallbackAdapterManager<IImsRegistrationCallback> {
            ImsCallbackAdapterManager<IImsRegistrationCallback> {

        public ImsRegistrationCallbackAdapter(Context context, Object lock) {
            super(context, lock, mSlotId);
@@ -328,7 +109,7 @@ public class MmTelFeatureConnection extends FeatureConnection {
        }
    }

    private class CapabilityCallbackManager extends CallbackAdapterManager<IImsCapabilityCallback> {
    private class CapabilityCallbackManager extends ImsCallbackAdapterManager<IImsCapabilityCallback> {

        public CapabilityCallbackManager(Context context, Object lock) {
            super(context, lock, mSlotId);
@@ -389,7 +170,7 @@ public class MmTelFeatureConnection extends FeatureConnection {
        }
    }

    private class ProvisioningCallbackManager extends CallbackAdapterManager<IImsConfigCallback> {
    private class ProvisioningCallbackManager extends ImsCallbackAdapterManager<IImsConfigCallback> {
        public ProvisioningCallbackManager (Context context, Object lock) {
            super(context, lock, mSlotId);
        }
+51 −52

File changed.

Preview size limit exceeded, changes collapsed.

+227 −59

File changed.

Preview size limit exceeded, changes collapsed.