Home | History | Annotate | Download | only in settings
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings;
     18 
     19 import static org.junit.Assert.*;
     20 import static org.mockito.Matchers.*;
     21 import static org.mockito.Mockito.verify;
     22 import static org.mockito.Mockito.when;
     23 import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
     24 import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
     25 import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
     26 import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
     27 import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
     28 import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
     29 import static android.net.ConnectivityManager.TETHERING_INVALID;
     30 import static android.net.ConnectivityManager.TETHERING_USB;
     31 import static android.net.ConnectivityManager.TETHERING_WIFI;
     32 import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
     33 import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
     34 
     35 import android.app.Activity;
     36 import android.app.AlarmManager;
     37 import android.app.PendingIntent;
     38 import android.app.usage.UsageStatsManager;
     39 import android.content.BroadcastReceiver;
     40 import android.content.Context;
     41 import android.content.ContextWrapper;
     42 import android.content.Intent;
     43 import android.content.IntentFilter;
     44 import android.content.pm.ActivityInfo;
     45 import android.content.pm.ApplicationInfo;
     46 import android.content.pm.ResolveInfo;
     47 import android.content.pm.PackageManager;
     48 import android.content.SharedPreferences;
     49 import android.content.SharedPreferences.Editor;
     50 import android.content.res.Resources;
     51 import android.net.ConnectivityManager;
     52 import android.net.wifi.WifiConfiguration;
     53 import android.net.wifi.WifiManager;
     54 import android.os.Bundle;
     55 import android.os.ResultReceiver;
     56 import android.os.SystemClock;
     57 import android.test.ServiceTestCase;
     58 import android.test.mock.MockResources;
     59 import android.util.Log;
     60 
     61 import com.android.settings.TetherService;
     62 
     63 import org.mockito.ArgumentCaptor;
     64 import org.mockito.Captor;
     65 import org.mockito.Mock;
     66 import org.mockito.MockitoAnnotations;
     67 
     68 import java.lang.ref.WeakReference;
     69 import java.util.ArrayList;
     70 import java.util.HashSet;
     71 import java.util.List;
     72 import java.util.Set;
     73 
     74 public class TetherServiceTest extends ServiceTestCase<TetherService> {
     75 
     76     private static final String TAG = "TetherServiceTest";
     77     private static final String FAKE_PACKAGE_NAME = "com.some.package.name";
     78     private static final String ENTITLEMENT_PACKAGE_NAME = "com.some.entitlement.name";
     79     private static final String TEST_RESPONSE_ACTION = "testProvisioningResponseAction";
     80     private static final String TEST_NO_UI_ACTION = "testNoUiProvisioningRequestAction";
     81     private static final int BOGUS_RECEIVER_RESULT = -5;
     82     private static final int TEST_CHECK_PERIOD = 100;
     83     private static final int MS_PER_HOUR = 60 * 60 * 1000;
     84     private static final int SHORT_TIMEOUT = 100;
     85     private static final int PROVISION_TIMEOUT = 1000;
     86 
     87     private TetherService mService;
     88     private MockResources mResources;
     89     private FakeUsageStatsManagerWrapper mUsageStatsManagerWrapper;
     90     int mLastReceiverResultCode = BOGUS_RECEIVER_RESULT;
     91     private int mLastTetherRequestType = TETHERING_INVALID;
     92     private int mProvisionResponse = BOGUS_RECEIVER_RESULT;
     93     private ProvisionReceiver mProvisionReceiver;
     94     private Receiver mResultReceiver;
     95 
     96     @Mock private AlarmManager mAlarmManager;
     97     @Mock private ConnectivityManager mConnectivityManager;
     98     @Mock private PackageManager mPackageManager;
     99     @Mock private WifiManager mWifiManager;
    100     @Mock private SharedPreferences mPrefs;
    101     @Mock private Editor mPrefEditor;
    102     @Captor private ArgumentCaptor<PendingIntent> mPiCaptor;
    103     @Captor private ArgumentCaptor<String> mStoredTypes;
    104 
    105     public TetherServiceTest() {
    106         super(TetherService.class);
    107     }
    108 
    109     @Override
    110     protected void setUp() throws Exception {
    111         super.setUp();
    112         MockitoAnnotations.initMocks(this);
    113 
    114         mResources = new MockResources();
    115         mContext = new TestContextWrapper(getContext());
    116         setContext(mContext);
    117 
    118         mResultReceiver = new Receiver(this);
    119         mLastReceiverResultCode = BOGUS_RECEIVER_RESULT;
    120         mProvisionResponse = Activity.RESULT_OK;
    121         mProvisionReceiver = new ProvisionReceiver();
    122         IntentFilter filter = new IntentFilter(TEST_NO_UI_ACTION);
    123         filter.addCategory(Intent.CATEGORY_DEFAULT);
    124         mContext.registerReceiver(mProvisionReceiver, filter);
    125 
    126         final String CURRENT_TYPES = "currentTethers";
    127         when(mPrefs.getString(CURRENT_TYPES, "")).thenReturn("");
    128         when(mPrefs.edit()).thenReturn(mPrefEditor);
    129         when(mPrefEditor.putString(eq(CURRENT_TYPES), mStoredTypes.capture())).thenReturn(
    130                 mPrefEditor);
    131         mUsageStatsManagerWrapper = new FakeUsageStatsManagerWrapper(mContext);
    132 
    133         ResolveInfo systemAppResolveInfo = new ResolveInfo();
    134         ActivityInfo systemActivityInfo = new ActivityInfo();
    135         systemActivityInfo.packageName = ENTITLEMENT_PACKAGE_NAME;
    136         ApplicationInfo systemAppInfo = new ApplicationInfo();
    137         systemAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
    138         systemActivityInfo.applicationInfo = systemAppInfo;
    139         systemAppResolveInfo.activityInfo = systemActivityInfo;
    140 
    141         ResolveInfo nonSystemResolveInfo = new ResolveInfo();
    142         ActivityInfo nonSystemActivityInfo = new ActivityInfo();
    143         nonSystemActivityInfo.packageName = FAKE_PACKAGE_NAME;
    144         nonSystemActivityInfo.applicationInfo = new ApplicationInfo();
    145         nonSystemResolveInfo.activityInfo = nonSystemActivityInfo;
    146 
    147         List<ResolveInfo> resolvers = new ArrayList();
    148         resolvers.add(nonSystemResolveInfo);
    149         resolvers.add(systemAppResolveInfo);
    150         when(mPackageManager.queryBroadcastReceivers(
    151                 any(Intent.class), eq(PackageManager.MATCH_ALL))).thenReturn(resolvers);
    152     }
    153 
    154     @Override
    155     protected void tearDown() throws Exception {
    156         mContext.unregisterReceiver(mProvisionReceiver);
    157         super.tearDown();
    158     }
    159 
    160     private void cancelAllProvisioning() {
    161         int[] types = new int[]{TETHERING_BLUETOOTH, TETHERING_WIFI, TETHERING_USB};
    162         for (int type : types) {
    163             Intent intent = new Intent();
    164             intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
    165             startService(intent);
    166         }
    167     }
    168 
    169     public void testStartForProvision() {
    170         runProvisioningForType(TETHERING_WIFI);
    171 
    172         assertTrue(waitForProvisionRequest(TETHERING_WIFI));
    173         assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
    174     }
    175 
    176     public void testStartKeepsProvisionAppActive() {
    177         setupService();
    178         getService().setUsageStatsManagerWrapper(mUsageStatsManagerWrapper);
    179 
    180         runProvisioningForType(TETHERING_WIFI);
    181 
    182         assertTrue(waitForProvisionRequest(TETHERING_WIFI));
    183         assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
    184         assertFalse(mUsageStatsManagerWrapper.isAppInactive(ENTITLEMENT_PACKAGE_NAME));
    185         // Non-system handler of the intent action should stay idle.
    186         assertTrue(mUsageStatsManagerWrapper.isAppInactive(FAKE_PACKAGE_NAME));
    187     }
    188 
    189     public void testScheduleRechecks() {
    190         Intent intent = new Intent();
    191         intent.putExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_WIFI);
    192         intent.putExtra(EXTRA_SET_ALARM, true);
    193         startService(intent);
    194 
    195         long period = TEST_CHECK_PERIOD * MS_PER_HOUR;
    196         verify(mAlarmManager).setRepeating(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
    197                 eq(period), mPiCaptor.capture());
    198         PendingIntent pi = mPiCaptor.getValue();
    199         assertEquals(TetherService.class.getName(), pi.getIntent().getComponent().getClassName());
    200     }
    201 
    202     public void testStartMultiple() {
    203         runProvisioningForType(TETHERING_WIFI);
    204 
    205         assertTrue(waitForProvisionRequest(TETHERING_WIFI));
    206         assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
    207 
    208         runProvisioningForType(TETHERING_USB);
    209 
    210         assertTrue(waitForProvisionRequest(TETHERING_USB));
    211         assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
    212 
    213         runProvisioningForType(TETHERING_BLUETOOTH);
    214 
    215         assertTrue(waitForProvisionRequest(TETHERING_BLUETOOTH));
    216         assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
    217     }
    218 
    219     public void testPersistTypes() {
    220         runProvisioningForType(TETHERING_WIFI);
    221 
    222         waitForProvisionRequest(TETHERING_WIFI);
    223         waitForProvisionResponse(TETHER_ERROR_NO_ERROR);
    224 
    225         runProvisioningForType(TETHERING_BLUETOOTH);
    226 
    227         waitForProvisionRequest(TETHERING_BLUETOOTH);
    228         waitForProvisionResponse(TETHER_ERROR_NO_ERROR);
    229 
    230         shutdownService();
    231         assertEquals(TETHERING_WIFI + "," + TETHERING_BLUETOOTH, mStoredTypes.getValue());
    232     }
    233 
    234     public void testFailureStopsTethering_Wifi() {
    235         mProvisionResponse = Activity.RESULT_CANCELED;
    236 
    237         runProvisioningForType(TETHERING_WIFI);
    238 
    239         assertTrue(waitForProvisionRequest(TETHERING_WIFI));
    240         assertTrue(waitForProvisionResponse(TETHER_ERROR_PROVISION_FAILED));
    241 
    242         verify(mConnectivityManager).stopTethering(ConnectivityManager.TETHERING_WIFI);
    243     }
    244 
    245     public void testFailureStopsTethering_Usb() {
    246         mProvisionResponse = Activity.RESULT_CANCELED;
    247 
    248         runProvisioningForType(TETHERING_USB);
    249 
    250         assertTrue(waitForProvisionRequest(TETHERING_USB));
    251         assertTrue(waitForProvisionResponse(TETHER_ERROR_PROVISION_FAILED));
    252 
    253         verify(mConnectivityManager).setUsbTethering(eq(false));
    254     }
    255 
    256     public void testCancelAlarm() {
    257         runProvisioningForType(TETHERING_WIFI);
    258 
    259         assertTrue(waitForProvisionRequest(TETHERING_WIFI));
    260         assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
    261 
    262         Intent intent = new Intent();
    263         intent.putExtra(EXTRA_REM_TETHER_TYPE, TETHERING_WIFI);
    264         startService(intent);
    265 
    266         verify(mAlarmManager).cancel(mPiCaptor.capture());
    267         PendingIntent pi = mPiCaptor.getValue();
    268         assertEquals(TetherService.class.getName(), pi.getIntent().getComponent().getClassName());
    269     }
    270 
    271     private void runProvisioningForType(int type) {
    272         Intent intent = new Intent();
    273         intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
    274         intent.putExtra(EXTRA_RUN_PROVISION, true);
    275         intent.putExtra(EXTRA_PROVISION_CALLBACK, mResultReceiver);
    276         startService(intent);
    277     }
    278 
    279     private boolean waitForAppInactive(UsageStatsManager usageStatsManager, String packageName) {
    280         long startTime = SystemClock.uptimeMillis();
    281         while (true) {
    282             if (usageStatsManager.isAppInactive(packageName)) {
    283                 return true;
    284             }
    285             if ((SystemClock.uptimeMillis() - startTime) > PROVISION_TIMEOUT) {
    286                 return false;
    287             }
    288             SystemClock.sleep(SHORT_TIMEOUT);
    289         }
    290     }
    291 
    292     private boolean waitForProvisionRequest(int expectedType) {
    293         long startTime = SystemClock.uptimeMillis();
    294         while (true) {
    295             if (mLastTetherRequestType == expectedType) {
    296                 mLastTetherRequestType = -1;
    297                 return true;
    298             }
    299             if ((SystemClock.uptimeMillis() - startTime) > PROVISION_TIMEOUT) {
    300                 Log.v(TAG, String.format(
    301                         "waitForProvisionRequest timeout: expected=%d, actual=%d",
    302                         expectedType, mLastTetherRequestType));
    303                 return false;
    304             }
    305             SystemClock.sleep(SHORT_TIMEOUT);
    306         }
    307     }
    308 
    309     private boolean waitForProvisionResponse(int expectedValue) {
    310         long startTime = SystemClock.uptimeMillis();
    311         while (true) {
    312             if (mLastReceiverResultCode == expectedValue) {
    313                 mLastReceiverResultCode = BOGUS_RECEIVER_RESULT;
    314                 return true;
    315             }
    316             if ((SystemClock.uptimeMillis() - startTime) > PROVISION_TIMEOUT) {
    317                 Log.v(TAG, String.format(
    318                         "waitForProvisionResponse timeout: expected=%d, actual=%d",
    319                         expectedValue, mLastReceiverResultCode));
    320                 return false;
    321             }
    322             SystemClock.sleep(SHORT_TIMEOUT);
    323         }
    324     }
    325 
    326     private static class MockResources extends android.test.mock.MockResources {
    327         @Override
    328         public int getInteger(int id) {
    329             switch(id) {
    330                 case com.android.internal.R.integer.config_mobile_hotspot_provision_check_period:
    331                     return TEST_CHECK_PERIOD;
    332                 default:
    333                     return 0;
    334             }
    335         }
    336 
    337         @Override
    338         public String getString(int id) {
    339             switch(id) {
    340                 case com.android.internal.R.string.config_mobile_hotspot_provision_response:
    341                     return TEST_RESPONSE_ACTION;
    342                 case com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui:
    343                     return TEST_NO_UI_ACTION;
    344                 default:
    345                     return null;
    346             }
    347         }
    348     }
    349 
    350     private class TestContextWrapper extends ContextWrapper {
    351 
    352         public TestContextWrapper(Context base) {
    353             super(base);
    354         }
    355 
    356         @Override
    357         public Resources getResources() {
    358             return mResources;
    359         }
    360 
    361         @Override
    362         public SharedPreferences getSharedPreferences(String name, int mode) {
    363             // Stub out prefs to control the persisted tether type list.
    364             if (name == "tetherPrefs") {
    365                 return mPrefs;
    366             }
    367             return super.getSharedPreferences(name, mode);
    368         }
    369 
    370         @Override
    371         public PackageManager getPackageManager() {
    372             return mPackageManager;
    373         }
    374 
    375         @Override
    376         public Object getSystemService(String name) {
    377             if (ALARM_SERVICE.equals(name)) {
    378                 return mAlarmManager;
    379             } else if (CONNECTIVITY_SERVICE.equals(name)) {
    380                 return mConnectivityManager;
    381             } else if (WIFI_SERVICE.equals(name)) {
    382                 return mWifiManager;
    383             }
    384 
    385             return super.getSystemService(name);
    386         }
    387     }
    388 
    389     private static final class Receiver extends ResultReceiver {
    390         final WeakReference<TetherServiceTest> mTest;
    391 
    392         Receiver(TetherServiceTest test) {
    393             super(null);
    394             mTest = new WeakReference<TetherServiceTest>(test);
    395         }
    396 
    397         @Override
    398         protected void onReceiveResult(int resultCode, Bundle resultData) {
    399             TetherServiceTest test = mTest.get();
    400             if (test != null) {
    401                 test.mLastReceiverResultCode = resultCode;
    402             }
    403         }
    404     };
    405 
    406     /**
    407      * Stubs out the provisioning app receiver.
    408      */
    409     private class ProvisionReceiver extends BroadcastReceiver {
    410         @Override
    411         public void onReceive(Context context, Intent intent) {
    412             mLastTetherRequestType = intent.getIntExtra("TETHER_TYPE", TETHERING_INVALID);
    413             sendResponse(mProvisionResponse, context);
    414         }
    415 
    416         private void sendResponse(int response, Context context) {
    417             Intent responseIntent = new Intent(TEST_RESPONSE_ACTION);
    418             responseIntent.putExtra(TetherService.EXTRA_RESULT, response);
    419             context.sendBroadcast(
    420                     responseIntent, android.Manifest.permission.CONNECTIVITY_INTERNAL);
    421         }
    422     }
    423 
    424     private static class FakeUsageStatsManagerWrapper
    425             extends TetherService.UsageStatsManagerWrapper {
    426         private final Set<String> mActivePackages;
    427 
    428         FakeUsageStatsManagerWrapper(Context context) {
    429             super(context);
    430             mActivePackages = new HashSet<>();
    431         }
    432 
    433         @Override
    434         void setAppInactive(String packageName, boolean isInactive) {
    435             if (!isInactive) {
    436                 mActivePackages.add(packageName);
    437             } else {
    438                 mActivePackages.remove(packageName);
    439             }
    440         }
    441 
    442         boolean isAppInactive(String packageName) {
    443             return !mActivePackages.contains(packageName);
    444         }
    445     }
    446 }
    447