1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.tv.settings.connectivity;
18
19import android.content.Context;
20import android.net.wifi.WifiConfiguration;
21import android.net.wifi.WifiConfiguration.AuthAlgorithm;
22import android.net.wifi.WifiConfiguration.KeyMgmt;
23import android.net.wifi.WifiInfo;
24import android.net.wifi.WifiManager;
25import android.text.TextUtils;
26import android.util.Log;
27
28import com.android.settingslib.wifi.AccessPoint;
29import com.android.tv.settings.R;
30
31import java.util.List;
32import java.util.regex.Matcher;
33import java.util.regex.Pattern;
34
35/**
36 * Helper class that deals with Wi-fi configuration
37 */
38public final class WifiConfigHelper {
39
40    private static final String TAG = "WifiConfigHelper";
41    private static final boolean DEBUG = false;
42
43    /**
44     * If there are exactly 12 hex digits, this looks like a BSSID
45     */
46    private static final String REGEX_HEX_BSSID = "[a-fA-F0-9]{12}";
47
48    // Allows underscore char to supports proxies that do not
49    // follow the spec
50    private static final String HC = "a-zA-Z0-9\\_";
51
52    // Matches blank input, ips, and domain names
53    private static final String HOSTNAME_REGEXP =
54            "^$|^[" + HC + "]+(\\-[" + HC + "]+)*(\\.[" + HC + "]+(\\-[" + HC + "]+)*)*$";
55    private static final Pattern HOSTNAME_PATTERN;
56    private static final String EXCLUSION_REGEXP =
57            "$|^(\\*)?\\.?[" + HC + "]+(\\-[" + HC + "]+)*(\\.[" + HC + "]+(\\-[" + HC + "]+)*)*$";
58    private static final Pattern EXCLUSION_PATTERN;
59    static {
60        HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
61        EXCLUSION_PATTERN = Pattern.compile(EXCLUSION_REGEXP);
62    }
63
64    public static void setConfigSsid(WifiConfiguration config, String ssid) {
65        // if this looks like a BSSID, don't quote it
66        if (!Pattern.matches(REGEX_HEX_BSSID, ssid)) {
67            config.SSID = AccessPoint.convertToQuotedString(ssid);
68        } else {
69            config.SSID = ssid;
70        }
71    }
72
73    public static void setConfigKeyManagementBySecurity(
74            WifiConfiguration config, WifiSecurity security) {
75        config.allowedKeyManagement.clear();
76        config.allowedAuthAlgorithms.clear();
77        switch (security) {
78            case NONE:
79                config.allowedKeyManagement.set(KeyMgmt.NONE);
80                break;
81            case WEP:
82                config.allowedKeyManagement.set(KeyMgmt.NONE);
83                config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
84                config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
85                break;
86            case PSK:
87                config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
88                break;
89            case EAP:
90                config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
91                config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
92                break;
93        }
94    }
95
96    /**
97     * validate syntax of hostname and port entries
98     * @return 0 on success, string resource ID on failure
99     */
100    public static int validate(String hostname, String port, String exclList) {
101        Matcher match = HOSTNAME_PATTERN.matcher(hostname);
102        String exclListArray[] = exclList.split(",");
103
104        if (!match.matches()) return R.string.proxy_error_invalid_host;
105
106        for (String excl : exclListArray) {
107            Matcher m = EXCLUSION_PATTERN.matcher(excl);
108            if (!m.matches()) return R.string.proxy_error_invalid_exclusion_list;
109        }
110
111        if (hostname.length() > 0 && port.length() == 0) {
112            return R.string.proxy_error_empty_port;
113        }
114
115        if (port.length() > 0) {
116            if (hostname.length() == 0) {
117                return R.string.proxy_error_empty_host_set_port;
118            }
119            int portVal = -1;
120            try {
121                portVal = Integer.parseInt(port);
122            } catch (NumberFormatException ex) {
123                return R.string.proxy_error_invalid_port;
124            }
125            if (portVal <= 0 || portVal > 0xFFFF) {
126                return R.string.proxy_error_invalid_port;
127            }
128        }
129        return 0;
130    }
131
132    public static WifiConfiguration getWifiConfiguration(WifiManager wifiManager, int networkId) {
133        List<WifiConfiguration> configuredNetworks = wifiManager.getConfiguredNetworks();
134        if (configuredNetworks != null) {
135            for (WifiConfiguration configuredNetwork : configuredNetworks) {
136                if (configuredNetwork.networkId == networkId) {
137                    return configuredNetwork;
138                }
139            }
140        }
141        return null;
142    }
143
144    /**
145     * Did this config come out of the supplicant?  NOT "Is the config currently in the supplicant?"
146     */
147    public static boolean isNetworkSaved(WifiConfiguration config) {
148        return config != null && config.networkId > -1;
149    }
150
151    /**
152     * Return the configured network that matches the ssid/security pair, or create one.
153     */
154    public static WifiConfiguration getConfiguration(Context context, String ssid,
155            WifiSecurity security) {
156        WifiConfiguration config = getFromConfiguredNetworks(context, ssid, security);
157
158        if (config == null) {
159            // No configured network found; populate a new one with the provided ssid / security.
160            config = new WifiConfiguration();
161            setConfigSsid(config, ssid);
162            setConfigKeyManagementBySecurity(config, security);
163        }
164        return config;
165    }
166
167    /**
168     * Save a wifi configuration.
169     */
170    public static boolean saveConfiguration(Context context, WifiConfiguration config) {
171        if (config == null) {
172            return false;
173        }
174
175        WifiManager wifiMan = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
176        int networkId = wifiMan.addNetwork(config);
177        if (networkId == -1) {
178          if (DEBUG) Log.e(TAG, "failed to add network: " + config.toString());
179          return false;
180        }
181
182        if (!wifiMan.enableNetwork(networkId, false)) {
183          if (DEBUG) Log.e(TAG, "enable network failed: " + networkId + "; " + config.toString());
184          return false;
185        }
186
187        if (!wifiMan.saveConfiguration()) {
188          if (DEBUG) Log.e(TAG, "failed to save: " + config.toString());
189          return false;
190        }
191
192        if (DEBUG) Log.d(TAG, "saved network: " + config.toString());
193        return true;
194    }
195
196    /**
197     * Forget a wifi configuration.
198     */
199    public static void forgetConfiguration(Context context, WifiConfiguration config) {
200        if (config == null) {
201            return;
202        }
203
204        WifiManager wifiMan = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
205        List<WifiConfiguration> configuredNetworks = wifiMan.getConfiguredNetworks();
206        if (configuredNetworks == null) {
207            if (DEBUG) Log.e(TAG, "failed to get configured networks");
208            return;
209        }
210
211        for (WifiConfiguration wc : configuredNetworks) {
212            if (wc != null && wc.SSID != null && TextUtils.equals(wc.SSID, config.SSID)) {
213                wifiMan.forget(wc.networkId, null);
214                if (DEBUG) Log.d(TAG, "forgot network config: " + wc.toString());
215                break;
216            }
217        }
218    }
219
220    /**
221     * Forget the current wifi connection.
222     */
223    public static void forgetWifiNetwork(Context context) {
224        WifiManager mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
225        WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
226        if (wifiInfo != null) {
227            int networkId = wifiInfo.getNetworkId();
228            if (networkId != -1) {
229                mWifiManager.forget(networkId, null);
230            }
231        }
232    }
233
234    /**
235     * @return A matching WifiConfiguration from the list of configured
236     * networks, or null if no matching network is found.
237     */
238    private static WifiConfiguration getFromConfiguredNetworks(Context context, String ssid,
239            WifiSecurity security) {
240        WifiManager wifiMan = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
241        List<WifiConfiguration> configuredNetworks = wifiMan.getConfiguredNetworks();
242        if (configuredNetworks != null) {
243            for (WifiConfiguration configuredNetwork : configuredNetworks) {
244                if (configuredNetwork == null || configuredNetwork.SSID == null) {
245                    continue;  // Does this ever really happen?
246                }
247
248                // If the SSID and the security match, that's our network.
249                String configuredSsid = WifiInfo.removeDoubleQuotes(configuredNetwork.SSID);
250                if (TextUtils.equals(configuredSsid, ssid)) {
251                    WifiSecurity configuredSecurity = WifiSecurity.getSecurity(configuredNetwork);
252                    if (configuredSecurity.equals(security)) {
253                        return configuredNetwork;
254                    }
255                }
256            }
257        }
258
259        return null;
260    }
261
262    private WifiConfigHelper() {
263    }
264}
265