f353517843442c4f87b0393ef7953a50fbaddbcf
[zxing.git] / android / src / com / google / zxing / client / android / wifi / WifiActivity.java
1 /*
2  * Copyright (C) 2010 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 java.util.List;
20 import java.util.regex.Pattern;
21
22 import android.app.Activity;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.net.ConnectivityManager;
26 import android.net.wifi.WifiConfiguration;
27 import android.net.wifi.WifiManager;
28 import android.os.Bundle;
29 import android.util.Log;
30 import android.widget.TextView;
31
32 import com.google.zxing.client.android.Intents;
33 import com.google.zxing.client.android.R;
34
35 /**
36  * A new activity showing the progress of Wifi connection
37  * 
38  * @author Vikram Aggarwal
39  */
40 public final class WifiActivity extends Activity  {
41
42   private static final String TAG = WifiActivity.class.getSimpleName();
43
44   private static final int MAX_ERROR_COUNT = 3;
45   private static final int FAILURE_NO_NETWORK_ID = -1;
46   private static final Pattern HEX_DIGITS_64 = Pattern.compile("[0-9A-Fa-f]{64}");
47
48   private WifiManager wifiManager;
49   private TextView statusView;
50   private WifiReceiver wifiReceiver;
51   private boolean receiverRegistered;
52   private int networkId;
53   private int errorCount;
54   private IntentFilter mWifiStateFilter;
55
56   void gotError() {
57     errorCount++;
58     Log.d(TAG, "Encountered another error.  Errorcount = " + errorCount);
59     if (errorCount > MAX_ERROR_COUNT){
60       errorCount = 0;
61       doError(R.string.wifi_connect_failed);
62     }
63   }
64
65   public enum NetworkType {
66     NETWORK_WEP, NETWORK_WPA, NETWORK_NOPASS, NETWORK_INVALID,
67   }
68
69   private int changeNetwork(NetworkSetting setting) {
70     // If the SSID is empty, throw an error and return
71     if (setting.getSsid() == null || setting.getSsid().length() == 0) {
72       return doError(R.string.wifi_ssid_missing);
73     }
74     // If the network type is invalid
75     if (setting.getNetworkType() == NetworkType.NETWORK_INVALID){
76       return doError(R.string.wifi_type_incorrect);
77     }
78
79     // If the password is empty, this is an unencrypted network
80     if (setting.getPassword() == null || setting.getPassword().length() == 0 ||
81         setting.getNetworkType() == null ||
82         setting.getNetworkType() == NetworkType.NETWORK_NOPASS) {
83       return changeNetworkUnEncrypted(setting);
84     }
85     if (setting.getNetworkType() == NetworkType.NETWORK_WPA) {
86       return changeNetworkWPA(setting);
87     } else {
88       return changeNetworkWEP(setting);
89     }
90   }
91
92   private int doError(int resource_string) {
93     statusView.setText(resource_string);
94     // Give up on the connection
95     wifiManager.disconnect();
96     if (networkId > 0) {
97       wifiManager.removeNetwork(networkId);
98       networkId = -1;
99     }
100     if (receiverRegistered) {
101       unregisterReceiver(wifiReceiver);
102       receiverRegistered = false;
103     }
104     return -1;
105   }
106
107   private WifiConfiguration changeNetworkCommon(NetworkSetting input){
108     statusView.setText(R.string.wifi_creating_network);
109     Log.d(TAG, "Adding new configuration: \nSSID: " + input.getSsid() + "\nType: " + input.getNetworkType());
110     WifiConfiguration config = new WifiConfiguration();
111
112     config.allowedAuthAlgorithms.clear();
113     config.allowedGroupCiphers.clear();
114     config.allowedKeyManagement.clear();
115     config.allowedPairwiseCiphers.clear();
116     config.allowedProtocols.clear();
117
118     // Android API insists that an ascii SSID must be quoted to be correctly handled.
119     config.SSID = NetworkUtil.convertToQuotedString(input.getSsid());
120     config.hiddenSSID = true;
121     return config;
122   }
123
124   private int requestNetworkChange(WifiConfiguration config){
125     statusView.setText(R.string.wifi_changing_network);
126     return updateNetwork(config, false);
127   }
128
129   // Adding a WEP network
130   private int changeNetworkWEP(NetworkSetting input) {
131     WifiConfiguration config = changeNetworkCommon(input);
132     String pass = input.getPassword();
133     if (NetworkUtil.isHexWepKey(pass)) {
134       config.wepKeys[0] = pass;
135     } else {
136       config.wepKeys[0] = NetworkUtil.convertToQuotedString(pass);
137     }
138     config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
139     config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
140     config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
141     config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
142     config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
143     config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
144     config.wepTxKeyIndex = 0;
145     return requestNetworkChange(config);
146   }
147
148   // Adding a WPA or WPA2 network
149   private int changeNetworkWPA(NetworkSetting input) {
150     WifiConfiguration config = changeNetworkCommon(input);
151     String pass = input.getPassword();
152     // Hex passwords that are 64 bits long are not to be quoted.
153     if (HEX_DIGITS_64.matcher(pass).matches()){
154       Log.d(TAG, "A 64 bit hex password entered.");
155       config.preSharedKey = pass;
156     } else {
157       Log.d(TAG, "A normal password entered: I am quoting it.");
158       config.preSharedKey = NetworkUtil.convertToQuotedString(pass);
159     }
160     config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
161     // For WPA
162     config.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
163     // For WPA2
164     config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
165     config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
166     config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
167     config.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
168     return requestNetworkChange(config);
169   }
170
171   // Adding an open, unsecured network
172   private int changeNetworkUnEncrypted(NetworkSetting input){
173     Log.d(TAG, "Empty password prompting a simple account setting");
174     WifiConfiguration config = changeNetworkCommon(input);
175     config.wepKeys[0] = "";
176     config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
177     config.wepTxKeyIndex = 0;
178     return requestNetworkChange(config);
179   }
180
181   /**
182    * If the given ssid name exists in the settings, then change its password to the one given here, and save
183    * @param ssid
184    */
185   private WifiConfiguration findNetworkInExistingConfig(String ssid){
186     List<WifiConfiguration> existingConfigs = wifiManager.getConfiguredNetworks();
187     for (WifiConfiguration existingConfig : existingConfigs) {
188       if (existingConfig.SSID.equals(ssid)) {
189         return existingConfig;
190       }
191     }
192     return null;
193   }
194
195   @Override
196   protected void onCreate(Bundle savedInstanceState) {
197     super.onCreate(savedInstanceState);
198
199     Intent intent = getIntent();
200     if (intent == null || (!intent.getAction().equals(Intents.WifiConnect.ACTION))) {
201       finish();
202       return;
203     }
204
205     String ssid = intent.getStringExtra(Intents.WifiConnect.SSID);
206     String password = intent.getStringExtra(Intents.WifiConnect.PASSWORD);
207     String networkType = intent.getStringExtra(Intents.WifiConnect.TYPE);
208     setContentView(R.layout.network);
209     statusView = (TextView) findViewById(R.id.networkStatus);
210
211     NetworkType networkT;
212     if (networkType.equals("WPA")) {
213       networkT = NetworkType.NETWORK_WPA;
214     } else if (networkType.equals("WEP")) {
215       networkT = NetworkType.NETWORK_WEP;
216     } else if (networkType.equals("nopass")) {
217      networkT = NetworkType.NETWORK_NOPASS;
218     } else {
219       doError(R.string.wifi_type_incorrect);
220       return;
221     }
222
223     // This is not available before onCreate
224     wifiManager = (WifiManager) this.getSystemService(WIFI_SERVICE);
225     // Start WiFi, otherwise nothing will work
226     wifiManager.setWifiEnabled(true);
227
228     // So we know when the network changes
229     wifiReceiver = new WifiReceiver(wifiManager, this, statusView, ssid);
230
231     // The order matters!
232     mWifiStateFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
233     mWifiStateFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
234     mWifiStateFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
235     mWifiStateFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
236     registerReceiver(wifiReceiver, mWifiStateFilter);
237     receiverRegistered = true;
238
239     if (password == null) {
240       password = "";
241     }
242     Log.d(TAG, "Adding new configuration: \nSSID: " + ssid + "Type: " + networkT);
243     NetworkSetting setting = new NetworkSetting(ssid, password, networkT);
244     changeNetwork(setting);
245   }
246
247   @Override
248   public void onPause() {
249     super.onPause();
250     if (receiverRegistered) {
251       unregisterReceiver(wifiReceiver);
252       receiverRegistered = false;
253     }
254   }
255
256   @Override
257   public void onResume() {
258     super.onResume();
259     if (wifiReceiver != null && mWifiStateFilter != null && !receiverRegistered) {
260       registerReceiver(wifiReceiver, mWifiStateFilter);
261       receiverRegistered = true;
262     }
263   }
264
265   @Override
266   protected void onDestroy() {
267     if (wifiReceiver != null) {
268       if (receiverRegistered) {
269         unregisterReceiver(wifiReceiver);
270         receiverRegistered = false;
271       }
272       wifiReceiver = null;
273     }
274     super.onDestroy();
275   }
276
277   /**
278    * Update the network: either create a new network or modify an existing network
279    * @param config the new network configuration
280    * @param disableOthers true if other networks must be disabled
281    * @return network ID of the connected network.
282    */
283   private int updateNetwork(WifiConfiguration config, boolean disableOthers) {
284     WifiConfiguration found = findNetworkInExistingConfig(config.SSID);
285     wifiManager.disconnect();
286     if (found == null) {
287       statusView.setText(R.string.wifi_creating_network);
288     } else {
289       statusView.setText(R.string.wifi_modifying_network);
290       Log.d(TAG, "Removing network " + found.networkId);
291       wifiManager.removeNetwork(found.networkId);
292       wifiManager.saveConfiguration();
293     }
294     networkId = wifiManager.addNetwork(config);
295     Log.d(TAG, "Inserted/Modified network " + networkId);
296     if (networkId < 0) {
297       return FAILURE_NO_NETWORK_ID;
298     }
299
300     // Try to disable the current network and start a new one.
301     if (!wifiManager.enableNetwork(networkId, disableOthers)) {
302       networkId = FAILURE_NO_NETWORK_ID;
303       return FAILURE_NO_NETWORK_ID;
304     }
305     errorCount = 0;
306     wifiManager.reassociate();
307     return networkId;
308   }
309
310 }