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

Commit b1c69e0a authored by Hwangoo Park's avatar Hwangoo Park Committed by Android (Google) Code Review
Browse files

Merge "Add domain selection connection for SMS"

parents 251ef71f c9d93096
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -150,12 +150,11 @@ public class DomainSelectionController {
                c = new NormalCallDomainSelectionConnection(phone, this);
            }
        } else if (selectorType == SELECTOR_TYPE_SMS) {
            // TODO(ag/20126511) uncomment when SmSDomainSelectionConnection is ready.
            /*if (isEmergency) {
            if (isEmergency) {
                c = new EmergencySmsDomainSelectionConnection(phone, this);
            } else {
                c = new SmsDomainSelectionConnection(phone, this);
            }*/
            }
        }

        addConnection(c);
+146 −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.domainselection;

import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WLAN;
import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WWAN;

import android.annotation.NonNull;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.data.ApnSetting;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.data.AccessNetworksManager;
import com.android.internal.telephony.emergency.EmergencyStateTracker;

/**
 * Manages the information of request and the callback binder for an emergency SMS.
 */
public class EmergencySmsDomainSelectionConnection extends SmsDomainSelectionConnection {
    private final Object mLock = new Object();
    private @NonNull EmergencyStateTracker mEmergencyStateTracker;
    private @TransportType int mPreferredTransportType =
            AccessNetworkConstants.TRANSPORT_TYPE_INVALID;

    public EmergencySmsDomainSelectionConnection(
            Phone phone, DomainSelectionController controller) {
        this(phone, controller, EmergencyStateTracker.getInstance());
    }

    @VisibleForTesting
    public EmergencySmsDomainSelectionConnection(Phone phone,
            DomainSelectionController controller, EmergencyStateTracker tracker) {
        super(phone, controller, true);
        mTag = "DomainSelectionConnection-EmergencySMS";
        mEmergencyStateTracker = tracker;
    }

    @Override
    public void onWlanSelected() {
        synchronized (mLock) {
            if (mPreferredTransportType != AccessNetworkConstants.TRANSPORT_TYPE_INVALID) {
                logi("Domain selection completion is in progress");
                return;
            }

            mEmergencyStateTracker.onEmergencyTransportChanged(MODE_EMERGENCY_WLAN);

            // Change the transport type if the current preferred transport type for an emergency
            // is not {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}.
            AccessNetworksManager anm = mPhone.getAccessNetworksManager();
            if (anm.getPreferredTransport(ApnSetting.TYPE_EMERGENCY)
                    != AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
                changePreferredTransport(AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
                // The {@link #onDomainSlected()} will be called after the preferred transport
                // is successfully changed and notified from the {@link AccessNetworksManager}.
                return;
            }

            super.onWlanSelected();
        }
    }

    @Override
    public void onWwanSelected() {
        mEmergencyStateTracker.onEmergencyTransportChanged(MODE_EMERGENCY_WWAN);
    }

    @Override
    public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain) {
        synchronized (mLock) {
            if (mPreferredTransportType != AccessNetworkConstants.TRANSPORT_TYPE_INVALID) {
                logi("Domain selection completion is in progress");
                return;
            }

            if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
                // Change the transport type if the current preferred transport type for
                // an emergency is not {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN}.
                AccessNetworksManager anm = mPhone.getAccessNetworksManager();
                if (anm.getPreferredTransport(ApnSetting.TYPE_EMERGENCY)
                        != AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
                    changePreferredTransport(AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
                    // The {@link #onDomainSlected()} will be called after the preferred transport
                    // is successfully changed and notified from the {@link AccessNetworksManager}.
                    return;
                }
            }

            super.onDomainSelected(domain);
        }
    }

    @Override
    public void finishSelection() {
        AccessNetworksManager anm = mPhone.getAccessNetworksManager();

        synchronized (mLock) {
            if (mPreferredTransportType != AccessNetworkConstants.TRANSPORT_TYPE_INVALID) {
                mPreferredTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
                anm.unregisterForQualifiedNetworksChanged(mHandler);
            }
        }

        super.finishSelection();
    }

