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

Commit 4841c770 authored by Brad Ebinger's avatar Brad Ebinger Committed by Gerrit Code Review
Browse files

Merge "Remove package if misbehaving when bound"

parents 059a3de7 f0177136
Loading
Loading
Loading
Loading
+18 −2
Original line number Diff line number Diff line
@@ -481,6 +481,13 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
                    Log.w(TAG, "onError: " + name + "returned with an error result");
                    scheduleQueryForFeatures(name, DELAY_DYNAMIC_QUERY_MS);
                }

                @Override
                public void onPermanentError(ComponentName name) {
                    Log.w(TAG, "onPermanentError: component=" + name);
                    mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE,
                            name.getPackageName()).sendToTarget();
                }
            };

    // Array index corresponds to slot Id associated with the service package name.
@@ -840,8 +847,8 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
        }
    }

    // Remove the ImsService from the cache. At this point, the ImsService will have already been
    // killed.
    // Remove the ImsService from the cache. This may have been due to the ImsService being removed
    // from the device or was returning permanent errors when bound.
    // Called from the handler ONLY
    private boolean maybeRemovedImsService(String packageName) {
        ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName);
@@ -1066,6 +1073,15 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
        handleFeaturesChanged(controller.getComponentName(), config.getServiceFeatures());
    }

    @Override
    public void imsServiceBindPermanentError(ComponentName name) {
        if (name == null) {
            return;
        }
        Log.w(TAG, "imsServiceBindPermanentError: component=" + name);
        mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE, name.getPackageName()).sendToTarget();
    }

    /**
     * Determines if the features specified should cause a bind or keep a binding active to an
     * ImsService.
+41 −18
Original line number Diff line number Diff line
@@ -71,10 +71,9 @@ public class ImsServiceController {
            synchronized (mLock) {
                mIsBound = true;
                mIsBinding = false;
                try {
                    Log.d(LOG_TAG, "ImsService(" + name + "): onServiceConnected with binder: "
                            + service);
                if (service != null) {
                    try {
                    setServiceController(service);
                    notifyImsServiceReady();
                    // create all associated features in the ImsService
@@ -92,7 +91,6 @@ public class ImsServiceController {
                }
            }
        }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
@@ -111,10 +109,27 @@ public class ImsServiceController {
                mIsBound = false;
            }
            cleanupConnection();
            // according to the docs, we should fully unbind before rebinding again.
            mContext.unbindService(mImsServiceConnection);
            Log.w(LOG_TAG, "ImsService(" + name + "): onBindingDied. Starting rebind...");
            startDelayedRebindToService();
        }

        @Override
        public void onNullBinding(ComponentName name) {
            Log.w(LOG_TAG, "ImsService(" + name + "): onNullBinding. Removing.");
            synchronized (mLock) {
                mIsBinding = false;
                mIsBound = false;
            }
            cleanupConnection();
            if (mCallbacks != null) {
                // Will trigger an unbind.
                mCallbacks.imsServiceBindPermanentError(getComponentName());
            }
        }

        // Does not clear features, just removes all active features.
        private void cleanupConnection() {
            cleanupAllFeatures();
            cleanUpService();
@@ -151,6 +166,12 @@ public class ImsServiceController {
         */
        void imsServiceFeaturesChanged(ImsFeatureConfiguration config,
                ImsServiceController controller);

        /**
         * Called by the ImsServiceController when there has been an error binding that is
         * not recoverable, such as the ImsService returning a null binder.
         */
        void imsServiceBindPermanentError(ComponentName name);
    }

    /**
@@ -385,6 +406,8 @@ public class ImsServiceController {
            removeImsServiceFeatureCallbacks();
            Log.i(LOG_TAG, "Unbinding ImsService: " + mComponentName);
            mContext.unbindService(mImsServiceConnection);
            mIsBound = false;
            mIsBinding = false;
            cleanUpService();
        }
    }
+16 −2
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.telephony.ims.aidl.IImsServiceController;
import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.util.Log;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@@ -71,8 +72,9 @@ public class ImsServiceFeatureQueryManager {
            if (service != null) {
                queryImsFeatures(IImsServiceController.Stub.asInterface(service));
            } else {
                Log.w(LOG_TAG, "onServiceConnected: " + name + " binder null, cleaning up.");
                Log.w(LOG_TAG, "onServiceConnected: " + name + " binder null.");
                cleanup();
                mListener.onPermanentError(name);
            }
        }

@@ -91,7 +93,14 @@ public class ImsServiceFeatureQueryManager {
                mListener.onError(mName);
                return;
            }
            Set<ImsFeatureConfiguration.FeatureSlotPair> servicePairs = config.getServiceFeatures();
            Set<ImsFeatureConfiguration.FeatureSlotPair> servicePairs;
            if (config == null) {
                // ensure that if the ImsService sent a null config, we return an empty feature
                // set to the ImsResolver.
                servicePairs = Collections.emptySet();
            } else {
                servicePairs = config.getServiceFeatures();
            }
            // Complete, remove from active queries and notify.
            cleanup();
            mListener.onComplete(mName, servicePairs);
@@ -117,6 +126,11 @@ public class ImsServiceFeatureQueryManager {
         * Called when a query has failed and should be retried.
         */
        void onError(ComponentName name);

        /**
         * Called when a query has failed due to a permanent error and should not be retried.
         */
        void onPermanentError(ComponentName name);
    }

    // Maps an active ImsService query (by Package Name String) its query.
