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