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

Commit 42320452 authored by Jeremy Klein's avatar Jeremy Klein Committed by Android (Google) Code Review
Browse files

Merge "Add unit tests for TetherService."

parents 3848ddc1 f6b6713d
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.TetherUtil;

import java.util.ArrayList;
@@ -46,7 +47,8 @@ public class TetherService extends Service {
    private static final String TAG = "TetherService";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    private static final String EXTRA_RESULT = "EntitlementResult";
    @VisibleForTesting
    public static final String EXTRA_RESULT = "EntitlementResult";

    // Activity results to match the activity provision protocol.
    // Default to something not ok.
@@ -295,7 +297,7 @@ public class TetherService extends Service {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (DEBUG) Log.d(TAG, "Got provision result " + intent);
            String provisionResponse = context.getResources().getString(
            String provisionResponse = getResources().getString(
                    com.android.internal.R.string.config_mobile_hotspot_provision_response);

            if (provisionResponse.equals(intent.getAction())) {
+358 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.settings;

import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
import static android.net.ConnectivityManager.TETHERING_INVALID;
import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.test.ServiceTestCase;
import android.test.mock.MockResources;
import android.util.Log;

import com.android.settings.TetherService;

import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.lang.ref.WeakReference;

public class TetherServiceTest extends ServiceTestCase<TetherService> {

    private static final String TAG = "TetherServiceTest";
    private static final String TEST_RESPONSE_ACTION = "testProvisioningResponseAction";
    private static final String TEST_NO_UI_ACTION = "testNoUiProvisioningRequestAction";
    private static final int BOGUS_RECEIVER_RESULT = -5;
    private static final int TEST_CHECK_PERIOD = 100;
    private static final int MS_PER_HOUR = 60 * 60 * 1000;
    private static final int SHORT_TIMEOUT = 100;
    private static final int PROVISION_TIMEOUT = 1000;

    private TetherService mService;
    private MockResources mResources;
    int mLastReceiverResultCode = BOGUS_RECEIVER_RESULT;
    private int mLastTetherRequestType = TETHERING_INVALID;
    private int mProvisionResponse = BOGUS_RECEIVER_RESULT;
    private ProvisionReceiver mProvisionReceiver;
    private Receiver mResultReceiver;

    @Mock private AlarmManager mAlarmManager;
    @Mock private ConnectivityManager mConnectivityManager;
    @Mock private WifiManager mWifiManager;
    @Mock private SharedPreferences mPrefs;
    @Mock private Editor mPrefEditor;
    @Captor private ArgumentCaptor<PendingIntent> mPiCaptor;
    @Captor private ArgumentCaptor<String> mStoredTypes;

    public TetherServiceTest() {
        super(TetherService.class);
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        MockitoAnnotations.initMocks(this);

        mResources = new MockResources();
        mContext = new TestContextWrapper(getContext());
        setContext(mContext);

        mResultReceiver = new Receiver(this);
        mLastReceiverResultCode = BOGUS_RECEIVER_RESULT;
        mProvisionResponse = Activity.RESULT_OK;
        mProvisionReceiver = new ProvisionReceiver();
        IntentFilter filter = new IntentFilter(TEST_NO_UI_ACTION);
        filter.addCategory(Intent.CATEGORY_DEFAULT);
        mContext.registerReceiver(mProvisionReceiver, filter);

        final String CURRENT_TYPES = "currentTethers";
        when(mPrefs.getString(CURRENT_TYPES, "")).thenReturn("");
        when(mPrefs.edit()).thenReturn(mPrefEditor);
        when(mPrefEditor.putString(eq(CURRENT_TYPES), mStoredTypes.capture())).thenReturn(
                mPrefEditor);
    }

    @Override
    protected void tearDown() throws Exception {
        mContext.unregisterReceiver(mProvisionReceiver);
        super.tearDown();
    }

    private void cancelAllProvisioning() {
        int[] types = new int[]{TETHERING_BLUETOOTH, TETHERING_WIFI, TETHERING_USB};
        for (int type : types) {
            Intent intent = new Intent();
            intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
            startService(intent);
        }
    }

    public void testStartForProvision() {
        runProvisioningForType(TETHERING_WIFI);

        assertTrue(waitForProvisionRequest(TETHERING_WIFI));
        assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
    }

    public void testScheduleRechecks() {
        Intent intent = new Intent();
        intent.putExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_WIFI);
        intent.putExtra(EXTRA_SET_ALARM, true);
        startService(intent);

        long period = TEST_CHECK_PERIOD * MS_PER_HOUR;
        verify(mAlarmManager).setRepeating(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
                eq(period), mPiCaptor.capture());
        PendingIntent pi = mPiCaptor.getValue();
        assertEquals(TetherService.class.getName(), pi.getIntent().getComponent().getClassName());
    }

    public void testStartMultiple() {
        runProvisioningForType(TETHERING_WIFI);

        assertTrue(waitForProvisionRequest(TETHERING_WIFI));
        assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));

        runProvisioningForType(TETHERING_USB);

        assertTrue(waitForProvisionRequest(TETHERING_USB));
        assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));

        runProvisioningForType(TETHERING_BLUETOOTH);

        assertTrue(waitForProvisionRequest(TETHERING_BLUETOOTH));
        assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
    }

    public void testPersistTypes() {
        runProvisioningForType(TETHERING_WIFI);

        waitForProvisionRequest(TETHERING_WIFI);
        waitForProvisionResponse(TETHER_ERROR_NO_ERROR);

        runProvisioningForType(TETHERING_BLUETOOTH);

        waitForProvisionRequest(TETHERING_BLUETOOTH);
        waitForProvisionResponse(TETHER_ERROR_NO_ERROR);

        shutdownService();
        assertEquals(TETHERING_WIFI + "," + TETHERING_BLUETOOTH, mStoredTypes.getValue());
    }

    public void testFailureStopsTethering_Wifi() {
        mProvisionResponse = Activity.RESULT_CANCELED;

        runProvisioningForType(TETHERING_WIFI);

        assertTrue(waitForProvisionRequest(TETHERING_WIFI));
        assertTrue(waitForProvisionResponse(TETHER_ERROR_PROVISION_FAILED));

        verify(mWifiManager).setWifiApEnabled(isNull(WifiConfiguration.class), eq(false));
    }

    public void testFailureStopsTethering_Usb() {
        mProvisionResponse = Activity.RESULT_CANCELED;

        runProvisioningForType(TETHERING_USB);

        assertTrue(waitForProvisionRequest(TETHERING_USB));
        assertTrue(waitForProvisionResponse(TETHER_ERROR_PROVISION_FAILED));

        verify(mConnectivityManager).setUsbTethering(eq(false));
    }

    public void testCancelAlarm() {
        runProvisioningForType(TETHERING_WIFI);

        assertTrue(waitForProvisionRequest(TETHERING_WIFI));
        assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));

        Intent intent = new Intent();
        intent.putExtra(EXTRA_REM_TETHER_TYPE, TETHERING_WIFI);
        startService(intent);

        verify(mAlarmManager).cancel(mPiCaptor.capture());
        PendingIntent pi = mPiCaptor.getValue();
        assertEquals(TetherService.class.getName(), pi.getIntent().getComponent().getClassName());
    }

    private void runProvisioningForType(int type) {
        Intent intent = new Intent();
        intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
        intent.putExtra(EXTRA_RUN_PROVISION, true);
        intent.putExtra(EXTRA_PROVISION_CALLBACK, mResultReceiver);
        startService(intent);
    }

    private boolean waitForProvisionRequest(int expectedType) {
        long startTime = SystemClock.uptimeMillis();
        while (true) {
            if (mLastTetherRequestType == expectedType) {
                mLastTetherRequestType = -1;
                return true;
            }
            if ((SystemClock.uptimeMillis() - startTime) > PROVISION_TIMEOUT) {
                Log.v(TAG, String.format(
                        "waitForProvisionRequest timeout: expected=%d, actual=%d",
                        expectedType, mLastTetherRequestType));
                return false;
            }
            SystemClock.sleep(SHORT_TIMEOUT);
        }
    }

    private boolean waitForProvisionResponse(int expectedValue) {
        long startTime = SystemClock.uptimeMillis();
        while (true) {
            if (mLastReceiverResultCode == expectedValue) {
                mLastReceiverResultCode = BOGUS_RECEIVER_RESULT;
                return true;
            }
            if ((SystemClock.uptimeMillis() - startTime) > PROVISION_TIMEOUT) {
                Log.v(TAG, String.format(
                        "waitForProvisionResponse timeout: expected=%d, actual=%d",
                        expectedValue, mLastReceiverResultCode));
                return false;
            }
            SystemClock.sleep(SHORT_TIMEOUT);
        }
    }

    private static class MockResources extends android.test.mock.MockResources {
        @Override
        public int getInteger(int id) {
            switch(id) {
                case com.android.internal.R.integer.config_mobile_hotspot_provision_check_period:
                    return TEST_CHECK_PERIOD;
                default:
                    return 0;
            }
        }

        @Override
        public String getString(int id) {
            switch(id) {
                case com.android.internal.R.string.config_mobile_hotspot_provision_response:
                    return TEST_RESPONSE_ACTION;
                case com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui:
                    return TEST_NO_UI_ACTION;
                default:
                    return null;
            }
        }
    }

    private class TestContextWrapper extends ContextWrapper {

        public TestContextWrapper(Context base) {
            super(base);
        }

        @Override
        public Resources getResources() {
            return mResources;
        }

        @Override
        public SharedPreferences getSharedPreferences(String name, int mode) {
            // Stub out prefs to control the persisted tether type list.
            if (name == "tetherPrefs") {
                return mPrefs;
            }
            return super.getSharedPreferences(name, mode);
        }

        @Override
        public Object getSystemService(String name) {
            if (ALARM_SERVICE.equals(name)) {
                return mAlarmManager;
            } else if (CONNECTIVITY_SERVICE.equals(name)) {
                return mConnectivityManager;
            } else if (WIFI_SERVICE.equals(name)) {
                return mWifiManager;
            }

            return super.getSystemService(name);
        }
    }

    private static final class Receiver extends ResultReceiver {
        final WeakReference<TetherServiceTest> mTest;

        Receiver(TetherServiceTest test) {
            super(null);
            mTest = new WeakReference<TetherServiceTest>(test);
        }

        @Override
        protected void onReceiveResult(int resultCode, Bundle resultData) {
            TetherServiceTest test = mTest.get();
            if (test != null) {
                test.mLastReceiverResultCode = resultCode;
            }
        }
    };

    /**
     * Stubs out the provisioning app receiver.
     */
    private class ProvisionReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            mLastTetherRequestType = intent.getIntExtra("TETHER_TYPE", TETHERING_INVALID);
            sendResponse(mProvisionResponse, context);
        }

        private void sendResponse(int response, Context context) {
            Intent responseIntent = new Intent(TEST_RESPONSE_ACTION);
            responseIntent.putExtra(TetherService.EXTRA_RESULT, response);
            context.sendBroadcast(
                    responseIntent, android.Manifest.permission.CONNECTIVITY_INTERNAL);
        }
    }
}