+103 −0
Original line number Diff line number Diff line
@@ -830,6 +830,97 @@ public class ImsResolverTest extends ImsTestBase {
        verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
    }

    /**
     * If a misbehaving ImsService returns null for the Binder connection when we perform a dynamic
     * feature query, verify we never perform a full bind for any features.
     */
    @Test
    @SmallTest
    public void testPermanentBindFailureDuringFeatureQuery() throws RemoteException {
        setupResolver(1/*numSlots*/);
        List<ResolveInfo> info = new ArrayList<>();
        Set<String> deviceFeatures = new HashSet<>();
        deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
        deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
        // Set the carrier override package for slot 0
        setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
        HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
        // Carrier service doesn't support the voice feature.
        carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
        // Use device default package, which will load the ImsService that the device provides
        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
        setupPackageQuery(info);
        ImsServiceController deviceController = mock(ImsServiceController.class);
        ImsServiceController carrierController = mock(ImsServiceController.class);
        setImsServiceControllerFactory(deviceController, carrierController);

        startBindCarrierConfigAlreadySet();
        // dynamic query results in a failure.
        setupDynamicQueryFeaturesFailure(TEST_CARRIER_DEFAULT_NAME, 1);

        // Verify that a bind never occurs for the carrier controller.
        verify(carrierController, never()).bind(any());
        verify(carrierController, never()).unbind();
        // Verify that all features are used to bind to the device ImsService since the carrier
        // ImsService failed to bind properly.
        HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
                convertToHashSet(deviceFeatures, 0);
        verify(deviceController).bind(deviceFeatureSet);
        verify(deviceController, never()).unbind();
        assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
    }

    /**
     * If a misbehaving ImsService returns null for the Binder connection when we perform bind,
     * verify the service is disconnected.
     */
    @Test
    @SmallTest
    public void testPermanentBindFailureDuringBind() throws RemoteException {
        setupResolver(1/*numSlots*/);
        List<ResolveInfo> info = new ArrayList<>();
        Set<String> deviceFeatures = new HashSet<>();
        deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
        deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
        // Set the carrier override package for slot 0
        setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
        HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
        // Carrier service doesn't support the voice feature.
        carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
        // Use device default package, which will load the ImsService that the device provides
        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
        setupPackageQuery(info);
        ImsServiceController deviceController = mock(ImsServiceController.class);
        ImsServiceController carrierController = mock(ImsServiceController.class);
        setImsServiceControllerFactory(deviceController, carrierController);

        startBindCarrierConfigAlreadySet();
        setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);

        // Verify that a bind never occurs for the carrier controller.
        verify(carrierController).bind(carrierFeatures);
        verify(carrierController, never()).unbind();
        // Verify that all features that are not defined in the carrier override are bound in the
        // device controller (including emergency voice for slot 0)
        HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
                convertToHashSet(deviceFeatures, 0);
        deviceFeatureSet.removeAll(carrierFeatures);
        verify(deviceController).bind(deviceFeatureSet);
        verify(deviceController, never()).unbind();
        assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());

        mTestImsResolver.imsServiceBindPermanentError(TEST_CARRIER_DEFAULT_NAME);
        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
        verify(carrierController).unbind();
        // Verify that the device ImsService features are changed to include the ones previously
        // taken by the carrier app.
        HashSet<ImsFeatureConfiguration.FeatureSlotPair> originalDeviceFeatureSet =
                convertToHashSet(deviceFeatures, 0);
        verify(deviceController).changeImsServiceFeatures(originalDeviceFeatureSet);
    }

    private void setupResolver(int numSlots) {
        when(mMockContext.getSystemService(eq(Context.CARRIER_CONFIG_SERVICE))).thenReturn(
                mMockCarrierConfigManager);
@@ -944,6 +1035,18 @@ public class ImsResolverTest extends ImsTestBase {
        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
    }

    private void setupDynamicQueryFeaturesFailure(ComponentName name, int times) {
        // wait for schedule to happen
        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
        // ensure that startQuery was called
        when(mMockQueryManager.startQuery(any(ComponentName.class), any(String.class)))
                .thenReturn(true);
        verify(mMockQueryManager, times(times)).startQuery(eq(name), any(String.class));
        mDynamicQueryListener.onPermanentError(name);
        // wait for handling of onPermanentError
        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
    }

    public void packageChanged(String packageName) {
        // Tell the package manager that a new device feature is installed
        Intent addPackageIntent = new Intent();
+35 −4
Original line number Diff line number Diff line
@@ -294,6 +294,26 @@ public class ImsServiceControllerTest extends ImsTestBase {
        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(2));
    }

    /**
     * Ensures that imsServiceBindPermanentError is called when the binder returns null.
     */
    @SmallTest
    @Test
    public void testBindServiceAndReturnedNull() throws RemoteException {
        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
        // Slot 1, MMTel
        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
        // Slot 1, RCS
        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));

        bindAndNullServiceError(testFeatures);

        verify(mMockCallbacks, never()).imsServiceFeatureCreated(anyInt(), anyInt(),
                eq(mTestImsServiceController));
        verify(mMockProxyCallbacks, never()).imsFeatureCreated(anyInt(), anyInt());
        verify(mMockCallbacks).imsServiceBindPermanentError(eq(mTestComponentName));
    }

    /**
     * Ensures ImsService and ImsResolver are notified when a feature is added.
     */
