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