1 /*
2  * Copyright (C) 2017 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 package com.android.settings.wifi;
17 
18 import static androidx.test.InstrumentationRegistry.getInstrumentation;
19 import static androidx.test.espresso.Espresso.onView;
20 import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
21 import static androidx.test.espresso.assertion.ViewAssertions.matches;
22 import static androidx.test.espresso.matcher.ViewMatchers.Visibility.VISIBLE;
23 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
24 import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
25 import static androidx.test.espresso.matcher.ViewMatchers.withId;
26 import static androidx.test.espresso.matcher.ViewMatchers.withText;
27 
28 import static com.google.common.truth.Truth.assertThat;
29 
30 import static org.hamcrest.Matchers.allOf;
31 import static org.hamcrest.Matchers.not;
32 import static org.hamcrest.Matchers.startsWith;
33 import static org.mockito.Mockito.times;
34 import static org.mockito.Mockito.verify;
35 import static org.mockito.Mockito.when;
36 
37 import android.content.Context;
38 import android.content.Intent;
39 import android.net.ConnectivityManager;
40 import android.net.NetworkInfo;
41 import android.net.wifi.WifiConfiguration;
42 import android.net.wifi.WifiInfo;
43 import android.net.wifi.WifiManager;
44 import android.net.wifi.WifiSsid;
45 import android.provider.Settings;
46 
47 import androidx.fragment.app.Fragment;
48 import androidx.test.InstrumentationRegistry;
49 import androidx.test.rule.ActivityTestRule;
50 import androidx.test.runner.AndroidJUnit4;
51 
52 import com.android.settings.Settings.WifiSettingsActivity;
53 import com.android.settingslib.utils.ThreadUtils;
54 import com.android.settingslib.wifi.AccessPoint;
55 import com.android.settingslib.wifi.TestAccessPointBuilder;
56 import com.android.settingslib.wifi.WifiTracker;
57 import com.android.settingslib.wifi.WifiTracker.WifiListener;
58 import com.android.settingslib.wifi.WifiTrackerFactory;
59 
60 import com.google.common.collect.Lists;
61 
62 import org.junit.Before;
63 import org.junit.Ignore;
64 import org.junit.Rule;
65 import org.junit.Test;
66 import org.junit.runner.RunWith;
67 import org.mockito.Mock;
68 import org.mockito.MockitoAnnotations;
69 
70 import java.util.List;
71 
72 @RunWith(AndroidJUnit4.class)
73 public class WifiSettingsUiTest {
74     private static final String TEST_SSID = "\"Test Ssid\"";
75     private static final String TEST_UNQUOTED_SSID = "Test Ssid";
76     private static final String TEST_BSSID = "0a:08:5c:67:89:00";
77     private static final int TEST_RSSI = 123;
78     private static final int TEST_NETWORK_ID = 1;
79 
80     // Keys used to lookup resources by name (see the resourceId/resourceString helper methods).
81     private static final String ID = "id";
82     private static final String STRING = "string";
83     private static final String WIFI_CONFIGURE_SETTINGS_PREFERENCE_TITLE =
84             "wifi_configure_settings_preference_title";
85     private static final String WIFI_SAVED_ACCESS_POINTS_LABEL = "wifi_saved_access_points_label";
86     private static final String WIFI_EMPTY_LIST_WIFI_OFF = "wifi_empty_list_wifi_off";
87     private static final String WIFI_DISPLAY_STATUS_CONNECTED = "wifi_display_status_connected";
88     private static final String WIFI_PASSWORD = "wifi_password";
89     private static final String WIFI_SHOW_PASSWORD = "wifi_show_password";
90     private static final String PASSWORD_LAYOUT = "password_layout";
91     private static final String PASSWORD = "password";
92 
93     @Mock
94     private WifiTracker mWifiTracker;
95     @Mock
96     private WifiManager mWifiManager;
97     private Context mContext;
98     private WifiListener mWifiListener;
99 
100     @Rule
101     public ActivityTestRule<WifiSettingsActivity> mActivityRule =
102             new ActivityTestRule<>(WifiSettingsActivity.class, true);
103 
104     @Before
setUp()105     public void setUp() {
106         MockitoAnnotations.initMocks(this);
107         mContext = InstrumentationRegistry.getTargetContext();
108         WifiTrackerFactory.setTestingWifiTracker(mWifiTracker);
109         when(mWifiTracker.getManager()).thenReturn(mWifiManager);
110     }
111 
112     /**
113      * Helper to get around the problem that directly accessing settings resource id's from
114      * com.android.settings.R via R.(type).(name) (eg R.id.password or
115      * R.string.wifi_configure_settings_preference_title) may not work due to mismatched resource
116      * ids. See b/37714546 and b/63546650.
117      */
resourceId(String type, String name)118     private int resourceId(String type, String name) {
119         return mContext.getResources().getIdentifier(name, type, mContext.getPackageName());
120     }
121 
122     /** Similar to {@link #resourceId}, but for accessing R.string.<name> values. */
resourceString(String name)123     private String resourceString(String name) {
124         return mContext.getResources().getString(resourceId(STRING, name));
125     }
126 
setupConnectedAccessPoint()127     private void setupConnectedAccessPoint() {
128         WifiConfiguration config = new WifiConfiguration();
129         config.SSID = TEST_SSID;
130         config.BSSID = TEST_BSSID;
131         config.networkId = TEST_NETWORK_ID;
132         WifiInfo wifiInfo = new WifiInfo();
133         wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(TEST_UNQUOTED_SSID));
134         wifiInfo.setBSSID(TEST_BSSID);
135         wifiInfo.setRssi(TEST_RSSI);
136         wifiInfo.setNetworkId(TEST_NETWORK_ID);
137         NetworkInfo networkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, null, null);
138         networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
139         AccessPoint accessPoint = new AccessPoint(mContext, config);
140         accessPoint.update(config, wifiInfo, networkInfo);
141 
142         assertThat(accessPoint.getSsidStr()).isEqualTo(TEST_UNQUOTED_SSID);
143         assertThat(accessPoint.getBssid()).isEqualTo(TEST_BSSID);
144         assertThat(accessPoint.getNetworkInfo()).isNotNull();
145         assertThat(accessPoint.isActive()).isTrue();
146         assertThat(accessPoint.getSettingsSummary()).isEqualTo(
147                 resourceString(WIFI_DISPLAY_STATUS_CONNECTED));
148 
149         when(mWifiTracker.getAccessPoints()).thenReturn(
150                 Lists.asList(accessPoint, new AccessPoint[] {}));
151     }
152 
153     /** Launch the activity via an Intent with a String extra. */
launchActivity(String extraName, String extraValue)154     private void launchActivity(String extraName, String extraValue) {
155         Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS);
156         if (extraName != null && extraValue != null) {
157             intent.putExtra(extraName, extraValue);
158         }
159         mActivityRule.launchActivity(intent);
160 
161         verify(mWifiTracker).getManager();
162 
163         List<Fragment> fragments =
164                 mActivityRule.getActivity().getSupportFragmentManager().getFragments();
165         assertThat(fragments.size()).isEqualTo(1);
166         mWifiListener = (WifiSettings) fragments.get(0);
167         assertThat(mWifiListener).isNotNull();
168     }
169 
170     /** Helper to launch the activity with no extra. */
launchActivity()171     private void launchActivity() {
172         launchActivity(null, null);
173     }
174 
setWifiState(int wifiState)175     private void setWifiState(int wifiState) {
176         when(mWifiManager.getWifiState()).thenReturn(wifiState);
177         when(mWifiManager.isWifiEnabled()).thenReturn(wifiState == WifiManager.WIFI_STATE_ENABLED);
178     }
179 
callOnWifiStateChanged(int state)180     private void callOnWifiStateChanged(int state) {
181         mActivityRule.getActivity().getMainThreadHandler()
182                 .post(() -> mWifiListener.onWifiStateChanged(state));
183     }
184 
185     @Test
launchActivityShouldSucceed()186     public void launchActivityShouldSucceed() {
187         launchActivity();
188     }
189 
190     @Test
shouldShowWifiPreferences()191     public void shouldShowWifiPreferences() {
192         launchActivity();
193 
194         onView(withText(resourceId(STRING, WIFI_CONFIGURE_SETTINGS_PREFERENCE_TITLE))).check(
195                 matches(isDisplayed()));
196     }
197 
198     @Test
noSavedNetworks_wifiDisabled_shouldNotShowSavedNetworksButton()199     public void noSavedNetworks_wifiDisabled_shouldNotShowSavedNetworksButton() {
200         setWifiState(WifiManager.WIFI_STATE_DISABLED);
201         when(mWifiTracker.getNumSavedNetworks()).thenReturn(0);
202 
203         launchActivity();
204 
205         onView(withText(resourceId(STRING, WIFI_SAVED_ACCESS_POINTS_LABEL))).check(
206                 doesNotExist());
207     }
208 
209     @Test
savedNetworksExist_shouldShowSavedNetworksButton()210     public void savedNetworksExist_shouldShowSavedNetworksButton() {
211         setWifiState(WifiManager.WIFI_STATE_ENABLED);
212         when(mWifiTracker.getNumSavedNetworks()).thenReturn(1);
213 
214         launchActivity();
215 
216         onView(allOf(withText(resourceId(STRING, WIFI_SAVED_ACCESS_POINTS_LABEL)),
217                 withEffectiveVisibility(VISIBLE))).check(matches(isDisplayed()));
218     }
219 
220     @Test
onDisableWifi_seeOffMessage()221     public void onDisableWifi_seeOffMessage() {
222         setWifiState(WifiManager.WIFI_STATE_DISABLED);
223 
224         launchActivity();
225         callOnWifiStateChanged(WifiManager.WIFI_STATE_DISABLED);
226 
227         onView(withText(startsWith(resourceString(WIFI_EMPTY_LIST_WIFI_OFF)))).check(
228                 matches(isDisplayed()));
229     }
230 
231     @Test
onEnableWifi_shouldNotSeeOffMessage()232     public void onEnableWifi_shouldNotSeeOffMessage() {
233         setWifiState(WifiManager.WIFI_STATE_ENABLED);
234 
235         launchActivity();
236         callOnWifiStateChanged(WifiManager.WIFI_STATE_ENABLED);
237 
238         onView(withText(startsWith(resourceString(WIFI_EMPTY_LIST_WIFI_OFF)))).check(
239                 doesNotExist());
240     }
241 
242     @Test
onConnected_shouldSeeConnectedMessage()243     public void onConnected_shouldSeeConnectedMessage() {
244         setWifiState(WifiManager.WIFI_STATE_ENABLED);
245         setupConnectedAccessPoint();
246         when(mWifiTracker.isConnected()).thenReturn(true);
247 
248         launchActivity();
249 
250         onView(withText(resourceString(WIFI_DISPLAY_STATUS_CONNECTED))).check(
251                 matches(isDisplayed()));
252     }
253 
254     @Test
changingSecurityStateOnApShouldNotCauseMultipleListItems()255     public void changingSecurityStateOnApShouldNotCauseMultipleListItems() {
256         setWifiState(WifiManager.WIFI_STATE_ENABLED);
257         TestAccessPointBuilder builder = new TestAccessPointBuilder(mContext)
258                 .setSsid(TEST_SSID)
259                 .setSecurity(AccessPoint.SECURITY_NONE)
260                 .setRssi(TEST_RSSI);
261         AccessPoint open = builder.build();
262 
263         builder.setSecurity(AccessPoint.SECURITY_EAP);
264         AccessPoint eap = builder.build();
265 
266         builder.setSecurity(AccessPoint.SECURITY_WEP);
267         AccessPoint wep = builder.build();
268 
269         // Return a different security state each time getAccessPoints is invoked
270         when(mWifiTracker.getAccessPoints())
271                 .thenReturn(Lists.newArrayList(open))
272                 .thenReturn(Lists.newArrayList(eap))
273                 .thenReturn(Lists.newArrayList(wep));
274 
275         launchActivity();
276 
277         onView(withText(TEST_SSID)).check(matches(isDisplayed()));
278 
279         ThreadUtils.postOnMainThread(() -> mWifiListener.onAccessPointsChanged());
280         onView(withText(TEST_SSID)).check(matches(isDisplayed()));
281 
282         ThreadUtils.postOnMainThread(() -> mWifiListener.onAccessPointsChanged());
283         onView(withText(TEST_SSID)).check(matches(isDisplayed()));
284     }
285 
286     @Test
wrongPasswordSavedNetwork()287     public void wrongPasswordSavedNetwork() {
288         setWifiState(WifiManager.WIFI_STATE_ENABLED);
289 
290         // Set up an AccessPoint that is disabled due to incorrect password.
291         WifiConfiguration config = new WifiConfiguration();
292         config.SSID = TEST_SSID;
293         config.BSSID = TEST_BSSID;
294         config.networkId = TEST_NETWORK_ID;
295         config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
296 
297         WifiConfiguration.NetworkSelectionStatus selectionStatus =
298                 new WifiConfiguration.NetworkSelectionStatus();
299         selectionStatus.setNetworkSelectionDisableReason(
300                 WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD);
301         selectionStatus.setNetworkSelectionStatus(
302                 WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED);
303         config.setNetworkSelectionStatus(selectionStatus);
304 
305         WifiInfo wifiInfo = new WifiInfo();
306         wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(TEST_UNQUOTED_SSID));
307         wifiInfo.setBSSID(TEST_BSSID);
308         wifiInfo.setRssi(TEST_RSSI);
309         wifiInfo.setNetworkId(TEST_NETWORK_ID);
310         AccessPoint accessPoint = new AccessPoint(mContext, config);
311         accessPoint.update(config, wifiInfo, null);
312 
313         // Make sure we've set up our access point correctly.
314         assertThat(accessPoint.getSsidStr()).isEqualTo(TEST_UNQUOTED_SSID);
315         assertThat(accessPoint.getBssid()).isEqualTo(TEST_BSSID);
316         assertThat(accessPoint.isActive()).isFalse();
317         assertThat(accessPoint.getConfig()).isNotNull();
318         WifiConfiguration.NetworkSelectionStatus networkStatus =
319                 accessPoint.getConfig().getNetworkSelectionStatus();
320         assertThat(networkStatus).isNotNull();
321         assertThat(networkStatus.isNetworkEnabled()).isFalse();
322         assertThat(networkStatus.getNetworkSelectionDisableReason()).isEqualTo(
323                 WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD);
324 
325         when(mWifiTracker.getAccessPoints()).thenReturn(Lists.newArrayList(accessPoint));
326         launchActivity(WifiSettings.EXTRA_START_CONNECT_SSID, accessPoint.getSsidStr());
327 
328         // Make sure that the password dialog is visible.
329         onView(withText(resourceId(STRING, WIFI_PASSWORD))).check(matches(isDisplayed()));
330         onView(withText(resourceId(STRING, WIFI_SHOW_PASSWORD))).check(matches(isDisplayed()));
331         onView(withId(resourceId(ID, PASSWORD_LAYOUT))).check(matches(isDisplayed()));
332         onView(withId(resourceId(ID, PASSWORD))).check(matches(isDisplayed()));
333     }
334 }
335