@@ -499,16 +519,27 @@ public class ImsServiceControllerTest extends ImsTestBase {
        verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
    }

    private void bindAndNullServiceError(
            HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures) {
        ServiceConnection connection = bindService(testFeatures);
        connection.onNullBinding(mTestComponentName);
    }

    private ServiceConnection bindAndConnectService(
            HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures) {
        ServiceConnection connection = bindService(testFeatures);
        IImsServiceController.Stub controllerStub = mock(IImsServiceController.Stub.class);
        when(controllerStub.queryLocalInterface(any())).thenReturn(mMockServiceControllerBinder);
        connection.onServiceConnected(mTestComponentName, controllerStub);
        return connection;
    }

    private ServiceConnection bindService(
            HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures) {
        ArgumentCaptor<ServiceConnection> serviceCaptor =
                ArgumentCaptor.forClass(ServiceConnection.class);
        assertTrue(mTestImsServiceController.bind(testFeatures));
        verify(mMockContext).bindService(any(), serviceCaptor.capture(), anyInt());
        IImsServiceController.Stub controllerStub = mock(IImsServiceController.Stub.class);
        when(controllerStub.queryLocalInterface(any())).thenReturn(mMockServiceControllerBinder);
        serviceCaptor.getValue().onServiceConnected(mTestComponentName,
                controllerStub);
        return serviceCaptor.getValue();
    }
}