Support notifying network switches via notifications and toasts. am: ddc5fd88fe am: 4549d8b4aa
am: 10acdf7ad1 Change-Id: I0324cfdb7c070464c2caa73fa5fad8d1031c1e76
This commit is contained in:
@@ -131,6 +131,7 @@ import com.android.server.am.BatteryStatsService;
|
|||||||
import com.android.server.connectivity.DataConnectionStats;
|
import com.android.server.connectivity.DataConnectionStats;
|
||||||
import com.android.server.connectivity.KeepaliveTracker;
|
import com.android.server.connectivity.KeepaliveTracker;
|
||||||
import com.android.server.connectivity.Nat464Xlat;
|
import com.android.server.connectivity.Nat464Xlat;
|
||||||
|
import com.android.server.connectivity.LingerMonitor;
|
||||||
import com.android.server.connectivity.NetworkAgentInfo;
|
import com.android.server.connectivity.NetworkAgentInfo;
|
||||||
import com.android.server.connectivity.NetworkDiagnostics;
|
import com.android.server.connectivity.NetworkDiagnostics;
|
||||||
import com.android.server.connectivity.NetworkMonitor;
|
import com.android.server.connectivity.NetworkMonitor;
|
||||||
@@ -438,6 +439,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
|
|
||||||
private KeepaliveTracker mKeepaliveTracker;
|
private KeepaliveTracker mKeepaliveTracker;
|
||||||
private NetworkNotificationManager mNotifier;
|
private NetworkNotificationManager mNotifier;
|
||||||
|
private LingerMonitor mLingerMonitor;
|
||||||
|
|
||||||
// sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
|
// sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
|
||||||
private final static int MIN_NET_ID = 100; // some reserved marks
|
private final static int MIN_NET_ID = 100; // some reserved marks
|
||||||
@@ -836,6 +838,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
mKeepaliveTracker = new KeepaliveTracker(mHandler);
|
mKeepaliveTracker = new KeepaliveTracker(mHandler);
|
||||||
mNotifier = new NetworkNotificationManager(mContext, mTelephonyManager,
|
mNotifier = new NetworkNotificationManager(mContext, mTelephonyManager,
|
||||||
mContext.getSystemService(NotificationManager.class));
|
mContext.getSystemService(NotificationManager.class));
|
||||||
|
mLingerMonitor = new LingerMonitor(mContext, mNotifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
private NetworkRequest createInternetRequestForTransport(int transportType) {
|
private NetworkRequest createInternetRequestForTransport(int transportType) {
|
||||||
@@ -2241,7 +2244,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!nai.networkMisc.provisioningNotificationDisabled) {
|
if (!nai.networkMisc.provisioningNotificationDisabled) {
|
||||||
mNotifier.showNotification(netId, NotificationType.SIGN_IN, nai,
|
mNotifier.showNotification(netId, NotificationType.SIGN_IN, nai, null,
|
||||||
(PendingIntent) msg.obj, nai.networkMisc.explicitlySelected);
|
(PendingIntent) msg.obj, nai.networkMisc.explicitlySelected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2392,6 +2395,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
}
|
}
|
||||||
mLegacyTypeTracker.remove(nai, wasDefault);
|
mLegacyTypeTracker.remove(nai, wasDefault);
|
||||||
rematchAllNetworksAndRequests(null, 0);
|
rematchAllNetworksAndRequests(null, 0);
|
||||||
|
mLingerMonitor.noteDisconnect(nai);
|
||||||
if (nai.created) {
|
if (nai.created) {
|
||||||
// Tell netd to clean up the configuration for this network
|
// Tell netd to clean up the configuration for this network
|
||||||
// (routing rules, DNS, etc).
|
// (routing rules, DNS, etc).
|
||||||
@@ -2713,7 +2717,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
|
PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
|
||||||
mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
|
mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
|
||||||
|
|
||||||
mNotifier.showNotification(nai.network.netId, NotificationType.NO_INTERNET, nai,
|
mNotifier.showNotification(nai.network.netId, NotificationType.NO_INTERNET, nai, null,
|
||||||
pendingIntent, true);
|
pendingIntent, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4249,6 +4253,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
return nai == getDefaultNetwork();
|
return nai == getDefaultNetwork();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isDefaultRequest(NetworkRequestInfo nri) {
|
||||||
|
return nri.request.requestId == mDefaultRequest.requestId;
|
||||||
|
}
|
||||||
|
|
||||||
public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
|
public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
|
||||||
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
|
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
|
||||||
int currentScore, NetworkMisc networkMisc) {
|
int currentScore, NetworkMisc networkMisc) {
|
||||||
@@ -4691,6 +4699,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
if (VDBG) log(" accepting network in place of " + currentNetwork.name());
|
if (VDBG) log(" accepting network in place of " + currentNetwork.name());
|
||||||
currentNetwork.removeRequest(nri.request.requestId);
|
currentNetwork.removeRequest(nri.request.requestId);
|
||||||
currentNetwork.lingerRequest(nri.request, now, mLingerDelayMs);
|
currentNetwork.lingerRequest(nri.request, now, mLingerDelayMs);
|
||||||
|
if (isDefaultRequest(nri)) {
|
||||||
|
mLingerMonitor.noteLingerDefaultNetwork(currentNetwork, newNetwork);
|
||||||
|
}
|
||||||
affectedNetworks.add(currentNetwork);
|
affectedNetworks.add(currentNetwork);
|
||||||
} else {
|
} else {
|
||||||
if (VDBG) log(" accepting network in place of null");
|
if (VDBG) log(" accepting network in place of null");
|
||||||
@@ -4708,7 +4719,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
// network. Think about if there is a way to reduce this. Push
|
// network. Think about if there is a way to reduce this. Push
|
||||||
// netid->request mapping to each factory?
|
// netid->request mapping to each factory?
|
||||||
sendUpdatedScoreToFactories(nri.request, newNetwork.getCurrentScore());
|
sendUpdatedScoreToFactories(nri.request, newNetwork.getCurrentScore());
|
||||||
if (mDefaultRequest.requestId == nri.request.requestId) {
|
if (isDefaultRequest(nri)) {
|
||||||
isNewDefault = true;
|
isNewDefault = true;
|
||||||
oldDefaultNetwork = currentNetwork;
|
oldDefaultNetwork = currentNetwork;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,256 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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.connectivity;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.NetworkCapabilities;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.util.SparseIntArray;
|
||||||
|
import android.util.SparseBooleanArray;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import com.android.internal.util.MessageUtils;
|
||||||
|
import com.android.server.connectivity.NetworkNotificationManager;
|
||||||
|
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
|
||||||
|
|
||||||
|
import static android.net.ConnectivityManager.NETID_UNSET;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that monitors default network linger events and possibly notifies the user of network
|
||||||
|
* switches.
|
||||||
|
*
|
||||||
|
* This class is not thread-safe and all its methods must be called on the ConnectivityService
|
||||||
|
* handler thread.
|
||||||
|
*/
|
||||||
|
public class LingerMonitor {
|
||||||
|
|
||||||
|
private static final boolean DBG = true;
|
||||||
|
private static final boolean VDBG = false;
|
||||||
|
private static final String TAG = LingerMonitor.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final HashMap<String, Integer> sTransportNames = makeTransportToNameMap();
|
||||||
|
private static final Intent CELLULAR_SETTINGS = new Intent().setComponent(new ComponentName(
|
||||||
|
"com.android.settings", "com.android.settings.Settings$DataUsageSummaryActivity"));
|
||||||
|
|
||||||
|
private static final int NOTIFY_TYPE_NONE = 0;
|
||||||
|
private static final int NOTIFY_TYPE_NOTIFICATION = 1;
|
||||||
|
private static final int NOTIFY_TYPE_TOAST = 2;
|
||||||
|
|
||||||
|
private static SparseArray<String> sNotifyTypeNames = MessageUtils.findMessageNames(
|
||||||
|
new Class[] { LingerMonitor.class }, new String[]{ "NOTIFY_TYPE_" });
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
private final NetworkNotificationManager mNotifier;
|
||||||
|
|
||||||
|
/** Current notifications. Maps the netId we switched away from to the netId we switched to. */
|
||||||
|
private final SparseIntArray mNotifications = new SparseIntArray();
|
||||||
|
|
||||||
|
/** Whether we ever notified that we switched away from a particular network. */
|
||||||
|
private final SparseBooleanArray mEverNotified = new SparseBooleanArray();
|
||||||
|
|
||||||
|
public LingerMonitor(Context context, NetworkNotificationManager notifier) {
|
||||||
|
mContext = context;
|
||||||
|
mNotifier = notifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HashMap<String, Integer> makeTransportToNameMap() {
|
||||||
|
SparseArray<String> numberToName = MessageUtils.findMessageNames(
|
||||||
|
new Class[] { NetworkCapabilities.class }, new String[]{ "TRANSPORT_" });
|
||||||
|
HashMap<String, Integer> nameToNumber = new HashMap<>();
|
||||||
|
for (int i = 0; i < numberToName.size(); i++) {
|
||||||
|
// MessageUtils will fail to initialize if there are duplicate constant values, so there
|
||||||
|
// are no duplicates here.
|
||||||
|
nameToNumber.put(numberToName.valueAt(i), numberToName.keyAt(i));
|
||||||
|
}
|
||||||
|
return nameToNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasTransport(NetworkAgentInfo nai, int transport) {
|
||||||
|
return nai.networkCapabilities.hasTransport(transport);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getNotificationSource(NetworkAgentInfo toNai) {
|
||||||
|
for (int i = 0; i < mNotifications.size(); i++) {
|
||||||
|
if (mNotifications.valueAt(i) == toNai.network.netId) {
|
||||||
|
return mNotifications.keyAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NETID_UNSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean everNotified(NetworkAgentInfo nai) {
|
||||||
|
return mEverNotified.get(nai.network.netId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isNotificationEnabled(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
|
||||||
|
// TODO: Evaluate moving to CarrierConfigManager.
|
||||||
|
String[] notifySwitches = mContext.getResources().getStringArray(
|
||||||
|
com.android.internal.R.array.config_networkNotifySwitches);
|
||||||
|
|
||||||
|
if (VDBG) {
|
||||||
|
Log.d(TAG, "Notify on network switches: " + Arrays.toString(notifySwitches));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String notifySwitch : notifySwitches) {
|
||||||
|
if (TextUtils.isEmpty(notifySwitch)) continue;
|
||||||
|
String[] transports = notifySwitch.split("-", 2);
|
||||||
|
if (transports.length != 2) {
|
||||||
|
Log.e(TAG, "Invalid network switch notification configuration: " + notifySwitch);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int fromTransport = sTransportNames.get("TRANSPORT_" + transports[0]);
|
||||||
|
int toTransport = sTransportNames.get("TRANSPORT_" + transports[1]);
|
||||||
|
if (hasTransport(fromNai, fromTransport) && hasTransport(toNai, toTransport)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showNotification(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
|
||||||
|
PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
|
||||||
|
mContext, 0, CELLULAR_SETTINGS, PendingIntent.FLAG_CANCEL_CURRENT, null,
|
||||||
|
UserHandle.CURRENT);
|
||||||
|
|
||||||
|
mNotifier.showNotification(fromNai.network.netId, NotificationType.NETWORK_SWITCH,
|
||||||
|
fromNai, toNai, pendingIntent, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes any notification that was put up as a result of switching to nai.
|
||||||
|
private void maybeStopNotifying(NetworkAgentInfo nai) {
|
||||||
|
int fromNetId = getNotificationSource(nai);
|
||||||
|
if (fromNetId != NETID_UNSET) {
|
||||||
|
mNotifications.delete(fromNetId);
|
||||||
|
mNotifier.clearNotification(fromNetId);
|
||||||
|
// Toasts can't be deleted.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify the user of a network switch using a notification or a toast.
|
||||||
|
private void notify(NetworkAgentInfo fromNai, NetworkAgentInfo toNai, boolean forceToast) {
|
||||||
|
boolean notify = false;
|
||||||
|
int notifyType = mContext.getResources().getInteger(
|
||||||
|
com.android.internal.R.integer.config_networkNotifySwitchType);
|
||||||
|
|
||||||
|
if (notifyType == NOTIFY_TYPE_NOTIFICATION && forceToast) {
|
||||||
|
notifyType = NOTIFY_TYPE_TOAST;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (notifyType) {
|
||||||
|
case NOTIFY_TYPE_NONE:
|
||||||
|
break;
|
||||||
|
case NOTIFY_TYPE_NOTIFICATION:
|
||||||
|
showNotification(fromNai, toNai);
|
||||||
|
notify = true;
|
||||||
|
break;
|
||||||
|
case NOTIFY_TYPE_TOAST:
|
||||||
|
mNotifier.showToast(fromNai, toNai);
|
||||||
|
notify = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.e(TAG, "Unknown notify type " + notifyType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VDBG) {
|
||||||
|
Log.d(TAG, "Notify type: " + sNotifyTypeNames.get(notifyType, "" + notifyType));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notify) {
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(TAG, "Notifying switch from=" + fromNai.name() + " to=" + toNai.name() +
|
||||||
|
" type=" + sNotifyTypeNames.get(notifyType, "unknown(" + notifyType + ")"));
|
||||||
|
}
|
||||||
|
mNotifications.put(fromNai.network.netId, toNai.network.netId);
|
||||||
|
mEverNotified.put(fromNai.network.netId, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default network changed from fromNai to toNai due to a change in score.
|
||||||
|
public void noteLingerDefaultNetwork(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
|
||||||
|
if (VDBG) {
|
||||||
|
Log.d(TAG, "noteLingerDefaultNetwork from=" + fromNai.name() +
|
||||||
|
" everValidated=" + fromNai.everValidated +
|
||||||
|
" lastValidated=" + fromNai.lastValidated +
|
||||||
|
" to=" + toNai.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are currently notifying the user because the device switched to fromNai, now that
|
||||||
|
// we are switching away from it we should remove the notification. This includes the case
|
||||||
|
// where we switch back to toNai because its score improved again (e.g., because it regained
|
||||||
|
// Internet access).
|
||||||
|
maybeStopNotifying(fromNai);
|
||||||
|
|
||||||
|
// If this network never validated, don't notify. Otherwise, we could do things like:
|
||||||
|
//
|
||||||
|
// 1. Unvalidated wifi connects.
|
||||||
|
// 2. Unvalidated mobile data connects.
|
||||||
|
// 3. Cell validates, and we show a notification.
|
||||||
|
// or:
|
||||||
|
// 1. User connects to wireless printer.
|
||||||
|
// 2. User turns on cellular data.
|
||||||
|
// 3. We show a notification.
|
||||||
|
if (!fromNai.everValidated) return;
|
||||||
|
|
||||||
|
// If this network is a captive portal, don't notify. This cannot happen on initial connect
|
||||||
|
// to a captive portal, because the everValidated check above will fail. However, it can
|
||||||
|
// happen if the captive portal reasserts itself (e.g., because its timeout fires). In that
|
||||||
|
// case, as soon as the captive portal reasserts itself, we'll show a sign-in notification.
|
||||||
|
// We don't want to overwrite that notification with this one; the user has already been
|
||||||
|
// notified, and of the two, the captive portal notification is the more useful one because
|
||||||
|
// it allows the user to sign in to the captive portal. In this case, display a toast
|
||||||
|
// in addition to the captive portal notification.
|
||||||
|
//
|
||||||
|
// Note that if the network we switch to is already up when the captive portal reappears,
|
||||||
|
// this won't work because NetworkMonitor tells ConnectivityService that the network is
|
||||||
|
// unvalidated (causing a switch) before asking it to show the sign in notification. In this
|
||||||
|
// case, the toast won't show and we'll only display the sign in notification. This is the
|
||||||
|
// best we can do at this time.
|
||||||
|
boolean forceToast = fromNai.networkCapabilities.hasCapability(
|
||||||
|
NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
|
||||||
|
|
||||||
|
// Only show the notification once, in order to avoid irritating the user every time.
|
||||||
|
// TODO: should we do this?
|
||||||
|
if (everNotified(fromNai)) {
|
||||||
|
if (VDBG) {
|
||||||
|
Log.d(TAG, "Not notifying handover from " + fromNai.name() + ", already notified");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNotificationEnabled(fromNai, toNai)) {
|
||||||
|
notify(fromNai, toNai, forceToast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void noteDisconnect(NetworkAgentInfo nai) {
|
||||||
|
mNotifications.delete(nai.network.netId);
|
||||||
|
mEverNotified.delete(nai.network.netId);
|
||||||
|
maybeStopNotifying(nai);
|
||||||
|
// No need to cancel notifications on nai: NetworkMonitor does that on disconnect.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -411,14 +411,17 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancel lingering. Called by ConnectivityService when a request is added to this network.
|
* Cancel lingering. Called by ConnectivityService when a request is added to this network.
|
||||||
|
* Returns true if the given request was lingering on this network, false otherwise.
|
||||||
*/
|
*/
|
||||||
public void unlingerRequest(NetworkRequest request) {
|
public boolean unlingerRequest(NetworkRequest request) {
|
||||||
LingerTimer timer = mLingerTimerForRequest.get(request.requestId);
|
LingerTimer timer = mLingerTimerForRequest.get(request.requestId);
|
||||||
if (timer != null) {
|
if (timer != null) {
|
||||||
if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + this.name());
|
if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + this.name());
|
||||||
mLingerTimers.remove(timer);
|
mLingerTimers.remove(timer);
|
||||||
mLingerTimerForRequest.remove(request.requestId);
|
mLingerTimerForRequest.remove(request.requestId);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getLingerExpiry() {
|
public long getLingerExpiry() {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.android.server.connectivity;
|
|||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.widget.Toast;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
@@ -34,9 +35,9 @@ import static android.net.NetworkCapabilities.*;
|
|||||||
|
|
||||||
public class NetworkNotificationManager {
|
public class NetworkNotificationManager {
|
||||||
|
|
||||||
public static enum NotificationType { SIGN_IN, NO_INTERNET; };
|
public static enum NotificationType { SIGN_IN, NO_INTERNET, NETWORK_SWITCH };
|
||||||
|
|
||||||
private static final String NOTIFICATION_ID = "CaptivePortal.Notification";
|
private static final String NOTIFICATION_ID = "Connectivity.Notification";
|
||||||
|
|
||||||
private static final String TAG = NetworkNotificationManager.class.getSimpleName();
|
private static final String TAG = NetworkNotificationManager.class.getSimpleName();
|
||||||
private static final boolean DBG = true;
|
private static final boolean DBG = true;
|
||||||
@@ -90,9 +91,15 @@ public class NetworkNotificationManager {
|
|||||||
* @param id an identifier that uniquely identifies this notification. This must match
|
* @param id an identifier that uniquely identifies this notification. This must match
|
||||||
* between show and hide calls. We use the NetID value but for legacy callers
|
* between show and hide calls. We use the NetID value but for legacy callers
|
||||||
* we concatenate the range of types with the range of NetIDs.
|
* we concatenate the range of types with the range of NetIDs.
|
||||||
|
* @param nai the network with which the notification is associated. For a SIGN_IN or
|
||||||
|
* NO_INTERNET notification, this is the network we're connecting to. For a
|
||||||
|
* NETWORK_SWITCH notification it's the network that we switched from. When this network
|
||||||
|
* disconnects the notification is removed.
|
||||||
|
* @param switchToNai for a NETWORK_SWITCH notification, the network we are switching to. Null
|
||||||
|
* in all other cases. Only used to determine the text of the notification.
|
||||||
*/
|
*/
|
||||||
public void showNotification(int id, NotificationType notifyType,
|
public void showNotification(int id, NotificationType notifyType, NetworkAgentInfo nai,
|
||||||
NetworkAgentInfo nai, PendingIntent intent, boolean highPriority) {
|
NetworkAgentInfo switchToNai, PendingIntent intent, boolean highPriority) {
|
||||||
int transportType;
|
int transportType;
|
||||||
String extraInfo;
|
String extraInfo;
|
||||||
if (nai != null) {
|
if (nai != null) {
|
||||||
@@ -136,29 +143,42 @@ public class NetworkNotificationManager {
|
|||||||
details = r.getString(R.string.network_available_sign_in_detailed, extraInfo);
|
details = r.getString(R.string.network_available_sign_in_detailed, extraInfo);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else if (notifyType == NotificationType.NETWORK_SWITCH) {
|
||||||
|
String fromTransport = getTransportName(transportType);
|
||||||
|
String toTransport = getTransportName(getFirstTransportType(switchToNai));
|
||||||
|
title = r.getString(R.string.network_switch_metered, toTransport);
|
||||||
|
details = r.getString(R.string.network_switch_metered_detail, toTransport,
|
||||||
|
fromTransport);
|
||||||
} else {
|
} else {
|
||||||
Slog.wtf(TAG, "Unknown notification type " + notifyType + "on network transport "
|
Slog.wtf(TAG, "Unknown notification type " + notifyType + "on network transport "
|
||||||
+ getTransportName(transportType));
|
+ getTransportName(transportType));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Notification notification = new Notification.Builder(mContext)
|
Notification.Builder builder = new Notification.Builder(mContext)
|
||||||
.setWhen(0)
|
.setWhen(System.currentTimeMillis())
|
||||||
|
.setShowWhen(notifyType == NotificationType.NETWORK_SWITCH)
|
||||||
.setSmallIcon(icon)
|
.setSmallIcon(icon)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setTicker(title)
|
.setTicker(title)
|
||||||
.setColor(mContext.getColor(
|
.setColor(mContext.getColor(
|
||||||
com.android.internal.R.color.system_notification_accent_color))
|
com.android.internal.R.color.system_notification_accent_color))
|
||||||
.setContentTitle(title)
|
.setContentTitle(title)
|
||||||
.setContentText(details)
|
|
||||||
.setContentIntent(intent)
|
.setContentIntent(intent)
|
||||||
.setLocalOnly(true)
|
.setLocalOnly(true)
|
||||||
.setPriority(highPriority ?
|
.setPriority(highPriority ?
|
||||||
Notification.PRIORITY_HIGH :
|
Notification.PRIORITY_HIGH :
|
||||||
Notification.PRIORITY_DEFAULT)
|
Notification.PRIORITY_DEFAULT)
|
||||||
.setDefaults(highPriority ? Notification.DEFAULT_ALL : 0)
|
.setDefaults(highPriority ? Notification.DEFAULT_ALL : 0)
|
||||||
.setOnlyAlertOnce(true)
|
.setOnlyAlertOnce(true);
|
||||||
.build();
|
|
||||||
|
if (notifyType == NotificationType.NETWORK_SWITCH) {
|
||||||
|
builder.setStyle(new Notification.BigTextStyle().bigText(details));
|
||||||
|
} else {
|
||||||
|
builder.setContentText(details);
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification notification = builder.build();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mNotificationManager.notifyAsUser(NOTIFICATION_ID, id, notification, UserHandle.ALL);
|
mNotificationManager.notifyAsUser(NOTIFICATION_ID, id, notification, UserHandle.ALL);
|
||||||
@@ -185,9 +205,17 @@ public class NetworkNotificationManager {
|
|||||||
if (visible) {
|
if (visible) {
|
||||||
Intent intent = new Intent(action);
|
Intent intent = new Intent(action);
|
||||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
|
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
|
||||||
showNotification(id, NotificationType.SIGN_IN, null, pendingIntent, false);
|
showNotification(id, NotificationType.SIGN_IN, null, null, pendingIntent, false);
|
||||||
} else {
|
} else {
|
||||||
clearNotification(id);
|
clearNotification(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void showToast(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
|
||||||
|
String fromTransport = getTransportName(getFirstTransportType(fromNai));
|
||||||
|
String toTransport = getTransportName(getFirstTransportType(toNai));
|
||||||
|
String text = mContext.getResources().getString(
|
||||||
|
R.string.network_switch_metered_toast, fromTransport, toTransport);
|
||||||
|
Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user