blob: 4f81dafe7305b5830f995b5ff5bfaff8079bad79 [file] [log] [blame]
Jason Monkd52356a2015-01-28 10:40:41 -05001/*
2 * Copyright (C) 2015 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.settingslib.wifi;
18
Sundeep Ghuman53200ed2017-06-21 16:54:36 -070019import android.annotation.IntDef;
Sundeep Ghumanbb399912018-01-29 18:31:15 -080020import android.annotation.MainThread;
Sundeep Ghuman271e5de2017-05-30 14:11:39 -070021import android.annotation.Nullable;
Jason Monk6980d122015-06-15 10:07:55 -040022import android.app.AppGlobals;
Jason Monkd52356a2015-01-28 10:40:41 -050023import android.content.Context;
Jason Monk6980d122015-06-15 10:07:55 -040024import android.content.pm.ApplicationInfo;
25import android.content.pm.IPackageManager;
26import android.content.pm.PackageManager;
Sanket Padawe7094d222015-05-01 16:55:00 -070027import android.net.ConnectivityManager;
Sanket Padawe7094d222015-05-01 16:55:00 -070028import android.net.NetworkCapabilities;
Jason Monkd52356a2015-01-28 10:40:41 -050029import android.net.NetworkInfo;
30import android.net.NetworkInfo.DetailedState;
31import android.net.NetworkInfo.State;
Sundeep Ghumana28050a2017-07-12 22:09:25 -070032import android.net.NetworkKey;
Stephen Chen36dd5cf12017-03-20 13:27:51 -070033import android.net.NetworkScoreManager;
34import android.net.NetworkScorerAppData;
Sundeep Ghuman5519b7b2016-12-14 17:53:31 -080035import android.net.ScoredNetwork;
Sanket Padawe7094d222015-05-01 16:55:00 -070036import android.net.wifi.IWifiManager;
Jason Monkd52356a2015-01-28 10:40:41 -050037import android.net.wifi.ScanResult;
38import android.net.wifi.WifiConfiguration;
39import android.net.wifi.WifiConfiguration.KeyMgmt;
Peter Qiu280581b2017-07-24 14:18:56 -070040import android.net.wifi.WifiEnterpriseConfig;
Jason Monkd52356a2015-01-28 10:40:41 -050041import android.net.wifi.WifiInfo;
42import android.net.wifi.WifiManager;
Sundeep Ghuman5519b7b2016-12-14 17:53:31 -080043import android.net.wifi.WifiNetworkScoreCache;
Peter Qiuced37db2017-03-14 15:51:22 -070044import android.net.wifi.hotspot2.PasspointConfiguration;
Jason Monkd52356a2015-01-28 10:40:41 -050045import android.os.Bundle;
Sundeep Ghuman04f7f342018-01-23 19:18:31 -080046import android.os.Parcelable;
Sanket Padawe7094d222015-05-01 16:55:00 -070047import android.os.RemoteException;
48import android.os.ServiceManager;
Mitchell Wills18af4932016-08-10 13:49:21 -070049import android.os.SystemClock;
Jason Monk6980d122015-06-15 10:07:55 -040050import android.os.UserHandle;
Jason Monk6980d122015-06-15 10:07:55 -040051import android.text.TextUtils;
Sundeep Ghuman04f7f342018-01-23 19:18:31 -080052import android.util.ArraySet;
Jason Monkd52356a2015-01-28 10:40:41 -050053import android.util.Log;
Jason Monkd52356a2015-01-28 10:40:41 -050054
Fan Zhangf7802ea2018-08-28 15:15:19 -070055import androidx.annotation.NonNull;
56
Sundeep Ghuman54bdcfa2017-03-08 19:52:05 -080057import com.android.internal.annotations.VisibleForTesting;
Jason Monkd52356a2015-01-28 10:40:41 -050058import com.android.settingslib.R;
Sundeep Ghumanbb399912018-01-29 18:31:15 -080059import com.android.settingslib.utils.ThreadUtils;
Jason Monkd52356a2015-01-28 10:40:41 -050060
Sundeep Ghuman53200ed2017-06-21 16:54:36 -070061import java.lang.annotation.Retention;
62import java.lang.annotation.RetentionPolicy;
Vinit Deshpandefcd46122015-06-11 18:22:23 -070063import java.util.ArrayList;
Sundeep Ghuman04f7f342018-01-23 19:18:31 -080064import java.util.Collection;
Eric Schwarzenbach0b8700f2017-07-25 14:32:21 -070065import java.util.HashMap;
Mitchell Wills18af4932016-08-10 13:49:21 -070066import java.util.Iterator;
Eric Schwarzenbach0b8700f2017-07-25 14:32:21 -070067import java.util.Map;
Sundeep Ghuman04f7f342018-01-23 19:18:31 -080068import java.util.Set;
Ajay Nadathurd7b689a2016-08-31 15:07:56 -070069import java.util.concurrent.atomic.AtomicInteger;
Jason Monkd52356a2015-01-28 10:40:41 -050070
Sundeep Ghumanbb399912018-01-29 18:31:15 -080071/**
72 * Represents a selectable Wifi Network for use in various wifi selection menus backed by
73 * {@link WifiTracker}.
74 *
75 * <p>An AccessPoint, which would be more fittingly named "WifiNetwork", is an aggregation of
76 * {@link ScanResult ScanResults} along with pertinent metadata (e.g. current connection info,
77 * network scores) required to successfully render the network to the user.
78 */
Jason Monkd52356a2015-01-28 10:40:41 -050079public class AccessPoint implements Comparable<AccessPoint> {
80 static final String TAG = "SettingsLib.AccessPoint";
81
82 /**
83 * Lower bound on the 2.4 GHz (802.11b/g/n) WLAN channels
84 */
85 public static final int LOWER_FREQ_24GHZ = 2400;
86
87 /**
88 * Upper bound on the 2.4 GHz (802.11b/g/n) WLAN channels
89 */
90 public static final int HIGHER_FREQ_24GHZ = 2500;
91
92 /**
93 * Lower bound on the 5.0 GHz (802.11a/h/j/n/ac) WLAN channels
94 */
95 public static final int LOWER_FREQ_5GHZ = 4900;
96
97 /**
98 * Upper bound on the 5.0 GHz (802.11a/h/j/n/ac) WLAN channels
99 */
100 public static final int HIGHER_FREQ_5GHZ = 5900;
101
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800102 /** The key which identifies this AccessPoint grouping. */
103 private String mKey;
104
Sundeep Ghuman53200ed2017-06-21 16:54:36 -0700105 @IntDef({Speed.NONE, Speed.SLOW, Speed.MODERATE, Speed.FAST, Speed.VERY_FAST})
106 @Retention(RetentionPolicy.SOURCE)
107 public @interface Speed {
108 /**
109 * Constant value representing an unlabeled / unscored network.
110 */
111 int NONE = 0;
112 /**
113 * Constant value representing a slow speed network connection.
114 */
115 int SLOW = 5;
116 /**
117 * Constant value representing a medium speed network connection.
118 */
119 int MODERATE = 10;
120 /**
121 * Constant value representing a fast speed network connection.
122 */
123 int FAST = 20;
124 /**
125 * Constant value representing a very fast speed network connection.
126 */
127 int VERY_FAST = 30;
128 }
Jason Monkd52356a2015-01-28 10:40:41 -0500129
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800130 /** The underlying set of scan results comprising this AccessPoint. */
131 private final ArraySet<ScanResult> mScanResults = new ArraySet<>();
Eric Schwarzenbach0b8700f2017-07-25 14:32:21 -0700132
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -0700133 /**
134 * Map of BSSIDs to scored networks for individual bssids.
135 *
136 * <p>This cache should not be evicted with scan results, as the values here are used to
137 * generate a fallback in the absence of scores for the visible APs.
138 */
Sundeep Ghumanfedf4612017-08-15 15:47:18 -0700139 private final Map<String, TimestampedScoredNetwork> mScoredNetworkCache = new HashMap<>();
140
Dave Schaefer98537432017-02-08 11:26:08 -0800141 static final String KEY_NETWORKINFO = "key_networkinfo";
142 static final String KEY_WIFIINFO = "key_wifiinfo";
Dave Schaefer98537432017-02-08 11:26:08 -0800143 static final String KEY_SSID = "key_ssid";
144 static final String KEY_SECURITY = "key_security";
Sundeep Ghumana28050a2017-07-12 22:09:25 -0700145 static final String KEY_SPEED = "key_speed";
Dave Schaefer98537432017-02-08 11:26:08 -0800146 static final String KEY_PSKTYPE = "key_psktype";
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800147 static final String KEY_SCANRESULTS = "key_scanresults";
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -0700148 static final String KEY_SCOREDNETWORKCACHE = "key_scorednetworkcache";
Dave Schaefer98537432017-02-08 11:26:08 -0800149 static final String KEY_CONFIG = "key_config";
Peter Qiuced37db2017-03-14 15:51:22 -0700150 static final String KEY_FQDN = "key_fqdn";
151 static final String KEY_PROVIDER_FRIENDLY_NAME = "key_provider_friendly_name";
Peter Qiu280581b2017-07-24 14:18:56 -0700152 static final String KEY_IS_CARRIER_AP = "key_is_carrier_ap";
153 static final String KEY_CARRIER_AP_EAP_TYPE = "key_carrier_ap_eap_type";
154 static final String KEY_CARRIER_NAME = "key_carrier_name";
Dave Schaefer98537432017-02-08 11:26:08 -0800155 static final AtomicInteger sLastId = new AtomicInteger(0);
Jason Monkd52356a2015-01-28 10:40:41 -0500156
Maurice Lam7cdbe192018-11-13 13:52:50 -0800157 /*
158 * NOTE: These constants for security and PSK types are saved to the bundle in saveWifiState,
159 * and sent across IPC. The numeric values should remain stable, otherwise the changes will need
160 * to be synced with other unbundled users of this library.
Jason Monkd52356a2015-01-28 10:40:41 -0500161 */
162 public static final int SECURITY_NONE = 0;
Hai Shalom9effa642018-11-12 11:43:52 -0800163 public static final int SECURITY_WEP = 1;
164 public static final int SECURITY_PSK = 2;
165 public static final int SECURITY_EAP = 3;
166 public static final int SECURITY_OWE = 4;
167 public static final int SECURITY_SAE = 5;
Hai Shalomaa6f9202018-10-16 14:30:02 -0700168 public static final int SECURITY_EAP_SUITE_B = 6;
Hai Shalom828b13c2018-11-13 16:43:14 -0800169 public static final int SECURITY_MAX_VAL = 7; // Has to be the last
Jason Monkd52356a2015-01-28 10:40:41 -0500170
171 private static final int PSK_UNKNOWN = 0;
172 private static final int PSK_WPA = 1;
173 private static final int PSK_WPA2 = 2;
174 private static final int PSK_WPA_WPA2 = 3;
175
Sundeep Ghumanaaa8a1b2017-03-13 14:40:56 -0700176 /**
177 * The number of distinct wifi levels.
178 *
179 * <p>Must keep in sync with {@link R.array.wifi_signal} and {@link WifiManager#RSSI_LEVELS}.
180 */
181 public static final int SIGNAL_LEVELS = 5;
Tony Mantlera0e03dd2016-01-27 15:57:03 -0800182
Sundeep Ghumanaaa8a1b2017-03-13 14:40:56 -0700183 public static final int UNREACHABLE_RSSI = Integer.MIN_VALUE;
Dave Schaefer98537432017-02-08 11:26:08 -0800184
Jason Monkd52356a2015-01-28 10:40:41 -0500185 private final Context mContext;
186
187 private String ssid;
Jason Monk60a82ff2016-02-25 13:55:03 -0500188 private String bssid;
Jason Monkd52356a2015-01-28 10:40:41 -0500189 private int security;
190 private int networkId = WifiConfiguration.INVALID_NETWORK_ID;
191
192 private int pskType = PSK_UNKNOWN;
193
194 private WifiConfiguration mConfig;
Jason Monkd52356a2015-01-28 10:40:41 -0500195
Dave Schaefer98537432017-02-08 11:26:08 -0800196 private int mRssi = UNREACHABLE_RSSI;
Jason Monkd52356a2015-01-28 10:40:41 -0500197
198 private WifiInfo mInfo;
199 private NetworkInfo mNetworkInfo;
Ajay Nadathurd7b689a2016-08-31 15:07:56 -0700200 AccessPointListener mAccessPointListener;
Jason Monkd52356a2015-01-28 10:40:41 -0500201
202 private Object mTag;
203
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -0700204 @Speed private int mSpeed = Speed.NONE;
Stephen Chen21f68682017-04-04 13:23:31 -0700205 private boolean mIsScoredNetworkMetered = false;
Sundeep Ghuman5519b7b2016-12-14 17:53:31 -0800206
Ajay Nadathurd7b689a2016-08-31 15:07:56 -0700207 // used to co-relate internal vs returned accesspoint.
208 int mId;
209
Peter Qiuced37db2017-03-14 15:51:22 -0700210 /**
211 * Information associated with the {@link PasspointConfiguration}. Only maintaining
212 * the relevant info to preserve spaces.
213 */
214 private String mFqdn;
215 private String mProviderFriendlyName;
216
Peter Qiu280581b2017-07-24 14:18:56 -0700217 private boolean mIsCarrierAp = false;
218 /**
219 * The EAP type {@link WifiEnterpriseConfig.Eap} associated with this AP if it is a carrier AP.
220 */
221 private int mCarrierApEapType = WifiEnterpriseConfig.Eap.NONE;
222 private String mCarrierName = null;
223
Jason Monkd52356a2015-01-28 10:40:41 -0500224 public AccessPoint(Context context, Bundle savedState) {
225 mContext = context;
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800226
227 if (savedState.containsKey(KEY_CONFIG)) {
228 mConfig = savedState.getParcelable(KEY_CONFIG);
229 }
Jason Monkd52356a2015-01-28 10:40:41 -0500230 if (mConfig != null) {
231 loadConfig(mConfig);
232 }
Vinit Deshpandefcd46122015-06-11 18:22:23 -0700233 if (savedState.containsKey(KEY_SSID)) {
234 ssid = savedState.getString(KEY_SSID);
235 }
236 if (savedState.containsKey(KEY_SECURITY)) {
237 security = savedState.getInt(KEY_SECURITY);
238 }
Sundeep Ghumana28050a2017-07-12 22:09:25 -0700239 if (savedState.containsKey(KEY_SPEED)) {
240 mSpeed = savedState.getInt(KEY_SPEED);
241 }
Vinit Deshpandefcd46122015-06-11 18:22:23 -0700242 if (savedState.containsKey(KEY_PSKTYPE)) {
243 pskType = savedState.getInt(KEY_PSKTYPE);
Jason Monkd52356a2015-01-28 10:40:41 -0500244 }
Sundeep Ghumana28050a2017-07-12 22:09:25 -0700245 mInfo = savedState.getParcelable(KEY_WIFIINFO);
Jason Monkd52356a2015-01-28 10:40:41 -0500246 if (savedState.containsKey(KEY_NETWORKINFO)) {
247 mNetworkInfo = savedState.getParcelable(KEY_NETWORKINFO);
248 }
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800249 if (savedState.containsKey(KEY_SCANRESULTS)) {
250 Parcelable[] scanResults = savedState.getParcelableArray(KEY_SCANRESULTS);
251 mScanResults.clear();
252 for (Parcelable result : scanResults) {
253 mScanResults.add((ScanResult) result);
Vinit Deshpandefcd46122015-06-11 18:22:23 -0700254 }
255 }
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -0700256 if (savedState.containsKey(KEY_SCOREDNETWORKCACHE)) {
Sundeep Ghumanfedf4612017-08-15 15:47:18 -0700257 ArrayList<TimestampedScoredNetwork> scoredNetworkArrayList =
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -0700258 savedState.getParcelableArrayList(KEY_SCOREDNETWORKCACHE);
Sundeep Ghumanfedf4612017-08-15 15:47:18 -0700259 for (TimestampedScoredNetwork timedScore : scoredNetworkArrayList) {
260 mScoredNetworkCache.put(timedScore.getScore().networkKey.wifiKey.bssid, timedScore);
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -0700261 }
262 }
Peter Qiuced37db2017-03-14 15:51:22 -0700263 if (savedState.containsKey(KEY_FQDN)) {
264 mFqdn = savedState.getString(KEY_FQDN);
265 }
266 if (savedState.containsKey(KEY_PROVIDER_FRIENDLY_NAME)) {
267 mProviderFriendlyName = savedState.getString(KEY_PROVIDER_FRIENDLY_NAME);
268 }
Peter Qiu280581b2017-07-24 14:18:56 -0700269 if (savedState.containsKey(KEY_IS_CARRIER_AP)) {
270 mIsCarrierAp = savedState.getBoolean(KEY_IS_CARRIER_AP);
271 }
272 if (savedState.containsKey(KEY_CARRIER_AP_EAP_TYPE)) {
273 mCarrierApEapType = savedState.getInt(KEY_CARRIER_AP_EAP_TYPE);
274 }
275 if (savedState.containsKey(KEY_CARRIER_NAME)) {
276 mCarrierName = savedState.getString(KEY_CARRIER_NAME);
277 }
Mitchell Wills5a42db22015-08-03 09:46:08 -0700278 update(mConfig, mInfo, mNetworkInfo);
Sundeep Ghumand1e44922017-08-07 11:21:38 -0700279
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800280 // Calculate required fields
281 updateKey();
Sundeep Ghuman54bdcfa2017-03-08 19:52:05 -0800282 updateRssi();
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800283
Ajay Nadathurd7b689a2016-08-31 15:07:56 -0700284 mId = sLastId.incrementAndGet();
Jason Monkd52356a2015-01-28 10:40:41 -0500285 }
286
Peter Qiuced37db2017-03-14 15:51:22 -0700287 public AccessPoint(Context context, WifiConfiguration config) {
Jason Monkd52356a2015-01-28 10:40:41 -0500288 mContext = context;
Peter Qiuced37db2017-03-14 15:51:22 -0700289 loadConfig(config);
Ajay Nadathurd7b689a2016-08-31 15:07:56 -0700290 mId = sLastId.incrementAndGet();
Jason Monkd52356a2015-01-28 10:40:41 -0500291 }
292
Peter Qiuced37db2017-03-14 15:51:22 -0700293 /**
294 * Initialize an AccessPoint object for a {@link PasspointConfiguration}. This is mainly
295 * used by "Saved Networks" page for managing the saved {@link PasspointConfiguration}.
296 */
297 public AccessPoint(Context context, PasspointConfiguration config) {
Jason Monkd52356a2015-01-28 10:40:41 -0500298 mContext = context;
Peter Qiuced37db2017-03-14 15:51:22 -0700299 mFqdn = config.getHomeSp().getFqdn();
300 mProviderFriendlyName = config.getHomeSp().getFriendlyName();
Ajay Nadathurd7b689a2016-08-31 15:07:56 -0700301 mId = sLastId.incrementAndGet();
302 }
303
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800304 AccessPoint(Context context, Collection<ScanResult> results) {
Peter Qiuced37db2017-03-14 15:51:22 -0700305 mContext = context;
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800306
307 if (results.isEmpty()) {
308 throw new IllegalArgumentException("Cannot construct with an empty ScanResult list");
309 }
310 mScanResults.addAll(results);
311
312 // Information derived from scan results
313 ScanResult firstResult = results.iterator().next();
314 ssid = firstResult.SSID;
315 bssid = firstResult.BSSID;
316 security = getSecurity(firstResult);
317 if (security == SECURITY_PSK) {
318 pskType = getPskType(firstResult);
319 }
320 updateKey();
321 updateRssi();
322
323 // Passpoint Info
324 mIsCarrierAp = firstResult.isCarrierAp;
325 mCarrierApEapType = firstResult.carrierApEapType;
326 mCarrierName = firstResult.carrierName;
327
Peter Qiuced37db2017-03-14 15:51:22 -0700328 mId = sLastId.incrementAndGet();
329 }
330
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800331 @VisibleForTesting void loadConfig(WifiConfiguration config) {
332 ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
333 bssid = config.BSSID;
334 security = getSecurity(config);
335 updateKey();
336 networkId = config.networkId;
337 mConfig = config;
338 }
339
340 /** Updates {@link #mKey} and should only called upon object creation/initialization. */
341 private void updateKey() {
342 // TODO(sghuman): Consolidate Key logic on ScanResultMatchInfo
343
344 StringBuilder builder = new StringBuilder();
345
346 if (TextUtils.isEmpty(getSsidStr())) {
347 builder.append(getBssid());
348 } else {
349 builder.append(getSsidStr());
350 }
351
352 builder.append(',').append(getSecurity());
353 mKey = builder.toString();
354 }
355
Ajay Nadathurd7b689a2016-08-31 15:07:56 -0700356 /**
Dave Schaefer98537432017-02-08 11:26:08 -0800357 * Returns a negative integer, zero, or a positive integer if this AccessPoint is less than,
358 * equal to, or greater than the other AccessPoint.
359 *
360 * Sort order rules for AccessPoints:
361 * 1. Active before inactive
362 * 2. Reachable before unreachable
363 * 3. Saved before unsaved
Eric Schwarzenbachf4f3842b2017-07-17 16:45:04 -0700364 * 4. Network speed value
Dave Schaefer98537432017-02-08 11:26:08 -0800365 * 5. Stronger signal before weaker signal
366 * 6. SSID alphabetically
367 *
368 * Note that AccessPoints with a signal are usually also Reachable,
369 * and will thus appear before unreachable saved AccessPoints.
370 */
Jason Monkd52356a2015-01-28 10:40:41 -0500371 @Override
Tony Mantlera0e03dd2016-01-27 15:57:03 -0800372 public int compareTo(@NonNull AccessPoint other) {
Jason Monkd52356a2015-01-28 10:40:41 -0500373 // Active one goes first.
374 if (isActive() && !other.isActive()) return -1;
375 if (!isActive() && other.isActive()) return 1;
376
377 // Reachable one goes before unreachable one.
Dave Schaefer98537432017-02-08 11:26:08 -0800378 if (isReachable() && !other.isReachable()) return -1;
379 if (!isReachable() && other.isReachable()) return 1;
Jason Monkd52356a2015-01-28 10:40:41 -0500380
Sundeep Ghuman05c41e22017-02-01 13:27:56 -0800381 // Configured (saved) one goes before unconfigured one.
Dave Schaefer98537432017-02-08 11:26:08 -0800382 if (isSaved() && !other.isSaved()) return -1;
383 if (!isSaved() && other.isSaved()) return 1;
Jason Monkd52356a2015-01-28 10:40:41 -0500384
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -0700385 // Faster speeds go before slower speeds - but only if visible change in speed label
Eric Schwarzenbachf4f3842b2017-07-17 16:45:04 -0700386 if (getSpeed() != other.getSpeed()) {
387 return other.getSpeed() - getSpeed();
Sundeep Ghuman05c41e22017-02-01 13:27:56 -0800388 }
389
Tony Mantlera0e03dd2016-01-27 15:57:03 -0800390 // Sort by signal strength, bucketed by level
391 int difference = WifiManager.calculateSignalLevel(other.mRssi, SIGNAL_LEVELS)
392 - WifiManager.calculateSignalLevel(mRssi, SIGNAL_LEVELS);
Jason Monkd52356a2015-01-28 10:40:41 -0500393 if (difference != 0) {
394 return difference;
395 }
Sundeep Ghumand44b8e42017-06-21 21:40:06 -0700396
Jason Monkd52356a2015-01-28 10:40:41 -0500397 // Sort by ssid.
Sundeep Ghumand44b8e42017-06-21 21:40:06 -0700398 difference = getSsidStr().compareToIgnoreCase(other.getSsidStr());
399 if (difference != 0) {
400 return difference;
401 }
402
403 // Do a case sensitive comparison to distinguish SSIDs that differ in case only
404 return getSsidStr().compareTo(other.getSsidStr());
Jason Monkd52356a2015-01-28 10:40:41 -0500405 }
406
407 @Override
408 public boolean equals(Object other) {
409 if (!(other instanceof AccessPoint)) return false;
410 return (this.compareTo((AccessPoint) other) == 0);
411 }
412
413 @Override
414 public int hashCode() {
415 int result = 0;
416 if (mInfo != null) result += 13 * mInfo.hashCode();
417 result += 19 * mRssi;
418 result += 23 * networkId;
419 result += 29 * ssid.hashCode();
420 return result;
421 }
422
423 @Override
424 public String toString() {
425 StringBuilder builder = new StringBuilder().append("AccessPoint(")
426 .append(ssid);
Sundeep Ghuman2b489902017-02-22 18:17:29 -0800427 if (bssid != null) {
428 builder.append(":").append(bssid);
429 }
Jason Monkd52356a2015-01-28 10:40:41 -0500430 if (isSaved()) {
431 builder.append(',').append("saved");
432 }
433 if (isActive()) {
434 builder.append(',').append("active");
435 }
436 if (isEphemeral()) {
437 builder.append(',').append("ephemeral");
438 }
439 if (isConnectable()) {
440 builder.append(',').append("connectable");
441 }
Hai Shalomaa6f9202018-10-16 14:30:02 -0700442 if ((security != SECURITY_NONE) && (security != SECURITY_OWE)) {
Jason Monkd52356a2015-01-28 10:40:41 -0500443 builder.append(',').append(securityToString(security, pskType));
444 }
Sundeep Ghuman2b489902017-02-22 18:17:29 -0800445 builder.append(",level=").append(getLevel());
Sundeep Ghuman53200ed2017-06-21 16:54:36 -0700446 if (mSpeed != Speed.NONE) {
Sundeep Ghuman55adc6b2017-06-05 16:46:59 -0700447 builder.append(",speed=").append(mSpeed);
Sundeep Ghuman8920e9c2017-04-27 16:16:41 -0700448 }
Stephen Chen21f68682017-04-04 13:23:31 -0700449 builder.append(",metered=").append(isMetered());
Sundeep Ghuman5519b7b2016-12-14 17:53:31 -0800450
Sundeep Ghumanc0cf8482018-01-26 18:23:34 -0800451 if (isVerboseLoggingEnabled()) {
Sundeep Ghumandb9b94c2017-09-06 11:46:21 -0700452 builder.append(",rssi=").append(mRssi);
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800453 builder.append(",scan cache size=").append(mScanResults.size());
Sundeep Ghumandb9b94c2017-09-06 11:46:21 -0700454 }
455
Jason Monkd52356a2015-01-28 10:40:41 -0500456 return builder.append(')').toString();
457 }
458
Sundeep Ghuman5519b7b2016-12-14 17:53:31 -0800459 /**
Sundeep Ghuman55adc6b2017-06-05 16:46:59 -0700460 * Updates the AccessPoint rankingScore, metering, and speed, returning true if the data has
Stephen Chen21f68682017-04-04 13:23:31 -0700461 * changed.
462 *
Sundeep Ghuman9bb85d32017-08-28 17:04:16 -0700463 * @param scoreCache The score cache to use to retrieve scores
464 * @param scoringUiEnabled Whether to show scoring and badging UI
465 * @param maxScoreCacheAgeMillis the maximum age in milliseconds of scores to consider when
466 * generating speed labels
Stephen Chen21f68682017-04-04 13:23:31 -0700467 */
Sundeep Ghuman9bb85d32017-08-28 17:04:16 -0700468 boolean update(
469 WifiNetworkScoreCache scoreCache,
470 boolean scoringUiEnabled,
471 long maxScoreCacheAgeMillis) {
Stephen Chen21f68682017-04-04 13:23:31 -0700472 boolean scoreChanged = false;
473 if (scoringUiEnabled) {
Sundeep Ghuman9bb85d32017-08-28 17:04:16 -0700474 scoreChanged = updateScores(scoreCache, maxScoreCacheAgeMillis);
Stephen Chen21f68682017-04-04 13:23:31 -0700475 }
476 return updateMetered(scoreCache) || scoreChanged;
477 }
478
479 /**
Sundeep Ghuman55adc6b2017-06-05 16:46:59 -0700480 * Updates the AccessPoint rankingScore and speed, returning true if the data has changed.
Sundeep Ghuman5519b7b2016-12-14 17:53:31 -0800481 *
Sundeep Ghuman9bb85d32017-08-28 17:04:16 -0700482 * <p>Any cached {@link TimestampedScoredNetwork} objects older than the given max age in millis
483 * will be removed when this method is invoked.
Sundeep Ghumanfedf4612017-08-15 15:47:18 -0700484 *
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -0700485 * <p>Precondition: {@link #mRssi} is up to date before invoking this method.
486 *
Sundeep Ghuman9bb85d32017-08-28 17:04:16 -0700487 * @param scoreCache The score cache to use to retrieve scores
488 * @param maxScoreCacheAgeMillis the maximum age in milliseconds of scores to consider when
489 * generating speed labels
490 *
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -0700491 * @return true if the set speed has changed
Sundeep Ghuman5519b7b2016-12-14 17:53:31 -0800492 */
Sundeep Ghuman9bb85d32017-08-28 17:04:16 -0700493 private boolean updateScores(WifiNetworkScoreCache scoreCache, long maxScoreCacheAgeMillis) {
Sundeep Ghumanfedf4612017-08-15 15:47:18 -0700494 long nowMillis = SystemClock.elapsedRealtime();
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800495 for (ScanResult result : mScanResults) {
Eric Schwarzenbach0b8700f2017-07-25 14:32:21 -0700496 ScoredNetwork score = scoreCache.getScoredNetwork(result);
497 if (score == null) {
498 continue;
499 }
Sundeep Ghumanfedf4612017-08-15 15:47:18 -0700500 TimestampedScoredNetwork timedScore = mScoredNetworkCache.get(result.BSSID);
501 if (timedScore == null) {
502 mScoredNetworkCache.put(
503 result.BSSID, new TimestampedScoredNetwork(score, nowMillis));
504 } else {
505 // Update data since the has been seen in the score cache
506 timedScore.update(score, nowMillis);
507 }
Eric Schwarzenbach0b8700f2017-07-25 14:32:21 -0700508 }
509
Sundeep Ghumanfedf4612017-08-15 15:47:18 -0700510 // Remove old cached networks
Sundeep Ghuman9bb85d32017-08-28 17:04:16 -0700511 long evictionCutoff = nowMillis - maxScoreCacheAgeMillis;
Sundeep Ghumanfedf4612017-08-15 15:47:18 -0700512 Iterator<TimestampedScoredNetwork> iterator = mScoredNetworkCache.values().iterator();
513 iterator.forEachRemaining(timestampedScoredNetwork -> {
514 if (timestampedScoredNetwork.getUpdatedTimestampMillis() < evictionCutoff) {
515 iterator.remove();
516 }
517 });
518
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -0700519 return updateSpeed();
520 }
521
522 /**
523 * Updates the internal speed, returning true if the update resulted in a speed label change.
524 */
525 private boolean updateSpeed() {
526 int oldSpeed = mSpeed;
527 mSpeed = generateAverageSpeedForSsid();
528
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -0700529 boolean changed = oldSpeed != mSpeed;
Sundeep Ghumanc0cf8482018-01-26 18:23:34 -0800530 if(isVerboseLoggingEnabled() && changed) {
Sundeep Ghumana28050a2017-07-12 22:09:25 -0700531 Log.i(TAG, String.format("%s: Set speed to %d", ssid, mSpeed));
Sundeep Ghuman5519b7b2016-12-14 17:53:31 -0800532 }
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -0700533 return changed;
534 }
Sundeep Ghuman5519b7b2016-12-14 17:53:31 -0800535
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -0700536 /** Creates a speed value for the current {@link #mRssi} by averaging all non zero badges. */
537 @Speed private int generateAverageSpeedForSsid() {
538 if (mScoredNetworkCache.isEmpty()) {
539 return Speed.NONE;
540 }
541
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -0700542 if (Log.isLoggable(TAG, Log.DEBUG)) {
543 Log.d(TAG, String.format("Generating fallbackspeed for %s using cache: %s",
544 getSsidStr(), mScoredNetworkCache));
545 }
546
Sundeep Ghumana7825692017-08-23 15:53:00 -0700547 // TODO(b/63073866): If flickering issues persist, consider mapping using getLevel rather
548 // than specific rssi value so score doesn't change without a visible wifi bar change. This
549 // issue is likely to be more evident for the active AP whose RSSI value is not half-lifed.
550
Sundeep Ghumanfedf4612017-08-15 15:47:18 -0700551 int count = 0;
552 int totalSpeed = 0;
553 for (TimestampedScoredNetwork timedScore : mScoredNetworkCache.values()) {
554 int speed = timedScore.getScore().calculateBadge(mRssi);
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -0700555 if (speed != Speed.NONE) {
556 count++;
557 totalSpeed += speed;
558 }
559 }
560 int speed = count == 0 ? Speed.NONE : totalSpeed / count;
Sundeep Ghumanc0cf8482018-01-26 18:23:34 -0800561 if (isVerboseLoggingEnabled()) {
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -0700562 Log.i(TAG, String.format("%s generated fallback speed is: %d", getSsidStr(), speed));
563 }
564 return roundToClosestSpeedEnum(speed);
Sundeep Ghuman5519b7b2016-12-14 17:53:31 -0800565 }
566
Stephen Chen21f68682017-04-04 13:23:31 -0700567 /**
568 * Updates the AccessPoint's metering based on {@link ScoredNetwork#meteredHint}, returning
569 * true if the metering changed.
570 */
571 private boolean updateMetered(WifiNetworkScoreCache scoreCache) {
572 boolean oldMetering = mIsScoredNetworkMetered;
573 mIsScoredNetworkMetered = false;
Sundeep Ghumana28050a2017-07-12 22:09:25 -0700574
575 if (isActive() && mInfo != null) {
Eric Schwarzenbach733ea262017-08-08 10:28:21 -0700576 NetworkKey key = NetworkKey.createFromWifiInfo(mInfo);
Sundeep Ghumana28050a2017-07-12 22:09:25 -0700577 ScoredNetwork score = scoreCache.getScoredNetwork(key);
578 if (score != null) {
579 mIsScoredNetworkMetered |= score.meteredHint;
Stephen Chen21f68682017-04-04 13:23:31 -0700580 }
Sundeep Ghumana28050a2017-07-12 22:09:25 -0700581 } else {
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800582 for (ScanResult result : mScanResults) {
Sundeep Ghumana28050a2017-07-12 22:09:25 -0700583 ScoredNetwork score = scoreCache.getScoredNetwork(result);
584 if (score == null) {
585 continue;
586 }
587 mIsScoredNetworkMetered |= score.meteredHint;
588 }
Stephen Chen21f68682017-04-04 13:23:31 -0700589 }
590 return oldMetering == mIsScoredNetworkMetered;
591 }
592
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800593 public static String getKey(ScanResult result) {
594 StringBuilder builder = new StringBuilder();
595
596 if (TextUtils.isEmpty(result.SSID)) {
597 builder.append(result.BSSID);
598 } else {
599 builder.append(result.SSID);
Mitchell Wills18af4932016-08-10 13:49:21 -0700600 }
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800601
602 builder.append(',').append(getSecurity(result));
603 return builder.toString();
Mitchell Wills18af4932016-08-10 13:49:21 -0700604 }
605
Sundeep Ghuman0d492e82018-01-26 12:45:02 -0800606 public static String getKey(WifiConfiguration config) {
607 StringBuilder builder = new StringBuilder();
608
609 if (TextUtils.isEmpty(config.SSID)) {
610 builder.append(config.BSSID);
611 } else {
612 builder.append(removeDoubleQuotes(config.SSID));
613 }
614
615 builder.append(',').append(getSecurity(config));
616 return builder.toString();
617 }
618
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800619 public String getKey() {
620 return mKey;
Jason Monkd52356a2015-01-28 10:40:41 -0500621 }
622
623 public boolean matches(WifiConfiguration config) {
Bartosz Fabianowski6fb07562016-01-12 15:43:19 +0100624 if (config.isPasspoint() && mConfig != null && mConfig.isPasspoint()) {
Shinji Sogof29e1222015-12-09 17:21:38 +0900625 return ssid.equals(removeDoubleQuotes(config.SSID)) && config.FQDN.equals(mConfig.FQDN);
Bartosz Fabianowski6fb07562016-01-12 15:43:19 +0100626 } else {
627 return ssid.equals(removeDoubleQuotes(config.SSID))
628 && security == getSecurity(config)
629 && (mConfig == null || mConfig.shared == config.shared);
630 }
Jason Monkd52356a2015-01-28 10:40:41 -0500631 }
632
633 public WifiConfiguration getConfig() {
634 return mConfig;
635 }
636
Peter Qiuced37db2017-03-14 15:51:22 -0700637 public String getPasspointFqdn() {
638 return mFqdn;
639 }
640
Jason Monkd52356a2015-01-28 10:40:41 -0500641 public void clearConfig() {
642 mConfig = null;
643 networkId = WifiConfiguration.INVALID_NETWORK_ID;
644 }
645
646 public WifiInfo getInfo() {
647 return mInfo;
648 }
649
Sundeep Ghumanaaa8a1b2017-03-13 14:40:56 -0700650 /**
651 * Returns the number of levels to show for a Wifi icon, from 0 to {@link #SIGNAL_LEVELS}-1.
652 *
653 * <p>Use {@#isReachable()} to determine if an AccessPoint is in range, as this method will
654 * always return at least 0.
655 */
Jason Monkd52356a2015-01-28 10:40:41 -0500656 public int getLevel() {
Tony Mantlera0e03dd2016-01-27 15:57:03 -0800657 return WifiManager.calculateSignalLevel(mRssi, SIGNAL_LEVELS);
Jason Monkd52356a2015-01-28 10:40:41 -0500658 }
659
Vinit Deshpandefcd46122015-06-11 18:22:23 -0700660 public int getRssi() {
Sundeep Ghuman54bdcfa2017-03-08 19:52:05 -0800661 return mRssi;
662 }
663
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800664 /**
665 * Returns the underlying scan result set.
666 *
667 * <p>Callers should not modify this set.
668 */
669 public Set<ScanResult> getScanResults() { return mScanResults; }
jackqdyulei9ee9b392017-12-22 11:31:49 -0800670
671 public Map<String, TimestampedScoredNetwork> getScoredNetworkCache() {
672 return mScoredNetworkCache;
673 }
674
Sundeep Ghuman54bdcfa2017-03-08 19:52:05 -0800675 /**
676 * Updates {@link #mRssi}.
677 *
678 * <p>If the given connection is active, the existing value of {@link #mRssi} will be returned.
679 * If the given AccessPoint is not active, a value will be calculated from previous scan
Sundeep Ghumance78a5f2017-03-15 19:06:14 -0700680 * results, returning the best RSSI for all matching AccessPoints averaged with the previous
681 * value. If the access point is not connected and there are no scan results, the rssi will be
682 * set to {@link #UNREACHABLE_RSSI}.
Sundeep Ghuman54bdcfa2017-03-08 19:52:05 -0800683 */
684 private void updateRssi() {
Sundeep Ghuman54bdcfa2017-03-08 19:52:05 -0800685 if (this.isActive()) {
686 return;
687 }
688
689 int rssi = UNREACHABLE_RSSI;
Sundeep Ghuman04f7f342018-01-23 19:18:31 -0800690 for (ScanResult result : mScanResults) {
Vinit Deshpandefcd46122015-06-11 18:22:23 -0700691 if (result.level > rssi) {
692 rssi = result.level;
693 }
694 }
695
Sundeep Ghumance78a5f2017-03-15 19:06:14 -0700696 if (rssi != UNREACHABLE_RSSI && mRssi != UNREACHABLE_RSSI) {
697 mRssi = (mRssi + rssi) / 2; // half-life previous value
698 } else {
699 mRssi = rssi;
700 }
Vinit Deshpandefcd46122015-06-11 18:22:23 -0700701 }
702
Stephen Chen21f68682017-04-04 13:23:31 -0700703 /**
Jeff Sharkey43d2a172017-07-12 10:50:42 -0600704 * Returns if the network should be considered metered.
Stephen Chen21f68682017-04-04 13:23:31 -0700705 */
706 public boolean isMetered() {
707 return mIsScoredNetworkMetered
Jeff Sharkey43d2a172017-07-12 10:50:42 -0600708 || WifiConfiguration.isMetered(mConfig, mInfo);
Stephen Chen21f68682017-04-04 13:23:31 -0700709 }
710
Jason Monkd52356a2015-01-28 10:40:41 -0500711 public NetworkInfo getNetworkInfo() {
712 return mNetworkInfo;
713 }
714
715 public int getSecurity() {
716 return security;
717 }
718
719 public String getSecurityString(boolean concise) {
720 Context context = mContext;
Tomoharu Hatano2bde6e72017-05-30 18:38:56 +0900721 if (isPasspoint() || isPasspointConfig()) {
Sanket Padawe194cce62015-07-10 10:53:31 -0700722 return concise ? context.getString(R.string.wifi_security_short_eap) :
723 context.getString(R.string.wifi_security_eap);
Sanket Padawed1878802015-05-12 10:27:19 -0700724 }
Jason Monkd52356a2015-01-28 10:40:41 -0500725 switch(security) {
726 case SECURITY_EAP:
727 return concise ? context.getString(R.string.wifi_security_short_eap) :
728 context.getString(R.string.wifi_security_eap);
Hai Shalomaa6f9202018-10-16 14:30:02 -0700729 case SECURITY_EAP_SUITE_B:
730 return concise ? context.getString(R.string.wifi_security_short_eap_suiteb) :
731 context.getString(R.string.wifi_security_eap_suiteb);
Jason Monkd52356a2015-01-28 10:40:41 -0500732 case SECURITY_PSK:
733 switch (pskType) {
734 case PSK_WPA:
735 return concise ? context.getString(R.string.wifi_security_short_wpa) :
736 context.getString(R.string.wifi_security_wpa);
737 case PSK_WPA2:
738 return concise ? context.getString(R.string.wifi_security_short_wpa2) :
739 context.getString(R.string.wifi_security_wpa2);
740 case PSK_WPA_WPA2:
741 return concise ? context.getString(R.string.wifi_security_short_wpa_wpa2) :
742 context.getString(R.string.wifi_security_wpa_wpa2);
743 case PSK_UNKNOWN:
744 default:
745 return concise ? context.getString(R.string.wifi_security_short_psk_generic)
746 : context.getString(R.string.wifi_security_psk_generic);
747 }
748 case SECURITY_WEP:
749 return concise ? context.getString(R.string.wifi_security_short_wep) :
750 context.getString(R.string.wifi_security_wep);
Hai Shalomaa6f9202018-10-16 14:30:02 -0700751 case SECURITY_SAE:
752 return concise ? context.getString(R.string.wifi_security_short_sae) :
753 context.getString(R.string.wifi_security_sae);
754 case SECURITY_OWE:
755 return concise ? context.getString(R.string.wifi_security_short_owe) :
756 context.getString(R.string.wifi_security_owe);
Jason Monkd52356a2015-01-28 10:40:41 -0500757 case SECURITY_NONE:
758 default:
759 return concise ? "" : context.getString(R.string.wifi_security_none);
760 }
761 }
762
Jason Monk6980d122015-06-15 10:07:55 -0400763 public String getSsidStr() {
Jason Monkd52356a2015-01-28 10:40:41 -0500764 return ssid;
765 }
766
Jason Monk60a82ff2016-02-25 13:55:03 -0500767 public String getBssid() {
768 return bssid;
769 }
770
Jason Monk6980d122015-06-15 10:07:55 -0400771 public CharSequence getSsid() {
pastychanga2998fc2018-06-26 13:37:34 +0800772 return ssid;
Jason Monk6980d122015-06-15 10:07:55 -0400773 }
774
Vinit Deshpandedcf00c92015-04-15 18:32:09 -0700775 public String getConfigName() {
776 if (mConfig != null && mConfig.isPasspoint()) {
777 return mConfig.providerFriendlyName;
Peter Qiuced37db2017-03-14 15:51:22 -0700778 } else if (mFqdn != null) {
779 return mProviderFriendlyName;
Vinit Deshpandedcf00c92015-04-15 18:32:09 -0700780 } else {
781 return ssid;
782 }
783 }
784
Jason Monkd52356a2015-01-28 10:40:41 -0500785 public DetailedState getDetailedState() {
Fan Zhang6acb7662016-10-17 12:40:03 -0700786 if (mNetworkInfo != null) {
787 return mNetworkInfo.getDetailedState();
788 }
789 Log.w(TAG, "NetworkInfo is null, cannot return detailed state");
790 return null;
Jason Monkd52356a2015-01-28 10:40:41 -0500791 }
792
Peter Qiu280581b2017-07-24 14:18:56 -0700793 public boolean isCarrierAp() {
794 return mIsCarrierAp;
795 }
796
797 public int getCarrierApEapType() {
798 return mCarrierApEapType;
799 }
800
801 public String getCarrierName() {
802 return mCarrierName;
803 }
804
Vinit Deshpandedcf00c92015-04-15 18:32:09 -0700805 public String getSavedNetworkSummary() {
Fan Zhang51365c32016-09-20 12:22:18 -0700806 WifiConfiguration config = mConfig;
807 if (config != null) {
Sanket Padawe56cfbfb2015-05-05 20:10:46 -0700808 PackageManager pm = mContext.getPackageManager();
809 String systemName = pm.getNameForUid(android.os.Process.SYSTEM_UID);
Fan Zhang51365c32016-09-20 12:22:18 -0700810 int userId = UserHandle.getUserId(config.creatorUid);
Sanket Padawe56cfbfb2015-05-05 20:10:46 -0700811 ApplicationInfo appInfo = null;
Fan Zhang51365c32016-09-20 12:22:18 -0700812 if (config.creatorName != null && config.creatorName.equals(systemName)) {
Sanket Padawe56cfbfb2015-05-05 20:10:46 -0700813 appInfo = mContext.getApplicationInfo();
814 } else {
815 try {
816 IPackageManager ipm = AppGlobals.getPackageManager();
Fan Zhang51365c32016-09-20 12:22:18 -0700817 appInfo = ipm.getApplicationInfo(config.creatorName, 0 /* flags */, userId);
Sanket Padawe56cfbfb2015-05-05 20:10:46 -0700818 } catch (RemoteException rex) {
819 }
820 }
821 if (appInfo != null &&
822 !appInfo.packageName.equals(mContext.getString(R.string.settings_package)) &&
823 !appInfo.packageName.equals(
824 mContext.getString(R.string.certinstaller_package))) {
825 return mContext.getString(R.string.saved_network, appInfo.loadLabel(pm));
826 }
Vinit Deshpandedcf00c92015-04-15 18:32:09 -0700827 }
Sanket Padawe56cfbfb2015-05-05 20:10:46 -0700828 return "";
Vinit Deshpandedcf00c92015-04-15 18:32:09 -0700829 }
830
Jason Monkd52356a2015-01-28 10:40:41 -0500831 public String getSummary() {
Fan Zhang51365c32016-09-20 12:22:18 -0700832 return getSettingsSummary(mConfig);
Vinit Deshpandedcf00c92015-04-15 18:32:09 -0700833 }
834
835 public String getSettingsSummary() {
Fan Zhang51365c32016-09-20 12:22:18 -0700836 return getSettingsSummary(mConfig);
837 }
838
839 private String getSettingsSummary(WifiConfiguration config) {
Jason Monkd52356a2015-01-28 10:40:41 -0500840 // Update to new summary
841 StringBuilder summary = new StringBuilder();
842
Fan Zhang51365c32016-09-20 12:22:18 -0700843 if (isActive() && config != null && config.isPasspoint()) {
Vinit Deshpandedcf00c92015-04-15 18:32:09 -0700844 // This is the active connection on passpoint
Jason Monkd52356a2015-01-28 10:40:41 -0500845 summary.append(getSummary(mContext, getDetailedState(),
Fan Zhang51365c32016-09-20 12:22:18 -0700846 false, config.providerFriendlyName));
Peter Qiu280581b2017-07-24 14:18:56 -0700847 } else if (isActive() && config != null && getDetailedState() == DetailedState.CONNECTED
848 && mIsCarrierAp) {
849 summary.append(String.format(mContext.getString(R.string.connected_via_carrier), mCarrierName));
Vinit Deshpandedcf00c92015-04-15 18:32:09 -0700850 } else if (isActive()) {
851 // This is the active connection on non-passpoint network
852 summary.append(getSummary(mContext, getDetailedState(),
Shirish Kalelec7a38ef2015-06-25 13:55:33 -0700853 mInfo != null && mInfo.isEphemeral()));
Yuxin Chang6b750862017-01-11 14:59:17 +0900854 } else if (config != null && config.isPasspoint()
855 && config.getNetworkSelectionStatus().isNetworkEnabled()) {
Vinit Deshpandedcf00c92015-04-15 18:32:09 -0700856 String format = mContext.getString(R.string.available_via_passpoint);
Fan Zhang51365c32016-09-20 12:22:18 -0700857 summary.append(String.format(format, config.providerFriendlyName));
858 } else if (config != null && config.hasNoInternetAccess()) {
Salvador Martinez5d937fc2016-09-23 08:54:20 -0700859 int messageID = config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()
860 ? R.string.wifi_no_internet_no_reconnect
861 : R.string.wifi_no_internet;
862 summary.append(mContext.getString(messageID));
Fan Zhang51365c32016-09-20 12:22:18 -0700863 } else if (config != null && !config.getNetworkSelectionStatus().isNetworkEnabled()) {
xinhef7705c32015-12-01 14:44:37 -0800864 WifiConfiguration.NetworkSelectionStatus networkStatus =
Fan Zhang51365c32016-09-20 12:22:18 -0700865 config.getNetworkSelectionStatus();
xinhef7705c32015-12-01 14:44:37 -0800866 switch (networkStatus.getNetworkSelectionDisableReason()) {
867 case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE:
Jason Monkd52356a2015-01-28 10:40:41 -0500868 summary.append(mContext.getString(R.string.wifi_disabled_password_failure));
xinhef7705c32015-12-01 14:44:37 -0800869 break;
Peter Qiu9c4c6ad2017-06-20 13:17:36 -0700870 case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD:
871 summary.append(mContext.getString(R.string.wifi_check_password_try_again));
872 break;
xinhef7705c32015-12-01 14:44:37 -0800873 case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE:
874 case WifiConfiguration.NetworkSelectionStatus.DISABLED_DNS_FAILURE:
875 summary.append(mContext.getString(R.string.wifi_disabled_network_failure));
876 break;
877 case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION:
878 summary.append(mContext.getString(R.string.wifi_disabled_generic));
879 break;
Jason Monkd52356a2015-01-28 10:40:41 -0500880 }
Amin Shaikh98773d42017-02-02 17:50:12 -0800881 } else if (config != null && config.getNetworkSelectionStatus().isNotRecommended()) {
882 summary.append(mContext.getString(R.string.wifi_disabled_by_recommendation_provider));
Peter Qiu280581b2017-07-24 14:18:56 -0700883 } else if (mIsCarrierAp) {
884 summary.append(String.format(mContext.getString(R.string.available_via_carrier), mCarrierName));
Dave Schaefer98537432017-02-08 11:26:08 -0800885 } else if (!isReachable()) { // Wifi out of range
Jason Monkd52356a2015-01-28 10:40:41 -0500886 summary.append(mContext.getString(R.string.wifi_not_in_range));
887 } else { // In range, not disabled.
Fan Zhang51365c32016-09-20 12:22:18 -0700888 if (config != null) { // Is saved network
Glen Kuhne446afac2017-06-22 16:02:33 -0700889 // Last attempt to connect to this failed. Show reason why
890 switch (config.recentFailure.getAssociationStatus()) {
891 case WifiConfiguration.RecentFailure.STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
892 summary.append(mContext.getString(
893 R.string.wifi_ap_unable_to_handle_new_sta));
894 break;
895 default:
896 // "Saved"
897 summary.append(mContext.getString(R.string.wifi_remembered));
898 break;
899 }
Jason Monkd52356a2015-01-28 10:40:41 -0500900 }
901 }
902
Sundeep Ghumanc0cf8482018-01-26 18:23:34 -0800903 if (isVerboseLoggingEnabled()) {
jackqdyulei9ee9b392017-12-22 11:31:49 -0800904 summary.append(WifiUtils.buildLoggingSummary(this, config));
Jason Monkd52356a2015-01-28 10:40:41 -0500905 }
Sundeep Ghuman271e5de2017-05-30 14:11:39 -0700906
Salvador Martinez6f6fe752018-03-21 16:33:53 -0700907 if (config != null && (WifiUtils.isMeteredOverridden(config) || config.meteredHint)) {
908 return mContext.getResources().getString(
909 R.string.preference_summary_default_combination,
910 WifiUtils.getMeteredLabel(mContext, config),
911 summary.toString());
912 }
913
Eric Schwarzenbachf4f3842b2017-07-17 16:45:04 -0700914 // If Speed label and summary are both present, use the preference combination to combine
915 // the two, else return the non-null one.
916 if (getSpeedLabel() != null && summary.length() != 0) {
Sundeep Ghumand3171ca2017-07-12 23:02:24 -0700917 return mContext.getResources().getString(
918 R.string.preference_summary_default_combination,
919 getSpeedLabel(),
920 summary.toString());
Eric Schwarzenbachf4f3842b2017-07-17 16:45:04 -0700921 } else if (getSpeedLabel() != null) {
922 return getSpeedLabel();
Sundeep Ghumand3171ca2017-07-12 23:02:24 -0700923 } else {
924 return summary.toString();
Sundeep Ghuman271e5de2017-05-30 14:11:39 -0700925 }
Jason Monkd52356a2015-01-28 10:40:41 -0500926 }
927
928 /**
Jason Monkd52356a2015-01-28 10:40:41 -0500929 * Return whether this is the active connection.
930 * For ephemeral connections (networkId is invalid), this returns false if the network is
931 * disconnected.
932 */
933 public boolean isActive() {
934 return mNetworkInfo != null &&
935 (networkId != WifiConfiguration.INVALID_NETWORK_ID ||
936 mNetworkInfo.getState() != State.DISCONNECTED);
937 }
938
939 public boolean isConnectable() {
940 return getLevel() != -1 && getDetailedState() == null;
941 }
942
943 public boolean isEphemeral() {
Shirish Kalelec7a38ef2015-06-25 13:55:33 -0700944 return mInfo != null && mInfo.isEphemeral() &&
945 mNetworkInfo != null && mNetworkInfo.getState() != State.DISCONNECTED;
Jason Monkd52356a2015-01-28 10:40:41 -0500946 }
947
Peter Qiuced37db2017-03-14 15:51:22 -0700948 /**
949 * Return true if this AccessPoint represents a Passpoint AP.
950 */
Vinit Deshpande5b7352c2015-07-09 16:53:12 -0700951 public boolean isPasspoint() {
952 return mConfig != null && mConfig.isPasspoint();
953 }
954
Mitchell Wills5a42db22015-08-03 09:46:08 -0700955 /**
Peter Qiuced37db2017-03-14 15:51:22 -0700956 * Return true if this AccessPoint represents a Passpoint provider configuration.
957 */
958 public boolean isPasspointConfig() {
959 return mFqdn != null;
960 }
961
962 /**
Mitchell Wills5a42db22015-08-03 09:46:08 -0700963 * Return whether the given {@link WifiInfo} is for this access point.
964 * If the current AP does not have a network Id then the config is used to
965 * match based on SSID and security.
966 */
967 private boolean isInfoForThisAccessPoint(WifiConfiguration config, WifiInfo info) {
Vinit Deshpande5b7352c2015-07-09 16:53:12 -0700968 if (isPasspoint() == false && networkId != WifiConfiguration.INVALID_NETWORK_ID) {
Jason Monkd52356a2015-01-28 10:40:41 -0500969 return networkId == info.getNetworkId();
Mitchell Wills5a42db22015-08-03 09:46:08 -0700970 } else if (config != null) {
971 return matches(config);
972 }
973 else {
Jason Monkd52356a2015-01-28 10:40:41 -0500974 // Might be an ephemeral connection with no WifiConfiguration. Try matching on SSID.
975 // (Note that we only do this if the WifiConfiguration explicitly equals INVALID).
976 // TODO: Handle hex string SSIDs.
977 return ssid.equals(removeDoubleQuotes(info.getSSID()));
978 }
979 }
980
981 public boolean isSaved() {
982 return networkId != WifiConfiguration.INVALID_NETWORK_ID;
983 }
984
985 public Object getTag() {
986 return mTag;
987 }
988
989 public void setTag(Object tag) {
990 mTag = tag;
991 }
992
993 /**
994 * Generate and save a default wifiConfiguration with common values.
995 * Can only be called for unsecured networks.
996 */
997 public void generateOpenNetworkConfig() {
Hai Shalomaa6f9202018-10-16 14:30:02 -0700998 if ((security != SECURITY_NONE) && (security != SECURITY_OWE)) {
Jason Monkd52356a2015-01-28 10:40:41 -0500999 throw new IllegalStateException();
Hai Shalomaa6f9202018-10-16 14:30:02 -07001000 }
Jason Monkd52356a2015-01-28 10:40:41 -05001001 if (mConfig != null)
1002 return;
1003 mConfig = new WifiConfiguration();
1004 mConfig.SSID = AccessPoint.convertToQuotedString(ssid);
Hai Shalomaa6f9202018-10-16 14:30:02 -07001005
1006 if (security == SECURITY_NONE) {
1007 mConfig.allowedKeyManagement.set(KeyMgmt.NONE);
1008 } else {
1009 mConfig.allowedKeyManagement.set(KeyMgmt.OWE);
1010 mConfig.requirePMF = true;
1011 }
Jason Monkd52356a2015-01-28 10:40:41 -05001012 }
1013
Jason Monkd52356a2015-01-28 10:40:41 -05001014 public void saveWifiState(Bundle savedState) {
Vinit Deshpandefcd46122015-06-11 18:22:23 -07001015 if (ssid != null) savedState.putString(KEY_SSID, getSsidStr());
1016 savedState.putInt(KEY_SECURITY, security);
Sundeep Ghumana28050a2017-07-12 22:09:25 -07001017 savedState.putInt(KEY_SPEED, mSpeed);
Vinit Deshpandefcd46122015-06-11 18:22:23 -07001018 savedState.putInt(KEY_PSKTYPE, pskType);
1019 if (mConfig != null) savedState.putParcelable(KEY_CONFIG, mConfig);
Jason Monkd52356a2015-01-28 10:40:41 -05001020 savedState.putParcelable(KEY_WIFIINFO, mInfo);
Sundeep Ghuman04f7f342018-01-23 19:18:31 -08001021 savedState.putParcelableArray(KEY_SCANRESULTS,
1022 mScanResults.toArray(new Parcelable[mScanResults.size()]));
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -07001023 savedState.putParcelableArrayList(KEY_SCOREDNETWORKCACHE,
1024 new ArrayList<>(mScoredNetworkCache.values()));
Jason Monkd52356a2015-01-28 10:40:41 -05001025 if (mNetworkInfo != null) {
1026 savedState.putParcelable(KEY_NETWORKINFO, mNetworkInfo);
1027 }
Peter Qiuced37db2017-03-14 15:51:22 -07001028 if (mFqdn != null) {
1029 savedState.putString(KEY_FQDN, mFqdn);
1030 }
1031 if (mProviderFriendlyName != null) {
1032 savedState.putString(KEY_PROVIDER_FRIENDLY_NAME, mProviderFriendlyName);
1033 }
Peter Qiu280581b2017-07-24 14:18:56 -07001034 savedState.putBoolean(KEY_IS_CARRIER_AP, mIsCarrierAp);
1035 savedState.putInt(KEY_CARRIER_AP_EAP_TYPE, mCarrierApEapType);
1036 savedState.putString(KEY_CARRIER_NAME, mCarrierName);
Jason Monkd52356a2015-01-28 10:40:41 -05001037 }
1038
1039 public void setListener(AccessPointListener listener) {
1040 mAccessPointListener = listener;
1041 }
1042
Sundeep Ghumand1e44922017-08-07 11:21:38 -07001043 /**
Sundeep Ghuman04f7f342018-01-23 19:18:31 -08001044 * Sets {@link #mScanResults} to the given collection.
Sundeep Ghumand1e44922017-08-07 11:21:38 -07001045 *
Sundeep Ghuman04f7f342018-01-23 19:18:31 -08001046 * @param scanResults a collection of scan results to add to the internal set
1047 * @throws IllegalArgumentException if any of the given ScanResults did not belong to this AP
Sundeep Ghumand1e44922017-08-07 11:21:38 -07001048 */
Sundeep Ghuman04f7f342018-01-23 19:18:31 -08001049 void setScanResults(Collection<ScanResult> scanResults) {
Sundeep Ghuman54bdcfa2017-03-08 19:52:05 -08001050
Sundeep Ghuman04f7f342018-01-23 19:18:31 -08001051 // Validate scan results are for current AP only
1052 String key = getKey();
1053 for (ScanResult result : scanResults) {
1054 String scanResultKey = AccessPoint.getKey(result);
1055 if (!mKey.equals(scanResultKey)) {
1056 throw new IllegalArgumentException(
1057 String.format("ScanResult %s\nkey of %s did not match current AP key %s",
1058 result, scanResultKey, key));
Jason Monkd52356a2015-01-28 10:40:41 -05001059 }
Sundeep Ghuman04f7f342018-01-23 19:18:31 -08001060 }
1061
1062
1063 int oldLevel = getLevel();
1064 mScanResults.clear();
1065 mScanResults.addAll(scanResults);
1066 updateRssi();
1067 int newLevel = getLevel();
1068
1069 // If newLevel is 0, there will be no displayed Preference since the AP is unreachable
1070 if (newLevel > 0 && newLevel != oldLevel) {
1071 // Only update labels on visible rssi changes
1072 updateSpeed();
Sundeep Ghuman5d14fd72018-02-15 14:49:15 -08001073 ThreadUtils.postOnMainThread(() -> {
1074 if (mAccessPointListener != null) {
1075 mAccessPointListener.onLevelChanged(this);
1076 }
1077 });
1078
Sundeep Ghuman04f7f342018-01-23 19:18:31 -08001079 }
1080
Sundeep Ghuman5d14fd72018-02-15 14:49:15 -08001081 ThreadUtils.postOnMainThread(() -> {
1082 if (mAccessPointListener != null) {
1083 mAccessPointListener.onAccessPointChanged(this);
1084 }
1085 });
Sundeep Ghuman04f7f342018-01-23 19:18:31 -08001086
1087 if (!scanResults.isEmpty()) {
1088 ScanResult result = scanResults.iterator().next();
1089
Jason Monkd52356a2015-01-28 10:40:41 -05001090 // This flag only comes from scans, is not easily saved in config
1091 if (security == SECURITY_PSK) {
1092 pskType = getPskType(result);
1093 }
Vinit Deshpandefcd46122015-06-11 18:22:23 -07001094
Peter Qiu280581b2017-07-24 14:18:56 -07001095 // The carrier info in the ScanResult is set by the platform based on the SSID and will
1096 // always be the same for all matching scan results.
1097 mIsCarrierAp = result.isCarrierAp;
1098 mCarrierApEapType = result.carrierApEapType;
1099 mCarrierName = result.carrierName;
Jason Monkd52356a2015-01-28 10:40:41 -05001100 }
Jason Monkd52356a2015-01-28 10:40:41 -05001101 }
1102
Sundeep Ghuman8c792882017-04-04 17:23:29 -07001103 /** Attempt to update the AccessPoint and return true if an update occurred. */
Sundeep Ghumand911da32017-07-05 20:06:05 -07001104 public boolean update(
1105 @Nullable WifiConfiguration config, WifiInfo info, NetworkInfo networkInfo) {
Sundeep Ghumandb9b94c2017-09-06 11:46:21 -07001106
Sundeep Ghuman8c792882017-04-04 17:23:29 -07001107 boolean updated = false;
1108 final int oldLevel = getLevel();
Mitchell Wills5a42db22015-08-03 09:46:08 -07001109 if (info != null && isInfoForThisAccessPoint(config, info)) {
Sundeep Ghuman8c792882017-04-04 17:23:29 -07001110 updated = (mInfo == null);
Sundeep Ghumanf4d33022017-06-05 19:47:36 -07001111 if (mConfig != config) {
1112 // We do not set updated = true as we do not want to increase the amount of sorting
1113 // and copying performed in WifiTracker at this time. If issues involving refresh
1114 // are still seen, we will investigate further.
1115 update(config); // Notifies the AccessPointListener of the change
1116 }
Glen Kuhned38708c2017-07-19 14:53:34 -07001117 if (mRssi != info.getRssi() && info.getRssi() != WifiInfo.INVALID_RSSI) {
Sundeep Ghuman8c792882017-04-04 17:23:29 -07001118 mRssi = info.getRssi();
1119 updated = true;
Sundeep Ghuman5c5cd7a2017-05-03 12:45:44 -07001120 } else if (mNetworkInfo != null && networkInfo != null
1121 && mNetworkInfo.getDetailedState() != networkInfo.getDetailedState()) {
Sundeep Ghuman96a53572017-04-20 21:25:41 -07001122 updated = true;
Jason Monkd52356a2015-01-28 10:40:41 -05001123 }
Sundeep Ghuman8c792882017-04-04 17:23:29 -07001124 mInfo = info;
Sundeep Ghuman8c792882017-04-04 17:23:29 -07001125 mNetworkInfo = networkInfo;
Jason Monkd52356a2015-01-28 10:40:41 -05001126 } else if (mInfo != null) {
Sundeep Ghuman8c792882017-04-04 17:23:29 -07001127 updated = true;
Jason Monkd52356a2015-01-28 10:40:41 -05001128 mInfo = null;
1129 mNetworkInfo = null;
Sundeep Ghuman8c792882017-04-04 17:23:29 -07001130 }
1131 if (updated && mAccessPointListener != null) {
Sundeep Ghuman5d14fd72018-02-15 14:49:15 -08001132 ThreadUtils.postOnMainThread(() -> {
1133 if (mAccessPointListener != null) {
1134 mAccessPointListener.onAccessPointChanged(this);
1135 }
1136 });
Sundeep Ghuman8c792882017-04-04 17:23:29 -07001137
1138 if (oldLevel != getLevel() /* current level */) {
Sundeep Ghuman5d14fd72018-02-15 14:49:15 -08001139 ThreadUtils.postOnMainThread(() -> {
1140 if (mAccessPointListener != null) {
1141 mAccessPointListener.onLevelChanged(this);
1142 }
1143 });
Jason Monkd52356a2015-01-28 10:40:41 -05001144 }
1145 }
Sundeep Ghumandb9b94c2017-09-06 11:46:21 -07001146
Sundeep Ghuman8c792882017-04-04 17:23:29 -07001147 return updated;
Jason Monkd52356a2015-01-28 10:40:41 -05001148 }
1149
Sundeep Ghumand911da32017-07-05 20:06:05 -07001150 void update(@Nullable WifiConfiguration config) {
Vinit Deshpandefc406002015-04-15 18:10:55 -07001151 mConfig = config;
Sundeep Ghumand911da32017-07-05 20:06:05 -07001152 networkId = config != null ? config.networkId : WifiConfiguration.INVALID_NETWORK_ID;
Sundeep Ghuman5d14fd72018-02-15 14:49:15 -08001153 ThreadUtils.postOnMainThread(() -> {
1154 if (mAccessPointListener != null) {
1155 mAccessPointListener.onAccessPointChanged(this);
1156 }
1157 });
Vinit Deshpandefc406002015-04-15 18:10:55 -07001158 }
Shirish Kalelec7a38ef2015-06-25 13:55:33 -07001159
Sundeep Ghuman54bdcfa2017-03-08 19:52:05 -08001160 @VisibleForTesting
Sanket Padawe0775a982015-08-19 14:57:46 -07001161 void setRssi(int rssi) {
1162 mRssi = rssi;
1163 }
1164
Sundeep Ghuman54bdcfa2017-03-08 19:52:05 -08001165 /** Sets the rssi to {@link #UNREACHABLE_RSSI}. */
1166 void setUnreachable() {
1167 setRssi(AccessPoint.UNREACHABLE_RSSI);
1168 }
1169
Sundeep Ghuman55adc6b2017-06-05 16:46:59 -07001170 int getSpeed() { return mSpeed;}
Sundeep Ghuman271e5de2017-05-30 14:11:39 -07001171
1172 @Nullable
1173 String getSpeedLabel() {
Eric Schwarzenbach0b8700f2017-07-25 14:32:21 -07001174 return getSpeedLabel(mSpeed);
1175 }
1176
1177 @Nullable
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -07001178 @Speed
Amin Shaikhe74dbdd2018-03-09 16:05:30 -05001179 private static int roundToClosestSpeedEnum(int speed) {
Sundeep Ghumanbe3a74c2017-08-01 17:50:59 -07001180 if (speed < Speed.SLOW) {
1181 return Speed.NONE;
1182 } else if (speed < (Speed.SLOW + Speed.MODERATE) / 2) {
1183 return Speed.SLOW;
1184 } else if (speed < (Speed.MODERATE + Speed.FAST) / 2) {
1185 return Speed.MODERATE;
1186 } else if (speed < (Speed.FAST + Speed.VERY_FAST) / 2) {
1187 return Speed.FAST;
1188 } else {
1189 return Speed.VERY_FAST;
1190 }
1191 }
1192
1193 @Nullable
jackqdyulei9ee9b392017-12-22 11:31:49 -08001194 String getSpeedLabel(@Speed int speed) {
Amin Shaikhe74dbdd2018-03-09 16:05:30 -05001195 return getSpeedLabel(mContext, speed);
1196 }
1197
1198 private static String getSpeedLabel(Context context, int speed) {
Eric Schwarzenbach0b8700f2017-07-25 14:32:21 -07001199 switch (speed) {
Sundeep Ghuman53200ed2017-06-21 16:54:36 -07001200 case Speed.VERY_FAST:
Amin Shaikhe74dbdd2018-03-09 16:05:30 -05001201 return context.getString(R.string.speed_label_very_fast);
Sundeep Ghuman53200ed2017-06-21 16:54:36 -07001202 case Speed.FAST:
Amin Shaikhe74dbdd2018-03-09 16:05:30 -05001203 return context.getString(R.string.speed_label_fast);
Sundeep Ghuman53200ed2017-06-21 16:54:36 -07001204 case Speed.MODERATE:
Amin Shaikhe74dbdd2018-03-09 16:05:30 -05001205 return context.getString(R.string.speed_label_okay);
Sundeep Ghuman53200ed2017-06-21 16:54:36 -07001206 case Speed.SLOW:
Amin Shaikhe74dbdd2018-03-09 16:05:30 -05001207 return context.getString(R.string.speed_label_slow);
Sundeep Ghuman53200ed2017-06-21 16:54:36 -07001208 case Speed.NONE:
Sundeep Ghuman271e5de2017-05-30 14:11:39 -07001209 default:
1210 return null;
1211 }
Sundeep Ghuman5519b7b2016-12-14 17:53:31 -08001212 }
1213
Amin Shaikhe74dbdd2018-03-09 16:05:30 -05001214 /** Return the speed label for a {@link ScoredNetwork} at the specified {@code rssi} level. */
1215 @Nullable
1216 public static String getSpeedLabel(Context context, ScoredNetwork scoredNetwork, int rssi) {
1217 return getSpeedLabel(context, roundToClosestSpeedEnum(scoredNetwork.calculateBadge(rssi)));
1218 }
1219
Dave Schaefer98537432017-02-08 11:26:08 -08001220 /** Return true if the current RSSI is reachable, and false otherwise. */
Sundeep Ghumanaaa8a1b2017-03-13 14:40:56 -07001221 public boolean isReachable() {
Dave Schaefer98537432017-02-08 11:26:08 -08001222 return mRssi != UNREACHABLE_RSSI;
1223 }
1224
Jason Monkd52356a2015-01-28 10:40:41 -05001225 public static String getSummary(Context context, String ssid, DetailedState state,
Vinit Deshpandefc406002015-04-15 18:10:55 -07001226 boolean isEphemeral, String passpointProvider) {
Vinit Deshpandedcf00c92015-04-15 18:32:09 -07001227 if (state == DetailedState.CONNECTED && ssid == null) {
Vinit Deshpandefc406002015-04-15 18:10:55 -07001228 if (TextUtils.isEmpty(passpointProvider) == false) {
Vinit Deshpandedcf00c92015-04-15 18:32:09 -07001229 // Special case for connected + passpoint networks.
Vinit Deshpandefc406002015-04-15 18:10:55 -07001230 String format = context.getString(R.string.connected_via_passpoint);
1231 return String.format(format, passpointProvider);
Vinit Deshpandedcf00c92015-04-15 18:32:09 -07001232 } else if (isEphemeral) {
Vinit Deshpandefc406002015-04-15 18:10:55 -07001233 // Special case for connected + ephemeral networks.
Stephen Chen36dd5cf12017-03-20 13:27:51 -07001234 final NetworkScoreManager networkScoreManager = context.getSystemService(
1235 NetworkScoreManager.class);
1236 NetworkScorerAppData scorer = networkScoreManager.getActiveScorer();
1237 if (scorer != null && scorer.getRecommendationServiceLabel() != null) {
1238 String format = context.getString(R.string.connected_via_network_scorer);
1239 return String.format(format, scorer.getRecommendationServiceLabel());
1240 } else {
1241 return context.getString(R.string.connected_via_network_scorer_default);
1242 }
Vinit Deshpandefc406002015-04-15 18:10:55 -07001243 }
Jason Monkd52356a2015-01-28 10:40:41 -05001244 }
1245
Sanket Padawe7094d222015-05-01 16:55:00 -07001246 // Case when there is wifi connected without internet connectivity.
1247 final ConnectivityManager cm = (ConnectivityManager)
1248 context.getSystemService(Context.CONNECTIVITY_SERVICE);
1249 if (state == DetailedState.CONNECTED) {
1250 IWifiManager wifiManager = IWifiManager.Stub.asInterface(
1251 ServiceManager.getService(Context.WIFI_SERVICE));
Lorenzo Colitti1317e042016-12-13 13:30:07 +09001252 NetworkCapabilities nc = null;
Sanket Padawe7094d222015-05-01 16:55:00 -07001253
1254 try {
Lorenzo Colitti1317e042016-12-13 13:30:07 +09001255 nc = cm.getNetworkCapabilities(wifiManager.getCurrentNetwork());
1256 } catch (RemoteException e) {}
1257
1258 if (nc != null) {
1259 if (nc.hasCapability(nc.NET_CAPABILITY_CAPTIVE_PORTAL)) {
Maurice Lam7f6fc862017-07-12 16:43:49 -07001260 int id = context.getResources()
1261 .getIdentifier("network_available_sign_in", "string", "android");
1262 return context.getString(id);
Lorenzo Colitti1317e042016-12-13 13:30:07 +09001263 } else if (!nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
1264 return context.getString(R.string.wifi_connected_no_internet);
1265 }
Sanket Padawe7094d222015-05-01 16:55:00 -07001266 }
1267 }
Fan Zhang6acb7662016-10-17 12:40:03 -07001268 if (state == null) {
1269 Log.w(TAG, "state is null, returning empty summary");
1270 return "";
1271 }
Jason Monkd52356a2015-01-28 10:40:41 -05001272 String[] formats = context.getResources().getStringArray((ssid == null)
1273 ? R.array.wifi_status : R.array.wifi_status_with_ssid);
1274 int index = state.ordinal();
1275
1276 if (index >= formats.length || formats[index].length() == 0) {
Sanket Padawe3e9e5fa2015-05-28 10:41:14 -07001277 return "";
Jason Monkd52356a2015-01-28 10:40:41 -05001278 }
1279 return String.format(formats[index], ssid);
1280 }
1281
1282 public static String getSummary(Context context, DetailedState state, boolean isEphemeral) {
Vinit Deshpandefc406002015-04-15 18:10:55 -07001283 return getSummary(context, null, state, isEphemeral, null);
1284 }
1285
1286 public static String getSummary(Context context, DetailedState state, boolean isEphemeral,
1287 String passpointProvider) {
1288 return getSummary(context, null, state, isEphemeral, passpointProvider);
Jason Monkd52356a2015-01-28 10:40:41 -05001289 }
1290
1291 public static String convertToQuotedString(String string) {
1292 return "\"" + string + "\"";
1293 }
1294
1295 private static int getPskType(ScanResult result) {
1296 boolean wpa = result.capabilities.contains("WPA-PSK");
1297 boolean wpa2 = result.capabilities.contains("WPA2-PSK");
1298 if (wpa2 && wpa) {
1299 return PSK_WPA_WPA2;
1300 } else if (wpa2) {
1301 return PSK_WPA2;
1302 } else if (wpa) {
1303 return PSK_WPA;
1304 } else {
1305 Log.w(TAG, "Received abnormal flag string: " + result.capabilities);
1306 return PSK_UNKNOWN;
1307 }
1308 }
1309
1310 private static int getSecurity(ScanResult result) {
1311 if (result.capabilities.contains("WEP")) {
1312 return SECURITY_WEP;
Hai Shalomaa6f9202018-10-16 14:30:02 -07001313 } else if (result.capabilities.contains("SAE")) {
1314 return SECURITY_SAE;
Jason Monkd52356a2015-01-28 10:40:41 -05001315 } else if (result.capabilities.contains("PSK")) {
1316 return SECURITY_PSK;
Hai Shalomaa6f9202018-10-16 14:30:02 -07001317 } else if (result.capabilities.contains("EAP_SUITE_B_192")) {
1318 return SECURITY_EAP_SUITE_B;
Jason Monkd52356a2015-01-28 10:40:41 -05001319 } else if (result.capabilities.contains("EAP")) {
1320 return SECURITY_EAP;
Hai Shalomaa6f9202018-10-16 14:30:02 -07001321 } else if (result.capabilities.contains("OWE")) {
1322 return SECURITY_OWE;
Jason Monkd52356a2015-01-28 10:40:41 -05001323 }
Hai Shalomaa6f9202018-10-16 14:30:02 -07001324
Jason Monkd52356a2015-01-28 10:40:41 -05001325 return SECURITY_NONE;
1326 }
1327
1328 static int getSecurity(WifiConfiguration config) {
Hai Shalomaa6f9202018-10-16 14:30:02 -07001329 if (config.allowedKeyManagement.get(KeyMgmt.SAE)) {
1330 return SECURITY_SAE;
1331 }
Jason Monkd52356a2015-01-28 10:40:41 -05001332 if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
1333 return SECURITY_PSK;
1334 }
Hai Shalomaa6f9202018-10-16 14:30:02 -07001335 if (config.allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) {
1336 return SECURITY_EAP_SUITE_B;
1337 }
Jason Monkd52356a2015-01-28 10:40:41 -05001338 if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
1339 config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
1340 return SECURITY_EAP;
1341 }
Hai Shalomaa6f9202018-10-16 14:30:02 -07001342 if (config.allowedKeyManagement.get(KeyMgmt.OWE)) {
1343 return SECURITY_OWE;
1344 }
Jason Monkd52356a2015-01-28 10:40:41 -05001345 return (config.wepKeys[0] != null) ? SECURITY_WEP : SECURITY_NONE;
1346 }
1347
1348 public static String securityToString(int security, int pskType) {
1349 if (security == SECURITY_WEP) {
1350 return "WEP";
1351 } else if (security == SECURITY_PSK) {
1352 if (pskType == PSK_WPA) {
1353 return "WPA";
1354 } else if (pskType == PSK_WPA2) {
1355 return "WPA2";
1356 } else if (pskType == PSK_WPA_WPA2) {
1357 return "WPA_WPA2";
1358 }
1359 return "PSK";
1360 } else if (security == SECURITY_EAP) {
1361 return "EAP";
Hai Shalomaa6f9202018-10-16 14:30:02 -07001362 } else if (security == SECURITY_SAE) {
1363 return "SAE";
1364 } else if (security == SECURITY_EAP_SUITE_B) {
1365 return "SUITE_B";
1366 } else if (security == SECURITY_OWE) {
1367 return "OWE";
Jason Monkd52356a2015-01-28 10:40:41 -05001368 }
1369 return "NONE";
1370 }
1371
1372 static String removeDoubleQuotes(String string) {
Jason Monk2b51cc32015-05-13 11:07:53 -04001373 if (TextUtils.isEmpty(string)) {
1374 return "";
1375 }
Jason Monkd52356a2015-01-28 10:40:41 -05001376 int length = string.length();
1377 if ((length > 1) && (string.charAt(0) == '"')
1378 && (string.charAt(length - 1) == '"')) {
1379 return string.substring(1, length - 1);
1380 }
1381 return string;
1382 }
1383
Sundeep Ghumanbb399912018-01-29 18:31:15 -08001384 /**
1385 * Callbacks relaying changes to the AccessPoint representation.
1386 *
1387 * <p>All methods are invoked on the Main Thread.
1388 */
Jason Monkd52356a2015-01-28 10:40:41 -05001389 public interface AccessPointListener {
Sundeep Ghumanbb399912018-01-29 18:31:15 -08001390 /**
1391 * Indicates a change to the externally visible state of the AccessPoint trigger by an
1392 * update of ScanResults, saved configuration state, connection state, or score
1393 * (labels/metered) state.
1394 *
1395 * <p>Clients should refresh their view of the AccessPoint to match the updated state when
1396 * this is invoked. Overall this method is extraneous if clients are listening to
1397 * {@link WifiTracker.WifiListener#onAccessPointsChanged()} callbacks.
1398 *
1399 * <p>Examples of changes include signal strength, connection state, speed label, and
1400 * generally anything that would impact the summary string.
1401 *
1402 * @param accessPoint The accessPoint object the listener was registered on which has
1403 * changed
1404 */
1405 @MainThread void onAccessPointChanged(AccessPoint accessPoint);
1406
1407 /**
1408 * Indicates the "wifi pie signal level" has changed, retrieved via calls to
1409 * {@link AccessPoint#getLevel()}.
1410 *
1411 * <p>This call is a subset of {@link #onAccessPointChanged(AccessPoint)} , hence is also
1412 * extraneous if the client is already reacting to that or the
1413 * {@link WifiTracker.WifiListener#onAccessPointsChanged()} callbacks.
1414 *
1415 * @param accessPoint The accessPoint object the listener was registered on whose level has
1416 * changed
1417 */
1418 @MainThread void onLevelChanged(AccessPoint accessPoint);
Jason Monkd52356a2015-01-28 10:40:41 -05001419 }
Sundeep Ghumanc0cf8482018-01-26 18:23:34 -08001420
1421 private static boolean isVerboseLoggingEnabled() {
1422 return WifiTracker.sVerboseLogging || Log.isLoggable(TAG, Log.VERBOSE);
1423 }
Jason Monkd52356a2015-01-28 10:40:41 -05001424}