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