• 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.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