    @Override
    protected void onQualifiedNetworksChanged() {
        AccessNetworksManager anm = mPhone.getAccessNetworksManager();
        int preferredTransportType = anm.getPreferredTransport(ApnSetting.TYPE_EMERGENCY);

        synchronized (mLock) {
            if (preferredTransportType == mPreferredTransportType) {
                mPreferredTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
                super.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS);
                anm.unregisterForQualifiedNetworksChanged(mHandler);
            }
        }
    }

    private void changePreferredTransport(@TransportType int transportType) {
        logi("Change preferred transport: " + transportType);
        initHandler();
        mPreferredTransportType = transportType;
        AccessNetworksManager anm = mPhone.getAccessNetworksManager();
        anm.registerForQualifiedNetworksChanged(mHandler, EVENT_QUALIFIED_NETWORKS_CHANGED);
        mPhone.notifyEmergencyDomainSelected(transportType);
    }
}
+83 −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.domainselection;

import static android.telephony.DomainSelectionService.SELECTOR_TYPE_SMS;

import android.annotation.NonNull;
import android.telephony.Annotation.DisconnectCauses;
import android.telephony.DomainSelectionService;
import android.telephony.NetworkRegistrationInfo;

import com.android.internal.telephony.Phone;

import java.util.concurrent.CompletableFuture;

/**
 * Manages the information of request and the callback binder for SMS.
 */
public class SmsDomainSelectionConnection extends DomainSelectionConnection {
    private DomainSelectionConnectionCallback mCallback;

    public SmsDomainSelectionConnection(Phone phone, DomainSelectionController controller) {
        this(phone, controller, false);
        mTag = "DomainSelectionConnection-SMS";
    }

    protected SmsDomainSelectionConnection(Phone phone, DomainSelectionController controller,
            boolean isEmergency) {
        super(phone, SELECTOR_TYPE_SMS, isEmergency, controller);
    }

    @Override
    public void onWlanSelected() {
        super.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS);
    }

    @Override
    public void onSelectionTerminated(@DisconnectCauses int cause) {
        if (mCallback != null) mCallback.onSelectionTerminated(cause);
    }

    @Override
    public void finishSelection() {
        CompletableFuture<Integer> future = getCompletableFuture();

        if (future != null && !future.isDone()) {
            cancelSelection();
        } else {
            super.finishSelection();
        }
    }

    /**
     * Requests a domain selection for SMS.
     *
     * @param attr The attributes required to determine the domain.
     * @param callback A callback to notify an error of the domain selection.
     * @return A {@link CompletableFuture} to get the selected domain
     *         {@link NetworkRegistrationInfo#DOMAIN_PS} or
     *         {@link NetworkRegistrationInfo#DOMAIN_CS}.
     */
    public @NonNull CompletableFuture<Integer> requestDomainSelection(
            @NonNull DomainSelectionService.SelectionAttributes attr,
            @NonNull DomainSelectionConnectionCallback callback) {
        mCallback = callback;
        selectDomain(attr);
        return getCompletableFuture();
    }
}
+399 −0

File added.

Preview size limit exceeded, changes collapsed.

+212 −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.domainselection;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;

import android.os.Handler;
import android.os.HandlerThread;
import android.telephony.DisconnectCause;
import android.telephony.DomainSelectionService;
import android.telephony.DomainSelector;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.TransportSelectorCallback;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableLooper;

import androidx.test.runner.AndroidJUnit4;

import com.android.internal.telephony.Phone;

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

import java.util.concurrent.CompletableFuture;

/**
 * Unit tests for SmsDomainSelectionConnection.
 */
@RunWith(AndroidJUnit4.class)
public class SmsDomainSelectionConnectionTest {
    private static final int SLOT_ID = 0;
    private static final int SUB_ID = 1;

    @Mock private Phone mPhone;
    @Mock private DomainSelectionController mDsController;
    @Mock private DomainSelectionConnection.DomainSelectionConnectionCallback mDscCallback;
    @Mock private DomainSelector mDomainSelector;

