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 
17 package com.android.server.wifi;
18 
19 import android.net.IpConfiguration.IpAssignment;
20 import android.net.IpConfiguration.ProxySettings;
21 import android.net.wifi.WifiConfiguration;
22 import android.net.wifi.WifiConfiguration.Status;
23 import android.net.wifi.WifiEnterpriseConfig;
24 import android.net.wifi.WifiSsid;
25 import android.net.wifi.WpsInfo;
26 import android.net.wifi.WpsResult;
27 import android.os.FileObserver;
28 import android.os.Process;
29 import android.security.Credentials;
30 import android.security.KeyChain;
31 import android.security.KeyStore;
32 import android.text.TextUtils;
33 import android.util.ArraySet;
34 import android.util.LocalLog;
35 import android.util.Log;
36 import android.util.SparseArray;
37 
38 import com.android.server.wifi.hotspot2.Utils;
39 
40 import org.json.JSONException;
41 import org.json.JSONObject;
42 
43 import java.io.BufferedReader;
44 import java.io.File;
45 import java.io.FileNotFoundException;
46 import java.io.FileReader;
47 import java.io.IOException;
48 import java.net.URLDecoder;
49 import java.nio.charset.StandardCharsets;
50 import java.security.PrivateKey;
51 import java.security.cert.Certificate;
52 import java.security.cert.CertificateException;
53 import java.security.cert.X509Certificate;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.BitSet;
57 import java.util.Collection;
58 import java.util.HashMap;
59 import java.util.HashSet;
60 import java.util.List;
61 import java.util.Map;
62 import java.util.Set;
63 
64 /**
65  * This class provides the API's to save/load/modify network configurations from a persistent
66  * config database.
67  * We use wpa_supplicant as our config database currently, but will be migrating to a different
68  * one sometime in the future.
69  * We use keystore for certificate/key management operations.
70  *
71  * NOTE: This class should only be used from WifiConfigManager!!!
72  */
73 public class WifiConfigStore {
74 
75     public static final String TAG = "WifiConfigStore";
76     // This is the only variable whose contents will not be interpreted by wpa_supplicant. We use it
77     // to store metadata that allows us to correlate a wpa_supplicant.conf entry with additional
78     // information about the same network stored in other files. The metadata is stored as a
79     // serialized JSON dictionary.
80     public static final String ID_STRING_VAR_NAME = "id_str";
81     public static final String ID_STRING_KEY_FQDN = "fqdn";
82     public static final String ID_STRING_KEY_CREATOR_UID = "creatorUid";
83     public static final String ID_STRING_KEY_CONFIG_KEY = "configKey";
84     public static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf";
85     public static final String SUPPLICANT_CONFIG_FILE_BACKUP = SUPPLICANT_CONFIG_FILE + ".tmp";
86 
87     // Value stored by supplicant to requirePMF
88     public static final int STORED_VALUE_FOR_REQUIRE_PMF = 2;
89 
90     private static final boolean DBG = true;
91     private static boolean VDBG = false;
92 
93     private final LocalLog mLocalLog;
94     private final WpaConfigFileObserver mFileObserver;
95     private final WifiNative mWifiNative;
96     private final KeyStore mKeyStore;
97     private final boolean mShowNetworks;
98     private final HashSet<String> mBssidBlacklist = new HashSet<String>();
99 
100     private final BackupManagerProxy mBackupManagerProxy;
101 
WifiConfigStore(WifiNative wifiNative, KeyStore keyStore, LocalLog localLog, boolean showNetworks, boolean verboseDebug)102     WifiConfigStore(WifiNative wifiNative, KeyStore keyStore, LocalLog localLog,
103             boolean showNetworks, boolean verboseDebug) {
104         mWifiNative = wifiNative;
105         mKeyStore = keyStore;
106         mShowNetworks = showNetworks;
107         mBackupManagerProxy = new BackupManagerProxy();
108 
109         if (mShowNetworks) {
110             mLocalLog = localLog;
111             mFileObserver = new WpaConfigFileObserver();
112             mFileObserver.startWatching();
113         } else {
114             mLocalLog = null;
115             mFileObserver = null;
116         }
117         VDBG = verboseDebug;
118     }
119 
removeDoubleQuotes(String string)120     private static String removeDoubleQuotes(String string) {
121         int length = string.length();
122         if ((length > 1) && (string.charAt(0) == '"')
123                 && (string.charAt(length - 1) == '"')) {
124             return string.substring(1, length - 1);
125         }
126         return string;
127     }
128 
129     /**
130      * Generate a string to be used as a key value by wpa_supplicant from
131      * 'set', within the set of strings from 'strings' for the variable concatenated.
132      * Also transform the internal string format that uses _ (for bewildering
133      * reasons) into a wpa_supplicant adjusted value, that uses - as a separator
134      * (most of the time at least...).
135      * @param set a bit set with a one for each corresponding string to be included from strings.
136      * @param strings the set of string literals to concatenate strinfs from.
137      * @return A wpa_supplicant formatted value.
138      */
makeString(BitSet set, String[] strings)139     private static String makeString(BitSet set, String[] strings) {
140         return makeStringWithException(set, strings, null);
141     }
142 
143     /**
144      * Same as makeString with an exclusion parameter.
145      * @param set a bit set with a one for each corresponding string to be included from strings.
146      * @param strings the set of string literals to concatenate strinfs from.
147      * @param exception literal string to be excluded from the _ to - transformation.
148      * @return A wpa_supplicant formatted value.
149      */
makeStringWithException(BitSet set, String[] strings, String exception)150     private static String makeStringWithException(BitSet set, String[] strings, String exception) {
151         StringBuilder result = new StringBuilder();
152 
153         /* Make sure all set bits are in [0, strings.length) to avoid
154          * going out of bounds on strings.  (Shouldn't happen, but...) */
155         BitSet trimmedSet = set.get(0, strings.length);
156 
157         List<String> valueSet = new ArrayList<>();
158         for (int bit = trimmedSet.nextSetBit(0);
159              bit >= 0;
160              bit = trimmedSet.nextSetBit(bit+1)) {
161             String currentName = strings[bit];
162             if (exception != null && currentName.equals(exception)) {
163                 valueSet.add(currentName);
164             } else {
165                 // Most wpa_supplicant strings use a dash whereas (for some bizarre
166                 // reason) the strings are defined with underscore in the code...
167                 valueSet.add(currentName.replace('_', '-'));
168             }
169         }
170         return TextUtils.join(" ", valueSet);
171     }
172 
173     /*
174      * Convert string to Hexadecimal before passing to wifi native layer
175      * In native function "doCommand()" have trouble in converting Unicode character string to UTF8
176      * conversion to hex is required because SSIDs can have space characters in them;
177      * and that can confuses the supplicant because it uses space charaters as delimiters
178      */
encodeSSID(String str)179     private static String encodeSSID(String str) {
180         return Utils.toHex(removeDoubleQuotes(str).getBytes(StandardCharsets.UTF_8));
181     }
182 
183     // Certificate and private key management for EnterpriseConfig
needsKeyStore(WifiEnterpriseConfig config)184     private static boolean needsKeyStore(WifiEnterpriseConfig config) {
185         return (!(config.getClientCertificate() == null && config.getCaCertificate() == null));
186     }
187 
isHardwareBackedKey(PrivateKey key)188     private static boolean isHardwareBackedKey(PrivateKey key) {
189         return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm());
190     }
191 
hasHardwareBackedKey(Certificate certificate)192     private static boolean hasHardwareBackedKey(Certificate certificate) {
193         return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm());
194     }
195 
needsSoftwareBackedKeyStore(WifiEnterpriseConfig config)196     private static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) {
197         java.lang.String client = config.getClientCertificateAlias();
198         if (!TextUtils.isEmpty(client)) {
199             // a valid client certificate is configured
200 
201             // BUGBUG: keyStore.get() never returns certBytes; because it is not
202             // taking WIFI_UID as a parameter. It always looks for certificate
203             // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that
204             // all certificates need software keystore until we get the get() API
205             // fixed.
206             return true;
207         }
208         return false;
209     }
210 
lookupString(String string, String[] strings)211     private int lookupString(String string, String[] strings) {
212         int size = strings.length;
213 
214         string = string.replace('-', '_');
215 
216         for (int i = 0; i < size; i++) {
217             if (string.equals(strings[i])) {
218                 return i;
219             }
220         }
221         loge("Failed to look-up a string: " + string);
222         return -1;
223     }
224 
readNetworkBitsetVariable(int netId, BitSet variable, String varName, String[] strings)225     private void readNetworkBitsetVariable(int netId, BitSet variable, String varName,
226             String[] strings) {
227         String value = mWifiNative.getNetworkVariable(netId, varName);
228         if (!TextUtils.isEmpty(value)) {
229             variable.clear();
230             String[] vals = value.split(" ");
231             for (String val : vals) {
232                 int index = lookupString(val, strings);
233                 if (0 <= index) {
234                     variable.set(index);
235                 }
236             }
237         }
238     }
239 
240     /**
241      * Read the variables from the supplicant daemon that are needed to
242      * fill in the WifiConfiguration object.
243      *
244      * @param config the {@link WifiConfiguration} object to be filled in.
245      */
readNetworkVariables(WifiConfiguration config)246     public void readNetworkVariables(WifiConfiguration config) {
247         if (config == null) {
248             return;
249         }
250         if (VDBG) localLog("readNetworkVariables: " + config.networkId);
251         int netId = config.networkId;
252         if (netId < 0) {
253             return;
254         }
255         /*
256          * TODO: maybe should have a native method that takes an array of
257          * variable names and returns an array of values. But we'd still
258          * be doing a round trip to the supplicant daemon for each variable.
259          */
260         String value;
261 
262         value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName);
263         if (!TextUtils.isEmpty(value)) {
264             if (value.charAt(0) != '"') {
265                 config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\"";
266                 //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted
267                 //supplicant string
268             } else {
269                 config.SSID = value;
270             }
271         } else {
272             config.SSID = null;
273         }
274 
275         value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName);
276         if (!TextUtils.isEmpty(value)) {
277             config.getNetworkSelectionStatus().setNetworkSelectionBSSID(value);
278         } else {
279             config.getNetworkSelectionStatus().setNetworkSelectionBSSID(null);
280         }
281 
282         value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName);
283         config.priority = -1;
284         if (!TextUtils.isEmpty(value)) {
285             try {
286                 config.priority = Integer.parseInt(value);
287             } catch (NumberFormatException ignore) {
288             }
289         }
290 
291         value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName);
292         config.hiddenSSID = false;
293         if (!TextUtils.isEmpty(value)) {
294             try {
295                 config.hiddenSSID = Integer.parseInt(value) != 0;
296             } catch (NumberFormatException ignore) {
297             }
298         }
299 
300         value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pmfVarName);
301         config.requirePMF = false;
302         if (!TextUtils.isEmpty(value)) {
303             try {
304                 config.requirePMF = Integer.parseInt(value) == STORED_VALUE_FOR_REQUIRE_PMF;
305             } catch (NumberFormatException ignore) {
306             }
307         }
308 
309         value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName);
310         config.wepTxKeyIndex = -1;
311         if (!TextUtils.isEmpty(value)) {
312             try {
313                 config.wepTxKeyIndex = Integer.parseInt(value);
314             } catch (NumberFormatException ignore) {
315             }
316         }
317 
318         for (int i = 0; i < 4; i++) {
319             value = mWifiNative.getNetworkVariable(netId,
320                     WifiConfiguration.wepKeyVarNames[i]);
321             if (!TextUtils.isEmpty(value)) {
322                 config.wepKeys[i] = value;
323             } else {
324                 config.wepKeys[i] = null;
325             }
326         }
327 
328         value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName);
329         if (!TextUtils.isEmpty(value)) {
330             config.preSharedKey = value;
331         } else {
332             config.preSharedKey = null;
333         }
334 
335         readNetworkBitsetVariable(config.networkId, config.allowedProtocols,
336                 WifiConfiguration.Protocol.varName, WifiConfiguration.Protocol.strings);
337 
338         readNetworkBitsetVariable(config.networkId, config.allowedKeyManagement,
339                 WifiConfiguration.KeyMgmt.varName, WifiConfiguration.KeyMgmt.strings);
340 
341         readNetworkBitsetVariable(config.networkId, config.allowedAuthAlgorithms,
342                 WifiConfiguration.AuthAlgorithm.varName, WifiConfiguration.AuthAlgorithm.strings);
343 
344         readNetworkBitsetVariable(config.networkId, config.allowedPairwiseCiphers,
345                 WifiConfiguration.PairwiseCipher.varName, WifiConfiguration.PairwiseCipher.strings);
346 
347         readNetworkBitsetVariable(config.networkId, config.allowedGroupCiphers,
348                 WifiConfiguration.GroupCipher.varName, WifiConfiguration.GroupCipher.strings);
349 
350         if (config.enterpriseConfig == null) {
351             config.enterpriseConfig = new WifiEnterpriseConfig();
352         }
353         config.enterpriseConfig.loadFromSupplicant(new SupplicantLoader(netId));
354     }
355 
356     /**
357      * Load all the configured networks from wpa_supplicant.
358      *
359      * @param configs       Map of configuration key to configuration objects corresponding to all
360      *                      the networks.
361      * @param networkExtras Map of extra configuration parameters stored in wpa_supplicant.conf
362      * @return Max priority of all the configs.
363      */
loadNetworks(Map<String, WifiConfiguration> configs, SparseArray<Map<String, String>> networkExtras)364     public int loadNetworks(Map<String, WifiConfiguration> configs,
365             SparseArray<Map<String, String>> networkExtras) {
366         int lastPriority = 0;
367         int last_id = -1;
368         boolean done = false;
369         while (!done) {
370             String listStr = mWifiNative.listNetworks(last_id);
371             if (listStr == null) {
372                 return lastPriority;
373             }
374             String[] lines = listStr.split("\n");
375             if (mShowNetworks) {
376                 localLog("loadNetworks:  ");
377                 for (String net : lines) {
378                     localLog(net);
379                 }
380             }
381             // Skip the first line, which is a header
382             for (int i = 1; i < lines.length; i++) {
383                 String[] result = lines[i].split("\t");
384                 // network-id | ssid | bssid | flags
385                 WifiConfiguration config = new WifiConfiguration();
386                 try {
387                     config.networkId = Integer.parseInt(result[0]);
388                     last_id = config.networkId;
389                 } catch (NumberFormatException e) {
390                     loge("Failed to read network-id '" + result[0] + "'");
391                     continue;
392                 }
393                 // Ignore the supplicant status, start all networks disabled.
394                 config.status = WifiConfiguration.Status.DISABLED;
395                 readNetworkVariables(config);
396                 // Parse the serialized JSON dictionary in ID_STRING_VAR_NAME once and cache the
397                 // result for efficiency.
398                 Map<String, String> extras = mWifiNative.getNetworkExtra(config.networkId,
399                         ID_STRING_VAR_NAME);
400                 if (extras == null) {
401                     extras = new HashMap<String, String>();
402                     // If ID_STRING_VAR_NAME did not contain a dictionary, assume that it contains
403                     // just a quoted FQDN. This is the legacy format that was used in Marshmallow.
404                     final String fqdn = Utils.unquote(mWifiNative.getNetworkVariable(
405                             config.networkId, ID_STRING_VAR_NAME));
406                     if (fqdn != null) {
407                         extras.put(ID_STRING_KEY_FQDN, fqdn);
408                         config.FQDN = fqdn;
409                         // Mark the configuration as a Hotspot 2.0 network.
410                         config.providerFriendlyName = "";
411                     }
412                 }
413                 networkExtras.put(config.networkId, extras);
414 
415                 if (config.priority > lastPriority) {
416                     lastPriority = config.priority;
417                 }
418                 config.setIpAssignment(IpAssignment.DHCP);
419                 config.setProxySettings(ProxySettings.NONE);
420                 if (!WifiServiceImpl.isValid(config)) {
421                     if (mShowNetworks) {
422                         localLog("Ignoring network " + config.networkId + " because configuration "
423                                 + "loaded from wpa_supplicant.conf is not valid.");
424                     }
425                     continue;
426                 }
427                 // The configKey is explicitly stored in wpa_supplicant.conf, because config does
428                 // not contain sufficient information to compute it at this point.
429                 String configKey = extras.get(ID_STRING_KEY_CONFIG_KEY);
430                 if (configKey == null) {
431                     // Handle the legacy case where the configKey is not stored in
432                     // wpa_supplicant.conf but can be computed straight away.
433                     // Force an update of this legacy network configuration by writing
434                     // the configKey for this network into wpa_supplicant.conf.
435                     configKey = config.configKey();
436                     saveNetworkMetadata(config);
437                 }
438                 final WifiConfiguration duplicateConfig = configs.put(configKey, config);
439                 if (duplicateConfig != null) {
440                     // The network is already known. Overwrite the duplicate entry.
441                     if (mShowNetworks) {
442                         localLog("Replacing duplicate network " + duplicateConfig.networkId
443                                 + " with " + config.networkId + ".");
444                     }
445                     // This can happen after the user manually connected to an AP and tried to use
446                     // WPS to connect the AP later. In this case, the supplicant will create a new
447                     // network for the AP although there is an existing network already.
448                     mWifiNative.removeNetwork(duplicateConfig.networkId);
449                 }
450             }
451             done = (lines.length == 1);
452         }
453         return lastPriority;
454     }
455 
456     /**
457      * Install keys for given enterprise network.
458      *
459      * @param existingConfig Existing config corresponding to the network already stored in our
460      *                       database. This maybe null if it's a new network.
461      * @param config         Config corresponding to the network.
462      * @return true if successful, false otherwise.
463      */
installKeys(WifiEnterpriseConfig existingConfig, WifiEnterpriseConfig config, String name)464     private boolean installKeys(WifiEnterpriseConfig existingConfig, WifiEnterpriseConfig config,
465             String name) {
466         boolean ret = true;
467         String privKeyName = Credentials.USER_PRIVATE_KEY + name;
468         String userCertName = Credentials.USER_CERTIFICATE + name;
469         if (config.getClientCertificate() != null) {
470             byte[] privKeyData = config.getClientPrivateKey().getEncoded();
471             if (DBG) {
472                 if (isHardwareBackedKey(config.getClientPrivateKey())) {
473                     Log.d(TAG, "importing keys " + name + " in hardware backed store");
474                 } else {
475                     Log.d(TAG, "importing keys " + name + " in software backed store");
476                 }
477             }
478             ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID,
479                     KeyStore.FLAG_NONE);
480 
481             if (!ret) {
482                 return ret;
483             }
484 
485             ret = putCertInKeyStore(userCertName, config.getClientCertificate());
486             if (!ret) {
487                 // Remove private key installed
488                 mKeyStore.delete(privKeyName, Process.WIFI_UID);
489                 return ret;
490             }
491         }
492 
493         X509Certificate[] caCertificates = config.getCaCertificates();
494         Set<String> oldCaCertificatesToRemove = new ArraySet<String>();
495         if (existingConfig != null && existingConfig.getCaCertificateAliases() != null) {
496             oldCaCertificatesToRemove.addAll(
497                     Arrays.asList(existingConfig.getCaCertificateAliases()));
498         }
499         List<String> caCertificateAliases = null;
500         if (caCertificates != null) {
501             caCertificateAliases = new ArrayList<String>();
502             for (int i = 0; i < caCertificates.length; i++) {
503                 String alias = caCertificates.length == 1 ? name
504                         : String.format("%s_%d", name, i);
505 
506                 oldCaCertificatesToRemove.remove(alias);
507                 ret = putCertInKeyStore(Credentials.CA_CERTIFICATE + alias, caCertificates[i]);
508                 if (!ret) {
509                     // Remove client key+cert
510                     if (config.getClientCertificate() != null) {
511                         mKeyStore.delete(privKeyName, Process.WIFI_UID);
512                         mKeyStore.delete(userCertName, Process.WIFI_UID);
513                     }
514                     // Remove added CA certs.
515                     for (String addedAlias : caCertificateAliases) {
516                         mKeyStore.delete(Credentials.CA_CERTIFICATE + addedAlias, Process.WIFI_UID);
517                     }
518                     return ret;
519                 } else {
520                     caCertificateAliases.add(alias);
521                 }
522             }
523         }
524         // Remove old CA certs.
525         for (String oldAlias : oldCaCertificatesToRemove) {
526             mKeyStore.delete(Credentials.CA_CERTIFICATE + oldAlias, Process.WIFI_UID);
527         }
528         // Set alias names
529         if (config.getClientCertificate() != null) {
530             config.setClientCertificateAlias(name);
531             config.resetClientKeyEntry();
532         }
533 
534         if (caCertificates != null) {
535             config.setCaCertificateAliases(
536                     caCertificateAliases.toArray(new String[caCertificateAliases.size()]));
537             config.resetCaCertificate();
538         }
539         return ret;
540     }
541 
putCertInKeyStore(String name, Certificate cert)542     private boolean putCertInKeyStore(String name, Certificate cert) {
543         try {
544             byte[] certData = Credentials.convertToPem(cert);
545             if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore");
546             return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE);
547 
548         } catch (IOException e1) {
549             return false;
550         } catch (CertificateException e2) {
551             return false;
552         }
553     }
554 
555     /**
556      * Remove enterprise keys from the network config.
557      *
558      * @param config Config corresponding to the network.
559      */
removeKeys(WifiEnterpriseConfig config)560     private void removeKeys(WifiEnterpriseConfig config) {
561         String client = config.getClientCertificateAlias();
562         // a valid client certificate is configured
563         if (!TextUtils.isEmpty(client)) {
564             if (DBG) Log.d(TAG, "removing client private key and user cert");
565             mKeyStore.delete(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
566             mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
567         }
568 
569         String[] aliases = config.getCaCertificateAliases();
570         // a valid ca certificate is configured
571         if (aliases != null) {
572             for (String ca : aliases) {
573                 if (!TextUtils.isEmpty(ca)) {
574                     if (DBG) Log.d(TAG, "removing CA cert: " + ca);
575                     mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
576                 }
577             }
578         }
579     }
580 
581     /**
582      * Update the network metadata info stored in wpa_supplicant network extra field.
583      * @param config Config corresponding to the network.
584      * @return true if successful, false otherwise.
585      */
saveNetworkMetadata(WifiConfiguration config)586     public boolean saveNetworkMetadata(WifiConfiguration config) {
587         final Map<String, String> metadata = new HashMap<String, String>();
588         if (config.isPasspoint()) {
589             metadata.put(ID_STRING_KEY_FQDN, config.FQDN);
590         }
591         metadata.put(ID_STRING_KEY_CONFIG_KEY, config.configKey());
592         metadata.put(ID_STRING_KEY_CREATOR_UID, Integer.toString(config.creatorUid));
593         if (!mWifiNative.setNetworkExtra(config.networkId, ID_STRING_VAR_NAME, metadata)) {
594             loge("failed to set id_str: " + metadata.toString());
595             return false;
596         }
597         return true;
598     }
599 
600     /**
601      * Save an entire network configuration to wpa_supplicant.
602      *
603      * @param config Config corresponding to the network.
604      * @param netId  Net Id of the network.
605      * @return true if successful, false otherwise.
606      */
saveNetwork(WifiConfiguration config, int netId)607     private boolean saveNetwork(WifiConfiguration config, int netId) {
608         if (config == null) {
609             return false;
610         }
611         if (VDBG) localLog("saveNetwork: " + netId);
612         if (config.SSID != null && !mWifiNative.setNetworkVariable(
613                 netId,
614                 WifiConfiguration.ssidVarName,
615                 encodeSSID(config.SSID))) {
616             loge("failed to set SSID: " + config.SSID);
617             return false;
618         }
619         if (!saveNetworkMetadata(config)) {
620             return false;
621         }
622         //set selected BSSID to supplicant
623         if (config.getNetworkSelectionStatus().getNetworkSelectionBSSID() != null) {
624             String bssid = config.getNetworkSelectionStatus().getNetworkSelectionBSSID();
625             if (!mWifiNative.setNetworkVariable(netId, WifiConfiguration.bssidVarName, bssid)) {
626                 loge("failed to set BSSID: " + bssid);
627                 return false;
628             }
629         }
630         String allowedKeyManagementString =
631                 makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
632         if (config.allowedKeyManagement.cardinality() != 0 && !mWifiNative.setNetworkVariable(
633                 netId,
634                 WifiConfiguration.KeyMgmt.varName,
635                 allowedKeyManagementString)) {
636             loge("failed to set key_mgmt: " + allowedKeyManagementString);
637             return false;
638         }
639         String allowedProtocolsString =
640                 makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
641         if (config.allowedProtocols.cardinality() != 0 && !mWifiNative.setNetworkVariable(
642                 netId,
643                 WifiConfiguration.Protocol.varName,
644                 allowedProtocolsString)) {
645             loge("failed to set proto: " + allowedProtocolsString);
646             return false;
647         }
648         String allowedAuthAlgorithmsString =
649                 makeString(config.allowedAuthAlgorithms,
650                         WifiConfiguration.AuthAlgorithm.strings);
651         if (config.allowedAuthAlgorithms.cardinality() != 0 && !mWifiNative.setNetworkVariable(
652                 netId,
653                 WifiConfiguration.AuthAlgorithm.varName,
654                 allowedAuthAlgorithmsString)) {
655             loge("failed to set auth_alg: " + allowedAuthAlgorithmsString);
656             return false;
657         }
658         String allowedPairwiseCiphersString = makeString(config.allowedPairwiseCiphers,
659                 WifiConfiguration.PairwiseCipher.strings);
660         if (config.allowedPairwiseCiphers.cardinality() != 0 && !mWifiNative.setNetworkVariable(
661                 netId,
662                 WifiConfiguration.PairwiseCipher.varName,
663                 allowedPairwiseCiphersString)) {
664             loge("failed to set pairwise: " + allowedPairwiseCiphersString);
665             return false;
666         }
667         // Make sure that the string "GTK_NOT_USED" is /not/ transformed - wpa_supplicant
668         // uses this literal value and not the 'dashed' version.
669         String allowedGroupCiphersString =
670                 makeStringWithException(config.allowedGroupCiphers,
671                         WifiConfiguration.GroupCipher.strings,
672                         WifiConfiguration.GroupCipher
673                                 .strings[WifiConfiguration.GroupCipher.GTK_NOT_USED]);
674         if (config.allowedGroupCiphers.cardinality() != 0 && !mWifiNative.setNetworkVariable(
675                 netId,
676                 WifiConfiguration.GroupCipher.varName,
677                 allowedGroupCiphersString)) {
678             loge("failed to set group: " + allowedGroupCiphersString);
679             return false;
680         }
681         // Prevent client screw-up by passing in a WifiConfiguration we gave it
682         // by preventing "*" as a key.
683         if (config.preSharedKey != null && !config.preSharedKey.equals("*")
684                 && !mWifiNative.setNetworkVariable(
685                 netId,
686                 WifiConfiguration.pskVarName,
687                 config.preSharedKey)) {
688             loge("failed to set psk");
689             return false;
690         }
691         boolean hasSetKey = false;
692         if (config.wepKeys != null) {
693             for (int i = 0; i < config.wepKeys.length; i++) {
694                 // Prevent client screw-up by passing in a WifiConfiguration we gave it
695                 // by preventing "*" as a key.
696                 if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
697                     if (!mWifiNative.setNetworkVariable(
698                             netId,
699                             WifiConfiguration.wepKeyVarNames[i],
700                             config.wepKeys[i])) {
701                         loge("failed to set wep_key" + i + ": " + config.wepKeys[i]);
702                         return false;
703                     }
704                     hasSetKey = true;
705                 }
706             }
707         }
708         if (hasSetKey) {
709             if (!mWifiNative.setNetworkVariable(
710                     netId,
711                     WifiConfiguration.wepTxKeyIdxVarName,
712                     Integer.toString(config.wepTxKeyIndex))) {
713                 loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex);
714                 return false;
715             }
716         }
717         if (!mWifiNative.setNetworkVariable(
718                 netId,
719                 WifiConfiguration.priorityVarName,
720                 Integer.toString(config.priority))) {
721             loge(config.SSID + ": failed to set priority: " + config.priority);
722             return false;
723         }
724         if (config.hiddenSSID && !mWifiNative.setNetworkVariable(
725                 netId,
726                 WifiConfiguration.hiddenSSIDVarName,
727                 Integer.toString(config.hiddenSSID ? 1 : 0))) {
728             loge(config.SSID + ": failed to set hiddenSSID: " + config.hiddenSSID);
729             return false;
730         }
731         if (config.requirePMF && !mWifiNative.setNetworkVariable(
732                 netId,
733                 WifiConfiguration.pmfVarName,
734                 Integer.toString(STORED_VALUE_FOR_REQUIRE_PMF))) {
735             loge(config.SSID + ": failed to set requirePMF: " + config.requirePMF);
736             return false;
737         }
738         if (config.updateIdentifier != null && !mWifiNative.setNetworkVariable(
739                 netId,
740                 WifiConfiguration.updateIdentiferVarName,
741                 config.updateIdentifier)) {
742             loge(config.SSID + ": failed to set updateIdentifier: " + config.updateIdentifier);
743             return false;
744         }
745         return true;
746     }
747 
748     /**
749      * Update/Install keys for given enterprise network.
750      *
751      * @param config         Config corresponding to the network.
752      * @param existingConfig Existing config corresponding to the network already stored in our
753      *                       database. This maybe null if it's a new network.
754      * @return true if successful, false otherwise.
755      */
updateNetworkKeys(WifiConfiguration config, WifiConfiguration existingConfig)756     private boolean updateNetworkKeys(WifiConfiguration config, WifiConfiguration existingConfig) {
757         WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
758         if (needsKeyStore(enterpriseConfig)) {
759             try {
760                 /* config passed may include only fields being updated.
761                  * In order to generate the key id, fetch uninitialized
762                  * fields from the currently tracked configuration
763                  */
764                 String keyId = config.getKeyIdForCredentials(existingConfig);
765 
766                 if (!installKeys(existingConfig != null
767                         ? existingConfig.enterpriseConfig : null, enterpriseConfig, keyId)) {
768                     loge(config.SSID + ": failed to install keys");
769                     return false;
770                 }
771             } catch (IllegalStateException e) {
772                 loge(config.SSID + " invalid config for key installation: " + e.getMessage());
773                 return false;
774             }
775         }
776         if (!enterpriseConfig.saveToSupplicant(
777                 new SupplicantSaver(config.networkId, config.SSID))) {
778             removeKeys(enterpriseConfig);
779             return false;
780         }
781         return true;
782     }
783 
784     /**
785      * Add or update a network configuration to wpa_supplicant.
786      *
787      * @param config         Config corresponding to the network.
788      * @param existingConfig Existing config corresponding to the network saved in our database.
789      * @return true if successful, false otherwise.
790      */
addOrUpdateNetwork(WifiConfiguration config, WifiConfiguration existingConfig)791     public boolean addOrUpdateNetwork(WifiConfiguration config, WifiConfiguration existingConfig) {
792         if (config == null) {
793             return false;
794         }
795         if (VDBG) localLog("addOrUpdateNetwork: " + config.networkId);
796         int netId = config.networkId;
797         boolean newNetwork = false;
798         /*
799          * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
800          * network configuration. Otherwise, the networkId should
801          * refer to an existing configuration.
802          */
803         if (netId == WifiConfiguration.INVALID_NETWORK_ID) {
804             newNetwork = true;
805             netId = mWifiNative.addNetwork();
806             if (netId < 0) {
807                 loge("Failed to add a network!");
808                 return false;
809             } else {
810                 logi("addOrUpdateNetwork created netId=" + netId);
811             }
812             // Save the new network ID to the config
813             config.networkId = netId;
814         }
815         if (!saveNetwork(config, netId)) {
816             if (newNetwork) {
817                 mWifiNative.removeNetwork(netId);
818                 loge("Failed to set a network variable, removed network: " + netId);
819             }
820             return false;
821         }
822         if (config.enterpriseConfig != null
823                 && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
824             return updateNetworkKeys(config, existingConfig);
825         }
826         // Stage the backup of the SettingsProvider package which backs this up
827         mBackupManagerProxy.notifyDataChanged();
828         return true;
829     }
830 
831     /**
832      * Remove the specified network and save config
833      *
834      * @param config Config corresponding to the network.
835      * @return {@code true} if it succeeds, {@code false} otherwise
836      */
removeNetwork(WifiConfiguration config)837     public boolean removeNetwork(WifiConfiguration config) {
838         if (config == null) {
839             return false;
840         }
841         if (VDBG) localLog("removeNetwork: " + config.networkId);
842         if (!mWifiNative.removeNetwork(config.networkId)) {
843             loge("Remove network in wpa_supplicant failed on " + config.networkId);
844             return false;
845         }
846         // Remove any associated keys
847         if (config.enterpriseConfig != null) {
848             removeKeys(config.enterpriseConfig);
849         }
850         // Stage the backup of the SettingsProvider package which backs this up
851         mBackupManagerProxy.notifyDataChanged();
852         return true;
853     }
854 
855     /**
856      * Select a network in wpa_supplicant.
857      *
858      * @param config Config corresponding to the network.
859      * @return true if successful, false otherwise.
860      */
selectNetwork(WifiConfiguration config, Collection<WifiConfiguration> configs)861     public boolean selectNetwork(WifiConfiguration config, Collection<WifiConfiguration> configs) {
862         if (config == null) {
863             return false;
864         }
865         if (VDBG) localLog("selectNetwork: " + config.networkId);
866         if (!mWifiNative.selectNetwork(config.networkId)) {
867             loge("Select network in wpa_supplicant failed on " + config.networkId);
868             return false;
869         }
870         config.status = Status.ENABLED;
871         markAllNetworksDisabledExcept(config.networkId, configs);
872         return true;
873     }
874 
875     /**
876      * Disable a network in wpa_supplicant.
877      *
878      * @param config Config corresponding to the network.
879      * @return true if successful, false otherwise.
880      */
disableNetwork(WifiConfiguration config)881     boolean disableNetwork(WifiConfiguration config) {
882         if (config == null) {
883             return false;
884         }
885         if (VDBG) localLog("disableNetwork: " + config.networkId);
886         if (!mWifiNative.disableNetwork(config.networkId)) {
887             loge("Disable network in wpa_supplicant failed on " + config.networkId);
888             return false;
889         }
890         config.status = Status.DISABLED;
891         return true;
892     }
893 
894     /**
895      * Set priority for a network in wpa_supplicant.
896      *
897      * @param config Config corresponding to the network.
898      * @return true if successful, false otherwise.
899      */
setNetworkPriority(WifiConfiguration config, int priority)900     public boolean setNetworkPriority(WifiConfiguration config, int priority) {
901         if (config == null) {
902             return false;
903         }
904         if (VDBG) localLog("setNetworkPriority: " + config.networkId);
905         if (!mWifiNative.setNetworkVariable(config.networkId,
906                 WifiConfiguration.priorityVarName, Integer.toString(priority))) {
907             loge("Set priority of network in wpa_supplicant failed on " + config.networkId);
908             return false;
909         }
910         config.priority = priority;
911         return true;
912     }
913 
914     /**
915      * Set SSID for a network in wpa_supplicant.
916      *
917      * @param config Config corresponding to the network.
918      * @return true if successful, false otherwise.
919      */
setNetworkSSID(WifiConfiguration config, String ssid)920     public boolean setNetworkSSID(WifiConfiguration config, String ssid) {
921         if (config == null) {
922             return false;
923         }
924         if (VDBG) localLog("setNetworkSSID: " + config.networkId);
925         if (!mWifiNative.setNetworkVariable(config.networkId, WifiConfiguration.ssidVarName,
926                 encodeSSID(ssid))) {
927             loge("Set SSID of network in wpa_supplicant failed on " + config.networkId);
928             return false;
929         }
930         config.SSID = ssid;
931         return true;
932     }
933 
934     /**
935      * Set BSSID for a network in wpa_supplicant from network selection.
936      *
937      * @param config Config corresponding to the network.
938      * @param bssid  BSSID to be set.
939      * @return true if successful, false otherwise.
940      */
setNetworkBSSID(WifiConfiguration config, String bssid)941     public boolean setNetworkBSSID(WifiConfiguration config, String bssid) {
942         // Sanity check the config is valid
943         if (config == null
944                 || (config.networkId == WifiConfiguration.INVALID_NETWORK_ID
945                 && config.SSID == null)) {
946             return false;
947         }
948         if (VDBG) localLog("setNetworkBSSID: " + config.networkId);
949         if (!mWifiNative.setNetworkVariable(config.networkId, WifiConfiguration.bssidVarName,
950                 bssid)) {
951             loge("Set BSSID of network in wpa_supplicant failed on " + config.networkId);
952             return false;
953         }
954         config.getNetworkSelectionStatus().setNetworkSelectionBSSID(bssid);
955         return true;
956     }
957 
958     /**
959      * Enable/Disable HS20 parameter in wpa_supplicant.
960      *
961      * @param enable Enable/Disable the parameter.
962      */
enableHS20(boolean enable)963     public void enableHS20(boolean enable) {
964         mWifiNative.setHs20(enable);
965     }
966 
967     /**
968      * Disables all the networks in the provided list in wpa_supplicant.
969      *
970      * @param configs Collection of configs which needs to be enabled.
971      * @return true if successful, false otherwise.
972      */
disableAllNetworks(Collection<WifiConfiguration> configs)973     public boolean disableAllNetworks(Collection<WifiConfiguration> configs) {
974         if (VDBG) localLog("disableAllNetworks");
975         boolean networkDisabled = false;
976         for (WifiConfiguration enabled : configs) {
977             if (disableNetwork(enabled)) {
978                 networkDisabled = true;
979             }
980         }
981         saveConfig();
982         return networkDisabled;
983     }
984 
985     /**
986      * Save the current configuration to wpa_supplicant.conf.
987      */
saveConfig()988     public boolean saveConfig() {
989         return mWifiNative.saveConfig();
990     }
991 
992     /**
993      * Read network variables from wpa_supplicant.conf.
994      *
995      * @param key The parameter to be parsed.
996      * @return Map of corresponding configKey to the value of the param requested.
997      */
readNetworkVariablesFromSupplicantFile(String key)998     public Map<String, String> readNetworkVariablesFromSupplicantFile(String key) {
999         Map<String, String> result = new HashMap<>();
1000         BufferedReader reader = null;
1001         try {
1002             reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE));
1003             result = readNetworkVariablesFromReader(reader, key);
1004         } catch (FileNotFoundException e) {
1005             if (VDBG) loge("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e);
1006         } catch (IOException e) {
1007             if (VDBG) loge("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e);
1008         } finally {
1009             try {
1010                 if (reader != null) {
1011                     reader.close();
1012                 }
1013             } catch (IOException e) {
1014                 if (VDBG) {
1015                     loge("Could not close reader for " + SUPPLICANT_CONFIG_FILE + ", " + e);
1016                 }
1017             }
1018         }
1019         return result;
1020     }
1021 
1022     /**
1023      * Read network variables from a given reader. This method is separate from
1024      * readNetworkVariablesFromSupplicantFile() for testing.
1025      *
1026      * @param reader The reader to read the network variables from.
1027      * @param key The parameter to be parsed.
1028      * @return Map of corresponding configKey to the value of the param requested.
1029      */
readNetworkVariablesFromReader(BufferedReader reader, String key)1030     public Map<String, String> readNetworkVariablesFromReader(BufferedReader reader, String key)
1031             throws IOException {
1032         Map<String, String> result = new HashMap<>();
1033         if (VDBG) localLog("readNetworkVariablesFromReader key=" + key);
1034         boolean found = false;
1035         String configKey = null;
1036         String value = null;
1037         for (String line = reader.readLine(); line != null; line = reader.readLine()) {
1038             if (line.matches("[ \\t]*network=\\{")) {
1039                 found = true;
1040                 configKey = null;
1041                 value = null;
1042             } else if (line.matches("[ \\t]*\\}")) {
1043                 found = false;
1044                 configKey = null;
1045                 value = null;
1046             }
1047             if (found) {
1048                 String trimmedLine = line.trim();
1049                 if (trimmedLine.startsWith(ID_STRING_VAR_NAME + "=")) {
1050                     try {
1051                         // Trim the quotes wrapping the id_str value.
1052                         final String encodedExtras = trimmedLine.substring(
1053                                 8, trimmedLine.length() -1);
1054                         final JSONObject json =
1055                                 new JSONObject(URLDecoder.decode(encodedExtras, "UTF-8"));
1056                         if (json.has(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY)) {
1057                             final Object configKeyFromJson =
1058                                     json.get(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY);
1059                             if (configKeyFromJson instanceof String) {
1060                                 configKey = (String) configKeyFromJson;
1061                             }
1062                         }
1063                     } catch (JSONException e) {
1064                         if (VDBG) {
1065                             loge("Could not get "+ WifiConfigStore.ID_STRING_KEY_CONFIG_KEY
1066                                     + ", " + e);
1067                         }
1068                     }
1069                 }
1070                 if (trimmedLine.startsWith(key + "=")) {
1071                     value = trimmedLine.substring(key.length() + 1);
1072                 }
1073                 if (configKey != null && value != null) {
1074                     result.put(configKey, value);
1075                 }
1076             }
1077         }
1078         return result;
1079     }
1080 
1081     /**
1082      * Checks if the network is a sim config.
1083      *
1084      * @param config Config corresponding to the network.
1085      * @return true if it is a sim config, false otherwise.
1086      */
isSimConfig(WifiConfiguration config)1087     public boolean isSimConfig(WifiConfiguration config) {
1088         if (config == null) {
1089             return false;
1090         }
1091 
1092         if (config.enterpriseConfig == null) {
1093             return false;
1094         }
1095 
1096         int method = config.enterpriseConfig.getEapMethod();
1097         return (method == WifiEnterpriseConfig.Eap.SIM
1098                 || method == WifiEnterpriseConfig.Eap.AKA
1099                 || method == WifiEnterpriseConfig.Eap.AKA_PRIME);
1100     }
1101 
1102     /**
1103      * Resets all sim networks from the provided network list.
1104      *
1105      * @param configs List of all the networks.
1106      */
resetSimNetworks(Collection<WifiConfiguration> configs)1107     public void resetSimNetworks(Collection<WifiConfiguration> configs) {
1108         if (VDBG) localLog("resetSimNetworks");
1109         for (WifiConfiguration config : configs) {
1110             if (isSimConfig(config)) {
1111                 /* This configuration may have cached Pseudonym IDs; lets remove them */
1112                 mWifiNative.setNetworkVariable(config.networkId, "identity", "NULL");
1113                 mWifiNative.setNetworkVariable(config.networkId, "anonymous_identity", "NULL");
1114             }
1115         }
1116     }
1117 
1118     /**
1119      * Clear BSSID blacklist in wpa_supplicant.
1120      */
clearBssidBlacklist()1121     public void clearBssidBlacklist() {
1122         if (VDBG) localLog("clearBlacklist");
1123         mBssidBlacklist.clear();
1124         mWifiNative.clearBlacklist();
1125         mWifiNative.setBssidBlacklist(null);
1126     }
1127 
1128     /**
1129      * Add a BSSID to the blacklist.
1130      *
1131      * @param bssid bssid to be added.
1132      */
blackListBssid(String bssid)1133     public void blackListBssid(String bssid) {
1134         if (bssid == null) {
1135             return;
1136         }
1137         if (VDBG) localLog("blackListBssid: " + bssid);
1138         mBssidBlacklist.add(bssid);
1139         // Blacklist at wpa_supplicant
1140         mWifiNative.addToBlacklist(bssid);
1141         // Blacklist at firmware
1142         String[] list = mBssidBlacklist.toArray(new String[mBssidBlacklist.size()]);
1143         mWifiNative.setBssidBlacklist(list);
1144     }
1145 
1146     /**
1147      * Checks if the provided bssid is blacklisted or not.
1148      *
1149      * @param bssid bssid to be checked.
1150      * @return true if present, false otherwise.
1151      */
isBssidBlacklisted(String bssid)1152     public boolean isBssidBlacklisted(String bssid) {
1153         return mBssidBlacklist.contains(bssid);
1154     }
1155 
1156     /* Mark all networks except specified netId as disabled */
markAllNetworksDisabledExcept(int netId, Collection<WifiConfiguration> configs)1157     private void markAllNetworksDisabledExcept(int netId, Collection<WifiConfiguration> configs) {
1158         for (WifiConfiguration config : configs) {
1159             if (config != null && config.networkId != netId) {
1160                 if (config.status != Status.DISABLED) {
1161                     config.status = Status.DISABLED;
1162                 }
1163             }
1164         }
1165     }
1166 
markAllNetworksDisabled(Collection<WifiConfiguration> configs)1167     private void markAllNetworksDisabled(Collection<WifiConfiguration> configs) {
1168         markAllNetworksDisabledExcept(WifiConfiguration.INVALID_NETWORK_ID, configs);
1169     }
1170 
1171     /**
1172      * Start WPS pin method configuration with pin obtained
1173      * from the access point
1174      *
1175      * @param config WPS configuration
1176      * @return Wps result containing status and pin
1177      */
startWpsWithPinFromAccessPoint(WpsInfo config, Collection<WifiConfiguration> configs)1178     public WpsResult startWpsWithPinFromAccessPoint(WpsInfo config,
1179             Collection<WifiConfiguration> configs) {
1180         WpsResult result = new WpsResult();
1181         if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) {
1182             /* WPS leaves all networks disabled */
1183             markAllNetworksDisabled(configs);
1184             result.status = WpsResult.Status.SUCCESS;
1185         } else {
1186             loge("Failed to start WPS pin method configuration");
1187             result.status = WpsResult.Status.FAILURE;
1188         }
1189         return result;
1190     }
1191 
1192     /**
1193      * Start WPS pin method configuration with obtained
1194      * from the device
1195      *
1196      * @return WpsResult indicating status and pin
1197      */
startWpsWithPinFromDevice(WpsInfo config, Collection<WifiConfiguration> configs)1198     public WpsResult startWpsWithPinFromDevice(WpsInfo config,
1199             Collection<WifiConfiguration> configs) {
1200         WpsResult result = new WpsResult();
1201         result.pin = mWifiNative.startWpsPinDisplay(config.BSSID);
1202         /* WPS leaves all networks disabled */
1203         if (!TextUtils.isEmpty(result.pin)) {
1204             markAllNetworksDisabled(configs);
1205             result.status = WpsResult.Status.SUCCESS;
1206         } else {
1207             loge("Failed to start WPS pin method configuration");
1208             result.status = WpsResult.Status.FAILURE;
1209         }
1210         return result;
1211     }
1212 
1213     /**
1214      * Start WPS push button configuration
1215      *
1216      * @param config WPS configuration
1217      * @return WpsResult indicating status and pin
1218      */
startWpsPbc(WpsInfo config, Collection<WifiConfiguration> configs)1219     public WpsResult startWpsPbc(WpsInfo config,
1220             Collection<WifiConfiguration> configs) {
1221         WpsResult result = new WpsResult();
1222         if (mWifiNative.startWpsPbc(config.BSSID)) {
1223             /* WPS leaves all networks disabled */
1224             markAllNetworksDisabled(configs);
1225             result.status = WpsResult.Status.SUCCESS;
1226         } else {
1227             loge("Failed to start WPS push button configuration");
1228             result.status = WpsResult.Status.FAILURE;
1229         }
1230         return result;
1231     }
1232 
logd(String s)1233     protected void logd(String s) {
1234         Log.d(TAG, s);
1235     }
1236 
logi(String s)1237     protected void logi(String s) {
1238         Log.i(TAG, s);
1239     }
1240 
loge(String s)1241     protected void loge(String s) {
1242         loge(s, false);
1243     }
1244 
loge(String s, boolean stack)1245     protected void loge(String s, boolean stack) {
1246         if (stack) {
1247             Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
1248                     + " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
1249                     + " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
1250                     + " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
1251         } else {
1252             Log.e(TAG, s);
1253         }
1254     }
1255 
log(String s)1256     protected void log(String s) {
1257         Log.d(TAG, s);
1258     }
1259 
localLog(String s)1260     private void localLog(String s) {
1261         if (mLocalLog != null) {
1262             mLocalLog.log(TAG + ": " + s);
1263         }
1264     }
1265 
localLogAndLogcat(String s)1266     private void localLogAndLogcat(String s) {
1267         localLog(s);
1268         Log.d(TAG, s);
1269     }
1270 
1271     private class SupplicantSaver implements WifiEnterpriseConfig.SupplicantSaver {
1272         private final int mNetId;
1273         private final String mSetterSSID;
1274 
SupplicantSaver(int netId, String setterSSID)1275         SupplicantSaver(int netId, String setterSSID) {
1276             mNetId = netId;
1277             mSetterSSID = setterSSID;
1278         }
1279 
1280         @Override
saveValue(String key, String value)1281         public boolean saveValue(String key, String value) {
1282             if (key.equals(WifiEnterpriseConfig.PASSWORD_KEY)
1283                     && value != null && value.equals("*")) {
1284                 // No need to try to set an obfuscated password, which will fail
1285                 return true;
1286             }
1287             if (key.equals(WifiEnterpriseConfig.REALM_KEY)
1288                     || key.equals(WifiEnterpriseConfig.PLMN_KEY)) {
1289                 // No need to save realm or PLMN in supplicant
1290                 return true;
1291             }
1292             // TODO: We need a way to clear values in wpa_supplicant as opposed to
1293             // mapping unset values to empty strings.
1294             if (value == null) {
1295                 value = "\"\"";
1296             }
1297             if (!mWifiNative.setNetworkVariable(mNetId, key, value)) {
1298                 loge(mSetterSSID + ": failed to set " + key + ": " + value);
1299                 return false;
1300             }
1301             return true;
1302         }
1303     }
1304 
1305     private class SupplicantLoader implements WifiEnterpriseConfig.SupplicantLoader {
1306         private final int mNetId;
1307 
SupplicantLoader(int netId)1308         SupplicantLoader(int netId) {
1309             mNetId = netId;
1310         }
1311 
1312         @Override
loadValue(String key)1313         public String loadValue(String key) {
1314             String value = mWifiNative.getNetworkVariable(mNetId, key);
1315             if (!TextUtils.isEmpty(value)) {
1316                 if (!enterpriseConfigKeyShouldBeQuoted(key)) {
1317                     value = removeDoubleQuotes(value);
1318                 }
1319                 return value;
1320             } else {
1321                 return null;
1322             }
1323         }
1324 
1325         /**
1326          * Returns true if a particular config key needs to be quoted when passed to the supplicant.
1327          */
enterpriseConfigKeyShouldBeQuoted(String key)1328         private boolean enterpriseConfigKeyShouldBeQuoted(String key) {
1329             switch (key) {
1330                 case WifiEnterpriseConfig.EAP_KEY:
1331                 case WifiEnterpriseConfig.ENGINE_KEY:
1332                     return false;
1333                 default:
1334                     return true;
1335             }
1336         }
1337     }
1338 
1339     // TODO(rpius): Remove this.
1340     private class WpaConfigFileObserver extends FileObserver {
1341 
WpaConfigFileObserver()1342         WpaConfigFileObserver() {
1343             super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE);
1344         }
1345 
1346         @Override
onEvent(int event, String path)1347         public void onEvent(int event, String path) {
1348             if (event == CLOSE_WRITE) {
1349                 File file = new File(SUPPLICANT_CONFIG_FILE);
1350                 if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length());
1351             }
1352         }
1353     }
1354 }
1355