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.content.BroadcastReceiver;
39 import android.content.Context;
40 import android.content.ContextWrapper;
41 import android.content.Intent;
42 import android.content.IntentFilter;
43 import android.content.SharedPreferences;
44 import android.content.SharedPreferences.Editor;
45 import android.content.res.Resources;
46 import android.net.ConnectivityManager;
47 import android.net.wifi.WifiConfiguration;
48 import android.net.wifi.WifiManager;
49 import android.os.Bundle;
50 import android.os.ResultReceiver;
51 import android.os.SystemClock;
52 import android.test.ServiceTestCase;
53 import android.test.mock.MockResources;
54 import android.util.Log;
55 
56 import com.android.settings.TetherService;
57 
58 import org.mockito.ArgumentCaptor;
59 import org.mockito.Captor;
60 import org.mockito.Mock;
61 import org.mockito.MockitoAnnotations;
62 
63 import java.lang.ref.WeakReference;
64 
65 public class TetherServiceTest extends ServiceTestCase<TetherService> {
66 
67     private static final String TAG = "TetherServiceTest";
68     private static final String TEST_RESPONSE_ACTION = "testProvisioningResponseAction";
69     private static final String TEST_NO_UI_ACTION = "testNoUiProvisioningRequestAction";
70     private static final int BOGUS_RECEIVER_RESULT = -5;
71     private static final int TEST_CHECK_PERIOD = 100;
72     private static final int MS_PER_HOUR = 60 * 60 * 1000;
73     private static final int SHORT_TIMEOUT = 100;
74     private static final int PROVISION_TIMEOUT = 1000;
75 
76     private TetherService mService;
77     private MockResources mResources;
78     int mLastReceiverResultCode = BOGUS_RECEIVER_RESULT;
79     private int mLastTetherRequestType = TETHERING_INVALID;
80     private int mProvisionResponse = BOGUS_RECEIVER_RESULT;
81     private ProvisionReceiver mProvisionReceiver;
82     private Receiver mResultReceiver;
83 
84     @Mock private AlarmManager mAlarmManager;
85     @Mock private ConnectivityManager mConnectivityManager;
86     @Mock private WifiManager mWifiManager;
87     @Mock private SharedPreferences mPrefs;
88     @Mock private Editor mPrefEditor;
89     @Captor private ArgumentCaptor<PendingIntent> mPiCaptor;
90     @Captor private ArgumentCaptor<String> mStoredTypes;
91 
TetherServiceTest()92     public TetherServiceTest() {
93         super(TetherService.class);
94     }
95 
96     @Override
setUp()97     protected void setUp() throws Exception {
98         super.setUp();
99         MockitoAnnotations.initMocks(this);
100 
101         mResources = new MockResources();
102         mContext = new TestContextWrapper(getContext());
103         setContext(mContext);
104 
105         mResultReceiver = new Receiver(this);
106         mLastReceiverResultCode = BOGUS_RECEIVER_RESULT;
107         mProvisionResponse = Activity.RESULT_OK;
108         mProvisionReceiver = new ProvisionReceiver();
109         IntentFilter filter = new IntentFilter(TEST_NO_UI_ACTION);
110         filter.addCategory(Intent.CATEGORY_DEFAULT);
111         mContext.registerReceiver(mProvisionReceiver, filter);
112 
113         final String CURRENT_TYPES = "currentTethers";
114         when(mPrefs.getString(CURRENT_TYPES, "")).thenReturn("");
115         when(mPrefs.edit()).thenReturn(mPrefEditor);
116         when(mPrefEditor.putString(eq(CURRENT_TYPES), mStoredTypes.capture())).thenReturn(
117                 mPrefEditor);
118     }
119 
120     @Override
tearDown()121     protected void tearDown() throws Exception {
122         mContext.unregisterReceiver(mProvisionReceiver);
123         super.tearDown();
124     }
125 
cancelAllProvisioning()126     private void cancelAllProvisioning() {
127         int[] types = new int[]{TETHERING_BLUETOOTH, TETHERING_WIFI, TETHERING_USB};
128         for (int type : types) {
129             Intent intent = new Intent();
130             intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
131             startService(intent);
132         }
133     }
134 
testStartForProvision()135     public void testStartForProvision() {
136         runProvisioningForType(TETHERING_WIFI);
137 
138         assertTrue(waitForProvisionRequest(TETHERING_WIFI));
139         assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
140     }
141 
testScheduleRechecks()142     public void testScheduleRechecks() {
143         Intent intent = new Intent();
144         intent.putExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_WIFI);
145         intent.putExtra(EXTRA_SET_ALARM, true);
146         startService(intent);
147 
148         long period = TEST_CHECK_PERIOD * MS_PER_HOUR;
149         verify(mAlarmManager).setRepeating(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
150                 eq(period), mPiCaptor.capture());
151         PendingIntent pi = mPiCaptor.getValue();
152         assertEquals(TetherService.class.getName(), pi.getIntent().getComponent().getClassName());
153     }
154 
testStartMultiple()155     public void testStartMultiple() {
156         runProvisioningForType(TETHERING_WIFI);
157 
158         assertTrue(waitForProvisionRequest(TETHERING_WIFI));
159         assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
160 
161         runProvisioningForType(TETHERING_USB);
162 
163         assertTrue(waitForProvisionRequest(TETHERING_USB));
164         assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
165 
166         runProvisioningForType(TETHERING_BLUETOOTH);
167 
168         assertTrue(waitForProvisionRequest(TETHERING_BLUETOOTH));
169         assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
170     }
171 
testPersistTypes()172     public void testPersistTypes() {
173         runProvisioningForType(TETHERING_WIFI);
174 
175         waitForProvisionRequest(TETHERING_WIFI);
176         waitForProvisionResponse(TETHER_ERROR_NO_ERROR);
177 
178         runProvisioningForType(TETHERING_BLUETOOTH);
179 
180         waitForProvisionRequest(TETHERING_BLUETOOTH);
181         waitForProvisionResponse(TETHER_ERROR_NO_ERROR);
182 
183         shutdownService();
184         assertEquals(TETHERING_WIFI + "," + TETHERING_BLUETOOTH, mStoredTypes.getValue());
185     }
186 
testFailureStopsTethering_Wifi()187     public void testFailureStopsTethering_Wifi() {
188         mProvisionResponse = Activity.RESULT_CANCELED;
189 
190         runProvisioningForType(TETHERING_WIFI);
191 
192         assertTrue(waitForProvisionRequest(TETHERING_WIFI));
193         assertTrue(waitForProvisionResponse(TETHER_ERROR_PROVISION_FAILED));
194 
195         verify(mWifiManager).setWifiApEnabled(isNull(WifiConfiguration.class), eq(false));
196     }
197 
testFailureStopsTethering_Usb()198     public void testFailureStopsTethering_Usb() {
199         mProvisionResponse = Activity.RESULT_CANCELED;
200 
201         runProvisioningForType(TETHERING_USB);
202 
203         assertTrue(waitForProvisionRequest(TETHERING_USB));
204         assertTrue(waitForProvisionResponse(TETHER_ERROR_PROVISION_FAILED));
205 
206         verify(mConnectivityManager).setUsbTethering(eq(false));
207     }
208 
testCancelAlarm()209     public void testCancelAlarm() {
210         runProvisioningForType(TETHERING_WIFI);
211 
212         assertTrue(waitForProvisionRequest(TETHERING_WIFI));
213         assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
214 
215         Intent intent = new Intent();
216         intent.putExtra(EXTRA_REM_TETHER_TYPE, TETHERING_WIFI);
217         startService(intent);
218 
219         verify(mAlarmManager).cancel(mPiCaptor.capture());
220         PendingIntent pi = mPiCaptor.getValue();
221         assertEquals(TetherService.class.getName(), pi.getIntent().getComponent().getClassName());
222     }
223 
runProvisioningForType(int type)224     private void runProvisioningForType(int type) {
225         Intent intent = new Intent();
226         intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
227         intent.putExtra(EXTRA_RUN_PROVISION, true);
228         intent.putExtra(EXTRA_PROVISION_CALLBACK, mResultReceiver);
229         startService(intent);
230     }
231 
waitForProvisionRequest(int expectedType)232     private boolean waitForProvisionRequest(int expectedType) {
233         long startTime = SystemClock.uptimeMillis();
234         while (true) {
235             if (mLastTetherRequestType == expectedType) {
236                 mLastTetherRequestType = -1;
237                 return true;
238             }
239             if ((SystemClock.uptimeMillis() - startTime) > PROVISION_TIMEOUT) {
240                 Log.v(TAG, String.format(
241                         "waitForProvisionRequest timeout: expected=%d, actual=%d",
242                         expectedType, mLastTetherRequestType));
243                 return false;
244             }
245             SystemClock.sleep(SHORT_TIMEOUT);
246         }
247     }
248 
waitForProvisionResponse(int expectedValue)249     private boolean waitForProvisionResponse(int expectedValue) {
250         long startTime = SystemClock.uptimeMillis();
251         while (true) {
252             if (mLastReceiverResultCode == expectedValue) {
253                 mLastReceiverResultCode = BOGUS_RECEIVER_RESULT;
254                 return true;
255             }
256             if ((SystemClock.uptimeMillis() - startTime) > PROVISION_TIMEOUT) {
257                 Log.v(TAG, String.format(
258                         "waitForProvisionResponse timeout: expected=%d, actual=%d",
259                         expectedValue, mLastReceiverResultCode));
260                 return false;
261             }
262             SystemClock.sleep(SHORT_TIMEOUT);
263         }
264     }
265 
266     private static class MockResources extends android.test.mock.MockResources {
267         @Override
getInteger(int id)268         public int getInteger(int id) {
269             switch(id) {
270                 case com.android.internal.R.integer.config_mobile_hotspot_provision_check_period:
271                     return TEST_CHECK_PERIOD;
272                 default:
273                     return 0;
274             }
275         }
276 
277         @Override
getString(int id)278         public String getString(int id) {
279             switch(id) {
280                 case com.android.internal.R.string.config_mobile_hotspot_provision_response:
281                     return TEST_RESPONSE_ACTION;
282                 case com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui:
283                     return TEST_NO_UI_ACTION;
284                 default:
285                     return null;
286             }
287         }
288     }
289 
290     private class TestContextWrapper extends ContextWrapper {
291 
TestContextWrapper(Context base)292         public TestContextWrapper(Context base) {
293             super(base);
294         }
295 
296         @Override
getResources()297         public Resources getResources() {
298             return mResources;
299         }
300 
301         @Override
getSharedPreferences(String name, int mode)302         public SharedPreferences getSharedPreferences(String name, int mode) {
303             // Stub out prefs to control the persisted tether type list.
304             if (name == "tetherPrefs") {
305                 return mPrefs;
306             }
307             return super.getSharedPreferences(name, mode);
308         }
309 
310         @Override
getSystemService(String name)311         public Object getSystemService(String name) {
312             if (ALARM_SERVICE.equals(name)) {
313                 return mAlarmManager;
314             } else if (CONNECTIVITY_SERVICE.equals(name)) {
315                 return mConnectivityManager;
316             } else if (WIFI_SERVICE.equals(name)) {
317                 return mWifiManager;
318             }
319 
320             return super.getSystemService(name);
321         }
322     }
323 
324     private static final class Receiver extends ResultReceiver {
325         final WeakReference<TetherServiceTest> mTest;
326 
Receiver(TetherServiceTest test)327         Receiver(TetherServiceTest test) {
328             super(null);
329             mTest = new WeakReference<TetherServiceTest>(test);
330         }
331 
332         @Override
onReceiveResult(int resultCode, Bundle resultData)333         protected void onReceiveResult(int resultCode, Bundle resultData) {
334             TetherServiceTest test = mTest.get();
335             if (test != null) {
336                 test.mLastReceiverResultCode = resultCode;
337             }
338         }
339     };
340 
341     /**
342      * Stubs out the provisioning app receiver.
343      */
344     private class ProvisionReceiver extends BroadcastReceiver {
345         @Override
onReceive(Context context, Intent intent)346         public void onReceive(Context context, Intent intent) {
347             mLastTetherRequestType = intent.getIntExtra("TETHER_TYPE", TETHERING_INVALID);
348             sendResponse(mProvisionResponse, context);
349         }
350 
sendResponse(int response, Context context)351         private void sendResponse(int response, Context context) {
352             Intent responseIntent = new Intent(TEST_RESPONSE_ACTION);
353             responseIntent.putExtra(TetherService.EXTRA_RESULT, response);
354             context.sendBroadcast(
355                     responseIntent, android.Manifest.permission.CONNECTIVITY_INTERNAL);
356         }
357     }
358 }
359