1 /* 2 * Copyright (C) 2011 ZXing authors 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.google.zxing.client.android.wifi; 18 19 import android.net.wifi.WifiConfiguration; 20 import android.net.wifi.WifiEnterpriseConfig; 21 import android.net.wifi.WifiManager; 22 import android.os.AsyncTask; 23 import android.util.Log; 24 25 import java.util.regex.Pattern; 26 27 import com.google.zxing.client.result.WifiParsedResult; 28 29 /** 30 * @author Vikram Aggarwal 31 * @author Sean Owen 32 * @author Steffen Kieß 33 */ 34 public final class WifiConfigManager extends AsyncTask<WifiParsedResult,Object,Object> { 35 36 private static final String TAG = WifiConfigManager.class.getSimpleName(); 37 38 private static final Pattern HEX_DIGITS = Pattern.compile("[0-9A-Fa-f]+"); 39 40 private final WifiManager wifiManager; 41 WifiConfigManager(WifiManager wifiManager)42 public WifiConfigManager(WifiManager wifiManager) { 43 this.wifiManager = wifiManager; 44 } 45 46 @Override doInBackground(WifiParsedResult... args)47 protected Object doInBackground(WifiParsedResult... args) { 48 WifiParsedResult theWifiResult = args[0]; 49 // Start WiFi, otherwise nothing will work 50 if (!wifiManager.isWifiEnabled()) { 51 Log.i(TAG, "Enabling wi-fi..."); 52 if (wifiManager.setWifiEnabled(true)) { 53 Log.i(TAG, "Wi-fi enabled"); 54 } else { 55 Log.w(TAG, "Wi-fi could not be enabled!"); 56 return null; 57 } 58 // This happens very quickly, but need to wait for it to enable. A little busy wait? 59 int count = 0; 60 while (!wifiManager.isWifiEnabled()) { 61 if (count >= 10) { 62 Log.i(TAG, "Took too long to enable wi-fi, quitting"); 63 return null; 64 } 65 Log.i(TAG, "Still waiting for wi-fi to enable..."); 66 try { 67 Thread.sleep(1000L); 68 } catch (InterruptedException ie) { 69 // continue 70 } 71 count++; 72 } 73 } 74 String networkTypeString = theWifiResult.getNetworkEncryption(); 75 NetworkType networkType; 76 try { 77 networkType = NetworkType.forIntentValue(networkTypeString); 78 } catch (IllegalArgumentException ignored) { 79 Log.w(TAG, "Bad network type"); 80 return null; 81 } 82 if (networkType == NetworkType.NO_PASSWORD) { 83 changeNetworkUnEncrypted(wifiManager, theWifiResult); 84 } else { 85 String password = theWifiResult.getPassword(); 86 if (password != null && !password.isEmpty()) { 87 switch (networkType) { 88 case WEP: 89 changeNetworkWEP(wifiManager, theWifiResult); 90 break; 91 case WPA: 92 changeNetworkWPA(wifiManager, theWifiResult); 93 break; 94 case WPA2_EAP: 95 changeNetworkWPA2EAP(wifiManager, theWifiResult); 96 break; 97 } 98 } 99 } 100 return null; 101 } 102 103 /** 104 * Update the network: either create a new network or modify an existing network 105 * @param config the new network configuration 106 */ updateNetwork(WifiManager wifiManager, WifiConfiguration config)107 private static void updateNetwork(WifiManager wifiManager, WifiConfiguration config) { 108 Integer foundNetworkID = findNetworkInExistingConfig(wifiManager, config.SSID); 109 if (foundNetworkID != null) { 110 Log.i(TAG, "Removing old configuration for network " + config.SSID); 111 wifiManager.removeNetwork(foundNetworkID); 112 wifiManager.saveConfiguration(); 113 } 114 int networkId = wifiManager.addNetwork(config); 115 if (networkId >= 0) { 116 // Try to disable the current network and start a new one. 117 if (wifiManager.enableNetwork(networkId, true)) { 118 Log.i(TAG, "Associating to network " + config.SSID); 119 wifiManager.saveConfiguration(); 120 } else { 121 Log.w(TAG, "Failed to enable network " + config.SSID); 122 } 123 } else { 124 Log.w(TAG, "Unable to add network " + config.SSID); 125 } 126 } 127 changeNetworkCommon(WifiParsedResult wifiResult)128 private static WifiConfiguration changeNetworkCommon(WifiParsedResult wifiResult) { 129 WifiConfiguration config = new WifiConfiguration(); 130 config.allowedAuthAlgorithms.clear(); 131 config.allowedGroupCiphers.clear(); 132 config.allowedKeyManagement.clear(); 133 config.allowedPairwiseCiphers.clear(); 134 config.allowedProtocols.clear(); 135 // Android API insists that an ascii SSID must be quoted to be correctly handled. 136 config.SSID = quoteNonHex(wifiResult.getSsid()); 137 config.hiddenSSID = wifiResult.isHidden(); 138 return config; 139 } 140 141 // Adding a WEP network changeNetworkWEP(WifiManager wifiManager, WifiParsedResult wifiResult)142 private static void changeNetworkWEP(WifiManager wifiManager, WifiParsedResult wifiResult) { 143 WifiConfiguration config = changeNetworkCommon(wifiResult); 144 config.wepKeys[0] = quoteNonHex(wifiResult.getPassword(), 10, 26, 58); 145 config.wepTxKeyIndex = 0; 146 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED); 147 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); 148 config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); 149 config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); 150 config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40); 151 config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104); 152 updateNetwork(wifiManager, config); 153 } 154 155 // Adding a WPA or WPA2 network changeNetworkWPA(WifiManager wifiManager, WifiParsedResult wifiResult)156 private static void changeNetworkWPA(WifiManager wifiManager, WifiParsedResult wifiResult) { 157 WifiConfiguration config = changeNetworkCommon(wifiResult); 158 // Hex passwords that are 64 bits long are not to be quoted. 159 config.preSharedKey = quoteNonHex(wifiResult.getPassword(), 64); 160 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); 161 config.allowedProtocols.set(WifiConfiguration.Protocol.WPA); // For WPA 162 config.allowedProtocols.set(WifiConfiguration.Protocol.RSN); // For WPA2 163 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); 164 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); 165 config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP); 166 config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); 167 config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); 168 config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); 169 updateNetwork(wifiManager, config); 170 } 171 172 // Adding a WPA2 enterprise (EAP) network changeNetworkWPA2EAP(WifiManager wifiManager, WifiParsedResult wifiResult)173 private static void changeNetworkWPA2EAP(WifiManager wifiManager, WifiParsedResult wifiResult) { 174 WifiConfiguration config = changeNetworkCommon(wifiResult); 175 // Hex passwords that are 64 bits long are not to be quoted. 176 config.preSharedKey = quoteNonHex(wifiResult.getPassword(), 64); 177 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); 178 config.allowedProtocols.set(WifiConfiguration.Protocol.RSN); // For WPA2 179 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); 180 config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP); 181 config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); 182 config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); 183 config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); 184 config.enterpriseConfig.setIdentity(wifiResult.getIdentity()); 185 config.enterpriseConfig.setAnonymousIdentity(wifiResult.getAnonymousIdentity()); 186 config.enterpriseConfig.setPassword(wifiResult.getPassword()); 187 config.enterpriseConfig.setEapMethod(parseEap(wifiResult.getEapMethod())); 188 config.enterpriseConfig.setPhase2Method(parsePhase2(wifiResult.getPhase2Method())); 189 updateNetwork(wifiManager, config); 190 } 191 192 // Adding an open, unsecured network changeNetworkUnEncrypted(WifiManager wifiManager, WifiParsedResult wifiResult)193 private static void changeNetworkUnEncrypted(WifiManager wifiManager, WifiParsedResult wifiResult) { 194 WifiConfiguration config = changeNetworkCommon(wifiResult); 195 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); 196 updateNetwork(wifiManager, config); 197 } 198 findNetworkInExistingConfig(WifiManager wifiManager, String ssid)199 private static Integer findNetworkInExistingConfig(WifiManager wifiManager, String ssid) { 200 Iterable<WifiConfiguration> existingConfigs = wifiManager.getConfiguredNetworks(); 201 if (existingConfigs != null) { 202 for (WifiConfiguration existingConfig : existingConfigs) { 203 String existingSSID = existingConfig.SSID; 204 if (existingSSID != null && existingSSID.equals(ssid)) { 205 return existingConfig.networkId; 206 } 207 } 208 } 209 return null; 210 } 211 quoteNonHex(String value, int... allowedLengths)212 private static String quoteNonHex(String value, int... allowedLengths) { 213 return isHexOfLength(value, allowedLengths) ? value : convertToQuotedString(value); 214 } 215 216 /** 217 * Encloses the incoming string inside double quotes, if it isn't already quoted. 218 * @param s the input string 219 * @return a quoted string, of the form "input". If the input string is null, it returns null 220 * as well. 221 */ convertToQuotedString(String s)222 private static String convertToQuotedString(String s) { 223 if (s == null || s.isEmpty()) { 224 return null; 225 } 226 // If already quoted, return as-is 227 if (s.charAt(0) == '"' && s.charAt(s.length() - 1) == '"') { 228 return s; 229 } 230 return '\"' + s + '\"'; 231 } 232 233 /** 234 * @param value input to check 235 * @param allowedLengths allowed lengths, if any 236 * @return true if value is a non-null, non-empty string of hex digits, and if allowed lengths are given, has 237 * an allowed length 238 */ isHexOfLength(CharSequence value, int... allowedLengths)239 private static boolean isHexOfLength(CharSequence value, int... allowedLengths) { 240 if (value == null || !HEX_DIGITS.matcher(value).matches()) { 241 return false; 242 } 243 if (allowedLengths.length == 0) { 244 return true; 245 } 246 for (int length : allowedLengths) { 247 if (value.length() == length) { 248 return true; 249 } 250 } 251 return false; 252 } 253 parseEap(String eapString)254 private static int parseEap(String eapString) { 255 if (eapString == null) { 256 return WifiEnterpriseConfig.Eap.NONE; 257 } 258 switch (eapString) { 259 case "NONE": 260 return WifiEnterpriseConfig.Eap.NONE; 261 case "PEAP": 262 return WifiEnterpriseConfig.Eap.PEAP; 263 case "PWD": 264 return WifiEnterpriseConfig.Eap.PWD; 265 case "TLS": 266 return WifiEnterpriseConfig.Eap.TLS; 267 case "TTLS": 268 return WifiEnterpriseConfig.Eap.TTLS; 269 default: 270 throw new IllegalArgumentException("Unknown value for EAP method: " + eapString); 271 } 272 } 273 parsePhase2(String phase2String)274 private static int parsePhase2(String phase2String) { 275 if (phase2String == null) { 276 return WifiEnterpriseConfig.Phase2.NONE; 277 } 278 switch (phase2String) { 279 case "GTC": 280 return WifiEnterpriseConfig.Phase2.GTC; 281 case "MSCHAP": 282 return WifiEnterpriseConfig.Phase2.MSCHAP; 283 case "MSCHAPV2": 284 return WifiEnterpriseConfig.Phase2.MSCHAPV2; 285 case "NONE": 286 return WifiEnterpriseConfig.Phase2.NONE; 287 case "PAP": 288 return WifiEnterpriseConfig.Phase2.PAP; 289 default: 290 throw new IllegalArgumentException("Unknown value for phase 2 method: " + phase2String); 291 } 292 } 293 294 } 295