15526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti/* 25526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * Copyright (C) 2016 The Android Open Source Project 35526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * 45526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * Licensed under the Apache License, Version 2.0 (the "License"); 55526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * you may not use this file except in compliance with the License. 65526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * You may obtain a copy of the License at 75526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * 85526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * http://www.apache.org/licenses/LICENSE-2.0 95526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * 105526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * Unless required by applicable law or agreed to in writing, software 115526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * distributed under the License is distributed on an "AS IS" BASIS, 125526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * See the License for the specific language governing permissions and 145526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * limitations under the License. 155526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti */ 165526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 175526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittipackage com.android.server.connectivity; 185526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 195526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittiimport android.app.PendingIntent; 205526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittiimport android.content.ComponentName; 215526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittiimport android.content.Context; 225526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittiimport android.content.Intent; 2384e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colittiimport android.net.NetworkCapabilities; 2484e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colittiimport android.os.SystemClock; 255526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittiimport android.os.UserHandle; 265526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittiimport android.text.TextUtils; 2784e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colittiimport android.text.format.DateUtils; 285526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittiimport android.util.Log; 295526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittiimport android.util.SparseArray; 305526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittiimport android.util.SparseIntArray; 315526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittiimport android.util.SparseBooleanArray; 325526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittiimport java.util.Arrays; 335526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittiimport java.util.HashMap; 345526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 3584e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colittiimport com.android.internal.R; 36a43a095106ae71842574115ed69fed2171b4c48cHugo Benichiimport com.android.internal.annotations.VisibleForTesting; 375526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittiimport com.android.internal.util.MessageUtils; 385526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittiimport com.android.server.connectivity.NetworkNotificationManager; 395526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittiimport com.android.server.connectivity.NetworkNotificationManager.NotificationType; 405526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 415526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittiimport static android.net.ConnectivityManager.NETID_UNSET; 425526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 435526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti/** 445526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * Class that monitors default network linger events and possibly notifies the user of network 455526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * switches. 465526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * 475526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * This class is not thread-safe and all its methods must be called on the ConnectivityService 485526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti * handler thread. 495526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti */ 505526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colittipublic class LingerMonitor { 515526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 525526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti private static final boolean DBG = true; 535526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti private static final boolean VDBG = false; 545526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti private static final String TAG = LingerMonitor.class.getSimpleName(); 555526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 5684e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti public static final int DEFAULT_NOTIFICATION_DAILY_LIMIT = 3; 5784e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti public static final long DEFAULT_NOTIFICATION_RATE_LIMIT_MILLIS = DateUtils.MINUTE_IN_MILLIS; 5884e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti 59a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi private static final HashMap<String, Integer> TRANSPORT_NAMES = makeTransportToNameMap(); 60a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi @VisibleForTesting 61a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi public static final Intent CELLULAR_SETTINGS = new Intent().setComponent(new ComponentName( 625526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti "com.android.settings", "com.android.settings.Settings$DataUsageSummaryActivity")); 635526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 64a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi @VisibleForTesting 65a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi public static final int NOTIFY_TYPE_NONE = 0; 66a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi public static final int NOTIFY_TYPE_NOTIFICATION = 1; 67a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi public static final int NOTIFY_TYPE_TOAST = 2; 685526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 695526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti private static SparseArray<String> sNotifyTypeNames = MessageUtils.findMessageNames( 705526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti new Class[] { LingerMonitor.class }, new String[]{ "NOTIFY_TYPE_" }); 715526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 725526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti private final Context mContext; 735526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti private final NetworkNotificationManager mNotifier; 7484e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti private final int mDailyLimit; 7584e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti private final long mRateLimitMillis; 7684e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti 7784e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti private long mFirstNotificationMillis; 7884e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti private long mLastNotificationMillis; 7984e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti private int mNotificationCounter; 805526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 815526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti /** Current notifications. Maps the netId we switched away from to the netId we switched to. */ 825526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti private final SparseIntArray mNotifications = new SparseIntArray(); 835526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 845526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti /** Whether we ever notified that we switched away from a particular network. */ 855526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti private final SparseBooleanArray mEverNotified = new SparseBooleanArray(); 865526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 8784e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti public LingerMonitor(Context context, NetworkNotificationManager notifier, 8884e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti int dailyLimit, long rateLimitMillis) { 895526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti mContext = context; 905526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti mNotifier = notifier; 9184e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti mDailyLimit = dailyLimit; 9284e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti mRateLimitMillis = rateLimitMillis; 935526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 945526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 955526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti private static HashMap<String, Integer> makeTransportToNameMap() { 965526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti SparseArray<String> numberToName = MessageUtils.findMessageNames( 975526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti new Class[] { NetworkCapabilities.class }, new String[]{ "TRANSPORT_" }); 985526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti HashMap<String, Integer> nameToNumber = new HashMap<>(); 995526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti for (int i = 0; i < numberToName.size(); i++) { 1005526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // MessageUtils will fail to initialize if there are duplicate constant values, so there 1015526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // are no duplicates here. 1025526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti nameToNumber.put(numberToName.valueAt(i), numberToName.keyAt(i)); 1035526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 1045526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti return nameToNumber; 1055526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 1065526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 1075526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti private static boolean hasTransport(NetworkAgentInfo nai, int transport) { 1085526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti return nai.networkCapabilities.hasTransport(transport); 1095526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 1105526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 1115526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti private int getNotificationSource(NetworkAgentInfo toNai) { 1125526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti for (int i = 0; i < mNotifications.size(); i++) { 1135526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti if (mNotifications.valueAt(i) == toNai.network.netId) { 1145526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti return mNotifications.keyAt(i); 1155526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 1165526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 1175526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti return NETID_UNSET; 1185526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 1195526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 1205526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti private boolean everNotified(NetworkAgentInfo nai) { 1215526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti return mEverNotified.get(nai.network.netId, false); 1225526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 1235526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 124a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi @VisibleForTesting 125a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi public boolean isNotificationEnabled(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) { 1265526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // TODO: Evaluate moving to CarrierConfigManager. 12784e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti String[] notifySwitches = 12884e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti mContext.getResources().getStringArray(R.array.config_networkNotifySwitches); 1295526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 1305526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti if (VDBG) { 1315526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti Log.d(TAG, "Notify on network switches: " + Arrays.toString(notifySwitches)); 1325526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 1335526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 1345526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti for (String notifySwitch : notifySwitches) { 1355526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti if (TextUtils.isEmpty(notifySwitch)) continue; 1365526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti String[] transports = notifySwitch.split("-", 2); 1375526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti if (transports.length != 2) { 1385526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti Log.e(TAG, "Invalid network switch notification configuration: " + notifySwitch); 1395526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti continue; 1405526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 141a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi int fromTransport = TRANSPORT_NAMES.get("TRANSPORT_" + transports[0]); 142a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi int toTransport = TRANSPORT_NAMES.get("TRANSPORT_" + transports[1]); 1435526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti if (hasTransport(fromNai, fromTransport) && hasTransport(toNai, toTransport)) { 1445526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti return true; 1455526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 1465526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 1475526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 1485526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti return false; 1495526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 1505526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 1515526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti private void showNotification(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) { 1525526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti mNotifier.showNotification(fromNai.network.netId, NotificationType.NETWORK_SWITCH, 153a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi fromNai, toNai, createNotificationIntent(), true); 154a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi } 155a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi 156a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi @VisibleForTesting 157a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi protected PendingIntent createNotificationIntent() { 158a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi return PendingIntent.getActivityAsUser(mContext, 0, CELLULAR_SETTINGS, 159a43a095106ae71842574115ed69fed2171b4c48cHugo Benichi PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); 1605526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 1615526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 1625526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // Removes any notification that was put up as a result of switching to nai. 1635526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti private void maybeStopNotifying(NetworkAgentInfo nai) { 1645526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti int fromNetId = getNotificationSource(nai); 1655526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti if (fromNetId != NETID_UNSET) { 1665526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti mNotifications.delete(fromNetId); 1675526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti mNotifier.clearNotification(fromNetId); 1685526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // Toasts can't be deleted. 1695526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 1705526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 1715526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 1725526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // Notify the user of a network switch using a notification or a toast. 1735526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti private void notify(NetworkAgentInfo fromNai, NetworkAgentInfo toNai, boolean forceToast) { 17484e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti int notifyType = 17584e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti mContext.getResources().getInteger(R.integer.config_networkNotifySwitchType); 1765526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti if (notifyType == NOTIFY_TYPE_NOTIFICATION && forceToast) { 1775526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti notifyType = NOTIFY_TYPE_TOAST; 1785526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 1795526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 18084e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti if (VDBG) { 18184e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti Log.d(TAG, "Notify type: " + sNotifyTypeNames.get(notifyType, "" + notifyType)); 18284e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti } 18384e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti 1845526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti switch (notifyType) { 1855526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti case NOTIFY_TYPE_NONE: 18684e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti return; 1875526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti case NOTIFY_TYPE_NOTIFICATION: 1885526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti showNotification(fromNai, toNai); 1895526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti break; 1905526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti case NOTIFY_TYPE_TOAST: 1915526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti mNotifier.showToast(fromNai, toNai); 1925526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti break; 1935526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti default: 1945526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti Log.e(TAG, "Unknown notify type " + notifyType); 19584e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti return; 1965526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 1975526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 19884e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti if (DBG) { 19984e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti Log.d(TAG, "Notifying switch from=" + fromNai.name() + " to=" + toNai.name() + 20084e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti " type=" + sNotifyTypeNames.get(notifyType, "unknown(" + notifyType + ")")); 2015526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 2025526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 20384e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti mNotifications.put(fromNai.network.netId, toNai.network.netId); 20484e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti mEverNotified.put(fromNai.network.netId, true); 2055526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 2065526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 2075526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // The default network changed from fromNai to toNai due to a change in score. 2085526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti public void noteLingerDefaultNetwork(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) { 2095526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti if (VDBG) { 2105526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti Log.d(TAG, "noteLingerDefaultNetwork from=" + fromNai.name() + 2115526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti " everValidated=" + fromNai.everValidated + 2125526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti " lastValidated=" + fromNai.lastValidated + 2135526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti " to=" + toNai.name()); 2145526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 2155526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 2165526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // If we are currently notifying the user because the device switched to fromNai, now that 2175526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // we are switching away from it we should remove the notification. This includes the case 2185526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // where we switch back to toNai because its score improved again (e.g., because it regained 2195526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // Internet access). 2205526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti maybeStopNotifying(fromNai); 2215526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 2225526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // If this network never validated, don't notify. Otherwise, we could do things like: 2235526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // 2245526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // 1. Unvalidated wifi connects. 2255526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // 2. Unvalidated mobile data connects. 2265526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // 3. Cell validates, and we show a notification. 2275526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // or: 2285526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // 1. User connects to wireless printer. 2295526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // 2. User turns on cellular data. 2305526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // 3. We show a notification. 2315526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti if (!fromNai.everValidated) return; 2325526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 2335526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // If this network is a captive portal, don't notify. This cannot happen on initial connect 2345526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // to a captive portal, because the everValidated check above will fail. However, it can 2355526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // happen if the captive portal reasserts itself (e.g., because its timeout fires). In that 2365526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // case, as soon as the captive portal reasserts itself, we'll show a sign-in notification. 2375526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // We don't want to overwrite that notification with this one; the user has already been 2385526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // notified, and of the two, the captive portal notification is the more useful one because 2395526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // it allows the user to sign in to the captive portal. In this case, display a toast 2405526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // in addition to the captive portal notification. 2415526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // 2425526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // Note that if the network we switch to is already up when the captive portal reappears, 2435526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // this won't work because NetworkMonitor tells ConnectivityService that the network is 2445526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // unvalidated (causing a switch) before asking it to show the sign in notification. In this 2455526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // case, the toast won't show and we'll only display the sign in notification. This is the 2465526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // best we can do at this time. 2475526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti boolean forceToast = fromNai.networkCapabilities.hasCapability( 2485526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL); 2495526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 2505526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // Only show the notification once, in order to avoid irritating the user every time. 2515526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // TODO: should we do this? 2525526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti if (everNotified(fromNai)) { 2535526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti if (VDBG) { 2545526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti Log.d(TAG, "Not notifying handover from " + fromNai.name() + ", already notified"); 2555526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 2565526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti return; 2575526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 2585526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 259c2e10bb0741446bb222809658a81cbe49dfa89fbLorenzo Colitti // Only show the notification if we switched away because a network became unvalidated, not 260c2e10bb0741446bb222809658a81cbe49dfa89fbLorenzo Colitti // because its score changed. 261c2e10bb0741446bb222809658a81cbe49dfa89fbLorenzo Colitti // TODO: instead of just skipping notification, keep a note of it, and show it if it becomes 262c2e10bb0741446bb222809658a81cbe49dfa89fbLorenzo Colitti // unvalidated. 263c2e10bb0741446bb222809658a81cbe49dfa89fbLorenzo Colitti if (fromNai.lastValidated) return; 264c2e10bb0741446bb222809658a81cbe49dfa89fbLorenzo Colitti 26584e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti if (!isNotificationEnabled(fromNai, toNai)) return; 26684e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti 26784e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti final long now = SystemClock.elapsedRealtime(); 26884e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti if (isRateLimited(now) || isAboveDailyLimit(now)) return; 26984e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti 27084e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti notify(fromNai, toNai, forceToast); 2715526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 2725526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti 2735526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti public void noteDisconnect(NetworkAgentInfo nai) { 2745526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti mNotifications.delete(nai.network.netId); 2755526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti mEverNotified.delete(nai.network.netId); 2765526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti maybeStopNotifying(nai); 2775526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti // No need to cancel notifications on nai: NetworkMonitor does that on disconnect. 2785526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti } 27984e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti 28084e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti private boolean isRateLimited(long now) { 28184e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti final long millisSinceLast = now - mLastNotificationMillis; 28284e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti if (millisSinceLast < mRateLimitMillis) { 28384e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti return true; 28484e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti } 28584e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti mLastNotificationMillis = now; 28684e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti return false; 28784e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti } 28884e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti 28984e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti private boolean isAboveDailyLimit(long now) { 29084e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti if (mFirstNotificationMillis == 0) { 29184e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti mFirstNotificationMillis = now; 29284e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti } 29384e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti final long millisSinceFirst = now - mFirstNotificationMillis; 29484e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti if (millisSinceFirst > DateUtils.DAY_IN_MILLIS) { 29584e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti mNotificationCounter = 0; 29684e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti mFirstNotificationMillis = 0; 29784e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti } 29884e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti if (mNotificationCounter >= mDailyLimit) { 29984e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti return true; 30084e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti } 30184e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti mNotificationCounter++; 30284e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti return false; 30384e6f1232c92988617a1155fd7356f90e6c84b4eLorenzo Colitti } 3045526f9c78c6fcad500c272ef14b7403e3395525eLorenzo Colitti} 305