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