165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/*
265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Copyright (C) 2014 The Android Open Source Project
365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Licensed under the Apache License, Version 2.0 (the "License");
565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * you may not use this file except in compliance with the License.
665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * You may obtain a copy of the License at
765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *      http://www.apache.org/licenses/LICENSE-2.0
965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
1065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Unless required by applicable law or agreed to in writing, software
1165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * distributed under the License is distributed on an "AS IS" BASIS,
1265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * See the License for the specific language governing permissions and
1465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * limitations under the License.
1565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */
1665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
1765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepackage com.android.tv.settings.connectivity;
1865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
1965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport com.android.tv.settings.R;
2065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
2165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.Context;
2265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.net.wifi.ScanResult;
2365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.net.wifi.WifiConfiguration;
2465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.net.wifi.WifiInfo;
2565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.net.wifi.WifiManager;
2665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.net.wifi.WifiConfiguration.AuthAlgorithm;
2765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.net.wifi.WifiConfiguration.KeyMgmt;
2865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.text.TextUtils;
2965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.Log;
3065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
3165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.util.List;
3265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.util.regex.Matcher;
3365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.util.regex.Pattern;
3465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
3565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/**
3665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Helper class that deals with Wi-fi configuration
3765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */
3865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepublic final class WifiConfigHelper {
3965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
4065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final String TAG = "WifiConfigHelper";
4165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final boolean DEBUG = false;
4265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
4365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
4465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * If there are exactly 12 hex digits, this looks like a BSSID
4565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
4665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final String REGEX_HEX_BSSID = "[a-fA-F0-9]{12}";
4765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
4865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    // Allows underscore char to supports proxies that do not
4965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    // follow the spec
5065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final String HC = "a-zA-Z0-9\\_";
5165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
5265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    // Matches blank input, ips, and domain names
5365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final String HOSTNAME_REGEXP =
5465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            "^$|^[" + HC + "]+(\\-[" + HC + "]+)*(\\.[" + HC + "]+(\\-[" + HC + "]+)*)*$";
5565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final Pattern HOSTNAME_PATTERN;
5665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final String EXCLUSION_REGEXP =
5765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            "$|^(\\*)?\\.?[" + HC + "]+(\\-[" + HC + "]+)*(\\.[" + HC + "]+(\\-[" + HC + "]+)*)*$";
5865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final Pattern EXCLUSION_PATTERN;
5965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    static {
6065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
6165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        EXCLUSION_PATTERN = Pattern.compile(EXCLUSION_REGEXP);
6265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
6365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
6465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static void setConfigSsid(WifiConfiguration config, String ssid) {
6565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        // if this looks like a BSSID, don't quote it
6665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (!Pattern.matches(REGEX_HEX_BSSID, ssid)) {
6765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            config.SSID = enquoteSsid(ssid);
6865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
6965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            config.SSID = ssid;
7065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
7165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
7265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
7365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static void setConfigSsid(WifiConfiguration config, ScanResult scanResult) {
7465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        // just enquote the SSID, if taken from a scan result, we assume that
7565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        // there is no possibility this is a BSSID in disguise.
7665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        config.SSID = enquoteSsid(scanResult.SSID);
7765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
7865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
7965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static void setConfigKeyManagementBySecurity(
8065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            WifiConfiguration config, WifiSecurity security) {
8165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        config.allowedKeyManagement.clear();
8265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        config.allowedAuthAlgorithms.clear();
8365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        switch (security) {
8465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            case NONE:
8565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                config.allowedKeyManagement.set(KeyMgmt.NONE);
8665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                break;
8765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            case WEP:
8865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                config.allowedKeyManagement.set(KeyMgmt.NONE);
8965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
9065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
9165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                break;
9265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            case PSK:
9365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
9465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                break;
9565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            case EAP:
9665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
9765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
9865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                break;
9965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
10065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
10165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
10265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static boolean areSameNetwork(WifiManager wifiManager, ScanResult scanResult,
10365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            WifiInfo wifiInfo) {
10465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (scanResult == null || wifiInfo == null) {
10565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return false;
10665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
10765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (scanResult.SSID == null || wifiInfo.getSSID() == null) {
10865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return false;
10965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
11065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (scanResult.BSSID == null || wifiInfo.getBSSID() == null) {
11165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return false;
11265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
11365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        String wifiInfoSSID = WifiInfo.removeDoubleQuotes(wifiInfo.getSSID());
11465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        String wifiInfoBSSID = wifiInfo.getBSSID();
11565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        WifiSecurity scanResultSecurity = WifiSecurity.getSecurity(scanResult);
11665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        WifiSecurity wifiInfoSecurity = getCurrentConnectionSecurity(wifiManager, wifiInfo);
11765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
11865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return (TextUtils.equals(scanResult.SSID, wifiInfoSSID) &&
11965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                TextUtils.equals(scanResult.BSSID, wifiInfoBSSID) &&
12065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                scanResultSecurity.equals(wifiInfoSecurity));
12165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
12265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
12365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static WifiSecurity getCurrentConnectionSecurity(WifiManager wifiManager,
12465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            WifiInfo currentWifiInfo) {
12565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (currentWifiInfo != null) {
12665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            WifiConfiguration wifiConfiguration = getWifiConfiguration(wifiManager,
12765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    currentWifiInfo.getNetworkId());
12865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (wifiConfiguration != null) {
12965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return WifiSecurity.getSecurity(wifiConfiguration);
13065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
13165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
13265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return WifiSecurity.NONE;
13365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
13465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
13565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
13665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * validate syntax of hostname and port entries
13765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * @return 0 on success, string resource ID on failure
13865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
13965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static int validate(String hostname, String port, String exclList) {
14065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Matcher match = HOSTNAME_PATTERN.matcher(hostname);
14165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        String exclListArray[] = exclList.split(",");
14265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
14365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (!match.matches()) return R.string.proxy_error_invalid_host;
14465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
14565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        for (String excl : exclListArray) {
14665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Matcher m = EXCLUSION_PATTERN.matcher(excl);
14765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (!m.matches()) return R.string.proxy_error_invalid_exclusion_list;
14865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
14965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
15065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (hostname.length() > 0 && port.length() == 0) {
15165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return R.string.proxy_error_empty_port;
15265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
15365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
15465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (port.length() > 0) {
15565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (hostname.length() == 0) {
15665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return R.string.proxy_error_empty_host_set_port;
15765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
15865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int portVal = -1;
15965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            try {
16065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                portVal = Integer.parseInt(port);
16165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } catch (NumberFormatException ex) {
16265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return R.string.proxy_error_invalid_port;
16365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
16465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (portVal <= 0 || portVal > 0xFFFF) {
16565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return R.string.proxy_error_invalid_port;
16665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
16765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
16865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return 0;
16965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
17065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
17165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static WifiConfiguration getWifiConfiguration(WifiManager wifiManager, int networkId) {
17265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        List<WifiConfiguration> configuredNetworks = wifiManager.getConfiguredNetworks();
17365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (configuredNetworks != null) {
17465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            for (WifiConfiguration configuredNetwork : configuredNetworks) {
17565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (configuredNetwork.networkId == networkId) {
17665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return configuredNetwork;
17765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
17865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
17965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
18065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return null;
18165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
18265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
18365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
18465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * Did this config come out of the supplicant?  NOT "Is the config currently in the supplicant?"
18565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
18665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static boolean isNetworkSaved(WifiConfiguration config) {
18765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return config != null && config.networkId > -1;
18865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
18965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
19065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
19165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * Return the configured network that matches the ScanResult, or create one.
19265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
19365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static WifiConfiguration getConfigurationForNetwork(Context context,
19465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            ScanResult network) {
19565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        WifiConfiguration config = getFromConfiguredNetworks(context, network.SSID,
19665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                WifiSecurity.getSecurity(network));
19765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (config == null) {
19865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            config = new WifiConfiguration();
19965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            setConfigSsid(config, network);
20065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            setConfigKeyManagementBySecurity(config, WifiSecurity.getSecurity(network));
20165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
20265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return config;
20365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
20465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
20565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
20665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * Return the configured network that matches the ssid/security pair, or create one.
20765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
20865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static WifiConfiguration getConfiguration(Context context, String ssid,
20965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            WifiSecurity security) {
21065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        WifiConfiguration config = getFromConfiguredNetworks(context, ssid, security);
21165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
21265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (config == null) {
21365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // No configured network found; populate a new one with the provided ssid / security.
21465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            config = new WifiConfiguration();
21565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            setConfigSsid(config, ssid);
21665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            setConfigKeyManagementBySecurity(config, security);
21765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
21865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return config;
21965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
22065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
22165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
22265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * Save a wifi configuration.
22365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
22465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static boolean saveConfiguration(Context context, WifiConfiguration config) {
22565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (config == null) {
22665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return false;
22765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
22865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
22965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        WifiManager wifiMan = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
23065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        int networkId = wifiMan.addNetwork(config);
23165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (networkId == -1) {
23265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane          if (DEBUG) Log.e(TAG, "failed to add network: " + config.toString());
23365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane          return false;
23465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
23565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
23665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (!wifiMan.enableNetwork(networkId, false)) {
23765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane          if (DEBUG) Log.e(TAG, "enable network failed: " + networkId + "; " + config.toString());
23865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane          return false;
23965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
24065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
24165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (!wifiMan.saveConfiguration()) {
24265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane          if (DEBUG) Log.e(TAG, "failed to save: " + config.toString());
24365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane          return false;
24465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
24565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
24665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (DEBUG) Log.d(TAG, "saved network: " + config.toString());
24765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return true;
24865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
24965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
25065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
25165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * Forget a wifi configuration.
25265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
25365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static void forgetConfiguration(Context context, WifiConfiguration config) {
25465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (config == null) {
25565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return;
25665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
25765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
25865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        WifiManager wifiMan = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
25965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        List<WifiConfiguration> configuredNetworks = wifiMan.getConfiguredNetworks();
26065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (configuredNetworks == null) {
26165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (DEBUG) Log.e(TAG, "failed to get configured networks");
26265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return;
26365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
26465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
26565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        for (WifiConfiguration wc : configuredNetworks) {
26665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (wc != null && wc.SSID != null && TextUtils.equals(wc.SSID, config.SSID)) {
26765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                wifiMan.forget(wc.networkId, null);
26865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (DEBUG) Log.d(TAG, "forgot network config: " + wc.toString());
26965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                break;
27065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
27165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
27265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
27365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
27465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static String enquoteSsid(String ssid) {
27565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return "\"".concat(ssid.replace("\"", "\\\"")).concat("\"");
27665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
27765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
27865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
27965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * @return A matching WifiConfiguration from the list of configured
28065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * networks, or null if no matching network is found.
28165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
28265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static WifiConfiguration getFromConfiguredNetworks(Context context, String ssid,
28365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            WifiSecurity security) {
28465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        WifiManager wifiMan = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
28565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        List<WifiConfiguration> configuredNetworks = wifiMan.getConfiguredNetworks();
28665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (configuredNetworks != null) {
28765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            for (WifiConfiguration configuredNetwork : configuredNetworks) {
28865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (configuredNetwork == null || configuredNetwork.SSID == null) {
28965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    continue;  // Does this ever really happen?
29065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
29165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
29265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                // If the SSID and the security match, that's our network.
29365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                String configuredSsid = WifiInfo.removeDoubleQuotes(configuredNetwork.SSID);
29465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (TextUtils.equals(configuredSsid, ssid)) {
29565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    WifiSecurity configuredSecurity = WifiSecurity.getSecurity(configuredNetwork);
29665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    if (configuredSecurity.equals(security)) {
29765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        return configuredNetwork;
29865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    }
29965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
30065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
30165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
30265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
30365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return null;
30465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
30565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
30665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private WifiConfigHelper() {
30765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
30865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane}
309