1 /* 2 * Copyright (C) 2015 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; 18 19 import android.content.DialogInterface; 20 import android.content.Intent; 21 import android.net.NetworkInfo; 22 import android.net.wifi.WifiConfiguration; 23 import android.net.wifi.WifiManager; 24 import android.os.Bundle; 25 import android.os.Handler; 26 import android.os.HandlerThread; 27 import android.os.Looper; 28 import android.os.Process; 29 import android.os.SimpleClock; 30 import android.os.SystemClock; 31 import android.text.TextUtils; 32 import android.util.Log; 33 34 import androidx.annotation.VisibleForTesting; 35 36 import com.android.settings.R; 37 import com.android.settings.SetupWizardUtils; 38 import com.android.settings.overlay.FeatureFactory; 39 import com.android.settings.wifi.dpp.WifiDppUtils; 40 import com.android.settingslib.core.lifecycle.ObservableActivity; 41 import com.android.settingslib.wifi.AccessPoint; 42 import com.android.wifitrackerlib.NetworkDetailsTracker; 43 import com.android.wifitrackerlib.WifiEntry; 44 45 import com.google.android.setupcompat.util.WizardManagerHelper; 46 import com.google.android.setupdesign.util.ThemeHelper; 47 48 import java.time.Clock; 49 import java.time.ZoneOffset; 50 51 /** 52 * The activity shows a Wi-fi editor dialog. 53 * 54 * TODO(b/152571756): This activity supports both WifiTrackerLib and SettingsLib because this is an 55 * exported UI component, some other APPs (e.g., SetupWizard) still use 56 * SettingsLib. Remove the SettingsLib compatible part after these APPs use 57 * WifiTrackerLib. 58 */ 59 public class WifiDialogActivity extends ObservableActivity implements WifiDialog.WifiDialogListener, 60 WifiDialog2.WifiDialog2Listener, DialogInterface.OnDismissListener { 61 62 private static final String TAG = "WifiDialogActivity"; 63 64 // For the callers which support WifiTrackerLib. 65 public static final String KEY_CHOSEN_WIFIENTRY_KEY = "key_chosen_wifientry_key"; 66 67 // For the callers which support SettingsLib. 68 public static final String KEY_ACCESS_POINT_STATE = "access_point_state"; 69 70 /** 71 * Boolean extra indicating whether this activity should connect to an access point on the 72 * caller's behalf. If this is set to false, the caller should check 73 * {@link #KEY_WIFI_CONFIGURATION} in the result data and save that using 74 * {@link WifiManager#connect(WifiConfiguration, ActionListener)}. Default is true. 75 */ 76 @VisibleForTesting 77 static final String KEY_CONNECT_FOR_CALLER = "connect_for_caller"; 78 79 public static final String KEY_WIFI_CONFIGURATION = "wifi_configuration"; 80 81 private static final int RESULT_CONNECTED = RESULT_FIRST_USER; 82 private static final int RESULT_FORGET = RESULT_FIRST_USER + 1; 83 84 private static final int REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER = 0; 85 86 // Max age of tracked WifiEntries. 87 private static final long MAX_SCAN_AGE_MILLIS = 15_000; 88 // Interval between initiating NetworkDetailsTracker scans. 89 private static final long SCAN_INTERVAL_MILLIS = 10_000; 90 91 private WifiDialog mDialog; 92 private AccessPoint mAccessPoint; 93 94 private WifiDialog2 mDialog2; 95 96 // The received intent supports a key of WifiTrackerLib or SettingsLib. 97 private boolean mIsWifiTrackerLib; 98 99 private Intent mIntent; 100 private NetworkDetailsTracker mNetworkDetailsTracker; 101 private HandlerThread mWorkerThread; 102 103 @Override onCreate(Bundle savedInstanceState)104 protected void onCreate(Bundle savedInstanceState) { 105 mIntent = getIntent(); 106 if (WizardManagerHelper.isSetupWizardIntent(mIntent)) { 107 setTheme(SetupWizardUtils.getTransparentTheme(this, mIntent)); 108 } 109 110 super.onCreate(savedInstanceState); 111 112 mIsWifiTrackerLib = !TextUtils.isEmpty(mIntent.getStringExtra(KEY_CHOSEN_WIFIENTRY_KEY)); 113 114 if (mIsWifiTrackerLib) { 115 mWorkerThread = new HandlerThread( 116 TAG + "{" + Integer.toHexString(System.identityHashCode(this)) + "}", 117 Process.THREAD_PRIORITY_BACKGROUND); 118 mWorkerThread.start(); 119 final Clock elapsedRealtimeClock = new SimpleClock(ZoneOffset.UTC) { 120 @Override 121 public long millis() { 122 return SystemClock.elapsedRealtime(); 123 } 124 }; 125 mNetworkDetailsTracker = FeatureFactory.getFactory(this) 126 .getWifiTrackerLibProvider() 127 .createNetworkDetailsTracker( 128 getLifecycle(), 129 this, 130 new Handler(Looper.getMainLooper()), 131 mWorkerThread.getThreadHandler(), 132 elapsedRealtimeClock, 133 MAX_SCAN_AGE_MILLIS, 134 SCAN_INTERVAL_MILLIS, 135 mIntent.getStringExtra(KEY_CHOSEN_WIFIENTRY_KEY)); 136 } else { 137 final Bundle accessPointState = mIntent.getBundleExtra(KEY_ACCESS_POINT_STATE); 138 if (accessPointState != null) { 139 mAccessPoint = new AccessPoint(this, accessPointState); 140 } 141 } 142 } 143 144 @Override onStart()145 protected void onStart() { 146 super.onStart(); 147 if (mDialog2 != null || mDialog != null) { 148 return; 149 } 150 151 if (WizardManagerHelper.isAnySetupWizard(getIntent())) { 152 final int targetStyle = ThemeHelper.isSetupWizardDayNightEnabled(this) 153 ? R.style.SuwAlertDialogThemeCompat_DayNight : 154 R.style.SuwAlertDialogThemeCompat_Light; 155 if (mIsWifiTrackerLib) { 156 mDialog2 = WifiDialog2.createModal(this, this, 157 mNetworkDetailsTracker.getWifiEntry(), 158 WifiConfigUiBase2.MODE_CONNECT, targetStyle); 159 } else { 160 mDialog = WifiDialog.createModal(this, this, mAccessPoint, 161 WifiConfigUiBase.MODE_CONNECT, targetStyle); 162 } 163 } else { 164 if (mIsWifiTrackerLib) { 165 mDialog2 = WifiDialog2.createModal(this, this, 166 mNetworkDetailsTracker.getWifiEntry(), WifiConfigUiBase2.MODE_CONNECT); 167 } else { 168 mDialog = WifiDialog.createModal( 169 this, this, mAccessPoint, WifiConfigUiBase.MODE_CONNECT); 170 } 171 } 172 173 if (mIsWifiTrackerLib) { 174 mDialog2.show(); 175 mDialog2.setOnDismissListener(this); 176 } else { 177 mDialog.show(); 178 mDialog.setOnDismissListener(this); 179 } 180 } 181 182 @Override finish()183 public void finish() { 184 overridePendingTransition(0, 0); 185 186 super.finish(); 187 } 188 189 @Override onDestroy()190 public void onDestroy() { 191 if (mIsWifiTrackerLib) { 192 if (mDialog2 != null && mDialog2.isShowing()) { 193 mDialog2 = null; 194 } 195 mWorkerThread.quit(); 196 } else { 197 if (mDialog != null && mDialog.isShowing()) { 198 mDialog = null; 199 } 200 } 201 202 super.onDestroy(); 203 } 204 205 @Override onForget(WifiDialog2 dialog)206 public void onForget(WifiDialog2 dialog) { 207 final WifiEntry wifiEntry = dialog.getController().getWifiEntry(); 208 if (wifiEntry != null && wifiEntry.canForget()) { 209 wifiEntry.forget(null /* callback */); 210 } 211 212 setResult(RESULT_FORGET); 213 finish(); 214 } 215 216 @Override onForget(WifiDialog dialog)217 public void onForget(WifiDialog dialog) { 218 final WifiManager wifiManager = getSystemService(WifiManager.class); 219 final AccessPoint accessPoint = dialog.getController().getAccessPoint(); 220 if (accessPoint != null) { 221 if (!accessPoint.isSaved()) { 222 if (accessPoint.getNetworkInfo() != null && 223 accessPoint.getNetworkInfo().getState() != NetworkInfo.State.DISCONNECTED) { 224 // Network is active but has no network ID - must be ephemeral. 225 wifiManager.disableEphemeralNetwork( 226 AccessPoint.convertToQuotedString(accessPoint.getSsidStr())); 227 } else { 228 // Should not happen, but a monkey seems to trigger it 229 Log.e(TAG, "Failed to forget invalid network " + accessPoint.getConfig()); 230 } 231 } else { 232 wifiManager.forget(accessPoint.getConfig().networkId, null /* listener */); 233 } 234 } 235 236 Intent resultData = new Intent(); 237 if (accessPoint != null) { 238 Bundle accessPointState = new Bundle(); 239 accessPoint.saveWifiState(accessPointState); 240 resultData.putExtra(KEY_ACCESS_POINT_STATE, accessPointState); 241 } 242 setResult(RESULT_FORGET); 243 finish(); 244 } 245 246 @Override onSubmit(WifiDialog2 dialog)247 public void onSubmit(WifiDialog2 dialog) { 248 final WifiEntry wifiEntry = dialog.getController().getWifiEntry(); 249 final WifiConfiguration config = dialog.getController().getConfig(); 250 251 if (getIntent().getBooleanExtra(KEY_CONNECT_FOR_CALLER, true)) { 252 if (config == null && wifiEntry != null && wifiEntry.canConnect()) { 253 wifiEntry.connect(null /* callback */); 254 } else { 255 getSystemService(WifiManager.class).connect(config, null /* listener */); 256 } 257 } 258 259 final Intent resultData = new Intent(); 260 if (config != null) { 261 resultData.putExtra(KEY_WIFI_CONFIGURATION, config); 262 } 263 setResult(RESULT_CONNECTED, resultData); 264 finish(); 265 } 266 267 @Override onSubmit(WifiDialog dialog)268 public void onSubmit(WifiDialog dialog) { 269 final WifiConfiguration config = dialog.getController().getConfig(); 270 final AccessPoint accessPoint = dialog.getController().getAccessPoint(); 271 final WifiManager wifiManager = getSystemService(WifiManager.class); 272 273 if (getIntent().getBooleanExtra(KEY_CONNECT_FOR_CALLER, true)) { 274 if (config == null) { 275 if (accessPoint != null && accessPoint.isSaved()) { 276 wifiManager.connect(accessPoint.getConfig(), null /* listener */); 277 } 278 } else { 279 wifiManager.save(config, null /* listener */); 280 if (accessPoint != null) { 281 // accessPoint is null for "Add network" 282 NetworkInfo networkInfo = accessPoint.getNetworkInfo(); 283 if (networkInfo == null || !networkInfo.isConnected()) { 284 wifiManager.connect(config, null /* listener */); 285 } 286 } 287 } 288 } 289 290 Intent resultData = new Intent(); 291 if (accessPoint != null) { 292 Bundle accessPointState = new Bundle(); 293 accessPoint.saveWifiState(accessPointState); 294 resultData.putExtra(KEY_ACCESS_POINT_STATE, accessPointState); 295 } 296 if (config != null) { 297 resultData.putExtra(KEY_WIFI_CONFIGURATION, config); 298 } 299 setResult(RESULT_CONNECTED, resultData); 300 finish(); 301 } 302 303 @Override onDismiss(DialogInterface dialogInterface)304 public void onDismiss(DialogInterface dialogInterface) { 305 mDialog2 = null; 306 mDialog = null; 307 finish(); 308 } 309 310 @Override onScan(WifiDialog2 dialog, String ssid)311 public void onScan(WifiDialog2 dialog, String ssid) { 312 Intent intent = WifiDppUtils.getEnrolleeQrCodeScannerIntent(dialog.getContext(), ssid); 313 WizardManagerHelper.copyWizardManagerExtras(mIntent, intent); 314 315 // Launch QR code scanner to join a network. 316 startActivityForResult(intent, REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER); 317 } 318 319 @Override onScan(WifiDialog dialog, String ssid)320 public void onScan(WifiDialog dialog, String ssid) { 321 Intent intent = WifiDppUtils.getEnrolleeQrCodeScannerIntent(dialog.getContext(), ssid); 322 WizardManagerHelper.copyWizardManagerExtras(mIntent, intent); 323 324 // Launch QR code scanner to join a network. 325 startActivityForResult(intent, REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER); 326 } 327 328 @Override onActivityResult(int requestCode, int resultCode, Intent data)329 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 330 super.onActivityResult(requestCode, resultCode, data); 331 332 if (requestCode == REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER) { 333 if (resultCode != RESULT_OK) { 334 return; 335 } 336 337 setResult(RESULT_CONNECTED, data); 338 finish(); 339 } 340 } 341 } 342