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

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

Merge "Remove package if misbehaving when bound"

am: 4841c770

Change-Id: Id67a7be3928235f86a5107d650f02c3c99f8bf8f
parents 844443c1 4841c770
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();
    }
}