diff options
5 files changed, 479 insertions, 154 deletions
diff --git a/service/java/com/android/server/wifi/ConnectToNetworkNotificationBuilder.java b/service/java/com/android/server/wifi/ConnectToNetworkNotificationBuilder.java new file mode 100644 index 000000000..c0960d405 --- /dev/null +++ b/service/java/com/android/server/wifi/ConnectToNetworkNotificationBuilder.java @@ -0,0 +1,143 @@ +/* + * 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.app.Notification; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.net.wifi.ScanResult; + +import com.android.internal.R; +import com.android.internal.notification.SystemNotificationChannels; + +/** + * Helper to create notifications for {@link OpenNetworkNotifier}. + */ +public class ConnectToNetworkNotificationBuilder { + + /** Intent when user dismissed the "Connect to Network" notification. */ + public static final String ACTION_USER_DISMISSED_NOTIFICATION = + "com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION"; + + /** Intent when user tapped the "Connect to Network" notification. */ + public static final String ACTION_USER_TAPPED_CONTENT = + "com.android.server.wifi.ConnectToNetworkNotification.USER_TAPPED_CONTENT"; + + /** Intent when user tapped action button to connect to recommended network. */ + public static final String ACTION_CONNECT_TO_NETWORK = + "com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK"; + + /** Intent when user tapped action button to open Wi-Fi Settings. */ + public static final String ACTION_PICK_WIFI_NETWORK = + "com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK"; + + /** Intent when user tapped "Failed to connect" notification to open Wi-Fi Settings. */ + public static final String ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE = + "com.android.server.wifi.ConnectToNetworkNotification.PICK_NETWORK_AFTER_FAILURE"; + + private Context mContext; + private Resources mResources; + private FrameworkFacade mFrameworkFacade; + + public ConnectToNetworkNotificationBuilder( + Context context, + FrameworkFacade framework) { + mContext = context; + mResources = context.getResources(); + mFrameworkFacade = framework; + } + + /** + * Creates the connect to network notification that alerts users of a recommended connectable + * network. + * + * @param numNetworks Number of available open networks nearby + */ + public Notification createConnectToNetworkNotification(int numNetworks) { + + CharSequence title = mResources.getQuantityText( + com.android.internal.R.plurals.wifi_available, numNetworks); + CharSequence content = mResources.getQuantityText( + com.android.internal.R.plurals.wifi_available_detailed, numNetworks); + + return createNotificationBuilder(title, content) + .setContentIntent(getPrivateBroadcast(ACTION_USER_TAPPED_CONTENT)) + .build(); + } + + /** + * Creates the notification that indicates the controller is attempting to connect to the + * recommended network. + * + * @param network The network to be recommended + */ + public Notification createNetworkConnectingNotification(ScanResult network) { + return createNotificationBuilder( + mContext.getText(R.string.wifi_available_title_connecting), network.SSID) + .setProgress(0 /* max */, 0 /* progress */, true /* indeterminate */) + .build(); + } + + /** + * Creates the notification that indicates the controller successfully connected to the + * recommended network. + * + * @param network The network to be recommended + */ + public Notification createNetworkConnectedNotification(ScanResult network) { + return createNotificationBuilder( + mContext.getText(R.string.wifi_available_title_connected), network.SSID) + .build(); + } + + /** + * Creates the notification that indicates the controller failed to connect to the recommended + * network. Tapping this notification opens the wifi picker. + */ + public Notification createNetworkFailedNotification() { + return createNotificationBuilder( + mContext.getText(R.string.wifi_available_title_failed_to_connect), + mContext.getText(R.string.wifi_available_content_failed_to_connect)) + .setContentIntent( + getPrivateBroadcast(ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE)) + .build(); + } + + private Notification.Builder createNotificationBuilder( + CharSequence title, CharSequence content) { + return mFrameworkFacade.makeNotificationBuilder(mContext, + SystemNotificationChannels.NETWORK_AVAILABLE) + .setSmallIcon(R.drawable.stat_notify_wifi_in_range) + .setAutoCancel(true) + .setTicker(title) + .setContentTitle(title) + .setContentText(content) + .setDeleteIntent(getPrivateBroadcast(ACTION_USER_DISMISSED_NOTIFICATION)) + .setShowWhen(false) + .setLocalOnly(true) + .setColor(mResources.getColor(R.color.system_notification_accent_color, + mContext.getTheme())); + } + + private PendingIntent getPrivateBroadcast(String action) { + Intent intent = new Intent(action).setPackage("android"); + return mFrameworkFacade.getBroadcast( + mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + } +} diff --git a/service/java/com/android/server/wifi/OpenNetworkNotificationBuilder.java b/service/java/com/android/server/wifi/OpenNetworkNotificationBuilder.java deleted file mode 100644 index 5963b57a3..000000000 --- a/service/java/com/android/server/wifi/OpenNetworkNotificationBuilder.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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 com.android.server.wifi.OpenNetworkNotifier.ACTION_USER_DISMISSED_NOTIFICATION; -import static com.android.server.wifi.OpenNetworkNotifier.ACTION_USER_TAPPED_CONTENT; - -import android.app.Notification; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; - -import com.android.internal.R; -import com.android.internal.notification.SystemNotificationChannels; - -/** - * Helper to create notifications for {@link OpenNetworkNotifier}. - */ -public class OpenNetworkNotificationBuilder { - - private Context mContext; - private Resources mResources; - private FrameworkFacade mFrameworkFacade; - - public OpenNetworkNotificationBuilder( - Context context, - FrameworkFacade framework) { - mContext = context; - mResources = context.getResources(); - mFrameworkFacade = framework; - } - - /** - * Creates the open network available notification that alerts users there are open networks - * nearby. - */ - public Notification createOpenNetworkAvailableNotification(int numNetworks) { - - CharSequence title = mResources.getQuantityText( - com.android.internal.R.plurals.wifi_available, numNetworks); - CharSequence content = mResources.getQuantityText( - com.android.internal.R.plurals.wifi_available_detailed, numNetworks); - - PendingIntent contentIntent = - mFrameworkFacade.getBroadcast( - mContext, - 0, - new Intent(ACTION_USER_TAPPED_CONTENT), - PendingIntent.FLAG_UPDATE_CURRENT); - return createNotificationBuilder(title, content) - .setContentIntent(contentIntent) - .build(); - } - - private Notification.Builder createNotificationBuilder( - CharSequence title, CharSequence content) { - PendingIntent deleteIntent = - mFrameworkFacade.getBroadcast( - mContext, - 0, - new Intent(ACTION_USER_DISMISSED_NOTIFICATION), - PendingIntent.FLAG_UPDATE_CURRENT); - return mFrameworkFacade.makeNotificationBuilder(mContext, - SystemNotificationChannels.NETWORK_AVAILABLE) - .setSmallIcon(R.drawable.stat_notify_wifi_in_range) - .setAutoCancel(true) - .setTicker(title) - .setContentTitle(title) - .setContentText(content) - .setDeleteIntent(deleteIntent) - .setShowWhen(false) - .setLocalOnly(true) - .setColor(mResources.getColor(R.color.system_notification_accent_color, - mContext.getTheme())); - } -} diff --git a/service/java/com/android/server/wifi/OpenNetworkNotifier.java b/service/java/com/android/server/wifi/OpenNetworkNotifier.java index 279223759..d2d45c37c 100644 --- a/service/java/com/android/server/wifi/OpenNetworkNotifier.java +++ b/service/java/com/android/server/wifi/OpenNetworkNotifier.java @@ -16,7 +16,15 @@ package com.android.server.wifi; +import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK; +import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.ACTION_PICK_WIFI_NETWORK; +import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE; +import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.ACTION_USER_DISMISSED_NOTIFICATION; +import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.ACTION_USER_TAPPED_CONTENT; + +import android.annotation.IntDef; import android.annotation.NonNull; +import android.app.Notification; import android.app.NotificationManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -42,6 +50,8 @@ import com.android.server.wifi.util.ScanResultUtil; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.Set; @@ -55,12 +65,39 @@ 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"; - static final String ACTION_CONNECT_TO_NETWORK = - "com.android.server.wifi.OpenNetworkNotifier.CONNECT_TO_NETWORK"; + /** Time in milliseconds to display the Connecting notification. */ + private static final int TIME_TO_SHOW_CONNECTING_MILLIS = 10000; + + /** Time in milliseconds to display the Connected notification. */ + private static final int TIME_TO_SHOW_CONNECTED_MILLIS = 5000; + + /** Time in milliseconds to display the Failed To Connect notification. */ + private static final int TIME_TO_SHOW_FAILED_MILLIS = 5000; + + /** The state of the notification */ + @IntDef({ + STATE_NO_NOTIFICATION, + STATE_SHOWING_RECOMMENDATION_NOTIFICATION, + STATE_CONNECTING_IN_NOTIFICATION, + STATE_CONNECTED_NOTIFICATION, + STATE_CONNECT_FAILED_NOTIFICATION + }) + @Retention(RetentionPolicy.SOURCE) + private @interface State {} + + /** No recommendation is made and no notifications are shown. */ + private static final int STATE_NO_NOTIFICATION = 0; + /** The initial notification recommending an open network to connect to is shown. */ + private static final int STATE_SHOWING_RECOMMENDATION_NOTIFICATION = 1; + /** The notification of status of connecting to the recommended network is shown. */ + private static final int STATE_CONNECTING_IN_NOTIFICATION = 2; + /** The notification that the connection to the recommended network was successful is shown. */ + private static final int STATE_CONNECTED_NOTIFICATION = 3; + /** The notification to show that connection to the recommended network failed is shown. */ + private static final int STATE_CONNECT_FAILED_NOTIFICATION = 4; + + /** Current state of the notification. */ + @State private int mState = STATE_NO_NOTIFICATION; /** Identifier of the {@link SsidSetStoreData}. */ private static final String STORE_DATA_IDENTIFIER = "OpenNetworkNotifierBlacklist"; @@ -79,8 +116,6 @@ public class OpenNetworkNotifier { /** Whether the user has set the setting to show the 'available networks' notification. */ private boolean mSettingEnabled; - /** Whether the notification is being shown. */ - private boolean mNotificationShown; /** Whether the screen is on or not. */ private boolean mScreenOn; @@ -95,7 +130,7 @@ public class OpenNetworkNotifier { private final WifiStateMachine mWifiStateMachine; private final Messenger mSrcMessenger; private final OpenNetworkRecommender mOpenNetworkRecommender; - private final OpenNetworkNotificationBuilder mOpenNetworkNotificationBuilder; + private final ConnectToNetworkNotificationBuilder mNotificationBuilder; private ScanResult mRecommendedNetwork; @@ -107,7 +142,8 @@ public class OpenNetworkNotifier { WifiConfigManager wifiConfigManager, WifiConfigStore wifiConfigStore, WifiStateMachine wifiStateMachine, - OpenNetworkRecommender openNetworkRecommender) { + OpenNetworkRecommender openNetworkRecommender, + ConnectToNetworkNotificationBuilder connectToNetworkNotificationBuilder) { mContext = context; mHandler = new Handler(looper); mFrameworkFacade = framework; @@ -115,7 +151,7 @@ public class OpenNetworkNotifier { mConfigManager = wifiConfigManager; mWifiStateMachine = wifiStateMachine; mOpenNetworkRecommender = openNetworkRecommender; - mOpenNetworkNotificationBuilder = new OpenNetworkNotificationBuilder(context, framework); + mNotificationBuilder = connectToNetworkNotificationBuilder; mScreenOn = false; mSrcMessenger = new Messenger(new Handler(looper, mConnectionStateCallback)); @@ -135,6 +171,8 @@ public class OpenNetworkNotifier { filter.addAction(ACTION_USER_DISMISSED_NOTIFICATION); filter.addAction(ACTION_USER_TAPPED_CONTENT); filter.addAction(ACTION_CONNECT_TO_NETWORK); + filter.addAction(ACTION_PICK_WIFI_NETWORK); + filter.addAction(ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE); mContext.registerReceiver( mBroadcastReceiver, filter, null /* broadcastPermission */, mHandler); } @@ -153,6 +191,12 @@ public class OpenNetworkNotifier { case ACTION_CONNECT_TO_NETWORK: handleConnectToNetworkAction(); break; + case ACTION_PICK_WIFI_NETWORK: + handleSeeAllNetworksAction(); + break; + case ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE: + handlePickWifiNetworkAfterConnectFailure(); + break; default: Log.e(TAG, "Unknown action " + intent.getAction()); } @@ -178,17 +222,17 @@ public class OpenNetworkNotifier { /** * Clears the pending notification. This is called by {@link WifiConnectivityManager} on stop. * - * @param resetRepeatDelay resets the time delay for repeated notification if true. + * @param resetRepeatTime resets the time delay for repeated notification if true. */ - public void clearPendingNotification(boolean resetRepeatDelay) { - if (resetRepeatDelay) { + public void clearPendingNotification(boolean resetRepeatTime) { + if (resetRepeatTime) { mNotificationRepeatTime = 0; } - if (mNotificationShown) { + if (mState != STATE_NO_NOTIFICATION) { getNotificationManager().cancel(SystemMessage.NOTE_NETWORK_AVAILABLE); + mState = STATE_NO_NOTIFICATION; mRecommendedNetwork = null; - mNotificationShown = false; } } @@ -205,11 +249,11 @@ public class OpenNetworkNotifier { */ public void handleScanResults(@NonNull List<ScanDetail> availableNetworks) { if (!isControllerEnabled()) { - clearPendingNotification(true /* resetRepeatDelay */); + clearPendingNotification(true /* resetRepeatTime */); return; } if (availableNetworks.isEmpty()) { - clearPendingNotification(false /* resetRepeatDelay */); + clearPendingNotification(false /* resetRepeatTime */); return; } @@ -217,14 +261,14 @@ public class OpenNetworkNotifier { // could occur between a user picking a network in settings and a network candidate picked // through network selection, which will happen because screen on triggers a new // connectivity scan. - if (mNotificationShown || !mScreenOn) { + if (mState != STATE_NO_NOTIFICATION || !mScreenOn) { return; } mRecommendedNetwork = mOpenNetworkRecommender.recommendNetwork( availableNetworks, new ArraySet<>(mBlacklistedSsids)); - postNotification(availableNetworks.size()); + postInitialNotification(availableNetworks.size()); } /** Handles screen state changes. */ @@ -232,28 +276,78 @@ public class OpenNetworkNotifier { mScreenOn = screenOn; } + /** + * Called by {@link WifiConnectivityManager} when Wi-Fi is connected. If the notification + * was in the connecting state, update the notification to show that it has connected to the + * recommended network. + */ + public void handleWifiConnected() { + if (mState != STATE_CONNECTING_IN_NOTIFICATION) { + clearPendingNotification(true /* resetRepeatTime */); + return; + } + + postNotification(mNotificationBuilder.createNetworkConnectedNotification( + mRecommendedNetwork)); + mState = STATE_CONNECTED_NOTIFICATION; + mHandler.postDelayed( + () -> { + if (mState == STATE_CONNECTED_NOTIFICATION) { + clearPendingNotification(true /* resetRepeatTime */); + } + }, + TIME_TO_SHOW_CONNECTED_MILLIS); + } + + /** + * Handles when a Wi-Fi connection attempt failed. + */ + public void handleConnectionFailure() { + if (mState != STATE_CONNECTING_IN_NOTIFICATION) { + return; + } + postNotification(mNotificationBuilder.createNetworkFailedNotification()); + mState = STATE_CONNECT_FAILED_NOTIFICATION; + mHandler.postDelayed( + () -> { + if (mState == STATE_CONNECT_FAILED_NOTIFICATION) { + clearPendingNotification(false /* resetRepeatTime */); + } + }, + TIME_TO_SHOW_FAILED_MILLIS); + } + private NotificationManager getNotificationManager() { return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); } - private void postNotification(int numNetworks) { + private void postInitialNotification(int numNetworks) { + if (mState != STATE_NO_NOTIFICATION + && mState != STATE_SHOWING_RECOMMENDATION_NOTIFICATION) { + return; + } // Not enough time has passed to show the notification again if (mClock.getWallClockMillis() < mNotificationRepeatTime) { return; } - getNotificationManager().notify( - SystemMessage.NOTE_NETWORK_AVAILABLE, - mOpenNetworkNotificationBuilder.createOpenNetworkAvailableNotification( - numNetworks)); - mNotificationShown = true; + postNotification(mNotificationBuilder.createConnectToNetworkNotification( + numNetworks)); + mState = STATE_SHOWING_RECOMMENDATION_NOTIFICATION; mNotificationRepeatTime = mClock.getWallClockMillis() + mNotificationRepeatDelay; } + private void postNotification(Notification notification) { + getNotificationManager().notify(SystemMessage.NOTE_NETWORK_AVAILABLE, notification); + } + private void handleConnectToNetworkAction() { - if (mRecommendedNetwork == null) { + if (mState != STATE_SHOWING_RECOMMENDATION_NOTIFICATION) { return; } + postNotification(mNotificationBuilder.createNetworkConnectingNotification( + mRecommendedNetwork)); + Log.d(TAG, "User initiated connection to recommended network: " + mRecommendedNetwork.SSID); WifiConfiguration network = ScanResultUtil.createNetworkFromScanResult(mRecommendedNetwork); Message msg = Message.obtain(); @@ -262,32 +356,52 @@ public class OpenNetworkNotifier { msg.obj = network; msg.replyTo = mSrcMessenger; mWifiStateMachine.sendMessage(msg); + + mState = STATE_CONNECTING_IN_NOTIFICATION; + mHandler.postDelayed( + () -> { + if (mState == STATE_CONNECTING_IN_NOTIFICATION) { + handleConnectionFailure(); + } + }, + TIME_TO_SHOW_CONNECTING_MILLIS); } - /** - * Handles when a Wi-Fi connection attempt failed. - */ - public void handleConnectionFailure() { - // Stub. Should post connection failure notification once implemented. + private void handleSeeAllNetworksAction() { + startWifiSettings(); } - /** Opens Wi-Fi picker. */ - private void handleUserClickedContentAction() { - mNotificationShown = false; + private void startWifiSettings() { mContext.startActivity( new Intent(Settings.ACTION_WIFI_SETTINGS) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + clearPendingNotification(false /* resetRepeatTime */); + } + + private void handlePickWifiNetworkAfterConnectFailure() { + startWifiSettings(); + } + + private void handleUserClickedContentAction() { + startWifiSettings(); + resetStateAndDelayNotification(); } private void handleUserDismissedAction() { - if (mRecommendedNetwork != null) { + if (mState == STATE_SHOWING_RECOMMENDATION_NOTIFICATION) { // 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; + resetStateAndDelayNotification(); + } + + private void resetStateAndDelayNotification() { + mState = STATE_NO_NOTIFICATION; + mNotificationRepeatTime = System.currentTimeMillis() + mNotificationRepeatDelay; + mRecommendedNetwork = null; } /** Dump ONA controller state. */ @@ -296,7 +410,7 @@ public class OpenNetworkNotifier { pw.println("mSettingEnabled " + mSettingEnabled); pw.println("currentTime: " + mClock.getWallClockMillis()); pw.println("mNotificationRepeatTime: " + mNotificationRepeatTime); - pw.println("mNotificationShown: " + mNotificationShown); + pw.println("mState: " + mState); pw.println("mBlacklistedSsids: " + mBlacklistedSsids.toString()); } @@ -327,7 +441,7 @@ public class OpenNetworkNotifier { public void onChange(boolean selfChange) { super.onChange(selfChange); mSettingEnabled = getValue(); - clearPendingNotification(true /* resetRepeatDelay */); + clearPendingNotification(true /* resetRepeatTime */); } private boolean getValue() { diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index a0d0e82d5..95a68f1b4 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -234,7 +234,8 @@ public class WifiInjector { mOpenNetworkNotifier = new OpenNetworkNotifier(mContext, mWifiStateMachineHandlerThread.getLooper(), mFrameworkFacade, mClock, mWifiConfigManager, mWifiConfigStore, mWifiStateMachine, - new OpenNetworkRecommender()); + new OpenNetworkRecommender(), + new ConnectToNetworkNotificationBuilder(mContext, mFrameworkFacade)); 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 7c1223a79..3af19e163 100644 --- a/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java +++ b/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java @@ -18,22 +18,23 @@ package com.android.server.wifi; import static com.android.server.wifi.OpenNetworkNotifier.DEFAULT_REPEAT_DELAY_SEC; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.Notification; import android.app.NotificationManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.net.wifi.ScanResult; +import android.net.wifi.WifiManager; import android.os.Message; +import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.os.test.TestLooper; @@ -42,7 +43,6 @@ import android.util.ArraySet; import org.junit.Before; import org.junit.Test; -import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -65,12 +65,13 @@ public class OpenNetworkNotifierTest { @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 WifiStateMachine mWifiStateMachine; @Mock private OpenNetworkRecommender mOpenNetworkRecommender; + @Mock private ConnectToNetworkNotificationBuilder mNotificationBuilder; @Mock private UserManager mUserManager; private OpenNetworkNotifier mNotificationController; + private TestLooper mLooper; private BroadcastReceiver mBroadcastReceiver; private ScanResult mDummyNetwork; private List<ScanDetail> mOpenNetworks; @@ -88,8 +89,6 @@ public class OpenNetworkNotifierTest { when(mFrameworkFacade.getIntegerSetting(mContext, Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, DEFAULT_REPEAT_DELAY_SEC)) .thenReturn(DEFAULT_REPEAT_DELAY_SEC); - when(mFrameworkFacade.makeNotificationBuilder(any(), anyString())) - .thenReturn(mNotificationBuilder); when(mContext.getSystemService(Context.USER_SERVICE)) .thenReturn(mUserManager); when(mContext.getResources()).thenReturn(mResources); @@ -102,10 +101,10 @@ public class OpenNetworkNotifierTest { mOpenNetworks.add(new ScanDetail(mDummyNetwork, null /* networkDetail */)); mBlacklistedSsids = new ArraySet<>(); - TestLooper mock_looper = new TestLooper(); + mLooper = new TestLooper(); mNotificationController = new OpenNetworkNotifier( - mContext, mock_looper.getLooper(), mFrameworkFacade, mClock, mWifiConfigManager, - mWifiConfigStore, mWifiStateMachine, mOpenNetworkRecommender); + mContext, mLooper.getLooper(), mFrameworkFacade, mClock, mWifiConfigManager, + mWifiConfigStore, mWifiStateMachine, mOpenNetworkRecommender, mNotificationBuilder); ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); @@ -121,6 +120,7 @@ public class OpenNetworkNotifierTest { mNotificationController.handleScanResults(mOpenNetworks); verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(1); verify(mNotificationManager).notify(anyInt(), any()); } @@ -144,6 +144,7 @@ public class OpenNetworkNotifierTest { mNotificationController.handleScanResults(mOpenNetworks); verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(1); verify(mNotificationManager).notify(anyInt(), any()); mNotificationController.handleScanResults(new ArrayList<>()); @@ -159,6 +160,7 @@ public class OpenNetworkNotifierTest { mNotificationController.handleScanResults(mOpenNetworks); verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(1); verify(mNotificationManager).notify(anyInt(), any()); mNotificationController.handleScreenStateChanged(false); @@ -176,6 +178,7 @@ public class OpenNetworkNotifierTest { mNotificationController.handleScanResults(mOpenNetworks); verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(1); verify(mNotificationManager).notify(anyInt(), any()); } @@ -188,6 +191,7 @@ public class OpenNetworkNotifierTest { mNotificationController.handleScanResults(mOpenNetworks); verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(1); verify(mNotificationManager).notify(anyInt(), any()); mNotificationController.clearPendingNotification(true); @@ -220,7 +224,7 @@ public class OpenNetworkNotifierTest { } /** - * When a notification is posted and cleared without reseting delay, the next scan with open + * When a notification is posted and cleared without resetting delay, the next scan with open * networks should not post another notification. */ @Test @@ -228,6 +232,7 @@ public class OpenNetworkNotifierTest { mNotificationController.handleScanResults(mOpenNetworks); verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(1); verify(mNotificationManager).notify(anyInt(), any()); mNotificationController.clearPendingNotification(false); @@ -238,11 +243,12 @@ public class OpenNetworkNotifierTest { verify(mOpenNetworkRecommender, times(2)).recommendNetwork( mOpenNetworks, mBlacklistedSsids); verify(mNotificationManager).notify(anyInt(), any()); + verify(mNotificationBuilder).createConnectToNetworkNotification(1); verify(mNotificationManager).cancel(anyInt()); } /** - * When a notification is posted and cleared without reseting delay, the next scan with open + * When a notification is posted and cleared without resetting delay, the next scan with open * networks should post a notification. */ @Test @@ -250,6 +256,7 @@ public class OpenNetworkNotifierTest { mNotificationController.handleScanResults(mOpenNetworks); verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(1); verify(mNotificationManager).notify(anyInt(), any()); mNotificationController.clearPendingNotification(true); @@ -258,6 +265,7 @@ public class OpenNetworkNotifierTest { verify(mOpenNetworkRecommender, times(2)).recommendNetwork( mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder, times(2)).createConnectToNetworkNotification(1); verify(mNotificationManager, times(2)).notify(anyInt(), any()); } @@ -269,12 +277,16 @@ public class OpenNetworkNotifierTest { mNotificationController.handleScanResults(mOpenNetworks); verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(1); verify(mNotificationManager).notify(anyInt(), any()); mBroadcastReceiver.onReceive( - mContext, new Intent(OpenNetworkNotifier.ACTION_USER_TAPPED_CONTENT)); + mContext, + new Intent(ConnectToNetworkNotificationBuilder.ACTION_USER_TAPPED_CONTENT)); - verify(mContext).startActivity(any()); + ArgumentCaptor<Intent> pickerIntentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mContext).startActivity(pickerIntentCaptor.capture()); + assertEquals(pickerIntentCaptor.getValue().getAction(), Settings.ACTION_WIFI_SETTINGS); } /** @@ -286,10 +298,12 @@ public class OpenNetworkNotifierTest { mNotificationController.handleScanResults(mOpenNetworks); verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(1); verify(mNotificationManager).notify(anyInt(), any()); mBroadcastReceiver.onReceive( - mContext, new Intent(OpenNetworkNotifier.ACTION_USER_DISMISSED_NOTIFICATION)); + mContext, + new Intent(ConnectToNetworkNotificationBuilder.ACTION_USER_DISMISSED_NOTIFICATION)); verify(mWifiConfigManager).saveToStore(false /* forceWrite */); @@ -301,7 +315,7 @@ public class OpenNetworkNotifierTest { } /** - * When a notification is posted and cleared without reseting delay, after the delay has passed + * When a notification is posted and cleared without resetting delay, after the delay has passed * the next scan with open networks should post a notification. */ @Test @@ -309,6 +323,7 @@ public class OpenNetworkNotifierTest { mNotificationController.handleScanResults(mOpenNetworks); verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(1); verify(mNotificationManager).notify(anyInt(), any()); mNotificationController.clearPendingNotification(false); @@ -341,6 +356,7 @@ public class OpenNetworkNotifierTest { mNotificationController.handleScanResults(mOpenNetworks); verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + verify(mNotificationBuilder).createConnectToNetworkNotification(1); verify(mNotificationManager).notify(anyInt(), any()); when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT)) @@ -352,30 +368,172 @@ public class OpenNetworkNotifierTest { } /** - * {@link OpenNetworkNotifier#ACTION_CONNECT_TO_NETWORK} does not connect to any network if - * there is no current recommendation. + * {@link ConnectToNetworkNotificationBuilder#ACTION_CONNECT_TO_NETWORK} does not connect to + * any network if the initial notification is not showing. */ @Test - public void actionConnectToNetwork_currentRecommendationIsNull_doesNothing() { + public void actionConnectToNetwork_notificationNotShowing_doesNothing() { mBroadcastReceiver.onReceive(mContext, - new Intent(OpenNetworkNotifier.ACTION_CONNECT_TO_NETWORK)); + new Intent(ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK)); verify(mWifiStateMachine, never()).sendMessage(any(Message.class)); } /** - * {@link OpenNetworkNotifier#ACTION_CONNECT_TO_NETWORK} connects to the currently recommended - * network if it exists. + * {@link ConnectToNetworkNotificationBuilder#ACTION_CONNECT_TO_NETWORK} connects to the + * currently recommended network if it exists. */ @Test - public void actionConnectToNetwork_currentRecommendationExists_connectsToNetwork() { + public void actionConnectToNetwork_currentRecommendationExists_connectsAndPostsNotification() { mNotificationController.handleScanResults(mOpenNetworks); verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + // Initial Notification + verify(mNotificationBuilder).createConnectToNetworkNotification(1); + verify(mNotificationManager).notify(anyInt(), any()); mBroadcastReceiver.onReceive(mContext, - new Intent(OpenNetworkNotifier.ACTION_CONNECT_TO_NETWORK)); + new Intent(ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK)); verify(mWifiStateMachine).sendMessage(any(Message.class)); + // Connecting Notification + verify(mNotificationBuilder).createNetworkConnectingNotification(mDummyNetwork); + verify(mNotificationManager, times(2)).notify(anyInt(), any()); + } + + /** + * {@link OpenNetworkNotifier#handleWifiConnected()} does not post connected notification if + * the connecting notification is not showing + */ + @Test + public void networkConnectionSuccess_wasNotInConnectingFlow_doesNothing() { + mNotificationController.handleWifiConnected(); + + verify(mNotificationManager, never()).notify(anyInt(), any()); + } + + /** + * {@link OpenNetworkNotifier#handleWifiConnected()} clears notification that is not connecting. + */ + @Test + public void networkConnectionSuccess_wasShowingNotification_clearsNotification() { + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + // Initial Notification + verify(mNotificationBuilder).createConnectToNetworkNotification(1); + verify(mNotificationManager).notify(anyInt(), any()); + + mNotificationController.handleWifiConnected(); + + verify(mNotificationManager).cancel(anyInt()); + } + + /** + * {@link OpenNetworkNotifier#handleWifiConnected()} posts the connected notification if + * the connecting notification is showing. + */ + @Test + public void networkConnectionSuccess_wasInConnectingFlow_postsConnectedNotification() { + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + // Initial Notification + verify(mNotificationBuilder).createConnectToNetworkNotification(1); + verify(mNotificationManager).notify(anyInt(), any()); + + mBroadcastReceiver.onReceive(mContext, + new Intent(ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK)); + + // Connecting Notification + verify(mNotificationBuilder).createNetworkConnectingNotification(mDummyNetwork); + verify(mNotificationManager, times(2)).notify(anyInt(), any()); + + mNotificationController.handleWifiConnected(); + + // Connected Notification + verify(mNotificationBuilder).createNetworkConnectedNotification(mDummyNetwork); + verify(mNotificationManager, times(3)).notify(anyInt(), any()); + } + + /** + * {@link OpenNetworkNotifier#handleConnectionFailure()} posts the Failed to Connect + * notification if the connecting notification is showing. + */ + @Test + public void networkConnectionFailure_wasNotInConnectingFlow_doesNothing() { + mNotificationController.handleConnectionFailure(); + + verify(mNotificationManager, never()).notify(anyInt(), any()); + } + + /** + * {@link OpenNetworkNotifier#handleConnectionFailure()} posts the Failed to Connect + * notification if the connecting notification is showing. + */ + @Test + public void networkConnectionFailure_wasInConnectingFlow_postsFailedToConnectNotification() { + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + // Initial Notification + verify(mNotificationBuilder).createConnectToNetworkNotification(1); + verify(mNotificationManager).notify(anyInt(), any()); + + mBroadcastReceiver.onReceive(mContext, + new Intent(ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK)); + + // Connecting Notification + verify(mNotificationBuilder).createNetworkConnectingNotification(mDummyNetwork); + verify(mNotificationManager, times(2)).notify(anyInt(), any()); + + mNotificationController.handleConnectionFailure(); + + // Failed to Connect Notification + verify(mNotificationBuilder).createNetworkFailedNotification(); + verify(mNotificationManager, times(3)).notify(anyInt(), any()); + } + + /** + * When a {@link WifiManager#CONNECT_NETWORK_FAILED} is received from the connection callback + * of {@link WifiStateMachine#sendMessage(Message)}, a Failed to Connect notification should + * be posted. On tapping this notification, Wi-Fi Settings should be launched. + */ + @Test + public void connectionFailedCallback_postsFailedToConnectNotification() throws RemoteException { + mNotificationController.handleScanResults(mOpenNetworks); + + verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids); + // Initial Notification + verify(mNotificationBuilder).createConnectToNetworkNotification(1); + verify(mNotificationManager).notify(anyInt(), any()); + + mBroadcastReceiver.onReceive(mContext, + new Intent(ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK)); + + ArgumentCaptor<Message> connectMessageCaptor = ArgumentCaptor.forClass(Message.class); + verify(mWifiStateMachine).sendMessage(connectMessageCaptor.capture()); + Message connectMessage = connectMessageCaptor.getValue(); + + // Connecting Notification + verify(mNotificationBuilder).createNetworkConnectingNotification(mDummyNetwork); + verify(mNotificationManager, times(2)).notify(anyInt(), any()); + + Message connectFailedMsg = Message.obtain(); + connectFailedMsg.what = WifiManager.CONNECT_NETWORK_FAILED; + connectMessage.replyTo.send(connectFailedMsg); + mLooper.dispatchAll(); + + // Failed to Connect Notification + verify(mNotificationBuilder).createNetworkFailedNotification(); + verify(mNotificationManager, times(3)).notify(anyInt(), any()); + + mBroadcastReceiver.onReceive(mContext, + new Intent(ConnectToNetworkNotificationBuilder + .ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE)); + + ArgumentCaptor<Intent> pickerIntentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mContext).startActivity(pickerIntentCaptor.capture()); + assertEquals(pickerIntentCaptor.getValue().getAction(), Settings.ACTION_WIFI_SETTINGS); } } |