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.ActionBar; 20 import android.app.settings.SettingsEnums; 21 import android.content.Intent; 22 import android.net.Uri; 23 import android.net.wifi.WifiConfiguration; 24 import android.net.wifi.WifiInfo; 25 import android.net.wifi.WifiManager; 26 import android.os.Bundle; 27 import android.provider.Settings; 28 import android.util.Log; 29 30 import androidx.annotation.VisibleForTesting; 31 import androidx.fragment.app.Fragment; 32 import androidx.fragment.app.FragmentManager; 33 import androidx.fragment.app.FragmentTransaction; 34 35 import com.android.settings.R; 36 import com.android.settings.core.InstrumentedActivity; 37 38 import java.util.List; 39 40 /** 41 * To provision "other" device with specified Wi-Fi network. 42 * 43 * Uses different intents to specify different provisioning ways. 44 * 45 * For intent action {@code ACTION_CONFIGURATOR_QR_CODE_SCANNER} and 46 * {@code android.settings.WIFI_DPP_CONFIGURATOR_QR_CODE_GENERATOR}, specify the Wi-Fi network to be 47 * provisioned in: 48 * 49 * {@code WifiDppUtils.EXTRA_WIFI_SECURITY} 50 * {@code WifiDppUtils.EXTRA_WIFI_SSID} 51 * {@code WifiDppUtils.EXTRA_WIFI_PRE_SHARED_KEY} 52 * {@code WifiDppUtils.EXTRA_WIFI_HIDDEN_SSID} 53 * 54 * For intent action {@link Settings#ACTION_PROCESS_WIFI_EASY_CONNECT_URI}, specify Wi-Fi 55 * Easy Connect bootstrapping information string in Intent's data URI. 56 */ 57 public class WifiDppConfiguratorActivity extends InstrumentedActivity implements 58 WifiNetworkConfig.Retriever, 59 WifiDppQrCodeGeneratorFragment.OnQrCodeGeneratorFragmentAddButtonClickedListener, 60 WifiDppQrCodeScannerFragment.OnScanWifiDppSuccessListener, 61 WifiDppAddDeviceFragment.OnClickChooseDifferentNetworkListener, 62 WifiNetworkListFragment.OnChooseNetworkListener { 63 64 private static final String TAG = "WifiDppConfiguratorActivity"; 65 66 public static final String ACTION_CONFIGURATOR_QR_CODE_SCANNER = 67 "android.settings.WIFI_DPP_CONFIGURATOR_QR_CODE_SCANNER"; 68 public 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 private FragmentManager mFragmentManager; 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 /** Secret extra that allows fake networks to show in UI for testing purposes */ 89 private boolean mIsTest; 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 setContentView(R.layout.wifi_dpp_activity); 101 mFragmentManager = getSupportFragmentManager(); 102 103 if (savedInstanceState != null) { 104 String qrCode = savedInstanceState.getString(KEY_QR_CODE); 105 106 mWifiDppQrCode = WifiQrCode.getValidWifiDppQrCodeOrNull(qrCode); 107 108 final String security = savedInstanceState.getString(KEY_WIFI_SECURITY); 109 final String ssid = savedInstanceState.getString(KEY_WIFI_SSID); 110 final String preSharedKey = savedInstanceState.getString(KEY_WIFI_PRESHARED_KEY); 111 final boolean hiddenSsid = savedInstanceState.getBoolean(KEY_WIFI_HIDDEN_SSID); 112 final int networkId = savedInstanceState.getInt(KEY_WIFI_NETWORK_ID); 113 final boolean isHotspot = savedInstanceState.getBoolean(KEY_IS_HOTSPOT); 114 115 mWifiNetworkConfig = WifiNetworkConfig.getValidConfigOrNull(security, ssid, 116 preSharedKey, hiddenSsid, networkId, isHotspot); 117 } else { 118 handleIntent(getIntent()); 119 } 120 121 ActionBar actionBar = getActionBar(); 122 if (actionBar != null) { 123 actionBar.setElevation(0); 124 actionBar.setDisplayShowTitleEnabled(false); 125 } 126 } 127 handleIntent(Intent intent)128 private void handleIntent(Intent intent) { 129 boolean cancelActivity = false; 130 WifiNetworkConfig config; 131 switch (intent.getAction()) { 132 case ACTION_CONFIGURATOR_QR_CODE_SCANNER: 133 config = WifiNetworkConfig.getValidConfigOrNull(intent); 134 if (config == null) { 135 cancelActivity = true; 136 } else { 137 mWifiNetworkConfig = config; 138 showQrCodeScannerFragment(/* addToBackStack= */ false); 139 } 140 break; 141 case ACTION_CONFIGURATOR_QR_CODE_GENERATOR: 142 config = WifiNetworkConfig.getValidConfigOrNull(intent); 143 if (config == null) { 144 cancelActivity = true; 145 } else { 146 mWifiNetworkConfig = config; 147 showQrCodeGeneratorFragment(); 148 } 149 break; 150 case Settings.ACTION_PROCESS_WIFI_EASY_CONNECT_URI: 151 final Uri uri = intent.getData(); 152 final String uriString = (uri == null) ? null : uri.toString(); 153 mIsTest = intent.getBooleanExtra(WifiDppUtils.EXTRA_TEST, false); 154 mWifiDppQrCode = WifiQrCode.getValidWifiDppQrCodeOrNull(uriString); 155 final boolean isDppSupported = WifiDppUtils.isWifiDppEnabled(this); 156 if (!isDppSupported) { 157 Log.d(TAG, "Device doesn't support Wifi DPP"); 158 } 159 if (mWifiDppQrCode == null || !isDppSupported) { 160 cancelActivity = true; 161 } else { 162 final WifiNetworkConfig connectedConfig = getConnectedWifiNetworkConfigOrNull(); 163 if (connectedConfig == null || !connectedConfig.isSupportWifiDpp(this)) { 164 showChooseSavedWifiNetworkFragment(/* addToBackStack */ false); 165 } else { 166 mWifiNetworkConfig = connectedConfig; 167 showAddDeviceFragment(/* addToBackStack */ false); 168 } 169 } 170 break; 171 default: 172 cancelActivity = true; 173 Log.e(TAG, "Launch with an invalid action"); 174 } 175 176 if (cancelActivity) { 177 finish(); 178 } 179 } 180 showQrCodeScannerFragment(boolean addToBackStack)181 private void showQrCodeScannerFragment(boolean addToBackStack) { 182 WifiDppQrCodeScannerFragment fragment = 183 (WifiDppQrCodeScannerFragment) mFragmentManager.findFragmentByTag( 184 WifiDppUtils.TAG_FRAGMENT_QR_CODE_SCANNER); 185 186 if (fragment == null) { 187 fragment = new WifiDppQrCodeScannerFragment(); 188 } else { 189 if (fragment.isVisible()) { 190 return; 191 } 192 193 // When the fragment in back stack but not on top of the stack, we can simply pop 194 // stack because current fragment transactions are arranged in an order 195 mFragmentManager.popBackStackImmediate(); 196 return; 197 } 198 final FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); 199 200 fragmentTransaction.replace(R.id.fragment_container, fragment, 201 WifiDppUtils.TAG_FRAGMENT_QR_CODE_SCANNER); 202 if (addToBackStack) { 203 fragmentTransaction.addToBackStack(/* name */ null); 204 } 205 fragmentTransaction.commit(); 206 } 207 showQrCodeGeneratorFragment()208 private void showQrCodeGeneratorFragment() { 209 WifiDppQrCodeGeneratorFragment fragment = 210 (WifiDppQrCodeGeneratorFragment) mFragmentManager.findFragmentByTag( 211 WifiDppUtils.TAG_FRAGMENT_QR_CODE_GENERATOR); 212 213 if (fragment == null) { 214 fragment = new WifiDppQrCodeGeneratorFragment(); 215 } else { 216 if (fragment.isVisible()) { 217 return; 218 } 219 220 // When the fragment in back stack but not on top of the stack, we can simply pop 221 // stack because current fragment transactions are arranged in an order 222 mFragmentManager.popBackStackImmediate(); 223 return; 224 } 225 final FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); 226 227 fragmentTransaction.replace(R.id.fragment_container, fragment, 228 WifiDppUtils.TAG_FRAGMENT_QR_CODE_GENERATOR); 229 fragmentTransaction.commit(); 230 } 231 showChooseSavedWifiNetworkFragment(boolean addToBackStack)232 private void showChooseSavedWifiNetworkFragment(boolean addToBackStack) { 233 WifiDppChooseSavedWifiNetworkFragment fragment = 234 (WifiDppChooseSavedWifiNetworkFragment) mFragmentManager.findFragmentByTag( 235 WifiDppUtils.TAG_FRAGMENT_CHOOSE_SAVED_WIFI_NETWORK); 236 237 if (fragment == null) { 238 fragment = new WifiDppChooseSavedWifiNetworkFragment(); 239 if (mIsTest) { 240 Bundle bundle = new Bundle(); 241 bundle.putBoolean(WifiDppUtils.EXTRA_TEST, true); 242 fragment.setArguments(bundle); 243 } 244 } else { 245 if (fragment.isVisible()) { 246 return; 247 } 248 249 // When the fragment in back stack but not on top of the stack, we can simply pop 250 // stack because current fragment transactions are arranged in an order 251 mFragmentManager.popBackStackImmediate(); 252 return; 253 } 254 final FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); 255 256 fragmentTransaction.replace(R.id.fragment_container, fragment, 257 WifiDppUtils.TAG_FRAGMENT_CHOOSE_SAVED_WIFI_NETWORK); 258 if (addToBackStack) { 259 fragmentTransaction.addToBackStack(/* name */ null); 260 } 261 fragmentTransaction.commit(); 262 } 263 showAddDeviceFragment(boolean addToBackStack)264 private void showAddDeviceFragment(boolean addToBackStack) { 265 WifiDppAddDeviceFragment fragment = 266 (WifiDppAddDeviceFragment) mFragmentManager.findFragmentByTag( 267 WifiDppUtils.TAG_FRAGMENT_ADD_DEVICE); 268 269 if (fragment == null) { 270 fragment = new WifiDppAddDeviceFragment(); 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_ADD_DEVICE); 285 if (addToBackStack) { 286 fragmentTransaction.addToBackStack(/* name */ null); 287 } 288 fragmentTransaction.commit(); 289 } 290 291 @Override getWifiNetworkConfig()292 public WifiNetworkConfig getWifiNetworkConfig() { 293 return mWifiNetworkConfig; 294 } 295 getWifiDppQrCode()296 public WifiQrCode getWifiDppQrCode() { 297 return mWifiDppQrCode; 298 } 299 300 @VisibleForTesting setWifiNetworkConfig(WifiNetworkConfig config)301 boolean setWifiNetworkConfig(WifiNetworkConfig config) { 302 if(!WifiNetworkConfig.isValidConfig(config)) { 303 return false; 304 } else { 305 mWifiNetworkConfig = new WifiNetworkConfig(config); 306 return true; 307 } 308 } 309 310 @VisibleForTesting setWifiDppQrCode(WifiQrCode wifiQrCode)311 boolean setWifiDppQrCode(WifiQrCode wifiQrCode) { 312 if (wifiQrCode == null) { 313 return false; 314 } 315 316 if (!WifiQrCode.SCHEME_DPP.equals(wifiQrCode.getScheme())) { 317 return false; 318 } 319 320 mWifiDppQrCode = new WifiQrCode(wifiQrCode.getQrCode()); 321 return true; 322 } 323 324 @Override onNavigateUp()325 public boolean onNavigateUp() { 326 if (!mFragmentManager.popBackStackImmediate()) { 327 finish(); 328 } 329 return true; 330 } 331 332 @Override onQrCodeGeneratorFragmentAddButtonClicked()333 public void onQrCodeGeneratorFragmentAddButtonClicked() { 334 showQrCodeScannerFragment(/* addToBackStack */ true); 335 } 336 337 @Override onScanWifiDppSuccess(WifiQrCode wifiQrCode)338 public void onScanWifiDppSuccess(WifiQrCode wifiQrCode) { 339 mWifiDppQrCode = wifiQrCode; 340 341 showAddDeviceFragment(/* addToBackStack */ true); 342 } 343 344 @Override onClickChooseDifferentNetwork()345 public void onClickChooseDifferentNetwork() { 346 showChooseSavedWifiNetworkFragment(/* addToBackStack */ true); 347 } 348 349 @Override onSaveInstanceState(Bundle outState)350 public void onSaveInstanceState(Bundle outState) { 351 if (mWifiDppQrCode != null) { 352 outState.putString(KEY_QR_CODE, mWifiDppQrCode.getQrCode()); 353 } 354 355 if (mWifiNetworkConfig != null) { 356 outState.putString(KEY_WIFI_SECURITY, mWifiNetworkConfig.getSecurity()); 357 outState.putString(KEY_WIFI_SSID, mWifiNetworkConfig.getSsid()); 358 outState.putString(KEY_WIFI_PRESHARED_KEY, mWifiNetworkConfig.getPreSharedKey()); 359 outState.putBoolean(KEY_WIFI_HIDDEN_SSID, mWifiNetworkConfig.getHiddenSsid()); 360 outState.putInt(KEY_WIFI_NETWORK_ID, mWifiNetworkConfig.getNetworkId()); 361 outState.putBoolean(KEY_IS_HOTSPOT, mWifiNetworkConfig.isHotspot()); 362 } 363 364 super.onSaveInstanceState(outState); 365 } 366 367 @Override onChooseNetwork(WifiNetworkConfig wifiNetworkConfig)368 public void onChooseNetwork(WifiNetworkConfig wifiNetworkConfig) { 369 mWifiNetworkConfig = new WifiNetworkConfig(wifiNetworkConfig); 370 371 showAddDeviceFragment(/* addToBackStack */ true); 372 } 373 getConnectedWifiNetworkConfigOrNull()374 private WifiNetworkConfig getConnectedWifiNetworkConfigOrNull() { 375 final WifiManager wifiManager = getSystemService(WifiManager.class); 376 if (!wifiManager.isWifiEnabled()) { 377 return null; 378 } 379 380 final WifiInfo connectionInfo = wifiManager.getConnectionInfo(); 381 if (connectionInfo == null) { 382 return null; 383 } 384 385 final int connectionNetworkId = connectionInfo.getNetworkId(); 386 final List<WifiConfiguration> configs = wifiManager.getConfiguredNetworks(); 387 for (WifiConfiguration wifiConfiguration : configs) { 388 if (wifiConfiguration.networkId == connectionNetworkId) { 389 return WifiNetworkConfig.getValidConfigOrNull( 390 WifiDppUtils.getSecurityString(wifiConfiguration), 391 wifiConfiguration.getPrintableSsid(), 392 wifiConfiguration.preSharedKey, 393 /* hiddenSsid */ false, 394 wifiConfiguration.networkId, 395 /* isHotspot */ false); 396 } 397 } 398 399 return null; 400 } 401 } 402