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

Commit 0ae7db00 authored by Brad Ebinger's avatar Brad Ebinger Committed by Gerrit Code Review
Browse files

Merge "Moves IMS Emergency Calling availability out"

parents 0f34557b bb57af55
Loading
Loading
Loading
Loading
+21 −44
Original line number Diff line number Diff line
@@ -102,7 +102,6 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
    @VisibleForTesting
    public static class ImsServiceInfo {
        public ComponentName name;
        public boolean supportsEmergencyMmTel = false;
        // Determines if features were created from metadata in the manifest or through dynamic
        // query.
        public boolean featureFromMetadata = true;
@@ -124,19 +123,6 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
        }

        void replaceFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> newFeatures) {
            // Emergency is a special case, filter out and if any slot supports it, set it to true.
            // TODO: support per-slot emergency call filtering
            List<ImsFeatureConfiguration.FeatureSlotPair> emergencyFeatures = newFeatures.stream()
                    .filter(f -> f.featureType == ImsFeature.FEATURE_EMERGENCY_MMTEL)
                    .collect(Collectors.toList());
            if (!emergencyFeatures.isEmpty()) {
                Log.i(TAG, "replaceFeatures: emergency calls enabled.");
                supportsEmergencyMmTel = true;
                newFeatures.removeAll(emergencyFeatures);
            } else {
                Log.i(TAG, "replaceFeatures: emergency calls disabled.");
                supportsEmergencyMmTel = false;
            }
            mSupportedFeatures.clear();
            mSupportedFeatures.addAll(newFeatures);
        }
@@ -153,7 +139,6 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal

            ImsServiceInfo that = (ImsServiceInfo) o;

            if (supportsEmergencyMmTel != that.supportsEmergencyMmTel) return false;
            if (name != null ? !name.equals(that.name) : that.name != null) return false;
            if (!mSupportedFeatures.equals(that.mSupportedFeatures)) {
                return false;
@@ -167,7 +152,6 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
            // We do not include mSupportedFeatures in hashcode because the internal structure
            // changes after adding.
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + (supportsEmergencyMmTel ? 1 : 0);
            result = 31 * result + (controllerFactory != null ? controllerFactory.hashCode() : 0);
            return result;
        }
@@ -185,8 +169,6 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
                res.append(feature.featureType);
                res.append(") ");
            }
            res.append("], supportsEmergency=");
            res.append(supportsEmergencyMmTel);
            return res.toString();
        }
    }
