diff options
| author | 2019-01-24 11:06:12 +0000 | |
|---|---|---|
| committer | 2019-01-24 11:06:12 +0000 | |
| commit | 6fa3d391b8109d761f3cece9c43bb46c23f58da6 (patch) | |
| tree | c11cf6a214c38b4446a7623e8a7b87be5b865f59 | |
| parent | 54d74bbf06f8d58972070a8c5f69daefa7dd8d3d (diff) | |
| parent | d2e045e040e543f5eda2821a0108a81667546eac (diff) | |
Merge "Popup a notification after logging in the captive portal network"
7 files changed, 155 insertions, 36 deletions
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 8518c7021ee7..b412f0fdb9b5 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3202,6 +3202,9 @@ <!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's message. --> <string name="wifi_no_internet_detailed">Tap for options</string> + <!-- A notification is shown after the user logs in to a captive portal network, to indicate that the network should now have internet connectivity. This is the message of notification. [CHAR LIMIT=50] --> + <string name="captive_portal_logged_in_detailed">Connected</string> + <!-- A notification is shown when the user's softap config has been changed due to underlying hardware restrictions. This is the notifications's title. [CHAR_LIMIT=NONE] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 8d832ca0227b..92fdd1db8ae4 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -690,6 +690,7 @@ <java-symbol type="string" name="capability_title_canControlMagnification" /> <java-symbol type="string" name="capability_desc_canPerformGestures" /> <java-symbol type="string" name="capability_title_canPerformGestures" /> + <java-symbol type="string" name="captive_portal_logged_in_detailed" /> <java-symbol type="string" name="cfTemplateForwarded" /> <java-symbol type="string" name="cfTemplateForwardedTime" /> <java-symbol type="string" name="cfTemplateNotForwarded" /> diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index fba639c3fc4a..8ee55e136c34 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -231,6 +231,8 @@ message SystemMessage { NOTE_NETWORK_LOST_INTERNET = 742; // The system default network switched to a different network NOTE_NETWORK_SWITCH = 743; + // Device logged-in captive portal network successfully + NOTE_NETWORK_LOGGED_IN = 744; // Notify the user that their work profile has been deleted // Package: android diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index fda7279c45d1..8b32afbb73dc 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -98,10 +98,10 @@ import android.net.VpnService; import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; import android.net.netlink.InetDiagMessage; +import android.net.shared.NetdService; import android.net.shared.NetworkMonitorUtils; import android.net.shared.PrivateDnsConfig; import android.net.util.MultinetworkPolicyTracker; -import android.net.shared.NetdService; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -238,6 +238,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // connect anyway?" dialog after the user selects a network that doesn't validate. private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000; + // How long to dismiss network notification. + private static final int TIMEOUT_NOTIFICATION_DELAY_MS = 20 * 1000; + // Default to 30s linger time-out. Modifiable only for testing. private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger"; private static final int DEFAULT_LINGER_DELAY_MS = 30_000; @@ -474,6 +477,11 @@ public class ConnectivityService extends IConnectivityManager.Stub public static final int EVENT_PROVISIONING_NOTIFICATION = 43; /** + * This event can handle dismissing notification by given network id. + */ + public static final int EVENT_TIMEOUT_NOTIFICATION = 44; + + /** * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification * should be shown. */ @@ -2477,6 +2485,11 @@ public class ConnectivityService extends IConnectivityManager.Stub final boolean valid = (msg.arg1 == NETWORK_TEST_RESULT_VALID); final boolean wasValidated = nai.lastValidated; final boolean wasDefault = isDefaultNetwork(nai); + if (nai.everCaptivePortalDetected && !nai.captivePortalLoginNotified + && valid) { + nai.captivePortalLoginNotified = true; + showNetworkNotification(nai, NotificationType.LOGGED_IN); + } final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : ""; @@ -2497,7 +2510,15 @@ public class ConnectivityService extends IConnectivityManager.Stub updateCapabilities(oldScore, nai, nai.networkCapabilities); // If score has changed, rebroadcast to NetworkFactories. b/17726566 if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai); - if (valid) handleFreshlyValidatedNetwork(nai); + if (valid) { + handleFreshlyValidatedNetwork(nai); + // Clear NO_INTERNET and LOST_INTERNET notifications if network becomes + // valid. + mNotifier.clearNotification(nai.network.netId, + NotificationType.NO_INTERNET); + mNotifier.clearNotification(nai.network.netId, + NotificationType.LOST_INTERNET); + } } updateInetCondition(nai); // Let the NetworkAgent know the state of its network @@ -2521,6 +2542,9 @@ public class ConnectivityService extends IConnectivityManager.Stub final int oldScore = nai.getCurrentScore(); nai.lastCaptivePortalDetected = visible; nai.everCaptivePortalDetected |= visible; + if (visible) { + nai.captivePortalLoginNotified = false; + } if (nai.lastCaptivePortalDetected && Settings.Global.CAPTIVE_PORTAL_MODE_AVOID == getCaptivePortalMode()) { if (DBG) log("Avoiding captive portal network: " + nai.name()); @@ -2532,7 +2556,10 @@ public class ConnectivityService extends IConnectivityManager.Stub updateCapabilities(oldScore, nai, nai.networkCapabilities); } if (!visible) { - mNotifier.clearNotification(netId); + // Only clear SIGN_IN and NETWORK_SWITCH notifications here, or else other + // notifications belong to the same network may be cleared unexpected. + mNotifier.clearNotification(netId, NotificationType.SIGN_IN); + mNotifier.clearNotification(netId, NotificationType.NETWORK_SWITCH); } else { if (nai == null) { loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor"); @@ -3238,9 +3265,15 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.decreaseIndent(); } - private void showValidationNotification(NetworkAgentInfo nai, NotificationType type) { + private void showNetworkNotification(NetworkAgentInfo nai, NotificationType type) { final String action; switch (type) { + case LOGGED_IN: + action = Settings.ACTION_WIFI_SETTINGS; + mHandler.removeMessages(EVENT_TIMEOUT_NOTIFICATION); + mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NOTIFICATION, + nai.network.netId, 0), TIMEOUT_NOTIFICATION_DELAY_MS); + break; case NO_INTERNET: action = ConnectivityManager.ACTION_PROMPT_UNVALIDATED; break; @@ -3253,10 +3286,12 @@ public class ConnectivityService extends IConnectivityManager.Stub } Intent intent = new Intent(action); - intent.setData(Uri.fromParts("netId", Integer.toString(nai.network.netId), null)); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.setClassName("com.android.settings", - "com.android.settings.wifi.WifiNoInternetDialog"); + if (type != NotificationType.LOGGED_IN) { + intent.setData(Uri.fromParts("netId", Integer.toString(nai.network.netId), null)); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setClassName("com.android.settings", + "com.android.settings.wifi.WifiNoInternetDialog"); + } PendingIntent pendingIntent = PendingIntent.getActivityAsUser( mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); @@ -3274,7 +3309,7 @@ public class ConnectivityService extends IConnectivityManager.Stub !nai.networkMisc.explicitlySelected || nai.networkMisc.acceptUnvalidated) { return; } - showValidationNotification(nai, NotificationType.NO_INTERNET); + showNetworkNotification(nai, NotificationType.NO_INTERNET); } private void handleNetworkUnvalidated(NetworkAgentInfo nai) { @@ -3283,7 +3318,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && mMultinetworkPolicyTracker.shouldNotifyWifiUnvalidated()) { - showValidationNotification(nai, NotificationType.LOST_INTERNET); + showNetworkNotification(nai, NotificationType.LOST_INTERNET); } } @@ -3429,6 +3464,9 @@ public class ConnectivityService extends IConnectivityManager.Stub case EVENT_DATA_SAVER_CHANGED: handleRestrictBackgroundChanged(toBool(msg.arg1)); break; + case EVENT_TIMEOUT_NOTIFICATION: + mNotifier.clearNotification(msg.arg1, NotificationType.LOGGED_IN); + break; } } } diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 9ea73fbb1882..d0cff25dbf05 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -152,6 +152,10 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // Whether a captive portal was found during the last network validation attempt. public boolean lastCaptivePortalDetected; + // Indicates the user was notified of a successful captive portal login since a portal was + // last detected. + public boolean captivePortalLoginNotified; + // Networks are lingered when they become unneeded as a result of their NetworkRequests being // satisfied by a higher-scoring network. so as to allow communication to wrap up before the // network is taken down. This usually only happens to the default network. Lingering ends with @@ -618,18 +622,19 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { } public String toString() { - return "NetworkAgentInfo{ ni{" + networkInfo + "} " + - "network{" + network + "} nethandle{" + network.getNetworkHandle() + "} " + - "lp{" + linkProperties + "} " + - "nc{" + networkCapabilities + "} Score{" + getCurrentScore() + "} " + - "everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} " + - "created{" + created + "} lingering{" + isLingering() + "} " + - "explicitlySelected{" + networkMisc.explicitlySelected + "} " + - "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " + - "everCaptivePortalDetected{" + everCaptivePortalDetected + "} " + - "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} " + - "clat{" + clatd + "} " + - "}"; + return "NetworkAgentInfo{ ni{" + networkInfo + "} " + + "network{" + network + "} nethandle{" + network.getNetworkHandle() + "} " + + "lp{" + linkProperties + "} " + + "nc{" + networkCapabilities + "} Score{" + getCurrentScore() + "} " + + "everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} " + + "created{" + created + "} lingering{" + isLingering() + "} " + + "explicitlySelected{" + networkMisc.explicitlySelected + "} " + + "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " + + "everCaptivePortalDetected{" + everCaptivePortalDetected + "} " + + "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} " + + "captivePortalLoginNotified{" + captivePortalLoginNotified + "} " + + "clat{" + clatd + "} " + + "}"; } public String name() { diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java index 36a2476d2ceb..b50477bc120f 100644 --- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java +++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java @@ -16,13 +16,16 @@ package com.android.server.connectivity; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; + import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.res.Resources; -import android.net.NetworkCapabilities; import android.net.wifi.WifiInfo; import android.os.UserHandle; import android.telephony.TelephonyManager; @@ -31,15 +34,12 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.widget.Toast; + import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; -import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; - public class NetworkNotificationManager { @@ -47,7 +47,8 @@ public class NetworkNotificationManager { LOST_INTERNET(SystemMessage.NOTE_NETWORK_LOST_INTERNET), NETWORK_SWITCH(SystemMessage.NOTE_NETWORK_SWITCH), NO_INTERNET(SystemMessage.NOTE_NETWORK_NO_INTERNET), - SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN); + SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN), + LOGGED_IN(SystemMessage.NOTE_NETWORK_LOGGED_IN); public final int eventId; @@ -192,6 +193,9 @@ public class NetworkNotificationManager { details = r.getString(R.string.network_available_sign_in_detailed, name); break; } + } else if (notifyType == NotificationType.LOGGED_IN) { + title = WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()); + details = r.getString(R.string.captive_portal_logged_in_detailed); } else if (notifyType == NotificationType.NETWORK_SWITCH) { String fromTransport = getTransportName(transportType); String toTransport = getTransportName(getFirstTransportType(switchToNai)); @@ -239,6 +243,18 @@ public class NetworkNotificationManager { } } + /** + * Clear the notification with the given id, only if it matches the given type. + */ + public void clearNotification(int id, NotificationType notifyType) { + final int previousEventId = mNotificationTypeMap.get(id); + final NotificationType previousNotifyType = NotificationType.getFromId(previousEventId); + if (notifyType != previousNotifyType) { + return; + } + clearNotification(id); + } + public void clearNotification(int id) { if (mNotificationTypeMap.indexOfKey(id) < 0) { return; @@ -290,6 +306,10 @@ public class NetworkNotificationManager { return (t != null) ? t.name() : "UNKNOWN"; } + /** + * A notification with a higher number will take priority over a notification with a lower + * number. + */ private static int priority(NotificationType t) { if (t == null) { return 0; @@ -302,6 +322,7 @@ public class NetworkNotificationManager { case NETWORK_SWITCH: return 2; case LOST_INTERNET: + case LOGGED_IN: return 1; default: return 0; diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java index 125fe7258e94..273b8fc3773b 100644 --- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java +++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java @@ -17,6 +17,7 @@ package com.android.server.connectivity; import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.*; + import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.eq; @@ -34,25 +35,23 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.NetworkCapabilities; import android.net.NetworkInfo; -import android.support.test.runner.AndroidJUnit4; import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; import android.telephony.TelephonyManager; import com.android.server.connectivity.NetworkNotificationManager.NotificationType; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.runner.RunWith; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; @RunWith(AndroidJUnit4.class) @SmallTest @@ -194,4 +193,54 @@ public class NetworkNotificationManagerTest { mManager.clearNotification(id); verify(mNotificationManager, times(1)).cancelAsUser(eq(tag), eq(SIGN_IN.eventId), any()); } + + @Test + public void testSameLevelNotifications() { + final int id = 101; + final String tag = NetworkNotificationManager.tagFor(id); + + mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false); + verify(mNotificationManager, times(1)) + .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any()); + + mManager.showNotification(id, LOST_INTERNET, mWifiNai, mCellNai, null, false); + verify(mNotificationManager, times(1)) + .notifyAsUser(eq(tag), eq(LOST_INTERNET.eventId), any(), any()); + } + + @Test + public void testClearNotificationByType() { + final int id = 101; + final String tag = NetworkNotificationManager.tagFor(id); + + // clearNotification(int id, NotificationType notifyType) will check if given type is equal + // to previous type or not. If they are equal then clear the notification; if they are not + // equal then return. + + mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false); + verify(mNotificationManager, times(1)) + .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any()); + + // Previous notification is LOGGED_IN and given type is LOGGED_IN too. The notification + // should be cleared. + mManager.clearNotification(id, LOGGED_IN); + verify(mNotificationManager, times(1)) + .cancelAsUser(eq(tag), eq(LOGGED_IN.eventId), any()); + + mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false); + verify(mNotificationManager, times(2)) + .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any()); + + // LOST_INTERNET notification popup after LOGGED_IN notification. + mManager.showNotification(id, LOST_INTERNET, mWifiNai, mCellNai, null, false); + verify(mNotificationManager, times(1)) + .notifyAsUser(eq(tag), eq(LOST_INTERNET.eventId), any(), any()); + + // Previous notification is LOST_INTERNET and given type is LOGGED_IN. The notification + // shouldn't be cleared. + mManager.clearNotification(id, LOGGED_IN); + // LOST_INTERNET shouldn't be cleared. + verify(mNotificationManager, never()) + .cancelAsUser(eq(tag), eq(LOST_INTERNET.eventId), any()); + } } |