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 android.os.UserManager.DISALLOW_ADD_WIFI_CONFIG; 20 21 import android.app.settings.SettingsEnums; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.net.Uri; 25 import android.net.wifi.WifiConfiguration; 26 import android.net.wifi.WifiInfo; 27 import android.net.wifi.WifiManager; 28 import android.os.Bundle; 29 import android.os.UserManager; 30 import android.provider.Settings; 31 import android.util.EventLog; 32 import android.util.Log; 33 34 import androidx.annotation.VisibleForTesting; 35 import androidx.fragment.app.FragmentTransaction; 36 37 import com.android.settings.R; 38 39 import java.util.List; 40 41 /** 42 * To provision "other" device with specified Wi-Fi network. 43 * 44 * Uses different intents to specify different provisioning ways. 45 * 46 * For intent action {@code ACTION_CONFIGURATOR_QR_CODE_SCANNER} and 47 * {@code android.settings.WIFI_DPP_CONFIGURATOR_QR_CODE_GENERATOR}, specify the Wi-Fi network to be 48 * provisioned in: 49 * 50 * {@code WifiDppUtils.EXTRA_WIFI_SECURITY} 51 * {@code WifiDppUtils.EXTRA_WIFI_SSID} 52 * {@code WifiDppUtils.EXTRA_WIFI_PRE_SHARED_KEY} 53 * {@code WifiDppUtils.EXTRA_WIFI_HIDDEN_SSID} 54 * 55 * For intent action {@link Settings#ACTION_PROCESS_WIFI_EASY_CONNECT_URI}, specify Wi-Fi 56 * Easy Connect bootstrapping information string in Intent's data URI. 57 */ 58 public class WifiDppConfiguratorActivity extends WifiDppBaseActivity implements 59 WifiNetworkConfig.Retriever, 60 WifiDppQrCodeScannerFragment.OnScanWifiDppSuccessListener, 61 WifiDppAddDeviceFragment.OnClickChooseDifferentNetworkListener, 62 WifiNetworkListFragment.OnChooseNetworkListener { 63 64 private static final String TAG = "WifiDppConfiguratorActivity"; 65 66 static final String ACTION_CONFIGURATOR_QR_CODE_SCANNER = 67 "android.settings.WIFI_DPP_CONFIGURATOR_QR_CODE_SCANNER"; 68 static final String ACTION_CONFIGURATOR_QR_CODE_GENERATOR = 69 "android.settings.WIFI_DPP_CONFIGURATOR_QR_CODE_GENERATOR"; 70 71 // Key for Bundle usage 72 private static final String KEY_QR_CODE = "key_qr_code"; 73 private static final String KEY_WIFI_SECURITY = "key_wifi_security"; 74 private static final String KEY_WIFI_SSID = "key_wifi_ssid"; 75 private static final String KEY_WIFI_PRESHARED_KEY = "key_wifi_preshared_key"; 76 private static final String KEY_WIFI_HIDDEN_SSID = "key_wifi_hidden_ssid"; 77 private static final String KEY_WIFI_NETWORK_ID = "key_wifi_network_id"; 78 private static final String KEY_IS_HOTSPOT = "key_is_hotspot"; 79 80 /** The Wi-Fi network which will be configured */ 81 private WifiNetworkConfig mWifiNetworkConfig; 82 83 /** The Wi-Fi DPP QR code from intent ACTION_PROCESS_WIFI_EASY_CONNECT_URI */ 84 private WifiQrCode mWifiDppQrCode; 85 86 /** 87 * The remote device's band support obtained as an (optional) extra 88 * EXTRA_EASY_CONNECT_BAND_LIST from the intent ACTION_PROCESS_WIFI_EASY_CONNECT_URI. 89 * 90 * The band support is provided as IEEE 802.11 Global Operating Classes. There may be a single 91 * or multiple operating classes specified. The array may also be a null if the extra wasn't 92 * specified. 93 */ 94 private int[] mWifiDppRemoteBandSupport; 95 96 @Override getMetricsCategory()97 public int getMetricsCategory() { 98 return SettingsEnums.SETTINGS_WIFI_DPP_CONFIGURATOR; 99 } 100 101 @Override onCreate(Bundle savedInstanceState)102 protected void onCreate(Bundle savedInstanceState) { 103 super.onCreate(savedInstanceState); 104 if (!isAddWifiConfigAllowed(getApplicationContext())) { 105 finish(); 106 return; 107 } 108 109 if (savedInstanceState != null) { 110 String qrCode = savedInstanceState.getString(KEY_QR_CODE); 111 112 mWifiDppQrCode = WifiQrCode.getValidWifiDppQrCodeOrNull(qrCode); 113 114 final String security = savedInstanceState.getString(KEY_WIFI_SECURITY); 115 final String ssid = savedInstanceState.getString(KEY_WIFI_SSID); 116 final String preSharedKey = savedInstanceState.getString(KEY_WIFI_PRESHARED_KEY); 117 final boolean hiddenSsid = savedInstanceState.getBoolean(KEY_WIFI_HIDDEN_SSID); 118 final int networkId = savedInstanceState.getInt(KEY_WIFI_NETWORK_ID); 119 final boolean isHotspot = savedInstanceState.getBoolean(KEY_IS_HOTSPOT); 120 121 mWifiNetworkConfig = WifiNetworkConfig.getValidConfigOrNull(security, ssid, 122 preSharedKey, hiddenSsid, networkId, isHotspot); 123 } 124 } 125 126 @Override handleIntent(Intent intent)127 protected void handleIntent(Intent intent) { 128 if (!isAddWifiConfigAllowed(getApplicationContext())) { 129 finish(); 130 return; 131 } 132 if (isGuestUser(getApplicationContext())) { 133 Log.e(TAG, "Guest user is not allowed to configure Wi-Fi!"); 134 EventLog.writeEvent(0x534e4554, "224772890", -1 /* UID */, "User is a guest"); 135 finish(); 136 return; 137 } 138 139 String action = intent != null ? intent.getAction() : null; 140 if (action == null) { 141 finish(); 142 return; 143 } 144 145 boolean cancelActivity = false; 146 WifiNetworkConfig config; 147 switch (action) { 148 case ACTION_CONFIGURATOR_QR_CODE_SCANNER: 149 config = WifiNetworkConfig.getValidConfigOrNull(intent); 150 if (config == null) { 151 cancelActivity = true; 152 } else { 153 mWifiNetworkConfig = config; 154 showQrCodeScannerFragment(); 155 } 156 break; 157 case ACTION_CONFIGURATOR_QR_CODE_GENERATOR: 158 config = WifiNetworkConfig.getValidConfigOrNull(intent); 159 if (config == null) { 160 cancelActivity = true; 161 } else { 162 mWifiNetworkConfig = config; 163 showQrCodeGeneratorFragment(); 164 } 165 break; 166 case Settings.ACTION_PROCESS_WIFI_EASY_CONNECT_URI: 167 WifiDppUtils.showLockScreen(this, 168 () -> handleActionProcessWifiEasyConnectUriIntent(intent)); 169 break; 170 default: 171 cancelActivity = true; 172 Log.e(TAG, "Launch with an invalid action"); 173 } 174 175 if (cancelActivity) { 176 finish(); 177 } 178 } 179 handleActionProcessWifiEasyConnectUriIntent(Intent intent)180 private void handleActionProcessWifiEasyConnectUriIntent(Intent intent) { 181 final Uri uri = intent.getData(); 182 final String uriString = (uri == null) ? null : uri.toString(); 183 mWifiDppQrCode = WifiQrCode.getValidWifiDppQrCodeOrNull(uriString); 184 mWifiDppRemoteBandSupport = intent.getIntArrayExtra( 185 Settings.EXTRA_EASY_CONNECT_BAND_LIST); // returns null if none 186 final boolean isDppSupported = WifiDppUtils.isWifiDppEnabled(this); 187 if (!isDppSupported) { 188 Log.e(TAG, 189 "ACTION_PROCESS_WIFI_EASY_CONNECT_URI for a device that doesn't " 190 + "support Wifi DPP - use WifiManager#isEasyConnectSupported"); 191 } 192 if (mWifiDppQrCode == null) { 193 Log.e(TAG, "ACTION_PROCESS_WIFI_EASY_CONNECT_URI with null URI!"); 194 } 195 if (mWifiDppQrCode == null || !isDppSupported) { 196 finish(); 197 } else { 198 final WifiNetworkConfig connectedConfig = getConnectedWifiNetworkConfigOrNull(); 199 if (connectedConfig == null || !connectedConfig.isSupportWifiDpp(this)) { 200 showChooseSavedWifiNetworkFragment(/* addToBackStack */ false); 201 } else { 202 mWifiNetworkConfig = connectedConfig; 203 showAddDeviceFragment(/* addToBackStack */ false); 204 } 205 } 206 } 207 208 @VisibleForTesting showQrCodeScannerFragment()209 void showQrCodeScannerFragment() { 210 WifiDppQrCodeScannerFragment fragment = 211 (WifiDppQrCodeScannerFragment) mFragmentManager.findFragmentByTag( 212 WifiDppUtils.TAG_FRAGMENT_QR_CODE_SCANNER); 213 214 if (fragment == null) { 215 fragment = new WifiDppQrCodeScannerFragment(); 216 } else { 217 if (fragment.isVisible()) { 218 return; 219 } 220 221 // When the fragment in back stack but not on top of the stack, we can simply pop 222 // stack because current fragment transactions are arranged in an order 223 mFragmentManager.popBackStackImmediate(); 224 return; 225 } 226 final FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); 227 228 fragmentTransaction.replace(R.id.fragment_container, fragment, 229 WifiDppUtils.TAG_FRAGMENT_QR_CODE_SCANNER); 230 fragmentTransaction.commit(); 231 } 232 showQrCodeGeneratorFragment()233 private void showQrCodeGeneratorFragment() { 234 WifiDppQrCodeGeneratorFragment fragment = 235 (WifiDppQrCodeGeneratorFragment) mFragmentManager.findFragmentByTag( 236 WifiDppUtils.TAG_FRAGMENT_QR_CODE_GENERATOR); 237 238 if (fragment == null) { 239 fragment = new WifiDppQrCodeGeneratorFragment(); 240 } else { 241 if (fragment.isVisible()) { 242 return; 243 } 244 245 // When the fragment in back stack but not on top of the stack, we can simply pop 246 // stack because current fragment transactions are arranged in an order 247 mFragmentManager.popBackStackImmediate(); 248 return; 249 } 250 final FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); 251 252 fragmentTransaction.replace(R.id.fragment_container, fragment, 253 WifiDppUtils.TAG_FRAGMENT_QR_CODE_GENERATOR); 254 fragmentTransaction.commit(); 255 } 256 showChooseSavedWifiNetworkFragment(boolean addToBackStack)257 private void showChooseSavedWifiNetworkFragment(boolean addToBackStack) { 258 WifiDppChooseSavedWifiNetworkFragment fragment = 259 (WifiDppChooseSavedWifiNetworkFragment) mFragmentManager.findFragmentByTag( 260 WifiDppUtils.TAG_FRAGMENT_CHOOSE_SAVED_WIFI_NETWORK); 261 262 if (fragment == null) { 263 fragment = new WifiDppChooseSavedWifiNetworkFragment(); 264 } else { 265 if (fragment.isVisible()) { 266 return; 267 } 268 269 // When the fragment in back stack but not on top of the stack, we can simply pop 270 // stack because current fragment transactions are arranged in an order 271 mFragmentManager.popBackStackImmediate(); 272 return; 273 } 274 final FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); 275 276 fragmentTransaction.replace(R.id.fragment_container, fragment, 277 WifiDppUtils.TAG_FRAGMENT_CHOOSE_SAVED_WIFI_NETWORK); 278 if (addToBackStack) { 279 fragmentTransaction.addToBackStack(/* name */ null); 280 } 281 fragmentTransaction.commit(); 282 } 283 showAddDeviceFragment(boolean addToBackStack)284 private void showAddDeviceFragment(boolean addToBackStack) { 285 WifiDppAddDeviceFragment fragment = 286 (WifiDppAddDeviceFragment) mFragmentManager.findFragmentByTag( 287 WifiDppUtils.TAG_FRAGMENT_ADD_DEVICE); 288 289 if (fragment == null) { 290 fragment = new WifiDppAddDeviceFragment(); 291 } else { 292 if (fragment.isVisible()) { 293 return; 294 } 295 296 // When the fragment in back stack but not on top of the stack, we can simply pop 297 // stack because current fragment transactions are arranged in an order 298 mFragmentManager.popBackStackImmediate(); 299 return; 300 } 301 final FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); 302 303 fragmentTransaction.replace(R.id.fragment_container, fragment, 304 WifiDppUtils.TAG_FRAGMENT_ADD_DEVICE); 305 if (addToBackStack) { 306 fragmentTransaction.addToBackStack(/* name */ null); 307 } 308 fragmentTransaction.commit(); 309 } 310 311 @Override getWifiNetworkConfig()312 public WifiNetworkConfig getWifiNetworkConfig() { 313 return mWifiNetworkConfig; 314 } 315 getWifiDppQrCode()316 WifiQrCode getWifiDppQrCode() { 317 return mWifiDppQrCode; 318 } 319 320 @VisibleForTesting setWifiNetworkConfig(WifiNetworkConfig config)321 boolean setWifiNetworkConfig(WifiNetworkConfig config) { 322 if(!WifiNetworkConfig.isValidConfig(config)) { 323 return false; 324 } else { 325 mWifiNetworkConfig = new WifiNetworkConfig(config); 326 return true; 327 } 328 } 329 330 @VisibleForTesting setWifiDppQrCode(WifiQrCode wifiQrCode)331 boolean setWifiDppQrCode(WifiQrCode wifiQrCode) { 332 if (wifiQrCode == null) { 333 return false; 334 } 335 336 if (!WifiQrCode.SCHEME_DPP.equals(wifiQrCode.getScheme())) { 337 return false; 338 } 339 340 mWifiDppQrCode = new WifiQrCode(wifiQrCode.getQrCode()); 341 return true; 342 } 343 344 @Override onScanWifiDppSuccess(WifiQrCode wifiQrCode)345 public void onScanWifiDppSuccess(WifiQrCode wifiQrCode) { 346 mWifiDppQrCode = wifiQrCode; 347 348 showAddDeviceFragment(/* addToBackStack */ true); 349 } 350 351 @Override onClickChooseDifferentNetwork()352 public void onClickChooseDifferentNetwork() { 353 showChooseSavedWifiNetworkFragment(/* addToBackStack */ true); 354 } 355 356 @Override onSaveInstanceState(Bundle outState)357 public void onSaveInstanceState(Bundle outState) { 358 if (mWifiDppQrCode != null) { 359 outState.putString(KEY_QR_CODE, mWifiDppQrCode.getQrCode()); 360 } 361 362 if (mWifiNetworkConfig != null) { 363 outState.putString(KEY_WIFI_SECURITY, mWifiNetworkConfig.getSecurity()); 364 outState.putString(KEY_WIFI_SSID, mWifiNetworkConfig.getSsid()); 365 outState.putString(KEY_WIFI_PRESHARED_KEY, mWifiNetworkConfig.getPreSharedKey()); 366 outState.putBoolean(KEY_WIFI_HIDDEN_SSID, mWifiNetworkConfig.getHiddenSsid()); 367 outState.putInt(KEY_WIFI_NETWORK_ID, mWifiNetworkConfig.getNetworkId()); 368 outState.putBoolean(KEY_IS_HOTSPOT, mWifiNetworkConfig.isHotspot()); 369 } 370 371 super.onSaveInstanceState(outState); 372 } 373 374 @Override onChooseNetwork(WifiNetworkConfig wifiNetworkConfig)375 public void onChooseNetwork(WifiNetworkConfig wifiNetworkConfig) { 376 mWifiNetworkConfig = new WifiNetworkConfig(wifiNetworkConfig); 377 378 showAddDeviceFragment(/* addToBackStack */ true); 379 } 380 getConnectedWifiNetworkConfigOrNull()381 private WifiNetworkConfig getConnectedWifiNetworkConfigOrNull() { 382 final WifiManager wifiManager = getSystemService(WifiManager.class); 383 if (!wifiManager.isWifiEnabled()) { 384 return null; 385 } 386 387 final WifiInfo connectionInfo = wifiManager.getConnectionInfo(); 388 if (connectionInfo == null) { 389 return null; 390 } 391 392 final int connectionNetworkId = connectionInfo.getNetworkId(); 393 final List<WifiConfiguration> configs = wifiManager.getConfiguredNetworks(); 394 for (WifiConfiguration wifiConfiguration : configs) { 395 if (wifiConfiguration.networkId == connectionNetworkId) { 396 return WifiNetworkConfig.getValidConfigOrNull( 397 WifiDppUtils.getSecurityString(wifiConfiguration), 398 wifiConfiguration.getPrintableSsid(), 399 wifiConfiguration.preSharedKey, 400 wifiConfiguration.hiddenSSID, 401 wifiConfiguration.networkId, 402 /* isHotspot */ false); 403 } 404 } 405 406 return null; 407 } 408 isGuestUser(Context context)409 private static boolean isGuestUser(Context context) { 410 if (context == null) return false; 411 final UserManager userManager = context.getSystemService(UserManager.class); 412 if (userManager == null) return false; 413 return userManager.isGuestUser(); 414 } 415 416 @VisibleForTesting isAddWifiConfigAllowed(Context context)417 static boolean isAddWifiConfigAllowed(Context context) { 418 UserManager userManager = context.getSystemService(UserManager.class); 419 if (userManager != null && userManager.hasUserRestriction(DISALLOW_ADD_WIFI_CONFIG)) { 420 Log.e(TAG, "The user is not allowed to add Wi-Fi configuration."); 421 return false; 422 } 423 return true; 424 } 425 } 426