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