1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi;
18
19import android.content.Context;
20
21import android.net.wifi.ScanResult;
22
23import android.net.wifi.WifiConfiguration;
24import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
25import android.net.wifi.WifiSsid;
26import android.os.Environment;
27import android.os.Process;
28import android.text.TextUtils;
29
30import android.util.LocalLog;
31import android.util.Log;
32
33import com.android.server.net.DelayedDiskWrite;
34
35import java.io.BufferedInputStream;
36import java.io.DataInputStream;
37import java.io.DataOutputStream;
38import java.io.EOFException;
39import java.io.FileInputStream;
40import java.io.FileNotFoundException;
41import java.io.IOException;
42import java.text.DateFormat;
43import java.util.BitSet;
44import java.util.HashMap;
45import java.util.HashSet;
46import java.util.List;
47import java.util.Map;
48import java.util.Set;
49import java.util.concurrent.ConcurrentHashMap;
50
51/**
52 * Provides an API to read and write the network history from WifiConfigurations to file
53 * This is largely separate and extra to the supplicant config file.
54 */
55public class WifiNetworkHistory {
56    public static final String TAG = "WifiNetworkHistory";
57    private static final boolean DBG = true;
58    private static final boolean VDBG = true;
59    static final String NETWORK_HISTORY_CONFIG_FILE = Environment.getDataDirectory()
60            + "/misc/wifi/networkHistory.txt";
61    /* Network History Keys */
62    private static final String SSID_KEY = "SSID";
63    static final String CONFIG_KEY = "CONFIG";
64    private static final String CONFIG_BSSID_KEY = "CONFIG_BSSID";
65    private static final String CHOICE_KEY = "CHOICE";
66    private static final String CHOICE_TIME_KEY = "CHOICE_TIME";
67    private static final String LINK_KEY = "LINK";
68    private static final String BSSID_KEY = "BSSID";
69    private static final String BSSID_KEY_END = "/BSSID";
70    private static final String RSSI_KEY = "RSSI";
71    private static final String FREQ_KEY = "FREQ";
72    private static final String DATE_KEY = "DATE";
73    private static final String MILLI_KEY = "MILLI";
74    private static final String NETWORK_ID_KEY = "ID";
75    private static final String PRIORITY_KEY = "PRIORITY";
76    private static final String DEFAULT_GW_KEY = "DEFAULT_GW";
77    private static final String AUTH_KEY = "AUTH";
78    private static final String BSSID_STATUS_KEY = "BSSID_STATUS";
79    private static final String SELF_ADDED_KEY = "SELF_ADDED";
80    private static final String FAILURE_KEY = "FAILURE";
81    private static final String DID_SELF_ADD_KEY = "DID_SELF_ADD";
82    private static final String PEER_CONFIGURATION_KEY = "PEER_CONFIGURATION";
83    static final String CREATOR_UID_KEY = "CREATOR_UID_KEY";
84    private static final String CONNECT_UID_KEY = "CONNECT_UID_KEY";
85    private static final String UPDATE_UID_KEY = "UPDATE_UID";
86    private static final String FQDN_KEY = "FQDN";
87    private static final String SCORER_OVERRIDE_KEY = "SCORER_OVERRIDE";
88    private static final String SCORER_OVERRIDE_AND_SWITCH_KEY = "SCORER_OVERRIDE_AND_SWITCH";
89    private static final String VALIDATED_INTERNET_ACCESS_KEY = "VALIDATED_INTERNET_ACCESS";
90    private static final String NO_INTERNET_ACCESS_REPORTS_KEY = "NO_INTERNET_ACCESS_REPORTS";
91    private static final String NO_INTERNET_ACCESS_EXPECTED_KEY = "NO_INTERNET_ACCESS_EXPECTED";
92    private static final String EPHEMERAL_KEY = "EPHEMERAL";
93    private static final String USE_EXTERNAL_SCORES_KEY = "USE_EXTERNAL_SCORES";
94    private static final String METERED_HINT_KEY = "METERED_HINT";
95    private static final String NUM_ASSOCIATION_KEY = "NUM_ASSOCIATION";
96    private static final String DELETED_EPHEMERAL_KEY = "DELETED_EPHEMERAL";
97    private static final String CREATOR_NAME_KEY = "CREATOR_NAME";
98    private static final String UPDATE_NAME_KEY = "UPDATE_NAME";
99    private static final String USER_APPROVED_KEY = "USER_APPROVED";
100    private static final String CREATION_TIME_KEY = "CREATION_TIME";
101    private static final String UPDATE_TIME_KEY = "UPDATE_TIME";
102    static final String SHARED_KEY = "SHARED";
103    private static final String NETWORK_SELECTION_STATUS_KEY = "NETWORK_SELECTION_STATUS";
104    private static final String NETWORK_SELECTION_DISABLE_REASON_KEY =
105            "NETWORK_SELECTION_DISABLE_REASON";
106    private static final String HAS_EVER_CONNECTED_KEY = "HAS_EVER_CONNECTED";
107
108    private static final String SEPARATOR = ":  ";
109    private static final String NL = "\n";
110
111    protected final DelayedDiskWrite mWriter;
112    Context mContext;
113    private final LocalLog mLocalLog;
114    /*
115     * Lost config list, whenever we read a config from networkHistory.txt that was not in
116     * wpa_supplicant.conf
117     */
118    HashSet<String> mLostConfigsDbg = new HashSet<String>();
119
120    public WifiNetworkHistory(Context c, LocalLog localLog, DelayedDiskWrite writer) {
121        mContext = c;
122        mWriter = writer;
123        mLocalLog = localLog;
124    }
125
126    /**
127     * Write network history to file, for configured networks
128     *
129     * @param networks List of ConfiguredNetworks to write to NetworkHistory
130     */
131    public void writeKnownNetworkHistory(final List<WifiConfiguration> networks,
132            final ConcurrentHashMap<Integer, ScanDetailCache> scanDetailCaches,
133            final Set<String> deletedEphemeralSSIDs) {
134
135        /* Make a copy */
136        //final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
137
138        //for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
139        //    networks.add(new WifiConfiguration(config));
140        //}
141
142        mWriter.write(NETWORK_HISTORY_CONFIG_FILE, new DelayedDiskWrite.Writer() {
143            public void onWriteCalled(DataOutputStream out) throws IOException {
144                for (WifiConfiguration config : networks) {
145                    //loge("onWriteCalled write SSID: " + config.SSID);
146                   /* if (config.getLinkProperties() != null)
147                        loge(" lp " + config.getLinkProperties().toString());
148                    else
149                        loge("attempt config w/o lp");
150                    */
151                    NetworkSelectionStatus status = config.getNetworkSelectionStatus();
152                    if (VDBG) {
153                        int numlink = 0;
154                        if (config.linkedConfigurations != null) {
155                            numlink = config.linkedConfigurations.size();
156                        }
157                        String disableTime;
158                        if (config.getNetworkSelectionStatus().isNetworkEnabled()) {
159                            disableTime = "";
160                        } else {
161                            disableTime = "Disable time: " + DateFormat.getInstance().format(
162                                    config.getNetworkSelectionStatus().getDisableTime());
163                        }
164                        logd("saving network history: " + config.configKey()  + " gw: "
165                                + config.defaultGwMacAddress + " Network Selection-status: "
166                                + status.getNetworkStatusString()
167                                + disableTime + " ephemeral=" + config.ephemeral
168                                + " choice:" + status.getConnectChoice()
169                                + " link:" + numlink
170                                + " status:" + config.status
171                                + " nid:" + config.networkId
172                                + " hasEverConnected: " + status.getHasEverConnected());
173                    }
174
175                    if (!isValid(config)) {
176                        continue;
177                    }
178
179                    if (config.SSID == null) {
180                        if (VDBG) {
181                            logv("writeKnownNetworkHistory trying to write config with null SSID");
182                        }
183                        continue;
184                    }
185                    if (VDBG) {
186                        logv("writeKnownNetworkHistory write config " + config.configKey());
187                    }
188                    out.writeUTF(CONFIG_KEY + SEPARATOR + config.configKey() + NL);
189
190                    if (config.SSID != null) {
191                        out.writeUTF(SSID_KEY + SEPARATOR + config.SSID + NL);
192                    }
193                    if (config.BSSID != null) {
194                        out.writeUTF(CONFIG_BSSID_KEY + SEPARATOR + config.BSSID + NL);
195                    } else {
196                        out.writeUTF(CONFIG_BSSID_KEY + SEPARATOR + "null" + NL);
197                    }
198                    if (config.FQDN != null) {
199                        out.writeUTF(FQDN_KEY + SEPARATOR + config.FQDN + NL);
200                    }
201
202                    out.writeUTF(PRIORITY_KEY + SEPARATOR + Integer.toString(config.priority) + NL);
203                    out.writeUTF(NETWORK_ID_KEY + SEPARATOR
204                            + Integer.toString(config.networkId) + NL);
205                    out.writeUTF(SELF_ADDED_KEY + SEPARATOR
206                            + Boolean.toString(config.selfAdded) + NL);
207                    out.writeUTF(DID_SELF_ADD_KEY + SEPARATOR
208                            + Boolean.toString(config.didSelfAdd) + NL);
209                    out.writeUTF(NO_INTERNET_ACCESS_REPORTS_KEY + SEPARATOR
210                            + Integer.toString(config.numNoInternetAccessReports) + NL);
211                    out.writeUTF(VALIDATED_INTERNET_ACCESS_KEY + SEPARATOR
212                            + Boolean.toString(config.validatedInternetAccess) + NL);
213                    out.writeUTF(NO_INTERNET_ACCESS_EXPECTED_KEY + SEPARATOR +
214                            Boolean.toString(config.noInternetAccessExpected) + NL);
215                    out.writeUTF(EPHEMERAL_KEY + SEPARATOR
216                            + Boolean.toString(config.ephemeral) + NL);
217                    out.writeUTF(METERED_HINT_KEY + SEPARATOR
218                            + Boolean.toString(config.meteredHint) + NL);
219                    out.writeUTF(USE_EXTERNAL_SCORES_KEY + SEPARATOR
220                            + Boolean.toString(config.useExternalScores) + NL);
221                    if (config.creationTime != null) {
222                        out.writeUTF(CREATION_TIME_KEY + SEPARATOR + config.creationTime + NL);
223                    }
224                    if (config.updateTime != null) {
225                        out.writeUTF(UPDATE_TIME_KEY + SEPARATOR + config.updateTime + NL);
226                    }
227                    if (config.peerWifiConfiguration != null) {
228                        out.writeUTF(PEER_CONFIGURATION_KEY + SEPARATOR
229                                + config.peerWifiConfiguration + NL);
230                    }
231                    out.writeUTF(SCORER_OVERRIDE_KEY + SEPARATOR
232                            + Integer.toString(config.numScorerOverride) + NL);
233                    out.writeUTF(SCORER_OVERRIDE_AND_SWITCH_KEY + SEPARATOR
234                            + Integer.toString(config.numScorerOverrideAndSwitchedNetwork) + NL);
235                    out.writeUTF(NUM_ASSOCIATION_KEY + SEPARATOR
236                            + Integer.toString(config.numAssociation) + NL);
237                    out.writeUTF(CREATOR_UID_KEY + SEPARATOR
238                            + Integer.toString(config.creatorUid) + NL);
239                    out.writeUTF(CONNECT_UID_KEY + SEPARATOR
240                            + Integer.toString(config.lastConnectUid) + NL);
241                    out.writeUTF(UPDATE_UID_KEY + SEPARATOR
242                            + Integer.toString(config.lastUpdateUid) + NL);
243                    out.writeUTF(CREATOR_NAME_KEY + SEPARATOR
244                            + config.creatorName + NL);
245                    out.writeUTF(UPDATE_NAME_KEY + SEPARATOR
246                            + config.lastUpdateName + NL);
247                    out.writeUTF(USER_APPROVED_KEY + SEPARATOR
248                            + Integer.toString(config.userApproved) + NL);
249                    out.writeUTF(SHARED_KEY + SEPARATOR + Boolean.toString(config.shared) + NL);
250                    String allowedKeyManagementString =
251                            makeString(config.allowedKeyManagement,
252                                    WifiConfiguration.KeyMgmt.strings);
253                    out.writeUTF(AUTH_KEY + SEPARATOR
254                            + allowedKeyManagementString + NL);
255                    out.writeUTF(NETWORK_SELECTION_STATUS_KEY + SEPARATOR
256                            + status.getNetworkSelectionStatus() + NL);
257                    out.writeUTF(NETWORK_SELECTION_DISABLE_REASON_KEY + SEPARATOR
258                            + status.getNetworkSelectionDisableReason() + NL);
259
260                    if (status.getConnectChoice() != null) {
261                        out.writeUTF(CHOICE_KEY + SEPARATOR + status.getConnectChoice() + NL);
262                        out.writeUTF(CHOICE_TIME_KEY + SEPARATOR
263                                + status.getConnectChoiceTimestamp() + NL);
264                    }
265
266                    if (config.linkedConfigurations != null) {
267                        log("writeKnownNetworkHistory write linked "
268                                + config.linkedConfigurations.size());
269
270                        for (String key : config.linkedConfigurations.keySet()) {
271                            out.writeUTF(LINK_KEY + SEPARATOR + key + NL);
272                        }
273                    }
274
275                    String macAddress = config.defaultGwMacAddress;
276                    if (macAddress != null) {
277                        out.writeUTF(DEFAULT_GW_KEY + SEPARATOR + macAddress + NL);
278                    }
279
280                    if (getScanDetailCache(config, scanDetailCaches) != null) {
281                        for (ScanDetail scanDetail : getScanDetailCache(config,
282                                    scanDetailCaches).values()) {
283                            ScanResult result = scanDetail.getScanResult();
284                            out.writeUTF(BSSID_KEY + SEPARATOR
285                                    + result.BSSID + NL);
286                            out.writeUTF(FREQ_KEY + SEPARATOR
287                                    + Integer.toString(result.frequency) + NL);
288
289                            out.writeUTF(RSSI_KEY + SEPARATOR
290                                    + Integer.toString(result.level) + NL);
291
292                            out.writeUTF(BSSID_KEY_END + NL);
293                        }
294                    }
295                    if (config.lastFailure != null) {
296                        out.writeUTF(FAILURE_KEY + SEPARATOR + config.lastFailure + NL);
297                    }
298                    out.writeUTF(HAS_EVER_CONNECTED_KEY + SEPARATOR
299                            + Boolean.toString(status.getHasEverConnected()) + NL);
300                    out.writeUTF(NL);
301                    // Add extra blank lines for clarity
302                    out.writeUTF(NL);
303                    out.writeUTF(NL);
304                }
305                if (deletedEphemeralSSIDs != null && deletedEphemeralSSIDs.size() > 0) {
306                    for (String ssid : deletedEphemeralSSIDs) {
307                        out.writeUTF(DELETED_EPHEMERAL_KEY);
308                        out.writeUTF(ssid);
309                        out.writeUTF(NL);
310                    }
311                }
312            }
313        });
314    }
315
316    /**
317     * Adds information stored in networkHistory.txt to the given configs. The configs are provided
318     * as a mapping from configKey to WifiConfiguration, because the WifiConfigurations themselves
319     * do not contain sufficient information to compute their configKeys until after the information
320     * that is stored in networkHistory.txt has been added to them.
321     *
322     * @param configs mapping from configKey to a WifiConfiguration that contains the information
323     *         information read from wpa_supplicant.conf
324     */
325    public void readNetworkHistory(Map<String, WifiConfiguration> configs,
326            ConcurrentHashMap<Integer, ScanDetailCache> scanDetailCaches,
327            Set<String> deletedEphemeralSSIDs) {
328        localLog("readNetworkHistory() path:" + NETWORK_HISTORY_CONFIG_FILE);
329
330        try (DataInputStream in =
331                     new DataInputStream(new BufferedInputStream(
332                             new FileInputStream(NETWORK_HISTORY_CONFIG_FILE)))) {
333
334            String bssid = null;
335            String ssid = null;
336
337            int freq = 0;
338            int status = 0;
339            long seen = 0;
340            int rssi = WifiConfiguration.INVALID_RSSI;
341            String caps = null;
342
343            WifiConfiguration config = null;
344            while (true) {
345                String line = in.readUTF();
346                if (line == null) {
347                    break;
348                }
349                int colon = line.indexOf(':');
350                if (colon < 0) {
351                    continue;
352                }
353
354                String key = line.substring(0, colon).trim();
355                String value = line.substring(colon + 1).trim();
356
357                if (key.equals(CONFIG_KEY)) {
358                    config = configs.get(value);
359
360                    // skip reading that configuration data
361                    // since we don't have a corresponding network ID
362                    if (config == null) {
363                        localLog("readNetworkHistory didnt find netid for hash="
364                                + Integer.toString(value.hashCode())
365                                + " key: " + value);
366                        mLostConfigsDbg.add(value);
367                        continue;
368                    } else {
369                        // After an upgrade count old connections as owned by system
370                        if (config.creatorName == null || config.lastUpdateName == null) {
371                            config.creatorName =
372                                mContext.getPackageManager().getNameForUid(Process.SYSTEM_UID);
373                            config.lastUpdateName = config.creatorName;
374
375                            if (DBG) {
376                                Log.w(TAG, "Upgrading network " + config.networkId
377                                        + " to " + config.creatorName);
378                            }
379                        }
380                    }
381                } else if (config != null) {
382                    NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
383                    switch (key) {
384                        case SSID_KEY:
385                            if (config.isPasspoint()) {
386                                break;
387                            }
388                            ssid = value;
389                            if (config.SSID != null && !config.SSID.equals(ssid)) {
390                                loge("Error parsing network history file, mismatched SSIDs");
391                                config = null; //error
392                                ssid = null;
393                            } else {
394                                config.SSID = ssid;
395                            }
396                            break;
397                        case CONFIG_BSSID_KEY:
398                            config.BSSID = value.equals("null") ? null : value;
399                            break;
400                        case FQDN_KEY:
401                            // Check for literal 'null' to be backwards compatible.
402                            config.FQDN = value.equals("null") ? null : value;
403                            break;
404                        case DEFAULT_GW_KEY:
405                            config.defaultGwMacAddress = value;
406                            break;
407                        case SELF_ADDED_KEY:
408                            config.selfAdded = Boolean.parseBoolean(value);
409                            break;
410                        case DID_SELF_ADD_KEY:
411                            config.didSelfAdd = Boolean.parseBoolean(value);
412                            break;
413                        case NO_INTERNET_ACCESS_REPORTS_KEY:
414                            config.numNoInternetAccessReports = Integer.parseInt(value);
415                            break;
416                        case VALIDATED_INTERNET_ACCESS_KEY:
417                            config.validatedInternetAccess = Boolean.parseBoolean(value);
418                            break;
419                        case NO_INTERNET_ACCESS_EXPECTED_KEY:
420                            config.noInternetAccessExpected = Boolean.parseBoolean(value);
421                            break;
422                        case CREATION_TIME_KEY:
423                            config.creationTime = value;
424                            break;
425                        case UPDATE_TIME_KEY:
426                            config.updateTime = value;
427                            break;
428                        case EPHEMERAL_KEY:
429                            config.ephemeral = Boolean.parseBoolean(value);
430                            break;
431                        case METERED_HINT_KEY:
432                            config.meteredHint = Boolean.parseBoolean(value);
433                            break;
434                        case USE_EXTERNAL_SCORES_KEY:
435                            config.useExternalScores = Boolean.parseBoolean(value);
436                            break;
437                        case CREATOR_UID_KEY:
438                            config.creatorUid = Integer.parseInt(value);
439                            break;
440                        case SCORER_OVERRIDE_KEY:
441                            config.numScorerOverride = Integer.parseInt(value);
442                            break;
443                        case SCORER_OVERRIDE_AND_SWITCH_KEY:
444                            config.numScorerOverrideAndSwitchedNetwork = Integer.parseInt(value);
445                            break;
446                        case NUM_ASSOCIATION_KEY:
447                            config.numAssociation = Integer.parseInt(value);
448                            break;
449                        case CONNECT_UID_KEY:
450                            config.lastConnectUid = Integer.parseInt(value);
451                            break;
452                        case UPDATE_UID_KEY:
453                            config.lastUpdateUid = Integer.parseInt(value);
454                            break;
455                        case FAILURE_KEY:
456                            config.lastFailure = value;
457                            break;
458                        case PEER_CONFIGURATION_KEY:
459                            config.peerWifiConfiguration = value;
460                            break;
461                        case NETWORK_SELECTION_STATUS_KEY:
462                            int networkStatusValue = Integer.parseInt(value);
463                            // Reset temporarily disabled network status
464                            if (networkStatusValue ==
465                                    NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED) {
466                                networkStatusValue =
467                                        NetworkSelectionStatus.NETWORK_SELECTION_ENABLED;
468                            }
469                            networkStatus.setNetworkSelectionStatus(networkStatusValue);
470                            break;
471                        case NETWORK_SELECTION_DISABLE_REASON_KEY:
472                            networkStatus.setNetworkSelectionDisableReason(Integer.parseInt(value));
473                            break;
474                        case CHOICE_KEY:
475                            networkStatus.setConnectChoice(value);
476                            break;
477                        case CHOICE_TIME_KEY:
478                            networkStatus.setConnectChoiceTimestamp(Long.parseLong(value));
479                            break;
480                        case LINK_KEY:
481                            if (config.linkedConfigurations == null) {
482                                config.linkedConfigurations = new HashMap<>();
483                            } else {
484                                config.linkedConfigurations.put(value, -1);
485                            }
486                            break;
487                        case BSSID_KEY:
488                            status = 0;
489                            ssid = null;
490                            bssid = null;
491                            freq = 0;
492                            seen = 0;
493                            rssi = WifiConfiguration.INVALID_RSSI;
494                            caps = "";
495                            break;
496                        case RSSI_KEY:
497                            rssi = Integer.parseInt(value);
498                            break;
499                        case FREQ_KEY:
500                            freq = Integer.parseInt(value);
501                            break;
502                        case DATE_KEY:
503                            /*
504                             * when reading the configuration from file we don't update the date
505                             * so as to avoid reading back stale or non-sensical data that would
506                             * depend on network time.
507                             * The date of a WifiConfiguration should only come from actual scan
508                             * result.
509                             *
510                            String s = key.replace(FREQ_KEY, "");
511                            seen = Integer.getInteger(s);
512                            */
513                            break;
514                        case BSSID_KEY_END:
515                            if ((bssid != null) && (ssid != null)) {
516                                if (getScanDetailCache(config, scanDetailCaches) != null) {
517                                    WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid);
518                                    ScanDetail scanDetail = new ScanDetail(wssid, bssid,
519                                            caps, rssi, freq, (long) 0, seen);
520                                    getScanDetailCache(config, scanDetailCaches).put(scanDetail);
521                                }
522                            }
523                            break;
524                        case DELETED_EPHEMERAL_KEY:
525                            if (!TextUtils.isEmpty(value)) {
526                                deletedEphemeralSSIDs.add(value);
527                            }
528                            break;
529                        case CREATOR_NAME_KEY:
530                            config.creatorName = value;
531                            break;
532                        case UPDATE_NAME_KEY:
533                            config.lastUpdateName = value;
534                            break;
535                        case USER_APPROVED_KEY:
536                            config.userApproved = Integer.parseInt(value);
537                            break;
538                        case SHARED_KEY:
539                            config.shared = Boolean.parseBoolean(value);
540                            break;
541                        case HAS_EVER_CONNECTED_KEY:
542                            networkStatus.setHasEverConnected(Boolean.parseBoolean(value));
543                            break;
544                    }
545                }
546            }
547        } catch (EOFException e) {
548            // do nothing
549        } catch (FileNotFoundException e) {
550            Log.i(TAG, "readNetworkHistory: no config file, " + e);
551        } catch (NumberFormatException e) {
552            Log.e(TAG, "readNetworkHistory: failed to parse, " + e, e);
553        } catch (IOException e) {
554            Log.e(TAG, "readNetworkHistory: failed to read, " + e, e);
555        }
556    }
557
558    /**
559     * Ported this out of WifiServiceImpl, I have no idea what it's doing
560     * <TODO> figure out what/why this is doing
561     * <TODO> Port it into WifiConfiguration, then remove all the silly business from ServiceImpl
562     */
563    public boolean isValid(WifiConfiguration config) {
564        if (config.allowedKeyManagement == null) {
565            return false;
566        }
567        if (config.allowedKeyManagement.cardinality() > 1) {
568            if (config.allowedKeyManagement.cardinality() != 2) {
569                return false;
570            }
571            if (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)) {
572                return false;
573            }
574            if ((!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X))
575                    && (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK))) {
576                return false;
577            }
578        }
579        return true;
580    }
581
582    private static String makeString(BitSet set, String[] strings) {
583        StringBuffer buf = new StringBuffer();
584        int nextSetBit = -1;
585
586        /* Make sure all set bits are in [0, strings.length) to avoid
587         * going out of bounds on strings.  (Shouldn't happen, but...) */
588        set = set.get(0, strings.length);
589
590        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
591            buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
592        }
593
594        // remove trailing space
595        if (set.cardinality() > 0) {
596            buf.setLength(buf.length() - 1);
597        }
598
599        return buf.toString();
600    }
601
602    protected void logv(String s) {
603        Log.v(TAG, s);
604    }
605    protected void logd(String s) {
606        Log.d(TAG, s);
607    }
608    protected void log(String s) {
609        Log.d(TAG, s);
610    }
611    protected void loge(String s) {
612        loge(s, false);
613    }
614    protected void loge(String s, boolean stack) {
615        if (stack) {
616            Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
617                    + " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
618                    + " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
619                    + " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
620        } else {
621            Log.e(TAG, s);
622        }
623    }
624
625    private void localLog(String s) {
626        if (mLocalLog != null) {
627            mLocalLog.log(s);
628        }
629    }
630
631    private ScanDetailCache getScanDetailCache(WifiConfiguration config,
632            ConcurrentHashMap<Integer, ScanDetailCache> scanDetailCaches) {
633        if (config == null || scanDetailCaches == null) return null;
634        ScanDetailCache cache = scanDetailCaches.get(config.networkId);
635        if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
636            cache = new ScanDetailCache(config);
637            scanDetailCaches.put(config.networkId, cache);
638        }
639        return cache;
640    }
641}
642