• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 
17 package com.android.settings.wifi.dpp;
18 
19 import static com.android.settings.wifi.dpp.WifiQrCode.SECURITY_NO_PASSWORD;
20 import static com.android.settings.wifi.dpp.WifiQrCode.SECURITY_SAE;
21 import static com.android.settings.wifi.dpp.WifiQrCode.SECURITY_WEP;
22 import static com.android.settings.wifi.dpp.WifiQrCode.SECURITY_WPA_PSK;
23 
24 import android.content.Context;
25 import android.content.Intent;
26 import android.net.wifi.WifiConfiguration;
27 import android.net.wifi.WifiConfiguration.KeyMgmt;
28 import android.net.wifi.WifiManager;
29 import android.text.TextUtils;
30 import android.util.Log;
31 
32 import androidx.annotation.VisibleForTesting;
33 
34 import java.util.ArrayList;
35 import java.util.List;
36 
37 /**
38  * Wraps the parameters of ZXing reader library's Wi-Fi Network config format.
39  * Please check {@code WifiQrCode} for detail of the format.
40  *
41  * Checks below members of {@code WifiDppUtils} for more information.
42  * EXTRA_WIFI_SECURITY / EXTRA_WIFI_SSID / EXTRA_WIFI_PRE_SHARED_KEY / EXTRA_WIFI_HIDDEN_SSID /
43  * EXTRA_QR_CODE
44  */
45 public class WifiNetworkConfig {
46 
47     static final String FAKE_SSID = "fake network";
48     static final String FAKE_PASSWORD = "password";
49     private static final String TAG = "WifiNetworkConfig";
50 
51     private String mSecurity;
52     private String mSsid;
53     private String mPreSharedKey;
54     private boolean mHiddenSsid;
55     private int mNetworkId;
56     private boolean mIsHotspot;
57 
58     @VisibleForTesting
WifiNetworkConfig(String security, String ssid, String preSharedKey, boolean hiddenSsid, int networkId, boolean isHotspot)59     WifiNetworkConfig(String security, String ssid, String preSharedKey,
60             boolean hiddenSsid, int networkId, boolean isHotspot) {
61         mSecurity = security;
62         mSsid = ssid;
63         mPreSharedKey = preSharedKey;
64         mHiddenSsid = hiddenSsid;
65         mNetworkId = networkId;
66         mIsHotspot = isHotspot;
67     }
68 
WifiNetworkConfig(WifiNetworkConfig config)69     public WifiNetworkConfig(WifiNetworkConfig config) {
70         mSecurity = config.mSecurity;
71         mSsid = config.mSsid;
72         mPreSharedKey = config.mPreSharedKey;
73         mHiddenSsid = config.mHiddenSsid;
74         mNetworkId = config.mNetworkId;
75         mIsHotspot = config.mIsHotspot;
76     }
77 
78     /**
79      * Wi-Fi DPP activities should implement this interface for fragments to retrieve the
80      * WifiNetworkConfig for configuration
81      */
82     public interface Retriever {
getWifiNetworkConfig()83         WifiNetworkConfig getWifiNetworkConfig();
84     }
85 
86     /**
87      * Retrieve WifiNetworkConfig from below 2 intents
88      *
89      * android.settings.WIFI_DPP_CONFIGURATOR_QR_CODE_GENERATOR
90      * android.settings.WIFI_DPP_CONFIGURATOR_QR_CODE_SCANNER
91      */
getValidConfigOrNull(Intent intent)92     static WifiNetworkConfig getValidConfigOrNull(Intent intent) {
93         final String security = intent.getStringExtra(WifiDppUtils.EXTRA_WIFI_SECURITY);
94         final String ssid = intent.getStringExtra(WifiDppUtils.EXTRA_WIFI_SSID);
95         final String preSharedKey = intent.getStringExtra(WifiDppUtils.EXTRA_WIFI_PRE_SHARED_KEY);
96         final boolean hiddenSsid = intent.getBooleanExtra(WifiDppUtils.EXTRA_WIFI_HIDDEN_SSID,
97                 false);
98         final int networkId = intent.getIntExtra(WifiDppUtils.EXTRA_WIFI_NETWORK_ID,
99                 WifiConfiguration.INVALID_NETWORK_ID);
100         final boolean isHotspot = intent.getBooleanExtra(WifiDppUtils.EXTRA_IS_HOTSPOT, false);
101 
102         return getValidConfigOrNull(security, ssid, preSharedKey, hiddenSsid, networkId, isHotspot);
103     }
104 
getValidConfigOrNull(String security, String ssid, String preSharedKey, boolean hiddenSsid, int networkId, boolean isHotspot)105     static WifiNetworkConfig getValidConfigOrNull(String security, String ssid,
106             String preSharedKey, boolean hiddenSsid, int networkId, boolean isHotspot) {
107         if (!isValidConfig(security, ssid, preSharedKey, hiddenSsid)) {
108             return null;
109         }
110 
111         return new WifiNetworkConfig(security, ssid, preSharedKey, hiddenSsid, networkId,
112                 isHotspot);
113     }
114 
isValidConfig(WifiNetworkConfig config)115     static boolean isValidConfig(WifiNetworkConfig config) {
116         if (config == null) {
117             return false;
118         } else {
119             return isValidConfig(config.mSecurity, config.mSsid, config.mPreSharedKey,
120                     config.mHiddenSsid);
121         }
122     }
123 
isValidConfig(String security, String ssid, String preSharedKey, boolean hiddenSsid)124     static boolean isValidConfig(String security, String ssid, String preSharedKey,
125             boolean hiddenSsid) {
126         if (!TextUtils.isEmpty(security) && !SECURITY_NO_PASSWORD.equals(security)) {
127             if (TextUtils.isEmpty(preSharedKey)) {
128                 return false;
129             }
130         }
131 
132         if (!hiddenSsid && TextUtils.isEmpty(ssid)) {
133             return false;
134         }
135 
136         return true;
137     }
138 
139     /**
140      * Escaped special characters "\", ";", ":", "," with a backslash
141      * See https://github.com/zxing/zxing/wiki/Barcode-Contents
142      */
escapeSpecialCharacters(String str)143     private String escapeSpecialCharacters(String str) {
144         if (TextUtils.isEmpty(str)) {
145             return str;
146         }
147 
148         StringBuilder buf = new StringBuilder();
149         for (int i = 0; i < str.length(); i++) {
150             char ch = str.charAt(i);
151             if (ch =='\\' || ch == ',' || ch == ';' || ch == ':') {
152                 buf.append('\\');
153             }
154             buf.append(ch);
155         }
156 
157         return buf.toString();
158     }
159 
160     /**
161      * Construct a barcode string for WiFi network login.
162      * See https://en.wikipedia.org/wiki/QR_code#WiFi_network_login
163      */
getQrCode()164     String getQrCode() {
165         final String empty = "";
166         return new StringBuilder("WIFI:")
167                 .append("S:")
168                 .append(escapeSpecialCharacters(mSsid))
169                 .append(";")
170                 .append("T:")
171                 .append(TextUtils.isEmpty(mSecurity) ? empty : mSecurity)
172                 .append(";")
173                 .append("P:")
174                 .append(TextUtils.isEmpty(mPreSharedKey) ? empty
175                         : escapeSpecialCharacters(mPreSharedKey))
176                 .append(";")
177                 .append("H:")
178                 .append(mHiddenSsid)
179                 .append(";;")
180                 .toString();
181     }
182 
getSecurity()183     public String getSecurity() {
184         return mSecurity;
185     }
186 
getSsid()187     public String getSsid() {
188         return mSsid;
189     }
190 
getPreSharedKey()191     public String getPreSharedKey() {
192         return mPreSharedKey;
193     }
194 
getHiddenSsid()195     public boolean getHiddenSsid() {
196         return mHiddenSsid;
197     }
198 
getNetworkId()199     public int getNetworkId() {
200         return mNetworkId;
201     }
202 
isHotspot()203     public boolean isHotspot() {
204         return mIsHotspot;
205     }
206 
isSupportWifiDpp(Context context)207     public boolean isSupportWifiDpp(Context context) {
208         if (!WifiDppUtils.isWifiDppEnabled(context)) {
209             return false;
210         }
211 
212         if (TextUtils.isEmpty(mSecurity)) {
213             return false;
214         }
215 
216         // DPP 1.0 only supports SAE and PSK.
217         final WifiManager wifiManager = context.getSystemService(WifiManager.class);
218         switch (mSecurity) {
219             case SECURITY_SAE:
220                 if (wifiManager.isWpa3SaeSupported()) {
221                     return true;
222                 }
223                 break;
224             case SECURITY_WPA_PSK:
225                 return true;
226             default:
227         }
228         return false;
229     }
230 
231     /**
232      * This is a simplified method from {@code WifiConfigController.getConfig()}
233      *
234      * @return When it's a open network, returns 2 WifiConfiguration in the List, the 1st is
235      *         open network and the 2nd is enhanced open network. Returns 1 WifiConfiguration in the
236      *         List for all other supported Wi-Fi securities.
237      */
getWifiConfigurations()238     List<WifiConfiguration> getWifiConfigurations() {
239         final List<WifiConfiguration> wifiConfigurations = new ArrayList<>();
240 
241         if (!isValidConfig(this)) {
242             return wifiConfigurations;
243         }
244 
245         if (TextUtils.isEmpty(mSecurity) || SECURITY_NO_PASSWORD.equals(mSecurity)) {
246             // TODO (b/129835824): we add both open network and enhanced open network to WifiManager
247             //                     for android Q, should improve it in the future.
248             final WifiConfiguration openNetworkWifiConfiguration = getBasicWifiConfiguration();
249             openNetworkWifiConfiguration.allowedKeyManagement.set(KeyMgmt.NONE);
250             wifiConfigurations.add(openNetworkWifiConfiguration);
251 
252             final WifiConfiguration enhancedOpenNetworkWifiConfiguration =
253                     getBasicWifiConfiguration();
254             enhancedOpenNetworkWifiConfiguration
255                     .setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
256             wifiConfigurations.add(enhancedOpenNetworkWifiConfiguration);
257             return wifiConfigurations;
258         }
259 
260         final WifiConfiguration wifiConfiguration = getBasicWifiConfiguration();
261         if (mSecurity.startsWith(SECURITY_WEP)) {
262             wifiConfiguration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP);
263 
264             // WEP-40, WEP-104, and 256-bit WEP (WEP-232?)
265             final int length = mPreSharedKey.length();
266             if ((length == 10 || length == 26 || length == 58)
267                     && mPreSharedKey.matches("[0-9A-Fa-f]*")) {
268                 wifiConfiguration.wepKeys[0] = mPreSharedKey;
269             } else {
270                 wifiConfiguration.wepKeys[0] = addQuotationIfNeeded(mPreSharedKey);
271             }
272         } else if (mSecurity.startsWith(SECURITY_WPA_PSK)) {
273             wifiConfiguration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
274 
275             if (mPreSharedKey.matches("[0-9A-Fa-f]{64}")) {
276                 wifiConfiguration.preSharedKey = mPreSharedKey;
277             } else {
278                 wifiConfiguration.preSharedKey = addQuotationIfNeeded(mPreSharedKey);
279             }
280         } else if (mSecurity.startsWith(SECURITY_SAE)) {
281             wifiConfiguration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
282             if (mPreSharedKey.length() != 0) {
283                 wifiConfiguration.preSharedKey = addQuotationIfNeeded(mPreSharedKey);
284             }
285         } else {
286             Log.w(TAG, "Unsupported security");
287             return wifiConfigurations;
288         }
289 
290         wifiConfigurations.add(wifiConfiguration);
291         return wifiConfigurations;
292     }
293 
getBasicWifiConfiguration()294     private WifiConfiguration getBasicWifiConfiguration() {
295         final WifiConfiguration wifiConfiguration = new WifiConfiguration();
296 
297         wifiConfiguration.SSID = addQuotationIfNeeded(mSsid);
298         wifiConfiguration.hiddenSSID = mHiddenSsid;
299         wifiConfiguration.networkId = mNetworkId;
300         return wifiConfiguration;
301     }
302 
addQuotationIfNeeded(String input)303     private String addQuotationIfNeeded(String input) {
304         if (TextUtils.isEmpty(input)) {
305             return "";
306         }
307 
308         if (input.length() >= 2 && input.startsWith("\"") && input.endsWith("\"")) {
309             return input;
310         }
311 
312         StringBuilder sb = new StringBuilder();
313         sb.append("\"").append(input).append("\"");
314         return sb.toString();
315     }
316 }
317