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

Commit d8d383f2 authored by Malcolm Chen's avatar Malcolm Chen
Browse files

In NetworkValidator, make sure timeout happens for available but

unvalidated network.

We used to rely on ConnectivityManager's API to do timeout. But problem
is, if a network becomes available but never validated, there's no
timeout and we fail to send any result. So now we are doing timeout
ourselves.

Bug: 134526729
Test: manual and unittests
Change-Id: If47def80a7176b1a45a0490561b473e16d19d945
Merged-In: If47def80a7176b1a45a0490561b473e16d19d945
parent 94f94250
Loading
Loading
Loading
Loading
+31 −6
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.os.Handler;
import android.telephony.SubscriptionManager;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;

@@ -58,8 +59,12 @@ public class CellularNetworkValidator {
    private ValidationCallback mValidationCallback;
    private Context mContext;
    private ConnectivityManager mConnectivityManager;
    private Handler mHandler = new Handler();
    private ConnectivityNetworkCallback mNetworkCallback;
    @VisibleForTesting
    public Handler mHandler = new Handler();
    @VisibleForTesting
    public ConnectivityNetworkCallback mNetworkCallback;
    @VisibleForTesting
    public Runnable mTimeoutCallback;

    /**
     * Callback to pass in when starting validation.
@@ -99,7 +104,8 @@ public class CellularNetworkValidator {
                .validationBeforeSwitchSupported;
    }

    private CellularNetworkValidator(Context context) {
    @VisibleForTesting
    public CellularNetworkValidator(Context context) {
        mContext = context;
        mConnectivityManager = (ConnectivityManager)
                mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -136,8 +142,22 @@ public class CellularNetworkValidator {

        mNetworkCallback = new ConnectivityNetworkCallback(subId);

        mConnectivityManager.requestNetwork(
                mNetworkRequest, mNetworkCallback, mHandler, mTimeoutInMs);
        mConnectivityManager.requestNetwork(mNetworkRequest, mNetworkCallback, mHandler);

        mTimeoutCallback = () -> {
            logd("timeout on subId " + subId + " validation.");
            reportValidationResult(false, subId);
        };

        mHandler.postDelayed(mTimeoutCallback, mTimeoutInMs);
    }

    private void removeTimeoutCallback() {
        // Remove timeout callback.
        if (mTimeoutCallback != null) {
            mHandler.removeCallbacks(mTimeoutCallback);
            mTimeoutCallback = null;
        }
    }

    /**
@@ -150,6 +170,8 @@ public class CellularNetworkValidator {
            mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
            mState = STATE_IDLE;
        }

        removeTimeoutCallback();
        mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
    }

@@ -179,6 +201,8 @@ public class CellularNetworkValidator {
        // If the validation result is not for current subId, do nothing.
        if (mSubId != subId) return;

        removeTimeoutCallback();

        // Deal with the result only when state is still VALIDATING. This is to avoid
        // receiving multiple callbacks in queue.
        if (mState == STATE_VALIDATING) {
@@ -198,7 +222,8 @@ public class CellularNetworkValidator {
        mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
    }

    class ConnectivityNetworkCallback extends ConnectivityManager.NetworkCallback {
    @VisibleForTesting
    public class ConnectivityNetworkCallback extends ConnectivityManager.NetworkCallback {
        private final int mSubId;

        ConnectivityNetworkCallback(int subId) {
+232 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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;


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

import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.HandlerThread;
import android.telephony.PhoneCapability;
import android.telephony.SubscriptionManager;
import android.test.suitebuilder.annotation.SmallTest;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class CellularNetworkValidatorTest extends TelephonyTest {
    private boolean mValidated = false;
    private CellularNetworkValidator mValidatorUT;
    private int mValidatedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
    private static final PhoneCapability CAPABILITY_WITH_VALIDATION_SUPPORTED =
            new PhoneCapability(1, 1, 0, null, true);
    private static final PhoneCapability CAPABILITY_WITHOUT_VALIDATION_SUPPORTED =
            new PhoneCapability(1, 1, 0, null, false);
    private HandlerThread mHandlerThread;

    CellularNetworkValidator.ValidationCallback mCallback = (validated, subId) -> {
        mValidated = validated;
        mValidatedSubId = subId;
    };

    @Before
    public void setUp() throws Exception {
        super.setUp(getClass().getSimpleName());

        doReturn(CAPABILITY_WITH_VALIDATION_SUPPORTED).when(mPhoneConfigurationManager)
                .getCurrentPhoneCapability();

        mHandlerThread = new HandlerThread("PhoneSwitcherTestThread") {
            @Override
            public void onLooperPrepared() {
                mValidatorUT = new CellularNetworkValidator(mContext);
            }
        };

        mHandlerThread.start();
        waitABit();

    }

    @After
    public void tearDown() throws Exception {
        super.tearDown();
    }

    /**
     * Test that a single phone case results in our phone being active and the RIL called
     */
    @Test
    @SmallTest
    public void testValidationSupported() throws Exception {
        doReturn(CAPABILITY_WITH_VALIDATION_SUPPORTED).when(mPhoneConfigurationManager)
                .getCurrentPhoneCapability();
        assertTrue(mValidatorUT.isValidationFeatureSupported());

        doReturn(CAPABILITY_WITHOUT_VALIDATION_SUPPORTED).when(mPhoneConfigurationManager)
                .getCurrentPhoneCapability();
        assertFalse(mValidatorUT.isValidationFeatureSupported());
    }

    /**
     * Test that a single phone case results in our phone being active and the RIL called
     */
    @Test
    @SmallTest
    public void testValidateSuccess() throws Exception {
        int subId = 1;
        int timeout = 1000;
        NetworkRequest expectedRequest = new NetworkRequest.Builder()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                .setNetworkSpecifier(String.valueOf(subId))
                .build();

        mValidatorUT.validate(subId, timeout, true, mCallback);

        assertTrue(mValidatorUT.isValidating());
        assertEquals(subId, mValidatorUT.getSubIdInValidation());
        verify(mConnectivityManager).requestNetwork(
                eq(expectedRequest), eq(mValidatorUT.mNetworkCallback), any());

        mValidatorUT.mNetworkCallback.onCapabilitiesChanged(null, new NetworkCapabilities()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED));

        assertTrue(mValidated);
        assertEquals(subId, mValidatedSubId);
        verify(mConnectivityManager).unregisterNetworkCallback(eq(mValidatorUT.mNetworkCallback));
        assertFalse(mValidatorUT.mHandler.hasCallbacks(mValidatorUT.mTimeoutCallback));
        assertFalse(mValidatorUT.isValidating());
        assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                mValidatorUT.getSubIdInValidation());
    }

    /**
     * Test that a single phone case results in our phone being active and the RIL called
     */
    @Test
    @SmallTest
    public void testValidateTimeout() throws Exception {
        int subId = 1;
        int timeout = 100;
        NetworkRequest expectedRequest = new NetworkRequest.Builder()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                .setNetworkSpecifier(String.valueOf(subId))
                .build();

        mValidatorUT.validate(subId, timeout, true, mCallback);

        assertTrue(mValidatorUT.isValidating());
        assertEquals(subId, mValidatorUT.getSubIdInValidation());
        verify(mConnectivityManager).requestNetwork(
                eq(expectedRequest), eq(mValidatorUT.mNetworkCallback), any());

        // Wait for timeout.
        waitABit();

        assertFalse(mValidated);
        assertEquals(subId, mValidatedSubId);
        verify(mConnectivityManager).unregisterNetworkCallback(eq(mValidatorUT.mNetworkCallback));
        assertFalse(mValidatorUT.mHandler.hasCallbacks(mValidatorUT.mTimeoutCallback));
        assertFalse(mValidatorUT.isValidating());
        assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                mValidatorUT.getSubIdInValidation());
    }

    /**
     * Test that a single phone case results in our phone being active and the RIL called
     */
    @Test
    @SmallTest
    public void testValidateFailure() throws Exception {
        int subId = 1;
        int timeout = 100;
        NetworkRequest expectedRequest = new NetworkRequest.Builder()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                .setNetworkSpecifier(String.valueOf(subId))
                .build();

        mValidatorUT.validate(subId, timeout, true, mCallback);

        assertTrue(mValidatorUT.isValidating());
        assertEquals(subId, mValidatorUT.getSubIdInValidation());
        verify(mConnectivityManager).requestNetwork(
                eq(expectedRequest), eq(mValidatorUT.mNetworkCallback), any());

        mValidatorUT.mNetworkCallback.onUnavailable();

        assertFalse(mValidated);
        assertEquals(subId, mValidatedSubId);
        verify(mConnectivityManager).unregisterNetworkCallback(eq(mValidatorUT.mNetworkCallback));
        assertFalse(mValidatorUT.mHandler.hasCallbacks(mValidatorUT.mTimeoutCallback));
        assertFalse(mValidatorUT.isValidating());
        assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                mValidatorUT.getSubIdInValidation());
    }

    /**
     * Test that a single phone case results in our phone being active and the RIL called
     */
    @Test
    @SmallTest
    public void testNetworkAvailableNotValidated() throws Exception {
        int subId = 1;
        int timeout = 100;
        NetworkRequest expectedRequest = new NetworkRequest.Builder()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                .setNetworkSpecifier(String.valueOf(subId))
                .build();

        mValidatorUT.validate(subId, timeout, true, mCallback);

        assertTrue(mValidatorUT.isValidating());
        assertEquals(subId, mValidatorUT.getSubIdInValidation());
        verify(mConnectivityManager).requestNetwork(
                eq(expectedRequest), eq(mValidatorUT.mNetworkCallback), any());

        mValidatorUT.mNetworkCallback.onAvailable(new Network(100));
        // Wait for timeout.
        waitABit();

        assertFalse(mValidated);
        assertEquals(subId, mValidatedSubId);
        verify(mConnectivityManager).unregisterNetworkCallback(eq(mValidatorUT.mNetworkCallback));
        assertFalse(mValidatorUT.mHandler.hasCallbacks(mValidatorUT.mTimeoutCallback));
        assertFalse(mValidatorUT.isValidating());
        assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                mValidatorUT.getSubIdInValidation());
    }

    private void waitABit() {
        try {
            Thread.sleep(250);
        } catch (Exception e) {
        }
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -272,6 +273,7 @@ public abstract class TelephonyTest {
    protected SubscriptionManager mSubscriptionManager;
    protected EuiccManager mEuiccManager;
    protected PackageManager mPackageManager;
    protected ConnectivityManager mConnectivityManager;
    protected SimulatedCommands mSimulatedCommands;
    protected ContextFixture mContextFixture;
    protected Context mContext;
@@ -398,6 +400,8 @@ public abstract class TelephonyTest {
        mSubscriptionManager = (SubscriptionManager) mContext.getSystemService(
                Context.TELEPHONY_SUBSCRIPTION_SERVICE);
        mEuiccManager = (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE);
        mConnectivityManager = (ConnectivityManager)
                mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        mPackageManager = mContext.getPackageManager();

        //mTelephonyComponentFactory