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