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

Commit da40162d authored by Brad Ebinger's avatar Brad Ebinger Committed by android-build-merger
Browse files

Merge "Moves IMS Emergency Calling availability out"

am: 0ae7db00

Change-Id: I6d9735c320230ff2d399ec2c72a11ecb696dd091
parents d868b495 0ae7db00
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.