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

Commit fa79aa44 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Manually call removeImsFeature in IMS compat fw"

parents 27e008ef 4c8c13c2
Loading
Loading
Loading
Loading
+30 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.internal.telephony.ims;

import android.content.ComponentName;
import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
@@ -36,6 +37,7 @@ import com.android.ims.ImsFeatureBinderRepository;
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.ims.internal.IImsMMTelFeature;
import com.android.ims.internal.IImsServiceController;
import com.android.internal.annotations.VisibleForTesting;

/**
 * Manages the Binding lifecycle of one ImsService as well as the relevant ImsFeatures that the
@@ -57,10 +59,33 @@ public class ImsServiceControllerCompat extends ImsServiceController {
    private final SparseArray<ImsRegistrationCompatAdapter> mRegCompatAdapters =
            new SparseArray<>();

    private final MmTelFeatureCompatFactory mMmTelFeatureFactory;

    /**
     * Used to inject test instances of MmTelFeatureCompatAdapter
     */
    @VisibleForTesting
    public interface MmTelFeatureCompatFactory {
        /**
         * @return A new instance of {@link MmTelFeatureCompatAdapter}
         */
        MmTelFeatureCompatAdapter create(Context context, int slotId,
                MmTelInterfaceAdapter compatFeature);
    }

    public ImsServiceControllerCompat(Context context, ComponentName componentName,
            ImsServiceController.ImsServiceControllerCallbacks callbacks,
            ImsFeatureBinderRepository repo) {
        super(context, componentName, callbacks, repo);
        mMmTelFeatureFactory = MmTelFeatureCompatAdapter::new;
    }

    @VisibleForTesting
    public ImsServiceControllerCompat(Context context, ComponentName componentName,
            ImsServiceControllerCallbacks callbacks, Handler handler, RebindRetry rebindRetry,
            ImsFeatureBinderRepository repo, MmTelFeatureCompatFactory factory) {
        super(context, componentName, callbacks, handler, rebindRetry, repo);
        mMmTelFeatureFactory = factory;
    }

    @Override
