Home | History | Annotate | Download | only in wifi
      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 
    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 
    162     AccessPoint(Context context, ScanResult result) {
    163         mContext = context;
    164         initWithScanResult(result);
    165     }
    166 
    167     AccessPoint(Context context, WifiConfiguration config) {
    168         mContext = context;
    169         loadConfig(config);
    170     }
    171 
    172     @Override
    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
    199     public boolean equals(Object other) {
    200         if (!(other instanceof AccessPoint)) return false;
    201         return (this.compareTo((AccessPoint) other) == 0);
    202     }
    203 
    204     @Override
    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
    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 
    236     public boolean matches(ScanResult result) {
    237         return ssid.equals(result.SSID) && security == getSecurity(result);
    238     }
    239 
    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 
    250     public WifiConfiguration getConfig() {
    251         return mConfig;
    252     }
    253 
    254     public void clearConfig() {
    255         mConfig = null;
    256         networkId = WifiConfiguration.INVALID_NETWORK_ID;
    257     }
    258 
    259     public WifiInfo getInfo() {
    260         return mInfo;
    261     }
    262 
    263     public int getLevel() {
    264         if (mRssi == Integer.MAX_VALUE) {
    265             return -1;
    266         }
    267         return WifiManager.calculateSignalLevel(mRssi, SIGNAL_LEVELS);
    268     }
    269 
    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 
    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 
    292     public NetworkInfo getNetworkInfo() {
    293         return mNetworkInfo;
    294     }
    295 
    296     public int getSecurity() {
    297         return security;
    298     }
    299 
    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 
    335     public String getSsidStr() {
    336         return ssid;
    337     }
    338 
    339     public String getBssid() {
    340         return bssid;
    341     }
    342 
    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 
    350     public String getConfigName() {
    351         if (mConfig != null && mConfig.isPasspoint()) {
    352             return mConfig.providerFriendlyName;
    353         } else {
    354             return ssid;
    355         }
    356     }
    357 
    358     public DetailedState getDetailedState() {
    359         return mNetworkInfo != null ? mNetworkInfo.getDetailedState() : null;
    360     }
    361 
    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 
    387     public String getSummary() {
    388         return getSettingsSummary();
    389     }
    390 
    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      */
    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      */
    595     public boolean isActive() {
    596         return mNetworkInfo != null &&
    597                 (networkId != WifiConfiguration.INVALID_NETWORK_ID ||
    598                  mNetworkInfo.getState() != State.DISCONNECTED);
    599     }
    600 
    601     public boolean isConnectable() {
    602         return getLevel() != -1 && getDetailedState() == null;
    603     }
    604 
    605     public boolean isEphemeral() {
    606         return mInfo != null && mInfo.isEphemeral() &&
    607                 mNetworkInfo != null && mNetworkInfo.getState() != State.DISCONNECTED;
    608     }
    609 
    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      */
    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 
    633     public boolean isSaved() {
    634         return networkId != WifiConfiguration.INVALID_NETWORK_ID;
    635     }
    636 
    637     public Object getTag() {
    638         return mTag;
    639     }
    640 
    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      */
    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 
    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 
    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 
    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 
    694     public void setListener(AccessPointListener listener) {
    695         mAccessPointListener = listener;
    696     }
    697 
    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 
    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 
    750     void update(WifiConfiguration config) {
    751         mConfig = config;
    752         networkId = config.networkId;
    753         if (mAccessPointListener != null) {
    754             mAccessPointListener.onAccessPointChanged(this);
    755         }
    756     }
    757 
    758     void setRssi(int rssi) {
    759         mRssi = rssi;
    760     }
    761 
    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 
    804     public static String getSummary(Context context, DetailedState state, boolean isEphemeral) {
    805         return getSummary(context, null, state, isEphemeral, null);
    806     }
    807 
    808     public static String getSummary(Context context, DetailedState state, boolean isEphemeral,
    809             String passpointProvider) {
    810         return getSummary(context, null, state, isEphemeral, passpointProvider);
    811     }
    812 
    813     public static String convertToQuotedString(String string) {
    814         return "\"" + string + "\"";
    815     }
    816 
    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 
    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 
    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 
    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 
    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 {
    885         void onAccessPointChanged(AccessPoint accessPoint);
    886         void onLevelChanged(AccessPoint accessPoint);
    887     }
    888 }
    889