xref: /aosp_15_r20/external/zxing/android/src/com/google/zxing/client/android/wifi/WifiConfigManager.java (revision 513427e33d61bc67fc40bc261642ac0b2a686b45)
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