@@ -186,6 +211,10 @@ public class ImsServiceControllerCompat extends ImsServiceController {
    protected final void removeImsFeature(int slotId, int featureType)
            throws RemoteException {
        if (featureType == ImsFeature.MMTEL) {
            MmTelFeatureCompatAdapter adapter = mMmTelCompatAdapters.get(slotId, null);
            // Need to manually call onFeatureRemoved here, since this is normally called by the
            // ImsService itself.
            if (adapter != null) adapter.onFeatureRemoved();
            mMmTelCompatAdapters.remove(slotId);
            mRegCompatAdapters.remove(slotId);
            mConfigCompatAdapters.remove(slotId);
@@ -218,7 +247,7 @@ public class ImsServiceControllerCompat extends ImsServiceController {
    private IImsMmTelFeature createMMTelCompat(int slotId)
            throws RemoteException {
        MmTelInterfaceAdapter interfaceAdapter = getInterface(slotId);
        MmTelFeatureCompatAdapter mmTelAdapter = new MmTelFeatureCompatAdapter(mContext, slotId,
        MmTelFeatureCompatAdapter mmTelAdapter = mMmTelFeatureFactory.create(mContext, slotId,
                interfaceAdapter);
        mMmTelCompatAdapters.put(slotId, mmTelAdapter);
        ImsRegistrationCompatAdapter regAdapter = new ImsRegistrationCompatAdapter();
+179 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.internal.telephony.ims;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.ComponentName;
import android.content.Context;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.test.suitebuilder.annotation.SmallTest;

import androidx.test.runner.AndroidJUnit4;

import com.android.ims.ImsFeatureBinderRepository;
import com.android.ims.ImsFeatureContainer;
import com.android.ims.internal.IImsConfig;
import com.android.ims.internal.IImsMMTelFeature;
import com.android.ims.internal.IImsServiceController;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;

import java.util.HashSet;

@RunWith(AndroidJUnit4.class)
public class ImsServiceControllerCompatTest extends ImsTestBase {

    private static final int SLOT_0 = 0;

    private static final ImsServiceController.RebindRetry REBIND_RETRY =
            new ImsServiceController.RebindRetry() {
                @Override
                public long getStartDelay() {
                    return 50;
                }

                @Override
                public long getMaximumDelay() {
                    return 1000;
                }
            };

    @Mock MmTelInterfaceAdapter mMockMmTelInterfaceAdapter;
    @Mock IImsMMTelFeature mMockRemoteMMTelFeature;
    @Mock IBinder mMockMMTelBinder;
    @Mock IImsServiceController mMockServiceControllerBinder;
    @Mock ImsServiceController.ImsServiceControllerCallbacks mMockCallbacks;
    @Mock IImsConfig mMockImsConfig;
    @Mock Context mMockContext;
    private final ComponentName mTestComponentName = new ComponentName("TestPkg",
            "ImsServiceControllerTest");
    private final Handler mHandler = new Handler(Looper.getMainLooper());
    private ImsServiceController mTestImsServiceController;
    private ImsFeatureBinderRepository mRepo;
    private MmTelFeatureCompatAdapter mMmTelCompatAdapterSpy;

    @Before
    @Override
    public void setUp() throws Exception {
        super.setUp();
        mRepo = new ImsFeatureBinderRepository();
        // Can't mock MmTelFeatureCompatAdapter due to final getBinder() method.
        mMmTelCompatAdapterSpy = spy(new MmTelFeatureCompatAdapter(mMockContext, SLOT_0,
                mMockMmTelInterfaceAdapter));
        mTestImsServiceController = new ImsServiceControllerCompat(mMockContext, mTestComponentName,
                mMockCallbacks, mHandler, REBIND_RETRY, mRepo,
                (a, b, c) -> mMmTelCompatAdapterSpy);
        when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true);
        when(mMockServiceControllerBinder.createMMTelFeature(anyInt()))
                .thenReturn(mMockRemoteMMTelFeature);
        when(mMockRemoteMMTelFeature.getConfigInterface()).thenReturn(mMockImsConfig);
        when(mMockRemoteMMTelFeature.asBinder()).thenReturn(mMockMMTelBinder);
    }


    @After
    @Override
    public void tearDown() throws Exception {
        mTestImsServiceController.stopBackoffTimerForTesting();
        mTestImsServiceController = null;
        // Make sure the handler is empty before finishing the test.
        waitForHandlerAction(mHandler, 1000);
        super.tearDown();
    }

    /**
     * Tests that the MmTelFeatureCompatAdapter is cleaned up properly and ImsServiceController
     * callbacks are properly called when an ImsService is bound and then crashes.
     */
    @SmallTest
    @Test
    public void testBindServiceAndCrashCleanUp() throws RemoteException {
        ServiceConnection conn = bindAndConnectService();
        // add the MMTelFeature
        verify(mMockServiceControllerBinder).createMMTelFeature(SLOT_0);
        verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                eq(ImsFeature.FEATURE_MMTEL), any());
        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
                eq(mTestImsServiceController));
        validateMmTelFeatureContainerExists(SLOT_0);
        // Remove the feature
        conn.onBindingDied(mTestComponentName);
        verify(mMmTelCompatAdapterSpy).onFeatureRemoved();
        verify(mMockServiceControllerBinder).removeImsFeature(eq(SLOT_0),
                eq(ImsFeature.FEATURE_MMTEL));
        validateMmTelFeatureContainerDoesntExist(SLOT_0);
    }

    private void validateMmTelFeatureContainerExists(int slotId) {
        ImsFeatureContainer fc =
                mRepo.getIfExists(slotId, ImsFeature.FEATURE_MMTEL).orElse(null);
        assertNotNull("MMTEL FeatureContainer should not be null", fc);
        assertEquals("ImsServiceController did not report MmTelFeature to service repo correctly",
                mMmTelCompatAdapterSpy.getBinder(), fc.imsFeature);
        assertEquals(0, (android.telephony.ims.ImsService.CAPABILITY_EMERGENCY_OVER_MMTEL
                & fc.getCapabilities()));
    }

    private void validateMmTelFeatureContainerDoesntExist(int slotId) {
        ImsFeatureContainer fc =
                mRepo.getIfExists(slotId, ImsFeature.FEATURE_MMTEL).orElse(null);
        assertNull("FeatureContainer should be null", fc);
    }

    private ServiceConnection bindAndConnectService() {
        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
                ImsFeature.FEATURE_MMTEL));
        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());
        return serviceCaptor.getValue();
    }
}