    private Handler mHandler;
    private TestableLooper mTestableLooper;
    private DomainSelectionService.SelectionAttributes mDsAttr;
    private SmsDomainSelectionConnection mDsConnection;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);

        HandlerThread handlerThread = new HandlerThread(
                SmsDomainSelectionConnectionTest.class.getSimpleName());
        handlerThread.start();

        mHandler = new Handler(handlerThread.getLooper());
        mDsConnection = new SmsDomainSelectionConnection(mPhone, mDsController);
        mDsConnection.getTransportSelectorCallback().onCreated(mDomainSelector);
        mDsAttr = new DomainSelectionService.SelectionAttributes.Builder(
                SLOT_ID, SUB_ID, DomainSelectionService.SELECTOR_TYPE_SMS).build();
    }

    @After
    public void tearDown() throws Exception {
        if (mTestableLooper != null) {
            mTestableLooper.destroy();
            mTestableLooper = null;
        }

        if (mHandler != null) {
            mHandler.getLooper().quit();
            mHandler = null;
        }

        mDomainSelector = null;
        mDsAttr = null;
        mDsConnection = null;
        mDscCallback = null;
        mDsController = null;
        mPhone = null;
    }

    @Test
    @SmallTest
    public void testRequestDomainSelection() {
        CompletableFuture<Integer> future =
                mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);

        assertNotNull(future);
        verify(mDsController).selectDomain(eq(mDsAttr), any(TransportSelectorCallback.class));
    }

    @Test
    @SmallTest
    public void testOnWlanSelected() throws Exception {
        setUpTestableLooper();
        CompletableFuture<Integer> future =
                mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
        future.thenAcceptAsync((domain) -> {
            assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
        }, mHandler::post);

        mDsConnection.onWlanSelected();
        processAllMessages();

        assertTrue(future.isDone());
    }

    @Test
    @SmallTest
    public void testOnSelectionTerminated() {
        CompletableFuture<Integer> future =
                mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
        mDsConnection.onSelectionTerminated(DisconnectCause.LOCAL);

        assertFalse(future.isDone());
        verify(mDscCallback).onSelectionTerminated(eq(DisconnectCause.LOCAL));
    }

    @Test
    @SmallTest
    public void testOnDomainSelectedPs() throws Exception {
        setUpTestableLooper();
        CompletableFuture<Integer> future =
                mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
        future.thenAcceptAsync((domain) -> {
            assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
        }, mHandler::post);

        mDsConnection.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS);
        processAllMessages();

        assertTrue(future.isDone());
    }

    @Test
    @SmallTest
    public void testOnDomainSelectedCs() throws Exception {
        setUpTestableLooper();
        CompletableFuture<Integer> future =
                mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
        future.thenAcceptAsync((domain) -> {
            assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_CS), domain);
        }, mHandler::post);

        mDsConnection.onDomainSelected(NetworkRegistrationInfo.DOMAIN_CS);
        processAllMessages();

        assertTrue(future.isDone());
    }

    @Test
    @SmallTest
    public void testFinishSelection() throws Exception {
        setUpTestableLooper();
        CompletableFuture<Integer> future =
                mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
        future.thenAcceptAsync((domain) -> {
            assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
        }, mHandler::post);

        mDsConnection.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS);
        processAllMessages();
        mDsConnection.finishSelection();

        verify(mDomainSelector).finishSelection();
    }

    @Test
    @SmallTest
    public void testCancelSelection() throws Exception {
        CompletableFuture<Integer> future =
                mDsConnection.requestDomainSelection(mDsAttr, mDscCallback);
        future.thenAcceptAsync((domain) -> {
            assertEquals(Integer.valueOf(NetworkRegistrationInfo.DOMAIN_PS), domain);
        }, mHandler::post);

        mDsConnection.finishSelection();

        verify(mDomainSelector).cancelSelection();
    }

    private void setUpTestableLooper() throws Exception {
        mTestableLooper = new TestableLooper(mHandler.getLooper());
    }

    private void processAllMessages() {
        while (!mTestableLooper.getLooper().getQueue().isIdle()) {
            mTestableLooper.processAllMessages();
        }
    }
}