blob: f7b01be48d887ea8b26e08d69eab7d37f432360c [file] [log] [blame]
Lorenzo Colittif3ae2ee2016-08-22 16:30:00 +09001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.connectivity;
18
19import android.app.Notification;
20import android.app.NotificationManager;
21import android.app.PendingIntent;
Lorenzo Colitti5526f9c2016-08-22 16:46:40 +090022import android.widget.Toast;
Lorenzo Colittif3ae2ee2016-08-22 16:30:00 +090023import android.content.Context;
Lorenzo Colitti0b599062016-08-22 22:36:19 +090024import android.content.Intent;
Lorenzo Colittif3ae2ee2016-08-22 16:30:00 +090025import android.content.res.Resources;
Lorenzo Colitti0b599062016-08-22 22:36:19 +090026import android.net.NetworkCapabilities;
Lorenzo Colittif3ae2ee2016-08-22 16:30:00 +090027import android.os.UserHandle;
28import android.telephony.TelephonyManager;
29import android.util.Slog;
30
31import com.android.internal.R;
32
Lorenzo Colitti0b599062016-08-22 22:36:19 +090033import static android.net.NetworkCapabilities.*;
Lorenzo Colittif3ae2ee2016-08-22 16:30:00 +090034
35
36public class NetworkNotificationManager {
37
Lorenzo Colitti9be58c52016-09-15 14:02:29 +090038 public static enum NotificationType { SIGN_IN, NO_INTERNET, LOST_INTERNET, NETWORK_SWITCH };
Lorenzo Colittif3ae2ee2016-08-22 16:30:00 +090039
Lorenzo Colitti5526f9c2016-08-22 16:46:40 +090040 private static final String NOTIFICATION_ID = "Connectivity.Notification";
Lorenzo Colittif3ae2ee2016-08-22 16:30:00 +090041
42 private static final String TAG = NetworkNotificationManager.class.getSimpleName();
43 private static final boolean DBG = true;
44 private static final boolean VDBG = false;
45
46 private final Context mContext;
47 private final TelephonyManager mTelephonyManager;
Lorenzo Colitti0b599062016-08-22 22:36:19 +090048 private final NotificationManager mNotificationManager;
Lorenzo Colittif3ae2ee2016-08-22 16:30:00 +090049
Lorenzo Colitti0b599062016-08-22 22:36:19 +090050 public NetworkNotificationManager(Context c, TelephonyManager t, NotificationManager n) {
51 mContext = c;
52 mTelephonyManager = t;
53 mNotificationManager = n;
54 }
55
56 // TODO: deal more gracefully with multi-transport networks.
57 private static int getFirstTransportType(NetworkAgentInfo nai) {
58 for (int i = 0; i < 64; i++) {
59 if (nai.networkCapabilities.hasTransport(i)) return i;
60 }
61 return -1;
62 }
63
64 private static String getTransportName(int transportType) {
65 Resources r = Resources.getSystem();
66 String[] networkTypes = r.getStringArray(R.array.network_switch_type_name);
67 try {
68 return networkTypes[transportType];
69 } catch (IndexOutOfBoundsException e) {
70 return r.getString(R.string.network_switch_type_name_unknown);
71 }
72 }
73
74 private static int getIcon(int transportType) {
75 return (transportType == TRANSPORT_WIFI) ?
76 R.drawable.stat_notify_wifi_in_range : // TODO: Distinguish ! from ?.
77 R.drawable.stat_notify_rssi_in_range;
Lorenzo Colittif3ae2ee2016-08-22 16:30:00 +090078 }
79
80 /**
81 * Show or hide network provisioning notifications.
82 *
83 * We use notifications for two purposes: to notify that a network requires sign in
84 * (NotificationType.SIGN_IN), or to notify that a network does not have Internet access
85 * (NotificationType.NO_INTERNET). We display at most one notification per ID, so on a
86 * particular network we can display the notification type that was most recently requested.
87 * So for example if a captive portal fails to reply within a few seconds of connecting, we
88 * might first display NO_INTERNET, and then when the captive portal check completes, display
89 * SIGN_IN.
90 *
91 * @param id an identifier that uniquely identifies this notification. This must match
92 * between show and hide calls. We use the NetID value but for legacy callers
93 * we concatenate the range of types with the range of NetIDs.
Lorenzo Colitti9be58c52016-09-15 14:02:29 +090094 * @param nai the network with which the notification is associated. For a SIGN_IN, NO_INTERNET,
95 * or LOST_INTERNET notification, this is the network we're connecting to. For a
Lorenzo Colitti5526f9c2016-08-22 16:46:40 +090096 * NETWORK_SWITCH notification it's the network that we switched from. When this network
97 * disconnects the notification is removed.
98 * @param switchToNai for a NETWORK_SWITCH notification, the network we are switching to. Null
99 * in all other cases. Only used to determine the text of the notification.
Lorenzo Colittif3ae2ee2016-08-22 16:30:00 +0900100 */
Lorenzo Colitti5526f9c2016-08-22 16:46:40 +0900101 public void showNotification(int id, NotificationType notifyType, NetworkAgentInfo nai,
102 NetworkAgentInfo switchToNai, PendingIntent intent, boolean highPriority) {
Lorenzo Colitti0b599062016-08-22 22:36:19 +0900103 int transportType;
104 String extraInfo;
105 if (nai != null) {
106 transportType = getFirstTransportType(nai);
107 extraInfo = nai.networkInfo.getExtraInfo();
108 // Only notify for Internet-capable networks.
109 if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)) return;
110 } else {
111 // Legacy notifications.
112 transportType = TRANSPORT_CELLULAR;
113 extraInfo = null;
114 }
115
116 if (DBG) {
117 Slog.d(TAG, "showNotification " + notifyType
118 + " transportType=" + getTransportName(transportType)
Lorenzo Colittif3ae2ee2016-08-22 16:30:00 +0900119 + " extraInfo=" + extraInfo + " highPriority=" + highPriority);
120 }
121
122 Resources r = Resources.getSystem();
Lorenzo Colitti0b599062016-08-22 22:36:19 +0900123 CharSequence title;
124 CharSequence details;
125 int icon = getIcon(transportType);
126 if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
127 title = r.getString(R.string.wifi_no_internet, 0);
128 details = r.getString(R.string.wifi_no_internet_detailed);
Lorenzo Colitti9be58c52016-09-15 14:02:29 +0900129 } else if (notifyType == NotificationType.LOST_INTERNET &&
130 transportType == TRANSPORT_WIFI) {
131 title = r.getString(R.string.wifi_no_internet, 0);
132 details = r.getString(R.string.wifi_no_internet_detailed);
Lorenzo Colitti0b599062016-08-22 22:36:19 +0900133 } else if (notifyType == NotificationType.SIGN_IN) {
134 switch (transportType) {
135 case TRANSPORT_WIFI:
136 title = r.getString(R.string.wifi_available_sign_in, 0);
137 details = r.getString(R.string.network_available_sign_in_detailed, extraInfo);
138 break;
139 case TRANSPORT_CELLULAR:
140 title = r.getString(R.string.network_available_sign_in, 0);
141 // TODO: Change this to pull from NetworkInfo once a printable
142 // name has been added to it
143 details = mTelephonyManager.getNetworkOperatorName();
144 break;
145 default:
146 title = r.getString(R.string.network_available_sign_in, 0);
147 details = r.getString(R.string.network_available_sign_in_detailed, extraInfo);
148 break;
Lorenzo Colittif3ae2ee2016-08-22 16:30:00 +0900149 }
Lorenzo Colitti5526f9c2016-08-22 16:46:40 +0900150 } else if (notifyType == NotificationType.NETWORK_SWITCH) {
151 String fromTransport = getTransportName(transportType);
152 String toTransport = getTransportName(getFirstTransportType(switchToNai));
153 title = r.getString(R.string.network_switch_metered, toTransport);
154 details = r.getString(R.string.network_switch_metered_detail, toTransport,
155 fromTransport);
Lorenzo Colittif3ae2ee2016-08-22 16:30:00 +0900156 } else {
Lorenzo Colitti0b599062016-08-22 22:36:19 +0900157 Slog.wtf(TAG, "Unknown notification type " + notifyType + "on network transport "
158 + getTransportName(transportType));
159 return;
160 }
161
Lorenzo Colitti5526f9c2016-08-22 16:46:40 +0900162 Notification.Builder builder = new Notification.Builder(mContext)
163 .setWhen(System.currentTimeMillis())
164 .setShowWhen(notifyType == NotificationType.NETWORK_SWITCH)
Lorenzo Colitti0b599062016-08-22 22:36:19 +0900165 .setSmallIcon(icon)
166 .setAutoCancel(true)
167 .setTicker(title)
168 .setColor(mContext.getColor(
169 com.android.internal.R.color.system_notification_accent_color))
170 .setContentTitle(title)
Lorenzo Colitti0b599062016-08-22 22:36:19 +0900171 .setContentIntent(intent)
172 .setLocalOnly(true)
173 .setPriority(highPriority ?
174 Notification.PRIORITY_HIGH :
175 Notification.PRIORITY_DEFAULT)
176 .setDefaults(highPriority ? Notification.DEFAULT_ALL : 0)
Lorenzo Colitti5526f9c2016-08-22 16:46:40 +0900177 .setOnlyAlertOnce(true);
178
179 if (notifyType == NotificationType.NETWORK_SWITCH) {
180 builder.setStyle(new Notification.BigTextStyle().bigText(details));
181 } else {
182 builder.setContentText(details);
183 }
184
185 Notification notification = builder.build();
Lorenzo Colitti0b599062016-08-22 22:36:19 +0900186
187 try {
188 mNotificationManager.notifyAsUser(NOTIFICATION_ID, id, notification, UserHandle.ALL);
189 } catch (NullPointerException npe) {
190 Slog.d(TAG, "setNotificationVisible: visible notificationManager npe=" + npe);
191 }
192 }
193
194 public void clearNotification(int id) {
195 if (DBG) {
196 Slog.d(TAG, "clearNotification id=" + id);
197 }
198 try {
199 mNotificationManager.cancelAsUser(NOTIFICATION_ID, id, UserHandle.ALL);
200 } catch (NullPointerException npe) {
201 Slog.d(TAG, "setNotificationVisible: cancel notificationManager npe=" + npe);
Lorenzo Colittif3ae2ee2016-08-22 16:30:00 +0900202 }
203 }
204
205 /**
206 * Legacy provisioning notifications coming directly from DcTracker.
207 */
Lorenzo Colitti0b599062016-08-22 22:36:19 +0900208 public void setProvNotificationVisible(boolean visible, int id, String action) {
209 if (visible) {
210 Intent intent = new Intent(action);
211 PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
Lorenzo Colitti5526f9c2016-08-22 16:46:40 +0900212 showNotification(id, NotificationType.SIGN_IN, null, null, pendingIntent, false);
Lorenzo Colitti0b599062016-08-22 22:36:19 +0900213 } else {
214 clearNotification(id);
215 }
Lorenzo Colittif3ae2ee2016-08-22 16:30:00 +0900216 }
Lorenzo Colitti5526f9c2016-08-22 16:46:40 +0900217
218 public void showToast(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
219 String fromTransport = getTransportName(getFirstTransportType(fromNai));
220 String toTransport = getTransportName(getFirstTransportType(toNai));
221 String text = mContext.getResources().getString(
222 R.string.network_switch_metered_toast, fromTransport, toTransport);
223 Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
224 }
Lorenzo Colittif3ae2ee2016-08-22 16:30:00 +0900225}