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