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