@@ -571,19 +553,6 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
        return null;
    }

    /**
     * @return true if the ImsService associated with this slot supports emergency calling over IMS,
     * false if the call should be placed over circuit switch instead.
     */
    public boolean isEmergencyMmTelAvailable(int slotId) {
        ImsServiceController controller = getImsServiceController(slotId, ImsFeature.FEATURE_MMTEL);
        if (controller != null) {
            return controller.canPlaceEmergencyCalls();
        }
        Log.w(TAG, "isEmergencyMmTelAvailable: No controller found for slot " + slotId);
        return false;
    }

    @VisibleForTesting
    public ImsServiceController getImsServiceController(int slotId, int feature) {
        if (slotId < 0 || slotId >= mNumSlots) {
@@ -619,7 +588,7 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
        ImsServiceController controller = getImsServiceController(slotId, feature);

        if (controller != null) {
            controller.addImsServiceFeatureListener(callback);
            controller.addImsServiceFeatureCallback(callback);
            return controller;
        }
        return null;
@@ -791,14 +760,11 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
        // features because it is not carrier/device.
        HashSet<ImsFeatureConfiguration.FeatureSlotPair> features =
                calculateFeaturesToCreate(newInfo);
        if (features.size() > 0) {
        if (shouldFeaturesCauseBind(features)) {
            try {
                if (controller != null) {
                    Log.i(TAG, "Updating features for ImsService: "
                            + controller.getComponentName());
                    Log.d(TAG, "Updating canPlaceEmergencyCalls: "
                            + newInfo.supportsEmergencyMmTel);
                    controller.setCanPlaceEmergencyCalls(newInfo.supportsEmergencyMmTel);
                    Log.d(TAG, "Updating Features - New Features: " + features);
                    controller.changeImsServiceFeatures(features);
                } else {
@@ -835,12 +801,11 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
    private void bindImsServiceWithFeatures(ImsServiceInfo info,
            HashSet<ImsFeatureConfiguration.FeatureSlotPair> features) {
        // Only bind if there are features that will be created by the service.
        if (features.size() > 0) {
        if (shouldFeaturesCauseBind(features)) {
            // Check to see if an active controller already exists
            ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, info);
            if (controller != null) {
                Log.i(TAG, "ImsService connection exists, updating features " + features
                        + ", updating supports emergency calling: " + info.supportsEmergencyMmTel);
                Log.i(TAG, "ImsService connection exists, updating features " + features);
                try {
                    controller.changeImsServiceFeatures(features);
                    // Features have been set, there was an error adding/removing. When the
@@ -851,11 +816,9 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
            } else {
                controller = info.controllerFactory.create(mContext, info.name, this);
                Log.i(TAG, "Binding ImsService: " + controller.getComponentName()
                        + " with features: " + features + ", supports emergency calling: "
                        + info.supportsEmergencyMmTel);
                        + " with features: " + features);
                controller.bind(features);
            }
            controller.setCanPlaceEmergencyCalls(info.supportsEmergencyMmTel);
            mActiveControllers.put(info.name, controller);
        }
    }
@@ -937,6 +900,20 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
        removeImsController(slotId, feature);
    }

    /**
     * Determines if the features specified should cause a bind or keep a binding active to an
     * ImsService.
     * @return true if MMTEL or RCS features are present, false if they are not or only
     * EMERGENCY_MMTEL is specified.
     */
    private boolean shouldFeaturesCauseBind(
            HashSet<ImsFeatureConfiguration.FeatureSlotPair> features) {
        long bindableFeatures = features.stream()
                // remove all emergency features
                .filter(f -> f.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL).count();
        return bindableFeatures > 0;
    }

    // Possibly rebind to another ImsService if currently installed ImsServices were changed or if
    // the SIM card has changed.
    // Called from the handler ONLY
@@ -1129,7 +1106,7 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
        ImsServiceInfo info = new ImsServiceInfo(mNumSlots);
        info.name = mStaticComponent;
        info.controllerFactory = mImsServiceControllerFactoryStaticBindingCompat;
        info.supportsEmergencyMmTel = true;
        info.addFeatureForAllSlots(ImsFeature.FEATURE_EMERGENCY_MMTEL);
        info.addFeatureForAllSlots(ImsFeature.FEATURE_MMTEL);
        infos.add(info);
        return infos;
@@ -1164,7 +1141,7 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
                    if (serviceInfo.metaData != null) {
                        if (serviceInfo.metaData.getBoolean(METADATA_EMERGENCY_MMTEL_FEATURE,
                                false)) {
                            info.supportsEmergencyMmTel = true;
                            info.addFeatureForAllSlots(ImsFeature.FEATURE_EMERGENCY_MMTEL);
                        }
                        if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) {
                            info.addFeatureForAllSlots(ImsFeature.FEATURE_MMTEL);
+53 −53
Original line number Diff line number Diff line
@@ -154,11 +154,11 @@ public class ImsServiceController {
     */
    public interface ImsServiceControllerCallbacks {
        /**
         * Called by ImsServiceController when a new feature has been created.
         * Called by ImsServiceController when a new MMTEL or RCS feature has been created.
         */
        void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller);
        /**
         * Called by ImsServiceController when a new feature has been removed.
         * Called by ImsServiceController when a new MMTEL or RCS feature has been removed.
         */
        void imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller);
    }
@@ -203,8 +203,6 @@ public class ImsServiceController {
    private Set<IImsServiceFeatureCallback> mImsStatusCallbacks = new HashSet<>();
    // Only added or removed, never accessed on purpose.
    private Set<ImsFeatureStatusCallback> mFeatureStatusCallbacks = new HashSet<>();
    // Determines whether or not emergency calls can be placed through this ImsService.
    private boolean mCanPlaceEmergencyCalls = false;

    protected final Object mLock = new Object();
    protected final Context mContext;
@@ -393,7 +391,7 @@ public class ImsServiceController {
            }
            // Clean up all features
            changeImsServiceFeatures(new HashSet<>());
            removeImsServiceFeatureListener();
            removeImsServiceFeatureCallbacks();
            mImsServiceControllerBinder.unlinkToDeath(mImsDeathRecipient, 0);
            Log.i(LOG_TAG, "Unbinding ImsService: " + mComponentName);
            mContext.unbindService(mImsServiceConnection);
@@ -434,30 +432,6 @@ public class ImsServiceController {
        }
    }

    /**
     * Sets the ability for the ImsService to place emergency calls. This is controlled by the
     * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL} feature attribute, which is set either as metadata
     * in the AndroidManifest service definition or via dynamic query in
     * {@link ImsService#querySupportedImsFeatures()}.
     */
    public void setCanPlaceEmergencyCalls(boolean canPlaceEmergencyCalls) {
        synchronized (mLock) {
            mCanPlaceEmergencyCalls = canPlaceEmergencyCalls;
        }
    }

    /**
     * Whether or not the ImsService connected to this controller is able to place emergency calls
     * over IMS.
     * @return true if this ImsService can place emergency calls over IMS, false if the framework
     * should instead place the emergency call over circuit switch.
     */
    public boolean canPlaceEmergencyCalls() {
        synchronized (mLock) {
            return mCanPlaceEmergencyCalls;
        }
    }

    @VisibleForTesting
    public IImsServiceController getImsServiceController() {
        return mIImsServiceController;
@@ -480,9 +454,20 @@ public class ImsServiceController {
    /**
     * Add a callback to ImsManager that signals a new feature that the ImsServiceProxy can handle.
     */
    public void addImsServiceFeatureListener(IImsServiceFeatureCallback callback) {
    public void addImsServiceFeatureCallback(IImsServiceFeatureCallback callback) {
        synchronized (mLock) {
            mImsStatusCallbacks.add(callback);
            if (mImsFeatures == null || mImsFeatures.isEmpty()) {
                return;
            }
            // notify the new status callback of the features that are available.
            try {
                for (ImsFeatureConfiguration.FeatureSlotPair i : mImsFeatures) {
                    callback.imsFeatureCreated(i.slotId, i.featureType);
                }
            } catch (RemoteException e) {
                Log.w(LOG_TAG, "addImsServiceFeatureCallback: exception notifying callback");
            }
        }
    }

@@ -587,7 +572,8 @@ public class ImsServiceController {
        return mIImsServiceController != null;
    }

    private void removeImsServiceFeatureListener() {
    @VisibleForTesting
    public void removeImsServiceFeatureCallbacks() {
        synchronized (mLock) {
            mImsStatusCallbacks.clear();
        }
@@ -671,6 +657,7 @@ public class ImsServiceController {
            Log.w(LOG_TAG, "addImsServiceFeature called with null values.");
            return;
        }
        if (featurePair.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL) {
            ImsFeatureStatusCallback c = new ImsFeatureStatusCallback(featurePair.slotId,
                    featurePair.featureType);
            mFeatureStatusCallbacks.add(c);
@@ -679,7 +666,12 @@ public class ImsServiceController {
            addImsFeatureBinder(featurePair.slotId, featurePair.featureType, f);
            // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
            mCallbacks.imsServiceFeatureCreated(featurePair.slotId, featurePair.featureType, this);
        // Send callback to ImsServiceProxy to change supported ImsFeatures
        } else {
            // Don't update ImsService for emergency MMTEL feature.
            Log.i(LOG_TAG, "supports emergency calling on slot " + featurePair.slotId);
        }
        // Send callback to ImsServiceProxy to change supported ImsFeatures including emergency
        // MMTEL state.
        sendImsFeatureCreatedCallback(featurePair.slotId, featurePair.featureType);
    }

@@ -690,6 +682,7 @@ public class ImsServiceController {
            Log.w(LOG_TAG, "removeImsServiceFeature called with null values.");
            return;
        }
        if (featurePair.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL) {
            ImsFeatureStatusCallback callbackToRemove = mFeatureStatusCallbacks.stream().filter(c ->
                    c.mSlotId == featurePair.slotId && c.mFeatureType == featurePair.featureType)
                    .findFirst().orElse(null);
@@ -702,6 +695,10 @@ public class ImsServiceController {
            removeImsFeatureBinder(featurePair.slotId, featurePair.featureType);
            // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
            mCallbacks.imsServiceFeatureRemoved(featurePair.slotId, featurePair.featureType, this);
        } else {
            // Don't update ImsService for emergency MMTEL feature.
            Log.i(LOG_TAG, "doesn't support emergency calling on slot " + featurePair.slotId);
        }
        // Send callback to ImsServiceProxy to change supported ImsFeatures
        // Ensure that ImsServiceProxy callback occurs after ImsResolver callback. If an
        // ImsManager requests the ImsService while it is being removed in ImsResolver, this
@@ -759,7 +756,10 @@ public class ImsServiceController {
        }
        synchronized (mLock) {
            for (ImsFeatureConfiguration.FeatureSlotPair feature : mImsFeatures) {
                if (feature.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL) {
                    // don't update ImsServiceController for emergency MMTEL.
                    mCallbacks.imsServiceFeatureRemoved(feature.slotId, feature.featureType, this);
                }
                sendImsFeatureRemovedCallback(feature.slotId, feature.featureType);
            }
        }
+3 −8
Original line number Diff line number Diff line
@@ -202,8 +202,6 @@ public class ImsResolverTest extends ImsTestBase {
        // Set CarrierConfig default package name and make it available to the package manager
        setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
        HashSet<ImsFeatureConfiguration.FeatureSlotPair> features = new HashSet<>();
        features.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
                ImsFeature.FEATURE_EMERGENCY_MMTEL));
        features.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_MMTEL));
        features.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
        setupPackageQuery(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true);
@@ -215,8 +213,6 @@ public class ImsResolverTest extends ImsTestBase {
        verify(controller).bind(features);
        verify(controller, never()).unbind();
        assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName());

        verify(controller).setCanPlaceEmergencyCalls(eq(true));
    }

    /**
@@ -240,8 +236,6 @@ public class ImsResolverTest extends ImsTestBase {
        verify(controller).bind(features);
        verify(controller, never()).unbind();
        assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName());
        // ensure emergency calling is disabled
        verify(controller, never()).setCanPlaceEmergencyCalls(eq(true));

        packageChanged(TEST_CARRIER_DEFAULT_NAME.getPackageName());
        HashSet<ImsFeatureConfiguration.FeatureSlotPair> newFeatures = new HashSet<>();
@@ -254,7 +248,6 @@ public class ImsResolverTest extends ImsTestBase {
        //Verify new feature is added to the carrier override.
        // add all features for slot 0
        verify(controller, atLeastOnce()).changeImsServiceFeatures(newFeatures);
        verify(controller).setCanPlaceEmergencyCalls(eq(true));
    }

    /**
@@ -998,7 +991,9 @@ public class ImsResolverTest extends ImsTestBase {
        for (String f : features) {
            switch (f) {
                case ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE:
                    if (!sInfo.supportsEmergencyMmTel) {
                    if (!sInfo.getSupportedFeatures().contains(
                            new ImsFeatureConfiguration.FeatureSlotPair(0,
                                    ImsFeature.FEATURE_EMERGENCY_MMTEL))) {
                        return false;
                    }
                    break;
+57 −1
Original line number Diff line number Diff line
@@ -89,7 +89,7 @@ public class ImsServiceControllerTest extends ImsTestBase {
        super.setUp();
        mTestImsServiceController = new ImsServiceController(mMockContext, mTestComponentName,
                mMockCallbacks, mHandler, REBIND_RETRY);
        mTestImsServiceController.addImsServiceFeatureListener(mMockProxyCallbacks);
        mTestImsServiceController.addImsServiceFeatureCallback(mMockProxyCallbacks);
        when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true);
    }

@@ -171,6 +171,62 @@ public class ImsServiceControllerTest extends ImsTestBase {
                mTestImsServiceController.getImsServiceControllerBinder());
    }

    /**
     * Tests Emergency MMTEL ImsServiceController callbacks are properly called when an ImsService
     * is bound and connected.
     */
    @FlakyTest
    @Test
    public void testBindEmergencyMmTel() throws RemoteException {
        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
        // Slot 1, Emergency MMTel
        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 0));
        // Slot 1, MmTel
        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));

        bindAndConnectService(testFeatures);

        IBinder binder = mMockServiceControllerBinder.getBinder().asBinder();
        verify(binder).linkToDeath(any(), anyInt());
        verify(mMockServiceControllerBinder).createMMTelFeature(eq(1));
        // We do not want this callback to happen for emergency MMTEL
        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(1), eq(0),
                eq(mTestImsServiceController));
        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
                eq(mTestImsServiceController));
        // Make sure this callback happens, which will notify the framework of emergency calling
        // availability.
        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(0));
        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
        assertEquals(mMockServiceControllerBinder.getBinder(),
                mTestImsServiceController.getImsServiceControllerBinder());
    }

    /**
     * Tests that if a callback is added after the ImsServiceController is already bound, we get a
     * imsFeatureCreated callback.
     */
    @FlakyTest
    @Test
    public void testCallbacksHappenWhenAddedAfterBind() throws RemoteException {
        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
        // Slot 1, Emergency MMTel
        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 0));
        // Slot 1, MmTel
        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
        mTestImsServiceController.removeImsServiceFeatureCallbacks();

        bindAndConnectService(testFeatures);
        // add the callback after bind
        mTestImsServiceController.addImsServiceFeatureCallback(mMockProxyCallbacks);

        // Make sure this callback happens for Emergency MMTEL and MMTEL
        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(0));
        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
        assertEquals(mMockServiceControllerBinder.getBinder(),
                mTestImsServiceController.getImsServiceControllerBinder());
    }

    /**
     * Tests ImsServiceController callbacks are properly called when an ImsService is bound and
     * connected.