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 */
16package com.android.settingslib.wifi;
17
18import static com.google.common.truth.Truth.assertThat;
19import static com.google.common.truth.Truth.assertWithMessage;
20
21import static org.mockito.Mockito.any;
22import static org.mockito.Mockito.when;
23
24import android.content.Context;
25import android.net.ConnectivityManager;
26import android.net.NetworkInfo;
27import android.net.ScoredNetwork;
28import android.net.wifi.ScanResult;
29import android.net.wifi.WifiConfiguration;
30import android.net.wifi.WifiInfo;
31import android.net.wifi.WifiNetworkScoreCache;
32import android.net.wifi.WifiSsid;
33import android.net.wifi.hotspot2.PasspointConfiguration;
34import android.net.wifi.hotspot2.pps.HomeSp;
35import android.os.Bundle;
36import android.os.SystemClock;
37import android.support.test.InstrumentationRegistry;
38import android.support.test.filters.SmallTest;
39import android.support.test.runner.AndroidJUnit4;
40import android.text.SpannableString;
41import android.text.style.TtsSpan;
42
43import org.junit.Before;
44import org.junit.Test;
45import org.junit.runner.RunWith;
46import org.mockito.Mock;
47import org.mockito.MockitoAnnotations;
48
49import java.util.ArrayList;
50import java.util.Collections;
51
52@SmallTest
53@RunWith(AndroidJUnit4.class)
54public class AccessPointTest {
55
56    private static final String TEST_SSID = "test_ssid";
57    private Context mContext;
58    @Mock private WifiNetworkScoreCache mWifiNetworkScoreCache;
59
60    @Before
61    public void setUp() {
62        MockitoAnnotations.initMocks(this);
63        mContext = InstrumentationRegistry.getTargetContext();
64    }
65
66    @Test
67    public void testSsidIsTelephoneSpan() {
68        final Bundle bundle = new Bundle();
69        bundle.putString("key_ssid", TEST_SSID);
70        final AccessPoint ap = new AccessPoint(InstrumentationRegistry.getTargetContext(), bundle);
71        final CharSequence ssid = ap.getSsid();
72
73        assertThat(ssid instanceof SpannableString).isTrue();
74
75        TtsSpan[] spans = ((SpannableString) ssid).getSpans(0, TEST_SSID.length(), TtsSpan.class);
76
77        assertThat(spans.length).isEqualTo(1);
78        assertThat(spans[0].getType()).isEqualTo(TtsSpan.TYPE_TELEPHONE);
79    }
80
81    @Test
82    public void testCopyAccessPoint_dataShouldMatch() {
83        WifiConfiguration configuration = createWifiConfiguration();
84        configuration.meteredHint = true;
85
86        NetworkInfo networkInfo =
87                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE");
88        AccessPoint originalAccessPoint = new AccessPoint(mContext, configuration);
89        WifiInfo wifiInfo = new WifiInfo();
90        wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID));
91        wifiInfo.setBSSID(configuration.BSSID);
92        originalAccessPoint.update(configuration, wifiInfo, networkInfo);
93        AccessPoint copy = new AccessPoint(mContext, originalAccessPoint);
94
95        assertThat(originalAccessPoint.getSsid().toString()).isEqualTo(copy.getSsid().toString());
96        assertThat(originalAccessPoint.getBssid()).isEqualTo(copy.getBssid());
97        assertThat(originalAccessPoint.getConfig()).isEqualTo(copy.getConfig());
98        assertThat(originalAccessPoint.getSecurity()).isEqualTo(copy.getSecurity());
99        assertThat(originalAccessPoint.isMetered()).isEqualTo(copy.isMetered());
100        assertThat(originalAccessPoint.compareTo(copy) == 0).isTrue();
101    }
102
103    @Test
104    public void testThatCopyAccessPoint_scanCacheShouldMatch() {
105        AccessPoint original = createAccessPointWithScanResultCache();
106        assertThat(original.getRssi()).isEqualTo(4);
107        AccessPoint copy = new AccessPoint(mContext, createWifiConfiguration());
108        assertThat(copy.getRssi()).isEqualTo(AccessPoint.UNREACHABLE_RSSI);
109        copy.copyFrom(original);
110        assertThat(original.getRssi()).isEqualTo(copy.getRssi());
111    }
112
113    @Test
114    public void testCompareTo_GivesActiveBeforeInactive() {
115        AccessPoint activeAp = new TestAccessPointBuilder(mContext).setActive(true).build();
116        AccessPoint inactiveAp = new TestAccessPointBuilder(mContext).setActive(false).build();
117
118        assertSortingWorks(activeAp, inactiveAp);
119    }
120
121    @Test
122    public void testCompareTo_GivesReachableBeforeUnreachable() {
123        AccessPoint nearAp = new TestAccessPointBuilder(mContext).setReachable(true).build();
124        AccessPoint farAp = new TestAccessPointBuilder(mContext).setReachable(false).build();
125
126        assertSortingWorks(nearAp, farAp);
127    }
128
129    @Test
130    public void testCompareTo_GivesSavedBeforeUnsaved() {
131        AccessPoint savedAp = new TestAccessPointBuilder(mContext).setSaved(true).build();
132        AccessPoint notSavedAp = new TestAccessPointBuilder(mContext).setSaved(false).build();
133
134        assertSortingWorks(savedAp, notSavedAp);
135    }
136
137    //TODO: add tests for mRankingScore sort order if ranking is exposed
138
139    @Test
140    public void testCompareTo_GivesHighLevelBeforeLowLevel() {
141        final int highLevel = AccessPoint.SIGNAL_LEVELS - 1;
142        final int lowLevel = 1;
143        assertThat(highLevel).isGreaterThan(lowLevel);
144
145        AccessPoint strongAp = new TestAccessPointBuilder(mContext).setLevel(highLevel).build();
146        AccessPoint weakAp = new TestAccessPointBuilder(mContext).setLevel(lowLevel).build();
147
148        assertSortingWorks(strongAp, weakAp);
149    }
150
151    @Test
152    public void testCompareTo_GivesSsidAlphabetically() {
153
154        final String firstName = "AAAAAA";
155        final String secondName = "zzzzzz";
156
157        AccessPoint firstAp = new TestAccessPointBuilder(mContext).setSsid(firstName).build();
158        AccessPoint secondAp = new TestAccessPointBuilder(mContext).setSsid(secondName).build();
159
160        assertThat(firstAp.getSsidStr().compareToIgnoreCase(secondAp.getSsidStr()) < 0).isTrue();
161        assertSortingWorks(firstAp, secondAp);
162    }
163
164    @Test
165    public void testCompareTo_AllSortingRulesCombined() {
166
167        AccessPoint active = new TestAccessPointBuilder(mContext).setActive(true).build();
168        AccessPoint reachableAndMinLevel = new TestAccessPointBuilder(mContext)
169                .setReachable(true).build();
170        AccessPoint saved = new TestAccessPointBuilder(mContext).setSaved(true).build();
171        AccessPoint highLevelAndReachable = new TestAccessPointBuilder(mContext)
172                .setLevel(AccessPoint.SIGNAL_LEVELS - 1).build();
173        AccessPoint firstName = new TestAccessPointBuilder(mContext).setSsid("a").build();
174        AccessPoint lastname = new TestAccessPointBuilder(mContext).setSsid("z").build();
175
176        ArrayList<AccessPoint> points = new ArrayList<AccessPoint>();
177        points.add(lastname);
178        points.add(firstName);
179        points.add(highLevelAndReachable);
180        points.add(saved);
181        points.add(reachableAndMinLevel);
182        points.add(active);
183
184        Collections.sort(points);
185        assertThat(points.indexOf(active)).isLessThan(points.indexOf(reachableAndMinLevel));
186        assertThat(points.indexOf(reachableAndMinLevel)).isLessThan(points.indexOf(saved));
187        // note: the saved AP will not appear before highLevelAndReachable,
188        // because all APs with a signal level are reachable,
189        // and isReachable() takes higher sorting precedence than isSaved().
190        assertThat(points.indexOf(saved)).isLessThan(points.indexOf(firstName));
191        assertThat(points.indexOf(highLevelAndReachable)).isLessThan(points.indexOf(firstName));
192        assertThat(points.indexOf(firstName)).isLessThan(points.indexOf(lastname));
193    }
194
195    @Test
196    public void testRssiIsSetFromScanResults() {
197        AccessPoint ap = createAccessPointWithScanResultCache();
198        int originalRssi = ap.getRssi();
199        assertThat(originalRssi).isNotEqualTo(AccessPoint.UNREACHABLE_RSSI);
200    }
201
202    @Test
203    public void testGetRssiShouldReturnSetRssiValue() {
204        AccessPoint ap = createAccessPointWithScanResultCache();
205        int originalRssi = ap.getRssi();
206        int newRssi = originalRssi - 10;
207        ap.setRssi(newRssi);
208        assertThat(ap.getRssi()).isEqualTo(newRssi);
209    }
210
211    @Test
212    public void testUpdateWithScanResultShouldAverageRssi() {
213        String ssid = "ssid";
214        int originalRssi = -65;
215        int newRssi = -80;
216        int expectedRssi = (originalRssi + newRssi) / 2;
217        AccessPoint ap =
218                new TestAccessPointBuilder(mContext).setSsid(ssid).setRssi(originalRssi).build();
219
220        ScanResult scanResult = new ScanResult();
221        scanResult.SSID = ssid;
222        scanResult.level = newRssi;
223        scanResult.BSSID = "bssid";
224        scanResult.timestamp = SystemClock.elapsedRealtime() * 1000;
225        scanResult.capabilities = "";
226        assertThat(ap.update(scanResult)).isTrue();
227
228        assertThat(ap.getRssi()).isEqualTo(expectedRssi);
229    }
230
231    @Test
232    public void testCreateFromPasspointConfig() {
233        PasspointConfiguration config = new PasspointConfiguration();
234        HomeSp homeSp = new HomeSp();
235        homeSp.setFqdn("test.com");
236        homeSp.setFriendlyName("Test Provider");
237        config.setHomeSp(homeSp);
238        AccessPoint ap = new AccessPoint(mContext, config);
239        assertThat(ap.isPasspointConfig()).isTrue();
240    }
241
242    @Test
243    public void testIsMetered_returnTrueWhenWifiConfigurationIsMetered() {
244        WifiConfiguration configuration = createWifiConfiguration();
245        configuration.meteredHint = true;
246
247        NetworkInfo networkInfo =
248                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE");
249        AccessPoint accessPoint = new AccessPoint(mContext, configuration);
250        WifiInfo wifiInfo = new WifiInfo();
251        wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID));
252        wifiInfo.setBSSID(configuration.BSSID);
253        wifiInfo.setNetworkId(configuration.networkId);
254        accessPoint.update(configuration, wifiInfo, networkInfo);
255
256        assertThat(accessPoint.isMetered()).isTrue();
257    }
258
259    @Test
260    public void testIsMetered_returnTrueWhenWifiInfoIsMetered() {
261        WifiConfiguration configuration = createWifiConfiguration();
262
263        NetworkInfo networkInfo =
264                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE");
265        AccessPoint accessPoint = new AccessPoint(mContext, configuration);
266        WifiInfo wifiInfo = new WifiInfo();
267        wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID));
268        wifiInfo.setBSSID(configuration.BSSID);
269        wifiInfo.setNetworkId(configuration.networkId);
270        wifiInfo.setMeteredHint(true);
271        accessPoint.update(configuration, wifiInfo, networkInfo);
272
273        assertThat(accessPoint.isMetered()).isTrue();
274    }
275
276    @Test
277    public void testIsMetered_returnTrueWhenNetworkInfoIsMetered() {
278        WifiConfiguration configuration = createWifiConfiguration();
279
280        NetworkInfo networkInfo =
281                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE");
282        networkInfo.setMetered(true);
283        AccessPoint accessPoint = new AccessPoint(mContext, configuration);
284        WifiInfo wifiInfo = new WifiInfo();
285        wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID));
286        wifiInfo.setBSSID(configuration.BSSID);
287        wifiInfo.setNetworkId(configuration.networkId);
288        accessPoint.update(configuration, wifiInfo, networkInfo);
289
290        assertThat(accessPoint.isMetered()).isTrue();
291    }
292
293    @Test
294    public void testIsMetered_returnTrueWhenScoredNetworkIsMetered() {
295        AccessPoint ap = createAccessPointWithScanResultCache();
296
297        when(mWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class)))
298                .thenReturn(
299                        new ScoredNetwork(
300                                null /* NetworkKey */,
301                                null /* rssiCurve */,
302                                true /* metered */));
303        ap.update(mWifiNetworkScoreCache, false /* scoringUiEnabled */);
304
305        assertThat(ap.isMetered()).isTrue();
306    }
307
308    @Test
309    public void testIsMetered_returnFalseByDefault() {
310        WifiConfiguration configuration = createWifiConfiguration();
311
312        NetworkInfo networkInfo =
313                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE");
314        AccessPoint accessPoint = new AccessPoint(mContext, configuration);
315        WifiInfo wifiInfo = new WifiInfo();
316        wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID));
317        wifiInfo.setBSSID(configuration.BSSID);
318        wifiInfo.setNetworkId(configuration.networkId);
319        accessPoint.update(configuration, wifiInfo, networkInfo);
320
321        assertThat(accessPoint.isMetered()).isFalse();
322    }
323
324    private AccessPoint createAccessPointWithScanResultCache() {
325        Bundle bundle = new Bundle();
326        ArrayList<ScanResult> scanResults = new ArrayList<>();
327        for (int i = 0; i < 5; i++) {
328            ScanResult scanResult = new ScanResult();
329            scanResult.level = i;
330            scanResult.BSSID = "bssid-" + i;
331            scanResult.timestamp = SystemClock.elapsedRealtime() * 1000;
332            scanResult.capabilities = "";
333            scanResults.add(scanResult);
334        }
335
336        bundle.putParcelableArrayList("key_scanresultcache", scanResults);
337        return new AccessPoint(mContext, bundle);
338    }
339
340    private WifiConfiguration createWifiConfiguration() {
341        WifiConfiguration configuration = new WifiConfiguration();
342        configuration.BSSID = "bssid";
343        configuration.SSID = "ssid";
344        configuration.networkId = 123;
345        return configuration;
346    }
347
348    /**
349    * Assert that the first AccessPoint appears after the second AccessPoint
350    * once sorting has been completed.
351    */
352    private void assertSortingWorks(AccessPoint first, AccessPoint second) {
353
354        ArrayList<AccessPoint> points = new ArrayList<AccessPoint>();
355
356        // add in reverse order so we can tell that sorting actually changed something
357        points.add(second);
358        points.add(first);
359        Collections.sort(points);
360        assertWithMessage(
361                String.format("After sorting: second AccessPoint should have higher array index "
362                    + "than the first, but found indicies second '%s' and first '%s'.",
363                    points.indexOf(second), points.indexOf(first)))
364            .that(points.indexOf(second)).isGreaterThan(points.indexOf(first));
365    }
366
367    @Test
368    public void testBuilder_setActive() {
369        AccessPoint activeAp = new TestAccessPointBuilder(mContext).setActive(true).build();
370        assertThat(activeAp.isActive()).isTrue();
371
372        AccessPoint inactiveAp = new TestAccessPointBuilder(mContext).setActive(false).build();
373        assertThat(inactiveAp.isActive()).isFalse();
374    }
375
376    @Test
377    public void testBuilder_setReachable() {
378        AccessPoint nearAp = new TestAccessPointBuilder(mContext).setReachable(true).build();
379        assertThat(nearAp.isReachable()).isTrue();
380
381        AccessPoint farAp = new TestAccessPointBuilder(mContext).setReachable(false).build();
382        assertThat(farAp.isReachable()).isFalse();
383    }
384
385    @Test
386    public void testBuilder_setSaved() {
387        AccessPoint savedAp = new TestAccessPointBuilder(mContext).setSaved(true).build();
388        assertThat(savedAp.isSaved()).isTrue();
389
390        AccessPoint newAp = new TestAccessPointBuilder(mContext).setSaved(false).build();
391        assertThat(newAp.isSaved()).isFalse();
392    }
393
394    @Test
395    public void testBuilder_setLevel() {
396        AccessPoint testAp;
397
398        for (int i = 0; i < AccessPoint.SIGNAL_LEVELS; i++) {
399            testAp = new TestAccessPointBuilder(mContext).setLevel(i).build();
400            assertThat(testAp.getLevel()).isEqualTo(i);
401        }
402
403        // numbers larger than the max level should be set to max
404        testAp = new TestAccessPointBuilder(mContext).setLevel(AccessPoint.SIGNAL_LEVELS).build();
405        assertThat(testAp.getLevel()).isEqualTo(AccessPoint.SIGNAL_LEVELS - 1);
406
407        // numbers less than 0 should give level 0
408        testAp = new TestAccessPointBuilder(mContext).setLevel(-100).build();
409        assertThat(testAp.getLevel()).isEqualTo(0);
410    }
411
412    @Test
413    public void testBuilder_settingReachableAfterLevelDoesNotAffectLevel() {
414        int level = 1;
415        assertThat(level).isLessThan(AccessPoint.SIGNAL_LEVELS - 1);
416
417        AccessPoint testAp =
418                new TestAccessPointBuilder(mContext).setLevel(level).setReachable(true).build();
419        assertThat(testAp.getLevel()).isEqualTo(level);
420    }
421
422    @Test
423    public void testBuilder_setSsid() {
424        String name = "AmazingSsid!";
425        AccessPoint namedAp = new TestAccessPointBuilder(mContext).setSsid(name).build();
426        assertThat(namedAp.getSsidStr()).isEqualTo(name);
427    }
428
429    @Test
430    public void testBuilder_passpointConfig() {
431        String fqdn = "Test.com";
432        String providerFriendlyName = "Test Provider";
433        AccessPoint ap = new TestAccessPointBuilder(mContext).setFqdn(fqdn)
434                .setProviderFriendlyName(providerFriendlyName).build();
435        assertThat(ap.isPasspointConfig()).isTrue();
436        assertThat(ap.getPasspointFqdn()).isEqualTo(fqdn);
437        assertThat(ap.getConfigName()).isEqualTo(providerFriendlyName);
438    }
439
440    @Test
441    public void testUpdateNetworkInfo_returnsTrue() {
442        int networkId = 123;
443        int rssi = -55;
444        WifiConfiguration config = new WifiConfiguration();
445        config.networkId = networkId;
446        WifiInfo wifiInfo = new WifiInfo();
447        wifiInfo.setNetworkId(networkId);
448        wifiInfo.setRssi(rssi);
449
450        NetworkInfo networkInfo =
451                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
452        networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTING, "", "");
453
454        AccessPoint ap = new TestAccessPointBuilder(mContext)
455                .setNetworkInfo(networkInfo)
456                .setNetworkId(networkId)
457                .setRssi(rssi)
458                .setWifiInfo(wifiInfo)
459                .build();
460
461        NetworkInfo newInfo = new NetworkInfo(networkInfo);
462        newInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
463        assertThat(ap.update(config, wifiInfo, newInfo)).isTrue();
464    }
465
466    @Test
467    public void testUpdateNetworkInfoWithSameInfo_returnsFalse() {
468        int networkId = 123;
469        int rssi = -55;
470        WifiConfiguration config = new WifiConfiguration();
471        config.networkId = networkId;
472        WifiInfo wifiInfo = new WifiInfo();
473        wifiInfo.setNetworkId(networkId);
474        wifiInfo.setRssi(rssi);
475
476        NetworkInfo networkInfo =
477                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
478        networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTING, "", "");
479
480        AccessPoint ap = new TestAccessPointBuilder(mContext)
481                .setNetworkInfo(networkInfo)
482                .setNetworkId(networkId)
483                .setRssi(rssi)
484                .setWifiInfo(wifiInfo)
485                .build();
486
487        NetworkInfo newInfo = new NetworkInfo(networkInfo); // same values
488        assertThat(ap.update(config, wifiInfo, newInfo)).isFalse();
489    }
490}
491