1 /*
2  * Copyright (C) 2010 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.settings.wifi;
18 
19 import com.android.settings.R;
20 
21 import android.content.Context;
22 import android.graphics.drawable.Drawable;
23 import android.graphics.drawable.StateListDrawable;
24 import android.net.NetworkInfo;
25 import android.net.NetworkInfo.DetailedState;
26 import android.net.NetworkInfo.State;
27 import android.net.wifi.ScanResult;
28 import android.net.wifi.WifiConfiguration;
29 import android.net.wifi.WifiConfiguration.KeyMgmt;
30 import android.net.wifi.WifiInfo;
31 import android.net.wifi.WifiManager;
32 import android.os.Bundle;
33 import android.preference.Preference;
34 import android.util.Log;
35 import android.util.LruCache;
36 import android.view.View;
37 import android.widget.ImageView;
38 import android.widget.TextView;
39 
40 import java.util.Map;
41 
42 
43 class AccessPoint extends Preference {
44     static final String TAG = "Settings.AccessPoint";
45 
46     /**
47      * Lower bound on the 2.4 GHz (802.11b/g/n) WLAN channels
48      */
49     public static final int LOWER_FREQ_24GHZ = 2400;
50 
51     /**
52      * Upper bound on the 2.4 GHz (802.11b/g/n) WLAN channels
53      */
54     public static final int HIGHER_FREQ_24GHZ = 2500;
55 
56     /**
57      * Lower bound on the 5.0 GHz (802.11a/h/j/n/ac) WLAN channels
58      */
59     public static final int LOWER_FREQ_5GHZ = 4900;
60 
61     /**
62      * Upper bound on the 5.0 GHz (802.11a/h/j/n/ac) WLAN channels
63      */
64     public static final int HIGHER_FREQ_5GHZ = 5900;
65 
66     /**
67      * Experimental: we should be able to show the user the list of BSSIDs and bands
68      *  for that SSID.
69      *  For now this data is used only with Verbose Logging so as to show the band and number
70      *  of BSSIDs on which that network is seen.
71      */
72     public LruCache<String, ScanResult> mScanResultCache;
73 
74 
75     private static final String KEY_NETWORKINFO = "key_networkinfo";
76     private static final String KEY_WIFIINFO = "key_wifiinfo";
77     private static final String KEY_SCANRESULT = "key_scanresult";
78     private static final String KEY_CONFIG = "key_config";
79 
80     private static final int[] STATE_SECURED = {
81         R.attr.state_encrypted
82     };
83     private static final int[] STATE_NONE = {};
84 
85     private static int[] wifi_signal_attributes = { R.attr.wifi_signal };
86 
87     /**
88      * These values are matched in string arrays -- changes must be kept in sync
89      */
90     static final int SECURITY_NONE = 0;
91     static final int SECURITY_WEP = 1;
92     static final int SECURITY_PSK = 2;
93     static final int SECURITY_EAP = 3;
94 
95     enum PskType {
96         UNKNOWN,
97         WPA,
98         WPA2,
99         WPA_WPA2
100     }
101 
102     String ssid;
103     String bssid;
104     int security;
105     int networkId = -1;
106     boolean wpsAvailable = false;
107     boolean showSummary = true;
108 
109     PskType pskType = PskType.UNKNOWN;
110 
111     private WifiConfiguration mConfig;
112     /* package */ScanResult mScanResult;
113 
114     private int mRssi = Integer.MAX_VALUE;
115     private long mSeen = 0;
116 
117     private WifiInfo mInfo;
118     private NetworkInfo mNetworkInfo;
119     private TextView mSummaryView;
120 
121     private static final int VISIBILITY_MAX_AGE_IN_MILLI = 1000000;
122     private static final int VISIBILITY_OUTDATED_AGE_IN_MILLI = 20000;
123     private static final int SECOND_TO_MILLI = 1000;
124 
getSecurity(WifiConfiguration config)125     static int getSecurity(WifiConfiguration config) {
126         if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
127             return SECURITY_PSK;
128         }
129         if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
130                 config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
131             return SECURITY_EAP;
132         }
133         return (config.wepKeys[0] != null) ? SECURITY_WEP : SECURITY_NONE;
134     }
135 
getSecurity(ScanResult result)136     private static int getSecurity(ScanResult result) {
137         if (result.capabilities.contains("WEP")) {
138             return SECURITY_WEP;
139         } else if (result.capabilities.contains("PSK")) {
140             return SECURITY_PSK;
141         } else if (result.capabilities.contains("EAP")) {
142             return SECURITY_EAP;
143         }
144         return SECURITY_NONE;
145     }
146 
getSecurityString(boolean concise)147     public String getSecurityString(boolean concise) {
148         Context context = getContext();
149         switch(security) {
150             case SECURITY_EAP:
151                 return concise ? context.getString(R.string.wifi_security_short_eap) :
152                     context.getString(R.string.wifi_security_eap);
153             case SECURITY_PSK:
154                 switch (pskType) {
155                     case WPA:
156                         return concise ? context.getString(R.string.wifi_security_short_wpa) :
157                             context.getString(R.string.wifi_security_wpa);
158                     case WPA2:
159                         return concise ? context.getString(R.string.wifi_security_short_wpa2) :
160                             context.getString(R.string.wifi_security_wpa2);
161                     case WPA_WPA2:
162                         return concise ? context.getString(R.string.wifi_security_short_wpa_wpa2) :
163                             context.getString(R.string.wifi_security_wpa_wpa2);
164                     case UNKNOWN:
165                     default:
166                         return concise ? context.getString(R.string.wifi_security_short_psk_generic)
167                                 : context.getString(R.string.wifi_security_psk_generic);
168                 }
169             case SECURITY_WEP:
170                 return concise ? context.getString(R.string.wifi_security_short_wep) :
171                     context.getString(R.string.wifi_security_wep);
172             case SECURITY_NONE:
173             default:
174                 return concise ? "" : context.getString(R.string.wifi_security_none);
175         }
176     }
177 
getPskType(ScanResult result)178     private static PskType getPskType(ScanResult result) {
179         boolean wpa = result.capabilities.contains("WPA-PSK");
180         boolean wpa2 = result.capabilities.contains("WPA2-PSK");
181         if (wpa2 && wpa) {
182             return PskType.WPA_WPA2;
183         } else if (wpa2) {
184             return PskType.WPA2;
185         } else if (wpa) {
186             return PskType.WPA;
187         } else {
188             Log.w(TAG, "Received abnormal flag string: " + result.capabilities);
189             return PskType.UNKNOWN;
190         }
191     }
192 
AccessPoint(Context context, WifiConfiguration config)193     AccessPoint(Context context, WifiConfiguration config) {
194         super(context);
195         loadConfig(config);
196         refresh();
197     }
198 
AccessPoint(Context context, ScanResult result)199     AccessPoint(Context context, ScanResult result) {
200         super(context);
201         loadResult(result);
202         refresh();
203     }
204 
AccessPoint(Context context, Bundle savedState)205     AccessPoint(Context context, Bundle savedState) {
206         super(context);
207 
208         mConfig = savedState.getParcelable(KEY_CONFIG);
209         if (mConfig != null) {
210             loadConfig(mConfig);
211         }
212         mScanResult = (ScanResult) savedState.getParcelable(KEY_SCANRESULT);
213         if (mScanResult != null) {
214             loadResult(mScanResult);
215         }
216         mInfo = (WifiInfo) savedState.getParcelable(KEY_WIFIINFO);
217         if (savedState.containsKey(KEY_NETWORKINFO)) {
218             mNetworkInfo = savedState.getParcelable(KEY_NETWORKINFO);
219         }
220         update(mInfo, mNetworkInfo);
221     }
222 
saveWifiState(Bundle savedState)223     public void saveWifiState(Bundle savedState) {
224         savedState.putParcelable(KEY_CONFIG, mConfig);
225         savedState.putParcelable(KEY_SCANRESULT, mScanResult);
226         savedState.putParcelable(KEY_WIFIINFO, mInfo);
227         if (mNetworkInfo != null) {
228             savedState.putParcelable(KEY_NETWORKINFO, mNetworkInfo);
229         }
230     }
231 
loadConfig(WifiConfiguration config)232     private void loadConfig(WifiConfiguration config) {
233         ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
234         bssid = config.BSSID;
235         security = getSecurity(config);
236         networkId = config.networkId;
237         mConfig = config;
238     }
239 
loadResult(ScanResult result)240     private void loadResult(ScanResult result) {
241         ssid = result.SSID;
242         bssid = result.BSSID;
243         security = getSecurity(result);
244         wpsAvailable = security != SECURITY_EAP && result.capabilities.contains("WPS");
245         if (security == SECURITY_PSK)
246             pskType = getPskType(result);
247         mRssi = result.level;
248         mScanResult = result;
249         if (result.seen > mSeen) {
250             mSeen = result.seen;
251         }
252     }
253 
254     @Override
onBindView(View view)255     protected void onBindView(View view) {
256         super.onBindView(view);
257         updateIcon(getLevel(), getContext());
258 
259         mSummaryView = (TextView) view.findViewById(com.android.internal.R.id.summary);
260         mSummaryView.setVisibility(showSummary ? View.VISIBLE : View.GONE);
261 
262         notifyChanged();
263     }
264 
updateIcon(int level, Context context)265     protected void updateIcon(int level, Context context) {
266         if (level == -1) {
267             setIcon(null);
268         } else {
269             Drawable drawable = getIcon();
270 
271             if (drawable == null) {
272                 // To avoid a drawing race condition, we first set the state (SECURE/NONE) and then
273                 // set the icon (drawable) to that state's drawable.
274                 StateListDrawable sld = (StateListDrawable) context.getTheme()
275                         .obtainStyledAttributes(wifi_signal_attributes).getDrawable(0);
276                 // If sld is null then we are indexing and therefore do not have access to
277                 // (nor need to display) the drawable.
278                 if (sld != null) {
279                     sld.setState((security != SECURITY_NONE) ? STATE_SECURED : STATE_NONE);
280                     drawable = sld.getCurrent();
281                     setIcon(drawable);
282                 }
283             }
284 
285             if (drawable != null) {
286                 drawable.setLevel(level);
287             }
288         }
289     }
290 
291     @Override
compareTo(Preference preference)292     public int compareTo(Preference preference) {
293         if (!(preference instanceof AccessPoint)) {
294             return 1;
295         }
296         AccessPoint other = (AccessPoint) preference;
297         // Active one goes first.
298         if (isActive() && !other.isActive()) return -1;
299         if (!isActive() && other.isActive()) return 1;
300 
301         // Reachable one goes before unreachable one.
302         if (mRssi != Integer.MAX_VALUE && other.mRssi == Integer.MAX_VALUE) return -1;
303         if (mRssi == Integer.MAX_VALUE && other.mRssi != Integer.MAX_VALUE) return 1;
304 
305         // Configured one goes before unconfigured one.
306         if (networkId != WifiConfiguration.INVALID_NETWORK_ID
307                 && other.networkId == WifiConfiguration.INVALID_NETWORK_ID) return -1;
308         if (networkId == WifiConfiguration.INVALID_NETWORK_ID
309                 && other.networkId != WifiConfiguration.INVALID_NETWORK_ID) return 1;
310 
311         // Sort by signal strength.
312         int difference = WifiManager.compareSignalLevel(other.mRssi, mRssi);
313         if (difference != 0) {
314             return difference;
315         }
316         // Sort by ssid.
317         return ssid.compareToIgnoreCase(other.ssid);
318     }
319 
320     @Override
equals(Object other)321     public boolean equals(Object other) {
322         if (!(other instanceof AccessPoint)) return false;
323         return (this.compareTo((AccessPoint) other) == 0);
324     }
325 
326     @Override
hashCode()327     public int hashCode() {
328         int result = 0;
329         if (mInfo != null) result += 13 * mInfo.hashCode();
330         result += 19 * mRssi;
331         result += 23 * networkId;
332         result += 29 * ssid.hashCode();
333         return result;
334     }
335 
update(ScanResult result)336     boolean update(ScanResult result) {
337         if (result.seen > mSeen) {
338             mSeen = result.seen;
339         }
340         if (WifiSettings.mVerboseLogging > 0) {
341             if (mScanResultCache == null) {
342                 mScanResultCache = new LruCache<String, ScanResult>(32);
343             }
344             mScanResultCache.put(result.BSSID, result);
345         }
346 
347         if (ssid.equals(result.SSID) && security == getSecurity(result)) {
348             if (WifiManager.compareSignalLevel(result.level, mRssi) > 0) {
349                 int oldLevel = getLevel();
350                 mRssi = result.level;
351                 if (getLevel() != oldLevel) {
352                     notifyChanged();
353                 }
354             }
355             // This flag only comes from scans, is not easily saved in config
356             if (security == SECURITY_PSK) {
357                 pskType = getPskType(result);
358             }
359             mScanResult = result;
360             refresh();
361             return true;
362         }
363         return false;
364     }
365 
366     /** Return whether the given {@link WifiInfo} is for this access point. */
isInfoForThisAccessPoint(WifiInfo info)367     private boolean isInfoForThisAccessPoint(WifiInfo info) {
368         if (networkId != WifiConfiguration.INVALID_NETWORK_ID) {
369             return networkId == info.getNetworkId();
370         } else {
371             // Might be an ephemeral connection with no WifiConfiguration. Try matching on SSID.
372             // (Note that we only do this if the WifiConfiguration explicitly equals INVALID).
373             // TODO: Handle hex string SSIDs.
374             return ssid.equals(removeDoubleQuotes(info.getSSID()));
375         }
376     }
377 
update(WifiInfo info, NetworkInfo networkInfo)378     void update(WifiInfo info, NetworkInfo networkInfo) {
379         boolean reorder = false;
380         if (info != null && isInfoForThisAccessPoint(info)) {
381             reorder = (mInfo == null);
382             mRssi = info.getRssi();
383             mInfo = info;
384             mNetworkInfo = networkInfo;
385             refresh();
386         } else if (mInfo != null) {
387             reorder = true;
388             mInfo = null;
389             mNetworkInfo = null;
390             refresh();
391         }
392         if (reorder) {
393             notifyHierarchyChanged();
394         }
395     }
396 
getLevel()397     int getLevel() {
398         if (mRssi == Integer.MAX_VALUE) {
399             return -1;
400         }
401         return WifiManager.calculateSignalLevel(mRssi, 4);
402     }
403 
getConfig()404     WifiConfiguration getConfig() {
405         return mConfig;
406     }
407 
getInfo()408     WifiInfo getInfo() {
409         return mInfo;
410     }
411 
getNetworkInfo()412     NetworkInfo getNetworkInfo() {
413         return mNetworkInfo;
414     }
415 
getState()416     DetailedState getState() {
417         return mNetworkInfo != null ? mNetworkInfo.getDetailedState() : null;
418     }
419 
removeDoubleQuotes(String string)420     static String removeDoubleQuotes(String string) {
421         int length = string.length();
422         if ((length > 1) && (string.charAt(0) == '"')
423                 && (string.charAt(length - 1) == '"')) {
424             return string.substring(1, length - 1);
425         }
426         return string;
427     }
428 
convertToQuotedString(String string)429     static String convertToQuotedString(String string) {
430         return "\"" + string + "\"";
431     }
432 
433     /**
434      * Shows or Hides the Summary of an AccessPoint.
435      *
436      * @param showSummary true will show the summary, false will hide the summary
437      */
setShowSummary(boolean showSummary)438     public void setShowSummary(boolean showSummary) {
439         this.showSummary = showSummary;
440         if (mSummaryView != null) {
441             mSummaryView.setVisibility(showSummary ? View.VISIBLE : View.GONE);
442         } // otherwise, will be handled in onBindView.
443     }
444 
445     /**
446      * Returns the visibility status of the WifiConfiguration.
447      *
448      * @return autojoin debugging information
449      * TODO: use a string formatter
450      * ["rssi 5Ghz", "num results on 5GHz" / "rssi 5Ghz", "num results on 5GHz"]
451      * For instance [-40,5/-30,2]
452      */
getVisibilityStatus()453     private String getVisibilityStatus() {
454         StringBuilder visibility = new StringBuilder();
455         StringBuilder scans24GHz = null;
456         StringBuilder scans5GHz = null;
457         String bssid = null;
458 
459         long now = System.currentTimeMillis();
460 
461         if (mInfo != null) {
462             bssid = mInfo.getBSSID();
463             if (bssid != null) {
464                 visibility.append(" ").append(bssid);
465             }
466             visibility.append(" rssi=").append(mInfo.getRssi());
467             visibility.append(" ");
468             visibility.append(" score=").append(mInfo.score);
469             visibility.append(String.format(" tx=%.1f,", mInfo.txSuccessRate));
470             visibility.append(String.format("%.1f,", mInfo.txRetriesRate));
471             visibility.append(String.format("%.1f ", mInfo.txBadRate));
472             visibility.append(String.format("rx=%.1f", mInfo.rxSuccessRate));
473         }
474 
475         if (mScanResultCache != null) {
476             int rssi5 = WifiConfiguration.INVALID_RSSI;
477             int rssi24 = WifiConfiguration.INVALID_RSSI;
478             int num5 = 0;
479             int num24 = 0;
480             int numBlackListed = 0;
481             int n24 = 0; // Number scan results we included in the string
482             int n5 = 0; // Number scan results we included in the string
483             Map<String, ScanResult> list = mScanResultCache.snapshot();
484             // TODO: sort list by RSSI or age
485             for (ScanResult result : list.values()) {
486                 if (result.seen == 0)
487                     continue;
488 
489                 if (result.autoJoinStatus != ScanResult.ENABLED) numBlackListed++;
490 
491                 if (result.frequency >= LOWER_FREQ_5GHZ
492                         && result.frequency <= HIGHER_FREQ_5GHZ) {
493                     // Strictly speaking: [4915, 5825]
494                     // number of known BSSID on 5GHz band
495                     num5 = num5 + 1;
496                 } else if (result.frequency >= LOWER_FREQ_24GHZ
497                         && result.frequency <= HIGHER_FREQ_24GHZ) {
498                     // Strictly speaking: [2412, 2482]
499                     // number of known BSSID on 2.4Ghz band
500                     num24 = num24 + 1;
501                 }
502 
503                 // Ignore results seen, older than 20 seconds
504                 if (now - result.seen > VISIBILITY_OUTDATED_AGE_IN_MILLI) continue;
505 
506                 if (result.frequency >= LOWER_FREQ_5GHZ
507                         && result.frequency <= HIGHER_FREQ_5GHZ) {
508                     if (result.level > rssi5) {
509                         rssi5 = result.level;
510                     }
511                     if (n5 < 4) {
512                         if (scans5GHz == null) scans5GHz = new StringBuilder();
513                         scans5GHz.append(" \n{").append(result.BSSID);
514                         if (bssid != null && result.BSSID.equals(bssid)) scans5GHz.append("*");
515                         scans5GHz.append("=").append(result.frequency);
516                         scans5GHz.append(",").append(result.level);
517                         if (result.autoJoinStatus != 0) {
518                             scans5GHz.append(",st=").append(result.autoJoinStatus);
519                         }
520                         if (result.numIpConfigFailures != 0) {
521                             scans5GHz.append(",ipf=").append(result.numIpConfigFailures);
522                         }
523                         scans5GHz.append("}");
524                         n5++;
525                     }
526                 } else if (result.frequency >= LOWER_FREQ_24GHZ
527                         && result.frequency <= HIGHER_FREQ_24GHZ) {
528                     if (result.level > rssi24) {
529                         rssi24 = result.level;
530                     }
531                     if (n24 < 4) {
532                         if (scans24GHz == null) scans24GHz = new StringBuilder();
533                         scans24GHz.append(" \n{").append(result.BSSID);
534                         if (bssid != null && result.BSSID.equals(bssid)) scans24GHz.append("*");
535                         scans24GHz.append("=").append(result.frequency);
536                         scans24GHz.append(",").append(result.level);
537                         if (result.autoJoinStatus != 0) {
538                             scans24GHz.append(",st=").append(result.autoJoinStatus);
539                         }
540                         if (result.numIpConfigFailures != 0) {
541                             scans24GHz.append(",ipf=").append(result.numIpConfigFailures);
542                         }
543                         scans24GHz.append("}");
544                         n24++;
545                     }
546                 }
547             }
548             visibility.append(" [");
549             if (num24 > 0) {
550                 visibility.append("(").append(num24).append(")");
551                 if (n24 <= 4) {
552                     if (scans24GHz != null) {
553                         visibility.append(scans24GHz.toString());
554                     }
555                 } else {
556                     visibility.append("max=").append(rssi24);
557                     if (scans24GHz != null) {
558                         visibility.append(",").append(scans24GHz.toString());
559                     }
560                 }
561             }
562             visibility.append(";");
563             if (num5 > 0) {
564                 visibility.append("(").append(num5).append(")");
565                 if (n5 <= 4) {
566                     if (scans5GHz != null) {
567                         visibility.append(scans5GHz.toString());
568                     }
569                 } else {
570                     visibility.append("max=").append(rssi5);
571                     if (scans5GHz != null) {
572                         visibility.append(",").append(scans5GHz.toString());
573                     }
574                 }
575             }
576             if (numBlackListed > 0)
577                 visibility.append("!").append(numBlackListed);
578             visibility.append("]");
579         } else {
580             if (mRssi != Integer.MAX_VALUE) {
581                 visibility.append(" rssi=");
582                 visibility.append(mRssi);
583                 if (mScanResult != null) {
584                     visibility.append(", f=");
585                     visibility.append(mScanResult.frequency);
586                 }
587             }
588         }
589 
590         return visibility.toString();
591     }
592 
593     /**
594      * Return whether this is the active connection.
595      * For ephemeral connections (networkId is invalid), this returns false if the network is
596      * disconnected.
597      */
isActive()598     boolean isActive() {
599         return mNetworkInfo != null &&
600                 (networkId != WifiConfiguration.INVALID_NETWORK_ID ||
601                  mNetworkInfo.getState() != State.DISCONNECTED);
602     }
603 
604     /**
605      * Updates the title and summary; may indirectly call notifyChanged().
606      */
refresh()607     private void refresh() {
608         setTitle(ssid);
609 
610         final Context context = getContext();
611         updateIcon(getLevel(), context);
612 
613         // Force new summary
614         setSummary(null);
615 
616         // Update to new summary
617         StringBuilder summary = new StringBuilder();
618 
619         if (isActive()) { // This is the active connection
620             summary.append(Summary.get(context, getState(),
621                     networkId == WifiConfiguration.INVALID_NETWORK_ID));
622         } else if (mConfig != null
623                 && mConfig.hasNoInternetAccess()) {
624             summary.append(context.getString(R.string.wifi_no_internet));
625         } else if (mConfig != null && ((mConfig.status == WifiConfiguration.Status.DISABLED &&
626                 mConfig.disableReason != WifiConfiguration.DISABLED_UNKNOWN_REASON)
627                || mConfig.autoJoinStatus
628                 >= WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE)) {
629             if (mConfig.autoJoinStatus
630                     >= WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
631                 if (mConfig.disableReason == WifiConfiguration.DISABLED_DHCP_FAILURE) {
632                     summary.append(context.getString(R.string.wifi_disabled_network_failure));
633                 } else if (mConfig.disableReason == WifiConfiguration.DISABLED_AUTH_FAILURE) {
634                     summary.append(context.getString(R.string.wifi_disabled_password_failure));
635                 } else {
636                     summary.append(context.getString(R.string.wifi_disabled_wifi_failure));
637                 }
638             } else {
639                 switch (mConfig.disableReason) {
640                     case WifiConfiguration.DISABLED_AUTH_FAILURE:
641                         summary.append(context.getString(R.string.wifi_disabled_password_failure));
642                         break;
643                     case WifiConfiguration.DISABLED_DHCP_FAILURE:
644                     case WifiConfiguration.DISABLED_DNS_FAILURE:
645                         summary.append(context.getString(R.string.wifi_disabled_network_failure));
646                         break;
647                     case WifiConfiguration.DISABLED_UNKNOWN_REASON:
648                     case WifiConfiguration.DISABLED_ASSOCIATION_REJECT:
649                         summary.append(context.getString(R.string.wifi_disabled_generic));
650                         break;
651                 }
652             }
653         } else if (mRssi == Integer.MAX_VALUE) { // Wifi out of range
654             summary.append(context.getString(R.string.wifi_not_in_range));
655         } else { // In range, not disabled.
656             if (mConfig != null) { // Is saved network
657                 summary.append(context.getString(R.string.wifi_remembered));
658             }
659         }
660 
661         if (WifiSettings.mVerboseLogging > 0) {
662             // Add RSSI/band information for this config, what was seen up to 6 seconds ago
663             // verbose WiFi Logging is only turned on thru developers settings
664             if (mInfo != null && mNetworkInfo != null) { // This is the active connection
665                 summary.append(" f=" + Integer.toString(mInfo.getFrequency()));
666             }
667             summary.append(" " + getVisibilityStatus());
668             if (mConfig != null && mConfig.autoJoinStatus > 0) {
669                 summary.append(" (" + mConfig.autoJoinStatus);
670                 if (mConfig.blackListTimestamp > 0) {
671                     long now = System.currentTimeMillis();
672                     long diff = (now - mConfig.blackListTimestamp)/1000;
673                     long sec = diff%60; //seconds
674                     long min = (diff/60)%60; //minutes
675                     long hour = (min/60)%60; //hours
676                     summary.append(", ");
677                     if (hour > 0) summary.append(Long.toString(hour) + "h ");
678                     summary.append( Long.toString(min) + "m ");
679                     summary.append( Long.toString(sec) + "s ");
680                 }
681                 summary.append(")");
682             }
683             if (mConfig != null && mConfig.numIpConfigFailures > 0) {
684                 summary.append(" ipf=").append(mConfig.numIpConfigFailures);
685             }
686             if (mConfig != null && mConfig.numConnectionFailures > 0) {
687                 summary.append(" cf=").append(mConfig.numConnectionFailures);
688             }
689             if (mConfig != null && mConfig.numAuthFailures > 0) {
690                 summary.append(" authf=").append(mConfig.numAuthFailures);
691             }
692             if (mConfig != null && mConfig.numNoInternetAccessReports > 0) {
693                 summary.append(" noInt=").append(mConfig.numNoInternetAccessReports);
694             }
695         }
696 
697         if (summary.length() > 0) {
698             setSummary(summary.toString());
699             setShowSummary(true);
700         } else {
701             setShowSummary(false);
702         }
703     }
704 
705     /**
706      * Generate and save a default wifiConfiguration with common values.
707      * Can only be called for unsecured networks.
708      * @hide
709      */
generateOpenNetworkConfig()710     protected void generateOpenNetworkConfig() {
711         if (security != SECURITY_NONE)
712             throw new IllegalStateException();
713         if (mConfig != null)
714             return;
715         mConfig = new WifiConfiguration();
716         mConfig.SSID = AccessPoint.convertToQuotedString(ssid);
717         mConfig.allowedKeyManagement.set(KeyMgmt.NONE);
718     }
719 }
720