• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TetherServiceTest()105     public TetherServiceTest() {
106         super(TetherService.class);
107     }
108 
109     @Override
setUp()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
tearDown()155     protected void tearDown() throws Exception {
156         mContext.unregisterReceiver(mProvisionReceiver);
157         super.tearDown();
158     }
159 
cancelAllProvisioning()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 
testStartForProvision()169     public void testStartForProvision() {
170         runProvisioningForType(TETHERING_WIFI);
171 
172         assertTrue(waitForProvisionRequest(TETHERING_WIFI));
173         assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
174     }
175 
testStartKeepsProvisionAppActive()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 
testScheduleRechecks()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 
testStartMultiple()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 
testPersistTypes()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 
testFailureStopsTethering_Wifi()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 
testFailureStopsTethering_Usb()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 
testCancelAlarm()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 
runProvisioningForType(int type)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 
waitForAppInactive(UsageStatsManager usageStatsManager, String packageName)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 
waitForProvisionRequest(int expectedType)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 
waitForProvisionResponse(int expectedValue)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
getInteger(int id)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
getString(int id)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 
TestContextWrapper(Context base)352         public TestContextWrapper(Context base) {
353             super(base);
354         }
355 
356         @Override
getResources()357         public Resources getResources() {
358             return mResources;
359         }
360 
361         @Override
getSharedPreferences(String name, int mode)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
getPackageManager()371         public PackageManager getPackageManager() {
372             return mPackageManager;
373         }
374 
375         @Override
getSystemService(String name)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 
Receiver(TetherServiceTest test)392         Receiver(TetherServiceTest test) {
393             super(null);
394             mTest = new WeakReference<TetherServiceTest>(test);
395         }
396 
397         @Override
onReceiveResult(int resultCode, Bundle resultData)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
onReceive(Context context, Intent intent)411         public void onReceive(Context context, Intent intent) {
412             mLastTetherRequestType = intent.getIntExtra("TETHER_TYPE", TETHERING_INVALID);
413             sendResponse(mProvisionResponse, context);
414         }
415 
sendResponse(int response, Context context)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 
FakeUsageStatsManagerWrapper(Context context)428         FakeUsageStatsManagerWrapper(Context context) {
429             super(context);
430             mActivePackages = new HashSet<>();
431         }
432 
433         @Override
setAppInactive(String packageName, boolean isInactive)434         void setAppInactive(String packageName, boolean isInactive) {
435             if (!isInactive) {
436                 mActivePackages.add(packageName);
437             } else {
438                 mActivePackages.remove(packageName);
439             }
440         }
441 
isAppInactive(String packageName)442         boolean isAppInactive(String packageName) {
443             return !mActivePackages.contains(packageName);
444         }
445     }
446 }
447