• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.settingslib.wifi;
18 
19 import android.app.AppGlobals;
20 import android.content.Context;
21 import android.content.pm.ApplicationInfo;
22 import android.content.pm.IPackageManager;
23 import android.content.pm.PackageManager;
24 import android.net.ConnectivityManager;
25 import android.net.Network;
26 import android.net.NetworkCapabilities;
27 import android.net.NetworkInfo;
28 import android.net.NetworkInfo.DetailedState;
29 import android.net.NetworkInfo.State;
30 import android.net.wifi.IWifiManager;
31 import android.net.wifi.ScanResult;
32 import android.net.wifi.WifiConfiguration;
33 import android.net.wifi.WifiConfiguration.KeyMgmt;
34 import android.net.wifi.WifiInfo;
35 import android.net.wifi.WifiManager;
36 import android.os.Bundle;
37 import android.os.RemoteException;
38 import android.os.ServiceManager;
39 import android.os.UserHandle;
40 import android.support.annotation.NonNull;
41 import android.text.Spannable;
42 import android.text.SpannableString;
43 import android.text.TextUtils;
44 import android.text.style.TtsSpan;
45 import android.util.Log;
46 import android.util.LruCache;
47 
48 import com.android.settingslib.R;
49 
50 import java.util.ArrayList;
51 import java.util.Map;
52 
53 
54 public class AccessPoint implements Comparable<AccessPoint> {
55     static final String TAG = "SettingsLib.AccessPoint";
56 
57     /**
58      * Lower bound on the 2.4 GHz (802.11b/g/n) WLAN channels
59      */
60     public static final int LOWER_FREQ_24GHZ = 2400;
61 
62     /**
63      * Upper bound on the 2.4 GHz (802.11b/g/n) WLAN channels
64      */
65     public static final int HIGHER_FREQ_24GHZ = 2500;
66 
67     /**
68      * Lower bound on the 5.0 GHz (802.11a/h/j/n/ac) WLAN channels
69      */
70     public static final int LOWER_FREQ_5GHZ = 4900;
71 
72     /**
73      * Upper bound on the 5.0 GHz (802.11a/h/j/n/ac) WLAN channels
74      */
75     public static final int HIGHER_FREQ_5GHZ = 5900;
76 
77 
78     /**
79      * Experimental: we should be able to show the user the list of BSSIDs and bands
80      *  for that SSID.
81      *  For now this data is used only with Verbose Logging so as to show the band and number
82      *  of BSSIDs on which that network is seen.
83      */
84     public LruCache<String, ScanResult> mScanResultCache = new LruCache<String, ScanResult>(32);
85 
86     private static final String KEY_NETWORKINFO = "key_networkinfo";
87     private static final String KEY_WIFIINFO = "key_wifiinfo";
88     private static final String KEY_SCANRESULT = "key_scanresult";
89     private static final String KEY_SSID = "key_ssid";
90     private static final String KEY_SECURITY = "key_security";
91     private static final String KEY_PSKTYPE = "key_psktype";
92     private static final String KEY_SCANRESULTCACHE = "key_scanresultcache";
93     private static final String KEY_CONFIG = "key_config";
94 
95     /**
96      * These values are matched in string arrays -- changes must be kept in sync
97      */
98     public static final int SECURITY_NONE = 0;
99     public static final int SECURITY_WEP = 1;
100     public static final int SECURITY_PSK = 2;
101     public static final int SECURITY_EAP = 3;
102 
103     private static final int PSK_UNKNOWN = 0;
104     private static final int PSK_WPA = 1;
105     private static final int PSK_WPA2 = 2;
106     private static final int PSK_WPA_WPA2 = 3;
107 
108     public static final int SIGNAL_LEVELS = 4;
109 
110     private final Context mContext;
111 
112     private String ssid;
113     private String bssid;
114     private int security;
115     private int networkId = WifiConfiguration.INVALID_NETWORK_ID;
116 
117     private int pskType = PSK_UNKNOWN;
118 
119     private WifiConfiguration mConfig;
120 
121     private int mRssi = Integer.MAX_VALUE;
122     private long mSeen = 0;
123 
124     private WifiInfo mInfo;
125     private NetworkInfo mNetworkInfo;
126     private AccessPointListener mAccessPointListener;
127 
128     private Object mTag;
129 
AccessPoint(Context context, Bundle savedState)130     public AccessPoint(Context context, Bundle savedState) {
131         mContext = context;
132         mConfig = savedState.getParcelable(KEY_CONFIG);
133         if (mConfig != null) {
134             loadConfig(mConfig);
135         }
136         if (savedState.containsKey(KEY_SSID)) {
137             ssid = savedState.getString(KEY_SSID);
138         }
139         if (savedState.containsKey(KEY_SECURITY)) {
140             security = savedState.getInt(KEY_SECURITY);
141         }
142         if (savedState.containsKey(KEY_PSKTYPE)) {
143             pskType = savedState.getInt(KEY_PSKTYPE);
144         }
145         mInfo = (WifiInfo) savedState.getParcelable(KEY_WIFIINFO);
146         if (savedState.containsKey(KEY_NETWORKINFO)) {
147             mNetworkInfo = savedState.getParcelable(KEY_NETWORKINFO);
148         }
149         if (savedState.containsKey(KEY_SCANRESULTCACHE)) {
150             ArrayList<ScanResult> scanResultArrayList =
151                     savedState.getParcelableArrayList(KEY_SCANRESULTCACHE);
152             mScanResultCache.evictAll();
153             for (ScanResult result : scanResultArrayList) {
154                 mScanResultCache.put(result.BSSID, result);
155             }
156         }
157         update(mConfig, mInfo, mNetworkInfo);
158         mRssi = getRssi();
159         mSeen = getSeen();
160     }
161 
AccessPoint(Context context, ScanResult result)162     AccessPoint(Context context, ScanResult result) {
163         mContext = context;
164         initWithScanResult(result);
165     }
166 
AccessPoint(Context context, WifiConfiguration config)167     AccessPoint(Context context, WifiConfiguration config) {
168         mContext = context;
169         loadConfig(config);
170     }
171 
172     @Override
compareTo(@onNull AccessPoint other)173     public int compareTo(@NonNull AccessPoint other) {
174         // Active one goes first.
175         if (isActive() && !other.isActive()) return -1;
176         if (!isActive() && other.isActive()) return 1;
177 
178         // Reachable one goes before unreachable one.
179         if (mRssi != Integer.MAX_VALUE && other.mRssi == Integer.MAX_VALUE) return -1;
180         if (mRssi == Integer.MAX_VALUE && other.mRssi != Integer.MAX_VALUE) return 1;
181 
182         // Configured one goes before unconfigured one.
183         if (networkId != WifiConfiguration.INVALID_NETWORK_ID
184                 && other.networkId == WifiConfiguration.INVALID_NETWORK_ID) return -1;
185         if (networkId == WifiConfiguration.INVALID_NETWORK_ID
186                 && other.networkId != WifiConfiguration.INVALID_NETWORK_ID) return 1;
187 
188         // Sort by signal strength, bucketed by level
189         int difference = WifiManager.calculateSignalLevel(other.mRssi, SIGNAL_LEVELS)
190                 - WifiManager.calculateSignalLevel(mRssi, SIGNAL_LEVELS);
191         if (difference != 0) {
192             return difference;
193         }
194         // Sort by ssid.
195         return ssid.compareToIgnoreCase(other.ssid);
196     }
197 
198     @Override
equals(Object other)199     public boolean equals(Object other) {
200         if (!(other instanceof AccessPoint)) return false;
201         return (this.compareTo((AccessPoint) other) == 0);
202     }
203 
204     @Override
hashCode()205     public int hashCode() {
206         int result = 0;
207         if (mInfo != null) result += 13 * mInfo.hashCode();
208         result += 19 * mRssi;
209         result += 23 * networkId;
210         result += 29 * ssid.hashCode();
211         return result;
212     }
213 
214     @Override
toString()215     public String toString() {
216         StringBuilder builder = new StringBuilder().append("AccessPoint(")
217                 .append(ssid);
218         if (isSaved()) {
219             builder.append(',').append("saved");
220         }
221         if (isActive()) {
222             builder.append(',').append("active");
223         }
224         if (isEphemeral()) {
225             builder.append(',').append("ephemeral");
226         }
227         if (isConnectable()) {
228             builder.append(',').append("connectable");
229         }
230         if (security != SECURITY_NONE) {
231             builder.append(',').append(securityToString(security, pskType));
232         }
233         return builder.append(')').toString();
234     }
235 
matches(ScanResult result)236     public boolean matches(ScanResult result) {
237         return ssid.equals(result.SSID) && security == getSecurity(result);
238     }
239 
matches(WifiConfiguration config)240     public boolean matches(WifiConfiguration config) {
241         if (config.isPasspoint() && mConfig != null && mConfig.isPasspoint()) {
242             return config.FQDN.equals(mConfig.providerFriendlyName);
243         } else {
244             return ssid.equals(removeDoubleQuotes(config.SSID))
245                     && security == getSecurity(config)
246                     && (mConfig == null || mConfig.shared == config.shared);
247         }
248     }
249 
getConfig()250     public WifiConfiguration getConfig() {
251         return mConfig;
252     }
253 
clearConfig()254     public void clearConfig() {
255         mConfig = null;
256         networkId = WifiConfiguration.INVALID_NETWORK_ID;
257     }
258 
getInfo()259     public WifiInfo getInfo() {
260         return mInfo;
261     }
262 
getLevel()263     public int getLevel() {
264         if (mRssi == Integer.MAX_VALUE) {
265             return -1;
266         }
267         return WifiManager.calculateSignalLevel(mRssi, SIGNAL_LEVELS);
268     }
269 
getRssi()270     public int getRssi() {
271         int rssi = Integer.MIN_VALUE;
272         for (ScanResult result : mScanResultCache.snapshot().values()) {
273             if (result.level > rssi) {
274                 rssi = result.level;
275             }
276         }
277 
278         return rssi;
279     }
280 
getSeen()281     public long getSeen() {
282         long seen = 0;
283         for (ScanResult result : mScanResultCache.snapshot().values()) {
284             if (result.timestamp > seen) {
285                 seen = result.timestamp;
286             }
287         }
288 
289         return seen;
290     }
291 
getNetworkInfo()292     public NetworkInfo getNetworkInfo() {
293         return mNetworkInfo;
294     }
295 
getSecurity()296     public int getSecurity() {
297         return security;
298     }
299 
getSecurityString(boolean concise)300     public String getSecurityString(boolean concise) {
301         Context context = mContext;
302         if (mConfig != null && mConfig.isPasspoint()) {
303             return concise ? context.getString(R.string.wifi_security_short_eap) :
304                 context.getString(R.string.wifi_security_eap);
305         }
306         switch(security) {
307             case SECURITY_EAP:
308                 return concise ? context.getString(R.string.wifi_security_short_eap) :
309                     context.getString(R.string.wifi_security_eap);
310             case SECURITY_PSK:
311                 switch (pskType) {
312                     case PSK_WPA:
313                         return concise ? context.getString(R.string.wifi_security_short_wpa) :
314                             context.getString(R.string.wifi_security_wpa);
315                     case PSK_WPA2:
316                         return concise ? context.getString(R.string.wifi_security_short_wpa2) :
317                             context.getString(R.string.wifi_security_wpa2);
318                     case PSK_WPA_WPA2:
319                         return concise ? context.getString(R.string.wifi_security_short_wpa_wpa2) :
320                             context.getString(R.string.wifi_security_wpa_wpa2);
321                     case PSK_UNKNOWN:
322                     default:
323                         return concise ? context.getString(R.string.wifi_security_short_psk_generic)
324                                 : context.getString(R.string.wifi_security_psk_generic);
325                 }
326             case SECURITY_WEP:
327                 return concise ? context.getString(R.string.wifi_security_short_wep) :
328                     context.getString(R.string.wifi_security_wep);
329             case SECURITY_NONE:
330             default:
331                 return concise ? "" : context.getString(R.string.wifi_security_none);
332         }
333     }
334 
getSsidStr()335     public String getSsidStr() {
336         return ssid;
337     }
338 
getBssid()339     public String getBssid() {
340         return bssid;
341     }
342 
getSsid()343     public CharSequence getSsid() {
344         SpannableString str = new SpannableString(ssid);
345         str.setSpan(new TtsSpan.VerbatimBuilder(ssid).build(), 0, ssid.length(),
346                 Spannable.SPAN_INCLUSIVE_INCLUSIVE);
347         return str;
348     }
349 
getConfigName()350     public String getConfigName() {
351         if (mConfig != null && mConfig.isPasspoint()) {
352             return mConfig.providerFriendlyName;
353         } else {
354             return ssid;
355         }
356     }
357 
getDetailedState()358     public DetailedState getDetailedState() {
359         return mNetworkInfo != null ? mNetworkInfo.getDetailedState() : null;
360     }
361 
getSavedNetworkSummary()362     public String getSavedNetworkSummary() {
363         if (mConfig != null) {
364             PackageManager pm = mContext.getPackageManager();
365             String systemName = pm.getNameForUid(android.os.Process.SYSTEM_UID);
366             int userId = UserHandle.getUserId(mConfig.creatorUid);
367             ApplicationInfo appInfo = null;
368             if (mConfig.creatorName != null && mConfig.creatorName.equals(systemName)) {
369                 appInfo = mContext.getApplicationInfo();
370             } else {
371                 try {
372                     IPackageManager ipm = AppGlobals.getPackageManager();
373                     appInfo = ipm.getApplicationInfo(mConfig.creatorName, 0 /* flags */, userId);
374                 } catch (RemoteException rex) {
375                 }
376             }
377             if (appInfo != null &&
378                     !appInfo.packageName.equals(mContext.getString(R.string.settings_package)) &&
379                     !appInfo.packageName.equals(
380                     mContext.getString(R.string.certinstaller_package))) {
381                 return mContext.getString(R.string.saved_network, appInfo.loadLabel(pm));
382             }
383         }
384         return "";
385     }
386 
getSummary()387     public String getSummary() {
388         return getSettingsSummary();
389     }
390 
getSettingsSummary()391     public String getSettingsSummary() {
392         // Update to new summary
393         StringBuilder summary = new StringBuilder();
394 
395         if (isActive() && mConfig != null && mConfig.isPasspoint()) {
396             // This is the active connection on passpoint
397             summary.append(getSummary(mContext, getDetailedState(),
398                     false, mConfig.providerFriendlyName));
399         } else if (isActive()) {
400             // This is the active connection on non-passpoint network
401             summary.append(getSummary(mContext, getDetailedState(),
402                     mInfo != null && mInfo.isEphemeral()));
403         } else if (mConfig != null && mConfig.isPasspoint()) {
404             String format = mContext.getString(R.string.available_via_passpoint);
405             summary.append(String.format(format, mConfig.providerFriendlyName));
406         } else if (mConfig != null && mConfig.hasNoInternetAccess()) {
407             summary.append(mContext.getString(R.string.wifi_no_internet));
408         } else if (mConfig != null && !mConfig.getNetworkSelectionStatus().isNetworkEnabled()) {
409             WifiConfiguration.NetworkSelectionStatus networkStatus =
410                     mConfig.getNetworkSelectionStatus();
411             switch (networkStatus.getNetworkSelectionDisableReason()) {
412                 case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE:
413                     summary.append(mContext.getString(R.string.wifi_disabled_password_failure));
414                     break;
415                 case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE:
416                 case WifiConfiguration.NetworkSelectionStatus.DISABLED_DNS_FAILURE:
417                     summary.append(mContext.getString(R.string.wifi_disabled_network_failure));
418                     break;
419                 case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION:
420                     summary.append(mContext.getString(R.string.wifi_disabled_generic));
421                     break;
422             }
423         } else if (mRssi == Integer.MAX_VALUE) { // Wifi out of range
424             summary.append(mContext.getString(R.string.wifi_not_in_range));
425         } else { // In range, not disabled.
426             if (mConfig != null) { // Is saved network
427                 summary.append(mContext.getString(R.string.wifi_remembered));
428             }
429         }
430 
431         if (WifiTracker.sVerboseLogging > 0) {
432             // Add RSSI/band information for this config, what was seen up to 6 seconds ago
433             // verbose WiFi Logging is only turned on thru developers settings
434             if (mInfo != null && mNetworkInfo != null) { // This is the active connection
435                 summary.append(" f=" + Integer.toString(mInfo.getFrequency()));
436             }
437             summary.append(" " + getVisibilityStatus());
438             if (mConfig != null && !mConfig.getNetworkSelectionStatus().isNetworkEnabled()) {
439                 summary.append(" (" + mConfig.getNetworkSelectionStatus().getNetworkStatusString());
440                 if (mConfig.getNetworkSelectionStatus().getDisableTime() > 0) {
441                     long now = System.currentTimeMillis();
442                     long diff = (now - mConfig.getNetworkSelectionStatus().getDisableTime()) / 1000;
443                     long sec = diff%60; //seconds
444                     long min = (diff/60)%60; //minutes
445                     long hour = (min/60)%60; //hours
446                     summary.append(", ");
447                     if (hour > 0) summary.append(Long.toString(hour) + "h ");
448                     summary.append( Long.toString(min) + "m ");
449                     summary.append( Long.toString(sec) + "s ");
450                 }
451                 summary.append(")");
452             }
453 
454             if (mConfig != null) {
455                 WifiConfiguration.NetworkSelectionStatus networkStatus =
456                         mConfig.getNetworkSelectionStatus();
457                 for (int index = WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE;
458                         index < WifiConfiguration.NetworkSelectionStatus
459                         .NETWORK_SELECTION_DISABLED_MAX; index++) {
460                     if (networkStatus.getDisableReasonCounter(index) != 0) {
461                         summary.append(" " + WifiConfiguration.NetworkSelectionStatus
462                                 .getNetworkDisableReasonString(index) + "="
463                                 + networkStatus.getDisableReasonCounter(index));
464                     }
465                 }
466             }
467         }
468         return summary.toString();
469     }
470 
471     /**
472      * Returns the visibility status of the WifiConfiguration.
473      *
474      * @return autojoin debugging information
475      * TODO: use a string formatter
476      * ["rssi 5Ghz", "num results on 5GHz" / "rssi 5Ghz", "num results on 5GHz"]
477      * For instance [-40,5/-30,2]
478      */
getVisibilityStatus()479     private String getVisibilityStatus() {
480         StringBuilder visibility = new StringBuilder();
481         StringBuilder scans24GHz = null;
482         StringBuilder scans5GHz = null;
483         String bssid = null;
484 
485         long now = System.currentTimeMillis();
486 
487         if (mInfo != null) {
488             bssid = mInfo.getBSSID();
489             if (bssid != null) {
490                 visibility.append(" ").append(bssid);
491             }
492             visibility.append(" rssi=").append(mInfo.getRssi());
493             visibility.append(" ");
494             visibility.append(" score=").append(mInfo.score);
495             visibility.append(String.format(" tx=%.1f,", mInfo.txSuccessRate));
496             visibility.append(String.format("%.1f,", mInfo.txRetriesRate));
497             visibility.append(String.format("%.1f ", mInfo.txBadRate));
498             visibility.append(String.format("rx=%.1f", mInfo.rxSuccessRate));
499         }
500 
501         int rssi5 = WifiConfiguration.INVALID_RSSI;
502         int rssi24 = WifiConfiguration.INVALID_RSSI;
503         int num5 = 0;
504         int num24 = 0;
505         int numBlackListed = 0;
506         int n24 = 0; // Number scan results we included in the string
507         int n5 = 0; // Number scan results we included in the string
508         Map<String, ScanResult> list = mScanResultCache.snapshot();
509         // TODO: sort list by RSSI or age
510         for (ScanResult result : list.values()) {
511 
512             if (result.frequency >= LOWER_FREQ_5GHZ
513                     && result.frequency <= HIGHER_FREQ_5GHZ) {
514                 // Strictly speaking: [4915, 5825]
515                 // number of known BSSID on 5GHz band
516                 num5 = num5 + 1;
517             } else if (result.frequency >= LOWER_FREQ_24GHZ
518                     && result.frequency <= HIGHER_FREQ_24GHZ) {
519                 // Strictly speaking: [2412, 2482]
520                 // number of known BSSID on 2.4Ghz band
521                 num24 = num24 + 1;
522             }
523 
524 
525             if (result.frequency >= LOWER_FREQ_5GHZ
526                     && result.frequency <= HIGHER_FREQ_5GHZ) {
527                 if (result.level > rssi5) {
528                     rssi5 = result.level;
529                 }
530                 if (n5 < 4) {
531                     if (scans5GHz == null) scans5GHz = new StringBuilder();
532                     scans5GHz.append(" \n{").append(result.BSSID);
533                     if (bssid != null && result.BSSID.equals(bssid)) scans5GHz.append("*");
534                     scans5GHz.append("=").append(result.frequency);
535                     scans5GHz.append(",").append(result.level);
536                     scans5GHz.append("}");
537                     n5++;
538                 }
539             } else if (result.frequency >= LOWER_FREQ_24GHZ
540                     && result.frequency <= HIGHER_FREQ_24GHZ) {
541                 if (result.level > rssi24) {
542                     rssi24 = result.level;
543                 }
544                 if (n24 < 4) {
545                     if (scans24GHz == null) scans24GHz = new StringBuilder();
546                     scans24GHz.append(" \n{").append(result.BSSID);
547                     if (bssid != null && result.BSSID.equals(bssid)) scans24GHz.append("*");
548                     scans24GHz.append("=").append(result.frequency);
549                     scans24GHz.append(",").append(result.level);
550                     scans24GHz.append("}");
551                     n24++;
552                 }
553             }
554         }
555         visibility.append(" [");
556         if (num24 > 0) {
557             visibility.append("(").append(num24).append(")");
558             if (n24 <= 4) {
559                 if (scans24GHz != null) {
560                     visibility.append(scans24GHz.toString());
561                 }
562             } else {
563                 visibility.append("max=").append(rssi24);
564                 if (scans24GHz != null) {
565                     visibility.append(",").append(scans24GHz.toString());
566                 }
567             }
568         }
569         visibility.append(";");
570         if (num5 > 0) {
571             visibility.append("(").append(num5).append(")");
572             if (n5 <= 4) {
573                 if (scans5GHz != null) {
574                     visibility.append(scans5GHz.toString());
575                 }
576             } else {
577                 visibility.append("max=").append(rssi5);
578                 if (scans5GHz != null) {
579                     visibility.append(",").append(scans5GHz.toString());
580                 }
581             }
582         }
583         if (numBlackListed > 0)
584             visibility.append("!").append(numBlackListed);
585         visibility.append("]");
586 
587         return visibility.toString();
588     }
589 
590     /**
591      * Return whether this is the active connection.
592      * For ephemeral connections (networkId is invalid), this returns false if the network is
593      * disconnected.
594      */
isActive()595     public boolean isActive() {
596         return mNetworkInfo != null &&
597                 (networkId != WifiConfiguration.INVALID_NETWORK_ID ||
598                  mNetworkInfo.getState() != State.DISCONNECTED);
599     }
600 
isConnectable()601     public boolean isConnectable() {
602         return getLevel() != -1 && getDetailedState() == null;
603     }
604 
isEphemeral()605     public boolean isEphemeral() {
606         return mInfo != null && mInfo.isEphemeral() &&
607                 mNetworkInfo != null && mNetworkInfo.getState() != State.DISCONNECTED;
608     }
609 
isPasspoint()610     public boolean isPasspoint() {
611         return mConfig != null && mConfig.isPasspoint();
612     }
613 
614     /**
615      * Return whether the given {@link WifiInfo} is for this access point.
616      * If the current AP does not have a network Id then the config is used to
617      * match based on SSID and security.
618      */
isInfoForThisAccessPoint(WifiConfiguration config, WifiInfo info)619     private boolean isInfoForThisAccessPoint(WifiConfiguration config, WifiInfo info) {
620         if (isPasspoint() == false && networkId != WifiConfiguration.INVALID_NETWORK_ID) {
621             return networkId == info.getNetworkId();
622         } else if (config != null) {
623             return matches(config);
624         }
625         else {
626             // Might be an ephemeral connection with no WifiConfiguration. Try matching on SSID.
627             // (Note that we only do this if the WifiConfiguration explicitly equals INVALID).
628             // TODO: Handle hex string SSIDs.
629             return ssid.equals(removeDoubleQuotes(info.getSSID()));
630         }
631     }
632 
isSaved()633     public boolean isSaved() {
634         return networkId != WifiConfiguration.INVALID_NETWORK_ID;
635     }
636 
getTag()637     public Object getTag() {
638         return mTag;
639     }
640 
setTag(Object tag)641     public void setTag(Object tag) {
642         mTag = tag;
643     }
644 
645     /**
646      * Generate and save a default wifiConfiguration with common values.
647      * Can only be called for unsecured networks.
648      */
generateOpenNetworkConfig()649     public void generateOpenNetworkConfig() {
650         if (security != SECURITY_NONE)
651             throw new IllegalStateException();
652         if (mConfig != null)
653             return;
654         mConfig = new WifiConfiguration();
655         mConfig.SSID = AccessPoint.convertToQuotedString(ssid);
656         mConfig.allowedKeyManagement.set(KeyMgmt.NONE);
657     }
658 
loadConfig(WifiConfiguration config)659     void loadConfig(WifiConfiguration config) {
660         if (config.isPasspoint())
661             ssid = config.providerFriendlyName;
662         else
663             ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
664 
665         bssid = config.BSSID;
666         security = getSecurity(config);
667         networkId = config.networkId;
668         mConfig = config;
669     }
670 
initWithScanResult(ScanResult result)671     private void initWithScanResult(ScanResult result) {
672         ssid = result.SSID;
673         bssid = result.BSSID;
674         security = getSecurity(result);
675         if (security == SECURITY_PSK)
676             pskType = getPskType(result);
677         mRssi = result.level;
678         mSeen = result.timestamp;
679     }
680 
saveWifiState(Bundle savedState)681     public void saveWifiState(Bundle savedState) {
682         if (ssid != null) savedState.putString(KEY_SSID, getSsidStr());
683         savedState.putInt(KEY_SECURITY, security);
684         savedState.putInt(KEY_PSKTYPE, pskType);
685         if (mConfig != null) savedState.putParcelable(KEY_CONFIG, mConfig);
686         savedState.putParcelable(KEY_WIFIINFO, mInfo);
687         savedState.putParcelableArrayList(KEY_SCANRESULTCACHE,
688                 new ArrayList<ScanResult>(mScanResultCache.snapshot().values()));
689         if (mNetworkInfo != null) {
690             savedState.putParcelable(KEY_NETWORKINFO, mNetworkInfo);
691         }
692     }
693 
setListener(AccessPointListener listener)694     public void setListener(AccessPointListener listener) {
695         mAccessPointListener = listener;
696     }
697 
update(ScanResult result)698     boolean update(ScanResult result) {
699         if (matches(result)) {
700             /* Update the LRU timestamp, if BSSID exists */
701             mScanResultCache.get(result.BSSID);
702 
703             /* Add or update the scan result for the BSSID */
704             mScanResultCache.put(result.BSSID, result);
705 
706             int oldLevel = getLevel();
707             int oldRssi = getRssi();
708             mSeen = getSeen();
709             mRssi = (getRssi() + oldRssi)/2;
710             int newLevel = getLevel();
711 
712             if (newLevel > 0 && newLevel != oldLevel && mAccessPointListener != null) {
713                 mAccessPointListener.onLevelChanged(this);
714             }
715             // This flag only comes from scans, is not easily saved in config
716             if (security == SECURITY_PSK) {
717                 pskType = getPskType(result);
718             }
719 
720             if (mAccessPointListener != null) {
721                 mAccessPointListener.onAccessPointChanged(this);
722             }
723 
724             return true;
725         }
726         return false;
727     }
728 
update(WifiConfiguration config, WifiInfo info, NetworkInfo networkInfo)729     boolean update(WifiConfiguration config, WifiInfo info, NetworkInfo networkInfo) {
730         boolean reorder = false;
731         if (info != null && isInfoForThisAccessPoint(config, info)) {
732             reorder = (mInfo == null);
733             mRssi = info.getRssi();
734             mInfo = info;
735             mNetworkInfo = networkInfo;
736             if (mAccessPointListener != null) {
737                 mAccessPointListener.onAccessPointChanged(this);
738             }
739         } else if (mInfo != null) {
740             reorder = true;
741             mInfo = null;
742             mNetworkInfo = null;
743             if (mAccessPointListener != null) {
744                 mAccessPointListener.onAccessPointChanged(this);
745             }
746         }
747         return reorder;
748     }
749 
update(WifiConfiguration config)750     void update(WifiConfiguration config) {
751         mConfig = config;
752         networkId = config.networkId;
753         if (mAccessPointListener != null) {
754             mAccessPointListener.onAccessPointChanged(this);
755         }
756     }
757 
setRssi(int rssi)758     void setRssi(int rssi) {
759         mRssi = rssi;
760     }
761 
getSummary(Context context, String ssid, DetailedState state, boolean isEphemeral, String passpointProvider)762     public static String getSummary(Context context, String ssid, DetailedState state,
763             boolean isEphemeral, String passpointProvider) {
764         if (state == DetailedState.CONNECTED && ssid == null) {
765             if (TextUtils.isEmpty(passpointProvider) == false) {
766                 // Special case for connected + passpoint networks.
767                 String format = context.getString(R.string.connected_via_passpoint);
768                 return String.format(format, passpointProvider);
769             } else if (isEphemeral) {
770                 // Special case for connected + ephemeral networks.
771                 return context.getString(R.string.connected_via_wfa);
772             }
773         }
774 
775         // Case when there is wifi connected without internet connectivity.
776         final ConnectivityManager cm = (ConnectivityManager)
777                 context.getSystemService(Context.CONNECTIVITY_SERVICE);
778         if (state == DetailedState.CONNECTED) {
779             IWifiManager wifiManager = IWifiManager.Stub.asInterface(
780                     ServiceManager.getService(Context.WIFI_SERVICE));
781             Network nw;
782 
783             try {
784                 nw = wifiManager.getCurrentNetwork();
785             } catch (RemoteException e) {
786                 nw = null;
787             }
788             NetworkCapabilities nc = cm.getNetworkCapabilities(nw);
789             if (nc != null && !nc.hasCapability(nc.NET_CAPABILITY_VALIDATED)) {
790                 return context.getString(R.string.wifi_connected_no_internet);
791             }
792         }
793 
794         String[] formats = context.getResources().getStringArray((ssid == null)
795                 ? R.array.wifi_status : R.array.wifi_status_with_ssid);
796         int index = state.ordinal();
797 
798         if (index >= formats.length || formats[index].length() == 0) {
799             return "";
800         }
801         return String.format(formats[index], ssid);
802     }
803 
getSummary(Context context, DetailedState state, boolean isEphemeral)804     public static String getSummary(Context context, DetailedState state, boolean isEphemeral) {
805         return getSummary(context, null, state, isEphemeral, null);
806     }
807 
getSummary(Context context, DetailedState state, boolean isEphemeral, String passpointProvider)808     public static String getSummary(Context context, DetailedState state, boolean isEphemeral,
809             String passpointProvider) {
810         return getSummary(context, null, state, isEphemeral, passpointProvider);
811     }
812 
convertToQuotedString(String string)813     public static String convertToQuotedString(String string) {
814         return "\"" + string + "\"";
815     }
816 
getPskType(ScanResult result)817     private static int getPskType(ScanResult result) {
818         boolean wpa = result.capabilities.contains("WPA-PSK");
819         boolean wpa2 = result.capabilities.contains("WPA2-PSK");
820         if (wpa2 && wpa) {
821             return PSK_WPA_WPA2;
822         } else if (wpa2) {
823             return PSK_WPA2;
824         } else if (wpa) {
825             return PSK_WPA;
826         } else {
827             Log.w(TAG, "Received abnormal flag string: " + result.capabilities);
828             return PSK_UNKNOWN;
829         }
830     }
831 
getSecurity(ScanResult result)832     private static int getSecurity(ScanResult result) {
833         if (result.capabilities.contains("WEP")) {
834             return SECURITY_WEP;
835         } else if (result.capabilities.contains("PSK")) {
836             return SECURITY_PSK;
837         } else if (result.capabilities.contains("EAP")) {
838             return SECURITY_EAP;
839         }
840         return SECURITY_NONE;
841     }
842 
getSecurity(WifiConfiguration config)843     static int getSecurity(WifiConfiguration config) {
844         if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
845             return SECURITY_PSK;
846         }
847         if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
848                 config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
849             return SECURITY_EAP;
850         }
851         return (config.wepKeys[0] != null) ? SECURITY_WEP : SECURITY_NONE;
852     }
853 
securityToString(int security, int pskType)854     public static String securityToString(int security, int pskType) {
855         if (security == SECURITY_WEP) {
856             return "WEP";
857         } else if (security == SECURITY_PSK) {
858             if (pskType == PSK_WPA) {
859                 return "WPA";
860             } else if (pskType == PSK_WPA2) {
861                 return "WPA2";
862             } else if (pskType == PSK_WPA_WPA2) {
863                 return "WPA_WPA2";
864             }
865             return "PSK";
866         } else if (security == SECURITY_EAP) {
867             return "EAP";
868         }
869         return "NONE";
870     }
871 
removeDoubleQuotes(String string)872     static String removeDoubleQuotes(String string) {
873         if (TextUtils.isEmpty(string)) {
874             return "";
875         }
876         int length = string.length();
877         if ((length > 1) && (string.charAt(0) == '"')
878                 && (string.charAt(length - 1) == '"')) {
879             return string.substring(1, length - 1);
880         }
881         return string;
882     }
883 
884     public interface AccessPointListener {
onAccessPointChanged(AccessPoint accessPoint)885         void onAccessPointChanged(AccessPoint accessPoint);
onLevelChanged(AccessPoint accessPoint)886         void onLevelChanged(AccessPoint accessPoint);
887     }
888 }
889