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.content.BroadcastReceiver; 39 import android.content.Context; 40 import android.content.ContextWrapper; 41 import android.content.Intent; 42 import android.content.IntentFilter; 43 import android.content.SharedPreferences; 44 import android.content.SharedPreferences.Editor; 45 import android.content.res.Resources; 46 import android.net.ConnectivityManager; 47 import android.net.wifi.WifiConfiguration; 48 import android.net.wifi.WifiManager; 49 import android.os.Bundle; 50 import android.os.ResultReceiver; 51 import android.os.SystemClock; 52 import android.test.ServiceTestCase; 53 import android.test.mock.MockResources; 54 import android.util.Log; 55 56 import com.android.settings.TetherService; 57 58 import org.mockito.ArgumentCaptor; 59 import org.mockito.Captor; 60 import org.mockito.Mock; 61 import org.mockito.MockitoAnnotations; 62 63 import java.lang.ref.WeakReference; 64 65 public class TetherServiceTest extends ServiceTestCase<TetherService> { 66 67 private static final String TAG = "TetherServiceTest"; 68 private static final String TEST_RESPONSE_ACTION = "testProvisioningResponseAction"; 69 private static final String TEST_NO_UI_ACTION = "testNoUiProvisioningRequestAction"; 70 private static final int BOGUS_RECEIVER_RESULT = -5; 71 private static final int TEST_CHECK_PERIOD = 100; 72 private static final int MS_PER_HOUR = 60 * 60 * 1000; 73 private static final int SHORT_TIMEOUT = 100; 74 private static final int PROVISION_TIMEOUT = 1000; 75 76 private TetherService mService; 77 private MockResources mResources; 78 int mLastReceiverResultCode = BOGUS_RECEIVER_RESULT; 79 private int mLastTetherRequestType = TETHERING_INVALID; 80 private int mProvisionResponse = BOGUS_RECEIVER_RESULT; 81 private ProvisionReceiver mProvisionReceiver; 82 private Receiver mResultReceiver; 83 84 @Mock private AlarmManager mAlarmManager; 85 @Mock private ConnectivityManager mConnectivityManager; 86 @Mock private WifiManager mWifiManager; 87 @Mock private SharedPreferences mPrefs; 88 @Mock private Editor mPrefEditor; 89 @Captor private ArgumentCaptor<PendingIntent> mPiCaptor; 90 @Captor private ArgumentCaptor<String> mStoredTypes; 91 TetherServiceTest()92 public TetherServiceTest() { 93 super(TetherService.class); 94 } 95 96 @Override setUp()97 protected void setUp() throws Exception { 98 super.setUp(); 99 MockitoAnnotations.initMocks(this); 100 101 mResources = new MockResources(); 102 mContext = new TestContextWrapper(getContext()); 103 setContext(mContext); 104 105 mResultReceiver = new Receiver(this); 106 mLastReceiverResultCode = BOGUS_RECEIVER_RESULT; 107 mProvisionResponse = Activity.RESULT_OK; 108 mProvisionReceiver = new ProvisionReceiver(); 109 IntentFilter filter = new IntentFilter(TEST_NO_UI_ACTION); 110 filter.addCategory(Intent.CATEGORY_DEFAULT); 111 mContext.registerReceiver(mProvisionReceiver, filter); 112 113 final String CURRENT_TYPES = "currentTethers"; 114 when(mPrefs.getString(CURRENT_TYPES, "")).thenReturn(""); 115 when(mPrefs.edit()).thenReturn(mPrefEditor); 116 when(mPrefEditor.putString(eq(CURRENT_TYPES), mStoredTypes.capture())).thenReturn( 117 mPrefEditor); 118 } 119 120 @Override tearDown()121 protected void tearDown() throws Exception { 122 mContext.unregisterReceiver(mProvisionReceiver); 123 super.tearDown(); 124 } 125 cancelAllProvisioning()126 private void cancelAllProvisioning() { 127 int[] types = new int[]{TETHERING_BLUETOOTH, TETHERING_WIFI, TETHERING_USB}; 128 for (int type : types) { 129 Intent intent = new Intent(); 130 intent.putExtra(EXTRA_REM_TETHER_TYPE, type); 131 startService(intent); 132 } 133 } 134 testStartForProvision()135 public void testStartForProvision() { 136 runProvisioningForType(TETHERING_WIFI); 137 138 assertTrue(waitForProvisionRequest(TETHERING_WIFI)); 139 assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR)); 140 } 141 testScheduleRechecks()142 public void testScheduleRechecks() { 143 Intent intent = new Intent(); 144 intent.putExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_WIFI); 145 intent.putExtra(EXTRA_SET_ALARM, true); 146 startService(intent); 147 148 long period = TEST_CHECK_PERIOD * MS_PER_HOUR; 149 verify(mAlarmManager).setRepeating(eq(AlarmManager.ELAPSED_REALTIME), anyLong(), 150 eq(period), mPiCaptor.capture()); 151 PendingIntent pi = mPiCaptor.getValue(); 152 assertEquals(TetherService.class.getName(), pi.getIntent().getComponent().getClassName()); 153 } 154 testStartMultiple()155 public void testStartMultiple() { 156 runProvisioningForType(TETHERING_WIFI); 157 158 assertTrue(waitForProvisionRequest(TETHERING_WIFI)); 159 assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR)); 160 161 runProvisioningForType(TETHERING_USB); 162 163 assertTrue(waitForProvisionRequest(TETHERING_USB)); 164 assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR)); 165 166 runProvisioningForType(TETHERING_BLUETOOTH); 167 168 assertTrue(waitForProvisionRequest(TETHERING_BLUETOOTH)); 169 assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR)); 170 } 171 testPersistTypes()172 public void testPersistTypes() { 173 runProvisioningForType(TETHERING_WIFI); 174 175 waitForProvisionRequest(TETHERING_WIFI); 176 waitForProvisionResponse(TETHER_ERROR_NO_ERROR); 177 178 runProvisioningForType(TETHERING_BLUETOOTH); 179 180 waitForProvisionRequest(TETHERING_BLUETOOTH); 181 waitForProvisionResponse(TETHER_ERROR_NO_ERROR); 182 183 shutdownService(); 184 assertEquals(TETHERING_WIFI + "," + TETHERING_BLUETOOTH, mStoredTypes.getValue()); 185 } 186 testFailureStopsTethering_Wifi()187 public void testFailureStopsTethering_Wifi() { 188 mProvisionResponse = Activity.RESULT_CANCELED; 189 190 runProvisioningForType(TETHERING_WIFI); 191 192 assertTrue(waitForProvisionRequest(TETHERING_WIFI)); 193 assertTrue(waitForProvisionResponse(TETHER_ERROR_PROVISION_FAILED)); 194 195 verify(mWifiManager).setWifiApEnabled(isNull(WifiConfiguration.class), eq(false)); 196 } 197 testFailureStopsTethering_Usb()198 public void testFailureStopsTethering_Usb() { 199 mProvisionResponse = Activity.RESULT_CANCELED; 200 201 runProvisioningForType(TETHERING_USB); 202 203 assertTrue(waitForProvisionRequest(TETHERING_USB)); 204 assertTrue(waitForProvisionResponse(TETHER_ERROR_PROVISION_FAILED)); 205 206 verify(mConnectivityManager).setUsbTethering(eq(false)); 207 } 208 testCancelAlarm()209 public void testCancelAlarm() { 210 runProvisioningForType(TETHERING_WIFI); 211 212 assertTrue(waitForProvisionRequest(TETHERING_WIFI)); 213 assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR)); 214 215 Intent intent = new Intent(); 216 intent.putExtra(EXTRA_REM_TETHER_TYPE, TETHERING_WIFI); 217 startService(intent); 218 219 verify(mAlarmManager).cancel(mPiCaptor.capture()); 220 PendingIntent pi = mPiCaptor.getValue(); 221 assertEquals(TetherService.class.getName(), pi.getIntent().getComponent().getClassName()); 222 } 223 runProvisioningForType(int type)224 private void runProvisioningForType(int type) { 225 Intent intent = new Intent(); 226 intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); 227 intent.putExtra(EXTRA_RUN_PROVISION, true); 228 intent.putExtra(EXTRA_PROVISION_CALLBACK, mResultReceiver); 229 startService(intent); 230 } 231 waitForProvisionRequest(int expectedType)232 private boolean waitForProvisionRequest(int expectedType) { 233 long startTime = SystemClock.uptimeMillis(); 234 while (true) { 235 if (mLastTetherRequestType == expectedType) { 236 mLastTetherRequestType = -1; 237 return true; 238 } 239 if ((SystemClock.uptimeMillis() - startTime) > PROVISION_TIMEOUT) { 240 Log.v(TAG, String.format( 241 "waitForProvisionRequest timeout: expected=%d, actual=%d", 242 expectedType, mLastTetherRequestType)); 243 return false; 244 } 245 SystemClock.sleep(SHORT_TIMEOUT); 246 } 247 } 248 waitForProvisionResponse(int expectedValue)249 private boolean waitForProvisionResponse(int expectedValue) { 250 long startTime = SystemClock.uptimeMillis(); 251 while (true) { 252 if (mLastReceiverResultCode == expectedValue) { 253 mLastReceiverResultCode = BOGUS_RECEIVER_RESULT; 254 return true; 255 } 256 if ((SystemClock.uptimeMillis() - startTime) > PROVISION_TIMEOUT) { 257 Log.v(TAG, String.format( 258 "waitForProvisionResponse timeout: expected=%d, actual=%d", 259 expectedValue, mLastReceiverResultCode)); 260 return false; 261 } 262 SystemClock.sleep(SHORT_TIMEOUT); 263 } 264 } 265 266 private static class MockResources extends android.test.mock.MockResources { 267 @Override getInteger(int id)268 public int getInteger(int id) { 269 switch(id) { 270 case com.android.internal.R.integer.config_mobile_hotspot_provision_check_period: 271 return TEST_CHECK_PERIOD; 272 default: 273 return 0; 274 } 275 } 276 277 @Override getString(int id)278 public String getString(int id) { 279 switch(id) { 280 case com.android.internal.R.string.config_mobile_hotspot_provision_response: 281 return TEST_RESPONSE_ACTION; 282 case com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui: 283 return TEST_NO_UI_ACTION; 284 default: 285 return null; 286 } 287 } 288 } 289 290 private class TestContextWrapper extends ContextWrapper { 291 TestContextWrapper(Context base)292 public TestContextWrapper(Context base) { 293 super(base); 294 } 295 296 @Override getResources()297 public Resources getResources() { 298 return mResources; 299 } 300 301 @Override getSharedPreferences(String name, int mode)302 public SharedPreferences getSharedPreferences(String name, int mode) { 303 // Stub out prefs to control the persisted tether type list. 304 if (name == "tetherPrefs") { 305 return mPrefs; 306 } 307 return super.getSharedPreferences(name, mode); 308 } 309 310 @Override getSystemService(String name)311 public Object getSystemService(String name) { 312 if (ALARM_SERVICE.equals(name)) { 313 return mAlarmManager; 314 } else if (CONNECTIVITY_SERVICE.equals(name)) { 315 return mConnectivityManager; 316 } else if (WIFI_SERVICE.equals(name)) { 317 return mWifiManager; 318 } 319 320 return super.getSystemService(name); 321 } 322 } 323 324 private static final class Receiver extends ResultReceiver { 325 final WeakReference<TetherServiceTest> mTest; 326 Receiver(TetherServiceTest test)327 Receiver(TetherServiceTest test) { 328 super(null); 329 mTest = new WeakReference<TetherServiceTest>(test); 330 } 331 332 @Override onReceiveResult(int resultCode, Bundle resultData)333 protected void onReceiveResult(int resultCode, Bundle resultData) { 334 TetherServiceTest test = mTest.get(); 335 if (test != null) { 336 test.mLastReceiverResultCode = resultCode; 337 } 338 } 339 }; 340 341 /** 342 * Stubs out the provisioning app receiver. 343 */ 344 private class ProvisionReceiver extends BroadcastReceiver { 345 @Override onReceive(Context context, Intent intent)346 public void onReceive(Context context, Intent intent) { 347 mLastTetherRequestType = intent.getIntExtra("TETHER_TYPE", TETHERING_INVALID); 348 sendResponse(mProvisionResponse, context); 349 } 350 sendResponse(int response, Context context)351 private void sendResponse(int response, Context context) { 352 Intent responseIntent = new Intent(TEST_RESPONSE_ACTION); 353 responseIntent.putExtra(TetherService.EXTRA_RESULT, response); 354 context.sendBroadcast( 355 responseIntent, android.Manifest.permission.CONNECTIVITY_INTERNAL); 356 } 357 } 358 } 359