2 * Copyright (C) 2016 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings.wifi.tether;
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;
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.WifiManager;
53 import android.os.Bundle;
54 import android.os.ResultReceiver;
55 import android.os.SystemClock;
56 import android.test.ServiceTestCase;
57 import android.util.Log;
59 import org.mockito.ArgumentCaptor;
60 import org.mockito.Captor;
61 import org.mockito.Mock;
62 import org.mockito.MockitoAnnotations;
64 import java.lang.ref.WeakReference;
65 import java.util.ArrayList;
66 import java.util.HashSet;
67 import java.util.List;
70 public class TetherServiceTest extends ServiceTestCase<TetherService> {
72 private static final String TAG = "TetherServiceTest";
73 private static final String FAKE_PACKAGE_NAME = "com.some.package.name";
74 private static final String ENTITLEMENT_PACKAGE_NAME = "com.some.entitlement.name";
75 private static final String TEST_RESPONSE_ACTION = "testProvisioningResponseAction";
76 private static final String TEST_NO_UI_ACTION = "testNoUiProvisioningRequestAction";
77 private static final int BOGUS_RECEIVER_RESULT = -5;
78 private static final int TEST_CHECK_PERIOD = 100;
79 private static final int MS_PER_HOUR = 60 * 60 * 1000;
80 private static final int SHORT_TIMEOUT = 100;
81 private static final int PROVISION_TIMEOUT = 1000;
83 private TetherService mService;
84 private MockResources mResources;
85 private FakeUsageStatsManagerWrapper mUsageStatsManagerWrapper;
86 int mLastReceiverResultCode = BOGUS_RECEIVER_RESULT;
87 private int mLastTetherRequestType = TETHERING_INVALID;
88 private int mProvisionResponse = BOGUS_RECEIVER_RESULT;
89 private ProvisionReceiver mProvisionReceiver;
90 private Receiver mResultReceiver;
92 @Mock private AlarmManager mAlarmManager;
93 @Mock private ConnectivityManager mConnectivityManager;
94 @Mock private PackageManager mPackageManager;
95 @Mock private WifiManager mWifiManager;
96 @Mock private SharedPreferences mPrefs;
97 @Mock private Editor mPrefEditor;
98 @Captor private ArgumentCaptor<PendingIntent> mPiCaptor;
99 @Captor private ArgumentCaptor<String> mStoredTypes;
101 public TetherServiceTest() {
102 super(TetherService.class);
106 protected void setUp() throws Exception {
108 MockitoAnnotations.initMocks(this);
110 mResources = new MockResources();
111 mContext = new TestContextWrapper(getContext());
112 setContext(mContext);
114 mResultReceiver = new Receiver(this);
115 mLastReceiverResultCode = BOGUS_RECEIVER_RESULT;
116 mProvisionResponse = Activity.RESULT_OK;
117 mProvisionReceiver = new ProvisionReceiver();
118 IntentFilter filter = new IntentFilter(TEST_NO_UI_ACTION);
119 filter.addCategory(Intent.CATEGORY_DEFAULT);
120 mContext.registerReceiver(mProvisionReceiver, filter);
122 final String CURRENT_TYPES = "currentTethers";
123 when(mPrefs.getString(CURRENT_TYPES, "")).thenReturn("");
124 when(mPrefs.edit()).thenReturn(mPrefEditor);
125 when(mPrefEditor.putString(eq(CURRENT_TYPES), mStoredTypes.capture())).thenReturn(
127 mUsageStatsManagerWrapper = new FakeUsageStatsManagerWrapper(mContext);
129 ResolveInfo systemAppResolveInfo = new ResolveInfo();
130 ActivityInfo systemActivityInfo = new ActivityInfo();
131 systemActivityInfo.packageName = ENTITLEMENT_PACKAGE_NAME;
132 ApplicationInfo systemAppInfo = new ApplicationInfo();
133 systemAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
134 systemActivityInfo.applicationInfo = systemAppInfo;
135 systemAppResolveInfo.activityInfo = systemActivityInfo;
137 ResolveInfo nonSystemResolveInfo = new ResolveInfo();
138 ActivityInfo nonSystemActivityInfo = new ActivityInfo();
139 nonSystemActivityInfo.packageName = FAKE_PACKAGE_NAME;
140 nonSystemActivityInfo.applicationInfo = new ApplicationInfo();
141 nonSystemResolveInfo.activityInfo = nonSystemActivityInfo;
143 List<ResolveInfo> resolvers = new ArrayList();
144 resolvers.add(nonSystemResolveInfo);
145 resolvers.add(systemAppResolveInfo);
146 when(mPackageManager.queryBroadcastReceivers(
147 any(Intent.class), eq(PackageManager.MATCH_ALL))).thenReturn(resolvers);
151 protected void tearDown() throws Exception {
152 mContext.unregisterReceiver(mProvisionReceiver);
156 private void cancelAllProvisioning() {
157 int[] types = new int[]{TETHERING_BLUETOOTH, TETHERING_WIFI, TETHERING_USB};
158 for (int type : types) {
159 Intent intent = new Intent();
160 intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
161 startService(intent);
165 public void testStartForProvision() {
166 runProvisioningForType(TETHERING_WIFI);
168 assertTrue(waitForProvisionRequest(TETHERING_WIFI));
169 assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
172 public void testStartKeepsProvisionAppActive() {
174 getService().setUsageStatsManagerWrapper(mUsageStatsManagerWrapper);
176 runProvisioningForType(TETHERING_WIFI);
178 assertTrue(waitForProvisionRequest(TETHERING_WIFI));
179 assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
180 assertFalse(mUsageStatsManagerWrapper.isAppInactive(ENTITLEMENT_PACKAGE_NAME));
181 // Non-system handler of the intent action should stay idle.
182 assertTrue(mUsageStatsManagerWrapper.isAppInactive(FAKE_PACKAGE_NAME));
185 public void testScheduleRechecks() {
186 Intent intent = new Intent();
187 intent.putExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_WIFI);
188 intent.putExtra(EXTRA_SET_ALARM, true);
189 startService(intent);
191 long period = TEST_CHECK_PERIOD * MS_PER_HOUR;
192 verify(mAlarmManager).setRepeating(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
193 eq(period), mPiCaptor.capture());
194 PendingIntent pi = mPiCaptor.getValue();
195 assertEquals(TetherService.class.getName(), pi.getIntent().getComponent().getClassName());
198 public void testStartMultiple() {
199 runProvisioningForType(TETHERING_WIFI);
201 assertTrue(waitForProvisionRequest(TETHERING_WIFI));
202 assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
204 runProvisioningForType(TETHERING_USB);
206 assertTrue(waitForProvisionRequest(TETHERING_USB));
207 assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
209 runProvisioningForType(TETHERING_BLUETOOTH);
211 assertTrue(waitForProvisionRequest(TETHERING_BLUETOOTH));
212 assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
215 public void testPersistTypes() {
216 runProvisioningForType(TETHERING_WIFI);
218 waitForProvisionRequest(TETHERING_WIFI);
219 waitForProvisionResponse(TETHER_ERROR_NO_ERROR);
221 runProvisioningForType(TETHERING_BLUETOOTH);
223 waitForProvisionRequest(TETHERING_BLUETOOTH);
224 waitForProvisionResponse(TETHER_ERROR_NO_ERROR);
227 assertEquals(TETHERING_WIFI + "," + TETHERING_BLUETOOTH, mStoredTypes.getValue());
230 public void testFailureStopsTethering_Wifi() {
231 mProvisionResponse = Activity.RESULT_CANCELED;
233 runProvisioningForType(TETHERING_WIFI);
235 assertTrue(waitForProvisionRequest(TETHERING_WIFI));
236 assertTrue(waitForProvisionResponse(TETHER_ERROR_PROVISION_FAILED));
238 verify(mConnectivityManager).stopTethering(ConnectivityManager.TETHERING_WIFI);
241 public void testFailureStopsTethering_Usb() {
242 mProvisionResponse = Activity.RESULT_CANCELED;
244 runProvisioningForType(TETHERING_USB);
246 assertTrue(waitForProvisionRequest(TETHERING_USB));
247 assertTrue(waitForProvisionResponse(TETHER_ERROR_PROVISION_FAILED));
249 verify(mConnectivityManager).setUsbTethering(eq(false));
252 public void testCancelAlarm() {
253 runProvisioningForType(TETHERING_WIFI);
255 assertTrue(waitForProvisionRequest(TETHERING_WIFI));
256 assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
258 Intent intent = new Intent();
259 intent.putExtra(EXTRA_REM_TETHER_TYPE, TETHERING_WIFI);
260 startService(intent);
262 verify(mAlarmManager).cancel(mPiCaptor.capture());
263 PendingIntent pi = mPiCaptor.getValue();
264 assertEquals(TetherService.class.getName(), pi.getIntent().getComponent().getClassName());
267 private void runProvisioningForType(int type) {
268 Intent intent = new Intent();
269 intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
270 intent.putExtra(EXTRA_RUN_PROVISION, true);
271 intent.putExtra(EXTRA_PROVISION_CALLBACK, mResultReceiver);
272 startService(intent);
275 private boolean waitForAppInactive(UsageStatsManager usageStatsManager, String packageName) {
276 long startTime = SystemClock.uptimeMillis();
278 if (usageStatsManager.isAppInactive(packageName)) {
281 if ((SystemClock.uptimeMillis() - startTime) > PROVISION_TIMEOUT) {
284 SystemClock.sleep(SHORT_TIMEOUT);
288 private boolean waitForProvisionRequest(int expectedType) {
289 long startTime = SystemClock.uptimeMillis();
291 if (mLastTetherRequestType == expectedType) {
292 mLastTetherRequestType = -1;
295 if ((SystemClock.uptimeMillis() - startTime) > PROVISION_TIMEOUT) {
296 Log.v(TAG, String.format(
297 "waitForProvisionRequest timeout: expected=%d, actual=%d",
298 expectedType, mLastTetherRequestType));
301 SystemClock.sleep(SHORT_TIMEOUT);
305 private boolean waitForProvisionResponse(int expectedValue) {
306 long startTime = SystemClock.uptimeMillis();
308 if (mLastReceiverResultCode == expectedValue) {
309 mLastReceiverResultCode = BOGUS_RECEIVER_RESULT;
312 if ((SystemClock.uptimeMillis() - startTime) > PROVISION_TIMEOUT) {
313 Log.v(TAG, String.format(
314 "waitForProvisionResponse timeout: expected=%d, actual=%d",
315 expectedValue, mLastReceiverResultCode));
318 SystemClock.sleep(SHORT_TIMEOUT);
322 private static class MockResources extends android.test.mock.MockResources {
324 public int getInteger(int id) {
326 case com.android.internal.R.integer.config_mobile_hotspot_provision_check_period:
327 return TEST_CHECK_PERIOD;
334 public String getString(int id) {
336 case com.android.internal.R.string.config_mobile_hotspot_provision_response:
337 return TEST_RESPONSE_ACTION;
338 case com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui:
339 return TEST_NO_UI_ACTION;
346 private class TestContextWrapper extends ContextWrapper {
348 public TestContextWrapper(Context base) {
353 public Resources getResources() {
358 public SharedPreferences getSharedPreferences(String name, int mode) {
359 // Stub out prefs to control the persisted tether type list.
360 if (name == "tetherPrefs") {
363 return super.getSharedPreferences(name, mode);
367 public PackageManager getPackageManager() {
368 return mPackageManager;
372 public Object getSystemService(String name) {
373 if (ALARM_SERVICE.equals(name)) {
374 return mAlarmManager;
375 } else if (CONNECTIVITY_SERVICE.equals(name)) {
376 return mConnectivityManager;
377 } else if (WIFI_SERVICE.equals(name)) {
381 return super.getSystemService(name);
385 private static final class Receiver extends ResultReceiver {
386 final WeakReference<TetherServiceTest> mTest;
388 Receiver(TetherServiceTest test) {
390 mTest = new WeakReference<TetherServiceTest>(test);
394 protected void onReceiveResult(int resultCode, Bundle resultData) {
395 TetherServiceTest test = mTest.get();
397 test.mLastReceiverResultCode = resultCode;
403 * Stubs out the provisioning app receiver.
405 private class ProvisionReceiver extends BroadcastReceiver {
407 public void onReceive(Context context, Intent intent) {
408 mLastTetherRequestType = intent.getIntExtra("TETHER_TYPE", TETHERING_INVALID);
409 sendResponse(mProvisionResponse, context);
412 private void sendResponse(int response, Context context) {
413 Intent responseIntent = new Intent(TEST_RESPONSE_ACTION);
414 responseIntent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
415 responseIntent.putExtra(TetherService.EXTRA_RESULT, response);
416 context.sendBroadcast(
417 responseIntent, android.Manifest.permission.CONNECTIVITY_INTERNAL);
421 private static class FakeUsageStatsManagerWrapper
422 extends TetherService.UsageStatsManagerWrapper {
423 private final Set<String> mActivePackages;
425 FakeUsageStatsManagerWrapper(Context context) {
427 mActivePackages = new HashSet<>();
431 void setAppInactive(String packageName, boolean isInactive) {
433 mActivePackages.add(packageName);
435 mActivePackages.remove(packageName);
439 boolean isAppInactive(String packageName) {
440 return !mActivePackages.contains(packageName);