diff options
7 files changed, 469 insertions, 68 deletions
diff --git a/service/java/com/android/server/wifi/OpenNetworkNotifier.java b/service/java/com/android/server/wifi/OpenNetworkNotifier.java index fc144c139..692c8e22c 100644 --- a/service/java/com/android/server/wifi/OpenNetworkNotifier.java +++ b/service/java/com/android/server/wifi/OpenNetworkNotifier.java @@ -29,6 +29,8 @@ import android.os.Looper; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.util.ArraySet; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; @@ -36,18 +38,25 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.List; +import java.util.Set; /** * Takes care of handling the "open wi-fi network available" notification + * + * NOTE: These API's are not thread safe and should only be used from WifiStateMachine thread. * @hide */ public class OpenNetworkNotifier { + private static final String TAG = "OpenNetworkNotifier"; + static final String ACTION_USER_DISMISSED_NOTIFICATION = "com.android.server.wifi.OpenNetworkNotifier.USER_DISMISSED_NOTIFICATION"; static final String ACTION_USER_TAPPED_CONTENT = "com.android.server.wifi.OpenNetworkNotifier.USER_TAPPED_CONTENT"; + /** Identifier of the {@link SsidSetStoreData}. */ + private static final String STORE_DATA_IDENTIFIER = "OpenNetworkNotifierBlacklist"; /** * The {@link Clock#getWallClockMillis()} must be at least this value for us * to show the notification again. @@ -68,10 +77,14 @@ public class OpenNetworkNotifier { /** Whether the screen is on or not. */ private boolean mScreenOn; + /** List of SSIDs blacklisted from recommendation. */ + private final Set<String> mBlacklistedSsids; + private final Context mContext; private final Handler mHandler; private final FrameworkFacade mFrameworkFacade; private final Clock mClock; + private final WifiConfigManager mConfigManager; private final OpenNetworkRecommender mOpenNetworkRecommender; private final OpenNetworkNotificationBuilder mOpenNetworkNotificationBuilder; @@ -82,15 +95,22 @@ public class OpenNetworkNotifier { Looper looper, FrameworkFacade framework, Clock clock, + WifiConfigManager wifiConfigManager, + WifiConfigStore wifiConfigStore, OpenNetworkRecommender openNetworkRecommender) { mContext = context; mHandler = new Handler(looper); mFrameworkFacade = framework; mClock = clock; + mConfigManager = wifiConfigManager; mOpenNetworkRecommender = openNetworkRecommender; mOpenNetworkNotificationBuilder = new OpenNetworkNotificationBuilder(context, framework); mScreenOn = false; + mBlacklistedSsids = new ArraySet<>(); + wifiConfigStore.registerStoreData(new SsidSetStoreData( + STORE_DATA_IDENTIFIER, new OpenNetworkNotifierStoreData())); + // Setting is in seconds mNotificationRepeatDelay = mFrameworkFacade.getIntegerSetting(context, Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, @@ -165,7 +185,7 @@ public class OpenNetworkNotifier { } mRecommendedNetwork = mOpenNetworkRecommender.recommendNetwork( - availableNetworks, mRecommendedNetwork); + availableNetworks, new ArraySet<>(mBlacklistedSsids)); postNotification(availableNetworks.size()); } @@ -201,8 +221,14 @@ public class OpenNetworkNotifier { .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); } - /** A delay is set before the next shown notification after user dismissal. */ private void handleUserDismissedAction() { + if (mRecommendedNetwork != null) { + // blacklist dismissed network + mBlacklistedSsids.add(mRecommendedNetwork.SSID); + mConfigManager.saveToStore(false /* forceWrite */); + Log.d(TAG, "Network is added to the open network notification blacklist: " + + mRecommendedNetwork.SSID); + } mNotificationShown = false; } @@ -213,6 +239,19 @@ public class OpenNetworkNotifier { pw.println("currentTime: " + mClock.getWallClockMillis()); pw.println("mNotificationRepeatTime: " + mNotificationRepeatTime); pw.println("mNotificationShown: " + mNotificationShown); + pw.println("mBlacklistedSsids: " + mBlacklistedSsids.toString()); + } + + private class OpenNetworkNotifierStoreData implements SsidSetStoreData.DataSource { + @Override + public Set<String> getSsids() { + return new ArraySet<>(mBlacklistedSsids); + } + + @Override + public void setSsids(Set<String> ssidList) { + mBlacklistedSsids.addAll(ssidList); + } } private class NotificationEnabledSettingObserver extends ContentObserver { diff --git a/service/java/com/android/server/wifi/OpenNetworkRecommender.java b/service/java/com/android/server/wifi/OpenNetworkRecommender.java index cd460e58b..5ceeddded 100644 --- a/service/java/com/android/server/wifi/OpenNetworkRecommender.java +++ b/service/java/com/android/server/wifi/OpenNetworkRecommender.java @@ -20,9 +20,12 @@ import android.annotation.NonNull; import android.net.wifi.ScanResult; import java.util.List; +import java.util.Set; /** * Helps recommend the best available network for {@link OpenNetworkNotifier}. + * + * NOTE: These API's are not thread safe and should only be used from WifiStateMachine thread. * @hide */ public class OpenNetworkRecommender { @@ -32,31 +35,24 @@ public class OpenNetworkRecommender { * * @param networks List of scan details to pick a recommendation. This list should not be null * or empty. - * @param currentRecommendation The currently recommended network. + * @param blacklistedSsids The list of SSIDs that should not be recommended. */ - public ScanResult recommendNetwork( - @NonNull List<ScanDetail> networks, ScanResult currentRecommendation) { - ScanResult currentUpdatedRecommendation = null; + public ScanResult recommendNetwork(@NonNull List<ScanDetail> networks, + @NonNull Set<String> blacklistedSsids) { ScanResult result = null; int highestRssi = Integer.MIN_VALUE; for (ScanDetail scanDetail : networks) { ScanResult scanResult = scanDetail.getScanResult(); - if (currentRecommendation != null - && currentRecommendation.SSID.equals(scanResult.SSID)) { - currentUpdatedRecommendation = scanResult; - } - if (scanResult.level > highestRssi) { result = scanResult; highestRssi = scanResult.level; } } - if (currentUpdatedRecommendation != null - && currentUpdatedRecommendation.level >= result.level) { - return currentUpdatedRecommendation; - } else { - return result; + + if (result != null && blacklistedSsids.contains(result.SSID)) { + result = null; } + return result; } } diff --git a/service/java/com/android/server/wifi/SsidSetStoreData.java b/service/java/com/android/server/wifi/SsidSetStoreData.java new file mode 100644 index 000000000..daed26a6a --- /dev/null +++ b/service/java/com/android/server/wifi/SsidSetStoreData.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import android.text.TextUtils; + +import com.android.server.wifi.util.XmlUtil; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +/** + * Store data for network notifiers. + * + * Below are the current configuration data for each respective store file: + * + * Share Store (system wide configurations) + * - No data + * + * User Store (user specific configurations) + * - Set of blacklisted SSIDs + */ +public class SsidSetStoreData implements WifiConfigStore.StoreData { + private static final String XML_TAG_SECTION_HEADER_SUFFIX = "ConfigData"; + private static final String XML_TAG_SSID_SET = "SSIDSet"; + + private final String mTagName; + private final DataSource mDataSource; + + /** + * Interface define the data source for the notifier store data. + */ + public interface DataSource { + /** + * Retrieve the SSID set from the data source. + * + * @return Set of SSIDs + */ + Set<String> getSsids(); + + /** + * Update the SSID set in the data source. + * + * @param ssidSet The set of SSIDs + */ + void setSsids(Set<String> ssidSet); + } + + /** + * Creates the SSID Set store data. + * + * @param name Identifier of the SSID set. + * @param dataSource The DataSource that implements the update and retrieval of the SSID set. + */ + SsidSetStoreData(String name, DataSource dataSource) { + mTagName = name + XML_TAG_SECTION_HEADER_SUFFIX; + mDataSource = dataSource; + } + + @Override + public void serializeData(XmlSerializer out, boolean shared) + throws XmlPullParserException, IOException { + if (shared) { + throw new XmlPullParserException("Share data not supported"); + } + Set<String> ssidSet = mDataSource.getSsids(); + if (ssidSet != null && !ssidSet.isEmpty()) { + XmlUtil.writeNextValue(out, XML_TAG_SSID_SET, mDataSource.getSsids()); + } + } + + @Override + public void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared) + throws XmlPullParserException, IOException { + if (shared) { + throw new XmlPullParserException("Share data not supported"); + } + + while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) { + String[] valueName = new String[1]; + Object value = XmlUtil.readCurrentValue(in, valueName); + if (TextUtils.isEmpty(valueName[0])) { + throw new XmlPullParserException("Missing value name"); + } + switch (valueName[0]) { + case XML_TAG_SSID_SET: + mDataSource.setSsids((Set<String>) value); + break; + default: + throw new XmlPullParserException("Unknown tag under " + + mTagName + ": " + valueName[0]); + } + } + } + + @Override + public void resetData(boolean shared) { + if (!shared) { + mDataSource.setSsids(new HashSet<>()); + } + } + + @Override + public String getName() { + return mTagName; + } + + @Override + public boolean supportShareData() { + return false; + } +} diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 60c5d2fb9..26d702477 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -233,7 +233,7 @@ public class WifiInjector { mCertManager = new WifiCertManager(mContext); mOpenNetworkNotifier = new OpenNetworkNotifier(mContext, mWifiStateMachineHandlerThread.getLooper(), mFrameworkFacade, mClock, - new OpenNetworkRecommender()); + mWifiConfigManager, mWifiConfigStore, new OpenNetworkRecommender()); mLockManager = new WifiLockManager(mContext, BatteryStatsService.getService()); mWifiController = new WifiController(mContext, mWifiStateMachine, mSettingsStore, mLockManager, mWifiServiceHandlerThread.getLooper(), mFrameworkFacade); diff --git a/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java b/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java index 29c068ddc..957fc2294 100644 --- a/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java +++ b/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java @@ -37,6 +37,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.test.TestLooper; import android.provider.Settings; +import android.util.ArraySet; import org.junit.Before; import org.junit.Test; @@ -47,6 +48,7 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; +import java.util.Set; /** * Unit tests for {@link OpenNetworkNotifier}. @@ -60,6 +62,8 @@ public class OpenNetworkNotifierTest { @Mock private Resources mResources; @Mock private FrameworkFacade mFrameworkFacade; @Mock private Clock mClock; + @Mock private WifiConfigStore mWifiConfigStore; + @Mock private WifiConfigManager mWifiConfigManager; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Notification.Builder mNotificationBuilder; @Mock private NotificationManager mNotificationManager; @Mock private OpenNetworkRecommender mOpenNetworkRecommender; @@ -67,6 +71,8 @@ public class OpenNetworkNotifierTest { private OpenNetworkNotifier mNotificationController; private BroadcastReceiver mBroadcastReceiver; private ScanResult mDummyNetwork; + private List<ScanDetail> mOpenNetworks; + private Set<String> mBlacklistedSsids; /** Initialize objects before each test run. */ @@ -90,11 +96,14 @@ public class OpenNetworkNotifierTest { mDummyNetwork.capabilities = "[ESS]"; mDummyNetwork.level = MIN_RSSI_LEVEL; when(mOpenNetworkRecommender.recommendNetwork(any(), any())).thenReturn(mDummyNetwork); + mOpenNetworks = new ArrayList<>(); + mOpenNetworks.add(new ScanDetail(mDummyNetwork, null /* networkDetail */)); + mBlacklistedSsids = new ArraySet<>(); TestLooper mock_looper = new TestLooper(); mNotificationController = new OpenNetworkNotifier( - mContext, mock_looper.getLooper(), mFrameworkFacade, - mClock, mOpenNetworkRecommender); + mContext, mock_looper.getLooper(), mFrameworkFacade, mClock, mWifiConfigManager, + mWifiConfigStore, mOpenNetworkRecommender); ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); @@ -102,20 +111,14 @@ public class OpenNetworkNotifierTest { mNotificationController.handleScreenStateChanged(true); } - private List<ScanDetail> createOpenScanResults() { - List<ScanDetail> scanResults = new ArrayList<>(); - scanResults.add(new ScanDetail(mDummyNetwork, null /* networkDetail */)); - return scanResults; - } - /** * When scan results with open networks are handled, a notification is posted. */ @Test public void handleScanResults_hasOpenNetworks_notificationDisplayed() { - mNotificationController.handleScanResults(createOpenScanResults()); + mNotificationController.handleScanResults(mOpenNetworks); - verify(mOpenNetworkRecommender).recommendNetwork(any(), any()); + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); verify(mNotificationManager).notify(anyInt(), any()); } @@ -136,9 +139,9 @@ public class OpenNetworkNotifierTest { */ @Test public void handleScanResults_notificationShown_emptyList_notificationCleared() { - mNotificationController.handleScanResults(createOpenScanResults()); + mNotificationController.handleScanResults(mOpenNetworks); - verify(mOpenNetworkRecommender).recommendNetwork(any(), any()); + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); verify(mNotificationManager).notify(anyInt(), any()); mNotificationController.handleScanResults(new ArrayList<>()); @@ -151,9 +154,9 @@ public class OpenNetworkNotifierTest { */ @Test public void handleScanResults_notificationShown_screenOff_emptyList_notificationCleared() { - mNotificationController.handleScanResults(createOpenScanResults()); + mNotificationController.handleScanResults(mOpenNetworks); - verify(mOpenNetworkRecommender).recommendNetwork(any(), any()); + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); verify(mNotificationManager).notify(anyInt(), any()); mNotificationController.handleScreenStateChanged(false); @@ -167,10 +170,10 @@ public class OpenNetworkNotifierTest { */ @Test public void handleScanResults_notificationShowing_doesNotRepostNotification() { - mNotificationController.handleScanResults(createOpenScanResults()); - mNotificationController.handleScanResults(createOpenScanResults()); + mNotificationController.handleScanResults(mOpenNetworks); + mNotificationController.handleScanResults(mOpenNetworks); - verify(mOpenNetworkRecommender).recommendNetwork(any(), any()); + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); verify(mNotificationManager).notify(anyInt(), any()); } @@ -180,9 +183,9 @@ public class OpenNetworkNotifierTest { */ @Test public void clearPendingNotification_clearsNotificationIfOneIsShowing() { - mNotificationController.handleScanResults(createOpenScanResults()); + mNotificationController.handleScanResults(mOpenNetworks); - verify(mOpenNetworkRecommender).recommendNetwork(any(), any()); + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); verify(mNotificationManager).notify(anyInt(), any()); mNotificationController.clearPendingNotification(true); @@ -208,7 +211,7 @@ public class OpenNetworkNotifierTest { @Test public void screenOff_handleScanResults_notificationNotDisplayed() { mNotificationController.handleScreenStateChanged(false); - mNotificationController.handleScanResults(createOpenScanResults()); + mNotificationController.handleScanResults(mOpenNetworks); verify(mOpenNetworkRecommender, never()).recommendNetwork(any(), any()); verify(mNotificationManager, never()).notify(anyInt(), any()); @@ -220,17 +223,18 @@ public class OpenNetworkNotifierTest { */ @Test public void postNotification_clearNotificationWithoutDelayReset_shouldNotPostNotification() { - mNotificationController.handleScanResults(createOpenScanResults()); + mNotificationController.handleScanResults(mOpenNetworks); - verify(mOpenNetworkRecommender).recommendNetwork(any(), any()); + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); verify(mNotificationManager).notify(anyInt(), any()); mNotificationController.clearPendingNotification(false); - mNotificationController.handleScanResults(createOpenScanResults()); + mNotificationController.handleScanResults(mOpenNetworks); // Recommendation made twice but no new notification posted. - verify(mOpenNetworkRecommender, times(2)).recommendNetwork(any(), any()); + verify(mOpenNetworkRecommender, times(2)).recommendNetwork( + mOpenNetworks, mBlacklistedSsids); verify(mNotificationManager).notify(anyInt(), any()); verify(mNotificationManager).cancel(anyInt()); } @@ -241,16 +245,17 @@ public class OpenNetworkNotifierTest { */ @Test public void postNotification_clearNotificationWithDelayReset_shouldPostNotification() { - mNotificationController.handleScanResults(createOpenScanResults()); + mNotificationController.handleScanResults(mOpenNetworks); - verify(mOpenNetworkRecommender).recommendNetwork(any(), any()); + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); verify(mNotificationManager).notify(anyInt(), any()); mNotificationController.clearPendingNotification(true); - mNotificationController.handleScanResults(createOpenScanResults()); + mNotificationController.handleScanResults(mOpenNetworks); - verify(mOpenNetworkRecommender, times(2)).recommendNetwork(any(), any()); + verify(mOpenNetworkRecommender, times(2)).recommendNetwork( + mOpenNetworks, mBlacklistedSsids); verify(mNotificationManager, times(2)).notify(anyInt(), any()); } @@ -259,9 +264,9 @@ public class OpenNetworkNotifierTest { */ @Test public void notificationTap_opensWifiSettings() { - mNotificationController.handleScanResults(createOpenScanResults()); + mNotificationController.handleScanResults(mOpenNetworks); - verify(mOpenNetworkRecommender).recommendNetwork(any(), any()); + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); verify(mNotificationManager).notify(anyInt(), any()); mBroadcastReceiver.onReceive( @@ -271,14 +276,37 @@ public class OpenNetworkNotifierTest { } /** + * When user dismissed notification and there is a recommended network, network ssid should be + * blacklisted. + */ + @Test + public void userDismissedNotification_shouldBlacklistNetwork() { + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationManager).notify(anyInt(), any()); + + mBroadcastReceiver.onReceive( + mContext, new Intent(OpenNetworkNotifier.ACTION_USER_DISMISSED_NOTIFICATION)); + + verify(mWifiConfigManager).saveToStore(false /* forceWrite */); + + mNotificationController.handleScanResults(mOpenNetworks); + + Set<String> expectedBlacklist = new ArraySet<>(); + expectedBlacklist.add(mDummyNetwork.SSID); + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, expectedBlacklist); + } + + /** * When a notification is posted and cleared without reseting delay, after the delay has passed * the next scan with open networks should post a notification. */ @Test public void delaySet_delayPassed_shouldPostNotification() { - mNotificationController.handleScanResults(createOpenScanResults()); + mNotificationController.handleScanResults(mOpenNetworks); - verify(mOpenNetworkRecommender).recommendNetwork(any(), any()); + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); verify(mNotificationManager).notify(anyInt(), any()); mNotificationController.clearPendingNotification(false); @@ -286,9 +314,10 @@ public class OpenNetworkNotifierTest { // twice the delay time passed when(mClock.getWallClockMillis()).thenReturn(DEFAULT_REPEAT_DELAY_SEC * 1000L * 2); - mNotificationController.handleScanResults(createOpenScanResults()); + mNotificationController.handleScanResults(mOpenNetworks); - verify(mOpenNetworkRecommender, times(2)).recommendNetwork(any(), any()); + verify(mOpenNetworkRecommender, times(2)).recommendNetwork( + mOpenNetworks, mBlacklistedSsids); verify(mNotificationManager, times(2)).notify(anyInt(), any()); } @@ -298,7 +327,7 @@ public class OpenNetworkNotifierTest { when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT)) .thenReturn(true); - mNotificationController.handleScanResults(createOpenScanResults()); + mNotificationController.handleScanResults(mOpenNetworks); verify(mOpenNetworkRecommender, never()).recommendNetwork(any(), any()); verify(mNotificationManager, never()).notify(anyInt(), any()); @@ -307,15 +336,15 @@ public class OpenNetworkNotifierTest { /** Verifies that {@link UserManager#DISALLOW_CONFIG_WIFI} clears the showing notification. */ @Test public void userHasDisallowConfigWifiRestriction_showingNotificationIsCleared() { - mNotificationController.handleScanResults(createOpenScanResults()); + mNotificationController.handleScanResults(mOpenNetworks); - verify(mOpenNetworkRecommender).recommendNetwork(any(), any()); + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); verify(mNotificationManager).notify(anyInt(), any()); when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT)) .thenReturn(true); - mNotificationController.handleScanResults(createOpenScanResults()); + mNotificationController.handleScanResults(mOpenNetworks); verify(mNotificationManager).cancel(anyInt()); } diff --git a/tests/wifitests/src/com/android/server/wifi/OpenNetworkRecommenderTest.java b/tests/wifitests/src/com/android/server/wifi/OpenNetworkRecommenderTest.java index becc1d2e8..720ec3797 100644 --- a/tests/wifitests/src/com/android/server/wifi/OpenNetworkRecommenderTest.java +++ b/tests/wifitests/src/com/android/server/wifi/OpenNetworkRecommenderTest.java @@ -17,14 +17,18 @@ package com.android.server.wifi; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import android.net.wifi.ScanResult; +import android.util.ArraySet; import org.junit.Before; import org.junit.Test; +import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; +import java.util.Set; /** * Tests for {@link OpenNetworkRecommender}. @@ -36,10 +40,13 @@ public class OpenNetworkRecommenderTest { private static final int MIN_RSSI_LEVEL = -127; private OpenNetworkRecommender mOpenNetworkRecommender; + private Set<String> mBlacklistedSsids; @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); mOpenNetworkRecommender = new OpenNetworkRecommender(); + mBlacklistedSsids = new ArraySet<>(); } private List<ScanDetail> createOpenScanResults(String... ssids) { @@ -59,7 +66,8 @@ public class OpenNetworkRecommenderTest { List<ScanDetail> scanResults = createOpenScanResults(TEST_SSID_1); scanResults.get(0).getScanResult().level = MIN_RSSI_LEVEL; - ScanResult actual = mOpenNetworkRecommender.recommendNetwork(scanResults, null); + ScanResult actual = mOpenNetworkRecommender.recommendNetwork( + scanResults, mBlacklistedSsids); ScanResult expected = scanResults.get(0).getScanResult(); assertEquals(expected, actual); } @@ -71,28 +79,24 @@ public class OpenNetworkRecommenderTest { scanResults.get(0).getScanResult().level = MIN_RSSI_LEVEL; scanResults.get(1).getScanResult().level = MIN_RSSI_LEVEL + 1; - ScanResult actual = mOpenNetworkRecommender.recommendNetwork(scanResults, null); + ScanResult actual = mOpenNetworkRecommender.recommendNetwork( + scanResults, mBlacklistedSsids); ScanResult expected = scanResults.get(1).getScanResult(); assertEquals(expected, actual); } /** - * If the current recommended network is present in the list for the next recommendation and has - * an equal RSSI, the recommendation should not change. + * If the best available open network is blacklisted, no networks should be recommended. */ @Test - public void currentRecommendationHasEquallyHighRssi_shouldNotChangeRecommendation() { + public void blacklistBestNetworkSsid_shouldNeverRecommendNetwork() { List<ScanDetail> scanResults = createOpenScanResults(TEST_SSID_1, TEST_SSID_2); scanResults.get(0).getScanResult().level = MIN_RSSI_LEVEL + 1; - scanResults.get(1).getScanResult().level = MIN_RSSI_LEVEL + 1; + scanResults.get(1).getScanResult().level = MIN_RSSI_LEVEL; + mBlacklistedSsids.add(TEST_SSID_1); - ScanResult currentRecommendation = new ScanResult(scanResults.get(1).getScanResult()); - // next recommendation does not depend on the rssi of the input recommendation. - currentRecommendation.level = MIN_RSSI_LEVEL; - - ScanResult expected = scanResults.get(1).getScanResult(); ScanResult actual = mOpenNetworkRecommender.recommendNetwork( - scanResults, currentRecommendation); - assertEquals(expected, actual); + scanResults, mBlacklistedSsids); + assertNull(actual); } } diff --git a/tests/wifitests/src/com/android/server/wifi/SsidSetStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/SsidSetStoreDataTest.java new file mode 100644 index 000000000..606b825ee --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/SsidSetStoreDataTest.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.util.Xml; + +import com.android.internal.util.FastXmlSerializer; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * Unit tests for {@link com.android.server.wifi.SsidSetStoreData}. + */ +public class SsidSetStoreDataTest { + private static final String TEST_NOTIFIER_NAME = "TestNetwork"; + private static final String TEST_SSID1 = "SSID 1"; + private static final String TEST_SSID2 = "SSID 2"; + private static final String TEST_SSID_SET_XML_STRING = + "<set name=\"SSIDSet\">\n" + + "<string>" + TEST_SSID1 + "</string>\n" + + "<string>" + TEST_SSID2 + "</string>\n" + + "</set>\n"; + private static final byte[] TEST_SSID_SET_XML_BYTES = + TEST_SSID_SET_XML_STRING.getBytes(StandardCharsets.UTF_8); + + @Mock SsidSetStoreData.DataSource mDataSource; + SsidSetStoreData mSsidSetStoreData; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mSsidSetStoreData = new SsidSetStoreData(TEST_NOTIFIER_NAME, mDataSource); + } + + /** + * Helper function for serializing configuration data to a XML block. + * + * @param shared Flag indicating serializing shared or user configurations + * @return byte[] of the XML data + * @throws Exception + */ + private byte[] serializeData(boolean shared) throws Exception { + final XmlSerializer out = new FastXmlSerializer(); + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + out.setOutput(outputStream, StandardCharsets.UTF_8.name()); + mSsidSetStoreData.serializeData(out, shared); + out.flush(); + return outputStream.toByteArray(); + } + + /** + * Helper function for parsing configuration data from a XML block. + * + * @param data XML data to parse from + * @param shared Flag indicating parsing of shared or user configurations + * @throws Exception + */ + private void deserializeData(byte[] data, boolean shared) throws Exception { + final XmlPullParser in = Xml.newPullParser(); + final ByteArrayInputStream inputStream = new ByteArrayInputStream(data); + in.setInput(inputStream, StandardCharsets.UTF_8.name()); + mSsidSetStoreData.deserializeData(in, in.getDepth(), shared); + } + + /** + * Verify that a XmlPullParserException will be thrown when attempting to serialize data + * to the share store. + * + * @throws Exception + */ + @Test(expected = XmlPullParserException.class) + public void serializeShareData() throws Exception { + serializeData(true /* shared */); + } + + /** + * Verify that a XmlPullParserException will be thrown when attempting to deserialize + * data from the share store. + * + * @throws Exception + */ + @Test(expected = XmlPullParserException.class) + public void deserializeShareData() throws Exception { + deserializeData(new byte[0], true /* shared */); + } + + /** + * Verify that serializing the user store data without any configuration doesn't cause any + * crash and no data should be serialized. + * + * @throws Exception + */ + @Test + public void serializeEmptyConfigs() throws Exception { + when(mDataSource.getSsids()).thenReturn(new HashSet<String>()); + assertEquals(0, serializeData(false /* shared */).length); + } + + /** + * Verify that parsing an empty data doesn't cause any crash and no configuration should + * be deserialized. + * + * @throws Exception + */ + @Test + public void deserializeEmptyStoreData() throws Exception { + deserializeData(new byte[0], false /* shared */); + verify(mDataSource, never()).setSsids(any(Set.class)); + } + + /** + * Verify that {@link SsidSetStoreData} does not support share data. + * + * @throws Exception + */ + @Test + public void supportShareData() throws Exception { + assertFalse(mSsidSetStoreData.supportShareData()); + } + + /** + * Verify that the store data is serialized correctly, matches the predefined test XML data. + * + * @throws Exception + */ + @Test + public void serializeSsidSet() throws Exception { + Set<String> ssidSet = new HashSet<>(); + ssidSet.add(TEST_SSID1); + ssidSet.add(TEST_SSID2); + when(mDataSource.getSsids()).thenReturn(ssidSet); + byte[] actualData = serializeData(false /* shared */); + assertTrue(Arrays.equals(TEST_SSID_SET_XML_BYTES, actualData)); + } + + /** + * Verify that the store data is deserialized correctly using the predefined test XML data. + * + * @throws Exception + */ + @Test + public void deserializeSsidSet() throws Exception { + Set<String> ssidSet = new HashSet<>(); + ssidSet.add(TEST_SSID1); + ssidSet.add(TEST_SSID2); + deserializeData(TEST_SSID_SET_XML_BYTES, false /* shared */); + verify(mDataSource).setSsids(eq(ssidSet)); + } + + /** + * Verify that a XmlPullParserException will be thrown when parsing a SSIDSet set with an + * unknown tag. + * + * @throws Exception + */ + @Test(expected = XmlPullParserException.class) + public void parseSetWithUnknownTag() throws Exception { + String ssidSet = + "<set name=\"SSIDSet\">\n" + + "<string>" + TEST_SSID1 + "</string>\n" + + "<string>" + TEST_SSID2 + "</string>\n" + + "<Unknown>" + "badInput" + "</Unknown>" // Unknown tag. + + "</set>\n"; + deserializeData(ssidSet.getBytes(StandardCharsets.UTF_8), false /* shared */); + } +} |