1 /* 2 * Copyright (C) 2019 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.wifitrackerlib; 18 19 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; 20 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 21 import static android.os.Build.VERSION_CODES; 22 23 import android.annotation.TargetApi; 24 import android.app.ActivityManager; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.net.ConnectivityDiagnosticsManager; 30 import android.net.ConnectivityManager; 31 import android.net.LinkProperties; 32 import android.net.Network; 33 import android.net.NetworkCapabilities; 34 import android.net.NetworkRequest; 35 import android.net.wifi.ScanResult; 36 import android.net.wifi.WifiManager; 37 import android.net.wifi.WifiScanner; 38 import android.net.wifi.sharedconnectivity.app.HotspotNetwork; 39 import android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus; 40 import android.net.wifi.sharedconnectivity.app.KnownNetwork; 41 import android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus; 42 import android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback; 43 import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager; 44 import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState; 45 import android.os.Build; 46 import android.os.Handler; 47 import android.os.Looper; 48 import android.telephony.SubscriptionManager; 49 import android.telephony.TelephonyManager; 50 import android.util.Log; 51 52 import androidx.annotation.AnyThread; 53 import androidx.annotation.MainThread; 54 import androidx.annotation.NonNull; 55 import androidx.annotation.Nullable; 56 import androidx.annotation.WorkerThread; 57 import androidx.core.os.BuildCompat; 58 import androidx.lifecycle.Lifecycle; 59 import androidx.lifecycle.LifecycleObserver; 60 import androidx.lifecycle.OnLifecycleEvent; 61 62 import java.time.Clock; 63 import java.util.ArrayList; 64 import java.util.List; 65 import java.util.concurrent.Executor; 66 67 /** 68 * Base class for WifiTracker functionality. 69 * 70 * This class provides the basic functions of issuing scans, receiving Wi-Fi related broadcasts, and 71 * keeping track of the Wi-Fi state. 72 * 73 * Subclasses are expected to provide their own API to clients and override the empty broadcast 74 * handling methods here to populate the data returned by their API. 75 * 76 * This class runs on two threads: 77 * 78 * The main thread 79 * - Processes lifecycle events (onStart, onStop) 80 * - Runs listener callbacks 81 * 82 * The worker thread 83 * - Drives the periodic scan requests 84 * - Handles the system broadcasts to update the API return values 85 * - Notifies the listener for updates to the API return values 86 * 87 * To keep synchronization simple, this means that the vast majority of work is done within the 88 * worker thread. Synchronized blocks are only to be used for data returned by the API updated by 89 * the worker thread and consumed by the main thread. 90 */ 91 92 public class BaseWifiTracker { 93 private final String mTag; 94 95 private static boolean sVerboseLogging; 96 isVerboseLoggingEnabled()97 public static boolean isVerboseLoggingEnabled() { 98 return BaseWifiTracker.sVerboseLogging; 99 } 100 101 private int mWifiState = WifiManager.WIFI_STATE_DISABLED; 102 103 private boolean mIsInitialized = false; 104 private boolean mIsScanningDisabled = false; 105 106 // Registered on the worker thread 107 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 108 @Override 109 @WorkerThread 110 public void onReceive(Context context, Intent intent) { 111 String action = intent.getAction(); 112 113 if (isVerboseLoggingEnabled()) { 114 Log.v(mTag, "Received broadcast: " + action); 115 } 116 117 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { 118 mWifiState = intent.getIntExtra( 119 WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED); 120 mScanner.onWifiStateChanged(mWifiState == WifiManager.WIFI_STATE_ENABLED); 121 notifyOnWifiStateChanged(); 122 handleWifiStateChangedAction(); 123 } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) { 124 handleScanResultsAvailableAction(intent); 125 } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action)) { 126 handleConfiguredNetworksChangedAction(intent); 127 } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { 128 handleNetworkStateChangedAction(intent); 129 } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) { 130 handleRssiChangedAction(intent); 131 } else if (TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED.equals(action)) { 132 handleDefaultSubscriptionChanged(intent.getIntExtra( 133 "subscription", SubscriptionManager.INVALID_SUBSCRIPTION_ID)); 134 } 135 } 136 }; 137 private final BaseWifiTracker.Scanner mScanner; 138 private final BaseWifiTrackerCallback mListener; 139 private final @NonNull LifecycleObserver mLifecycleObserver; 140 141 protected final WifiTrackerInjector mInjector; 142 protected final Context mContext; 143 protected final @NonNull ActivityManager mActivityManager; 144 protected final WifiManager mWifiManager; 145 protected final ConnectivityManager mConnectivityManager; 146 protected final ConnectivityDiagnosticsManager mConnectivityDiagnosticsManager; 147 protected final Handler mMainHandler; 148 protected final Handler mWorkerHandler; 149 protected final long mMaxScanAgeMillis; 150 protected final long mScanIntervalMillis; 151 protected final ScanResultUpdater mScanResultUpdater; 152 153 @Nullable protected SharedConnectivityManager mSharedConnectivityManager = null; 154 155 // Network request for listening on changes to Wifi link properties and network capabilities 156 // such as captive portal availability. 157 private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder() 158 .clearCapabilities() 159 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) 160 .addTransportType(TRANSPORT_WIFI) 161 .addTransportType(TRANSPORT_CELLULAR) // For VCN-over-Wifi 162 .build(); 163 164 private final ConnectivityManager.NetworkCallback mNetworkCallback = 165 new ConnectivityManager.NetworkCallback( 166 ConnectivityManager.NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) { 167 @Override 168 @WorkerThread 169 public void onLinkPropertiesChanged(@NonNull Network network, 170 @NonNull LinkProperties lp) { 171 handleLinkPropertiesChanged(network, lp); 172 } 173 174 @Override 175 @WorkerThread 176 public void onCapabilitiesChanged(@NonNull Network network, 177 @NonNull NetworkCapabilities networkCapabilities) { 178 handleNetworkCapabilitiesChanged(network, networkCapabilities); 179 } 180 181 @Override 182 @WorkerThread 183 public void onLost(@NonNull Network network) { 184 handleNetworkLost(network); 185 } 186 }; 187 188 private final ConnectivityManager.NetworkCallback mDefaultNetworkCallback = 189 new ConnectivityManager.NetworkCallback( 190 ConnectivityManager.NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) { 191 @Override 192 @WorkerThread 193 public void onCapabilitiesChanged(@NonNull Network network, 194 @NonNull NetworkCapabilities networkCapabilities) { 195 List<Network> underlyingNetworks = 196 networkCapabilities.getUnderlyingNetworks(); 197 if (underlyingNetworks != null) { 198 Network currentWifiNetwork = mWifiManager.getCurrentNetwork(); 199 if (underlyingNetworks.contains(currentWifiNetwork)) { 200 // If the default network has an underlying Wi-Fi network (e.g. it's 201 // a VPN), treat the Wi-Fi network as the default network. 202 handleDefaultNetworkCapabilitiesChanged(currentWifiNetwork, 203 new NetworkCapabilities.Builder(networkCapabilities) 204 .setTransportInfo(mWifiManager.getConnectionInfo()) 205 .build()); 206 return; 207 } 208 } 209 handleDefaultNetworkCapabilitiesChanged(network, networkCapabilities); 210 } 211 212 @WorkerThread 213 public void onLost(@NonNull Network network) { 214 handleDefaultNetworkLost(); 215 } 216 }; 217 218 private final ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback 219 mConnectivityDiagnosticsCallback = 220 new ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback() { 221 @Override 222 public void onConnectivityReportAvailable( 223 @NonNull ConnectivityDiagnosticsManager.ConnectivityReport report) { 224 handleConnectivityReportAvailable(report); 225 } 226 }; 227 228 private final Executor mConnectivityDiagnosticsExecutor = new Executor() { 229 @Override 230 public void execute(Runnable command) { 231 mWorkerHandler.post(command); 232 } 233 }; 234 235 @TargetApi(VERSION_CODES.UPSIDE_DOWN_CAKE) 236 private final Executor mSharedConnectivityExecutor = new Executor() { 237 @Override 238 public void execute(Runnable command) { 239 mWorkerHandler.post(command); 240 } 241 }; 242 243 @TargetApi(VERSION_CODES.UPSIDE_DOWN_CAKE) 244 @Nullable 245 private SharedConnectivityClientCallback mSharedConnectivityCallback = null; 246 247 @TargetApi(VERSION_CODES.UPSIDE_DOWN_CAKE) 248 @NonNull createSharedConnectivityCallback()249 private SharedConnectivityClientCallback createSharedConnectivityCallback() { 250 return new SharedConnectivityClientCallback() { 251 @Override 252 public void onHotspotNetworksUpdated(@NonNull List<HotspotNetwork> networks) { 253 handleHotspotNetworksUpdated(networks); 254 } 255 256 @Override 257 public void onKnownNetworksUpdated(@NonNull List<KnownNetwork> networks) { 258 handleKnownNetworksUpdated(networks); 259 } 260 261 @Override 262 public void onSharedConnectivitySettingsChanged( 263 @NonNull SharedConnectivitySettingsState state) { 264 handleSharedConnectivitySettingsChanged(state); 265 } 266 267 @Override 268 public void onHotspotNetworkConnectionStatusChanged( 269 @NonNull HotspotNetworkConnectionStatus status) { 270 handleHotspotNetworkConnectionStatusChanged(status); 271 } 272 273 @Override 274 public void onKnownNetworkConnectionStatusChanged( 275 @NonNull KnownNetworkConnectionStatus status) { 276 handleKnownNetworkConnectionStatusChanged(status); 277 } 278 279 @Override 280 public void onServiceConnected() { 281 handleServiceConnected(); 282 } 283 284 @Override 285 public void onServiceDisconnected() { 286 handleServiceDisconnected(); 287 } 288 289 @Override 290 public void onRegisterCallbackFailed(Exception exception) { 291 handleRegisterCallbackFailed(exception); 292 } 293 }; 294 } 295 296 /** 297 * Constructor for BaseWifiTracker. 298 * @param injector Injector for commonly referenced objects. 299 * @param lifecycle Lifecycle to register the internal LifecycleObserver with. Note that we 300 * register the LifecycleObserver inside the constructor, which may cause an 301 * NPE if the Lifecycle invokes onStart/onStop/onDestroyed within 302 * {@link Lifecycle#addObserver}. To avoid this, pass {@code null} here and 303 * register the LifecycleObserver from {@link #getLifecycleObserver()} 304 * instead. 305 * @param context Context for registering broadcast receiver and for resource strings. 306 * @param wifiManager Provides all Wi-Fi info. 307 * @param connectivityManager Provides network info. 308 * @param mainHandler Handler for processing listener callbacks. 309 * @param workerHandler Handler for processing all broadcasts and running the Scanner. 310 * @param clock Clock used for evaluating the age of scans 311 * @param maxScanAgeMillis Max age for tracked WifiEntries. 312 * @param scanIntervalMillis Interval between initiating scans. 313 */ 314 @SuppressWarnings("StaticAssignmentInConstructor") 315 BaseWifiTracker( 316 @NonNull WifiTrackerInjector injector, 317 @Nullable Lifecycle lifecycle, @NonNull Context context, 318 @NonNull WifiManager wifiManager, 319 @NonNull ConnectivityManager connectivityManager, 320 @NonNull Handler mainHandler, 321 @NonNull Handler workerHandler, 322 @NonNull Clock clock, 323 long maxScanAgeMillis, 324 long scanIntervalMillis, 325 BaseWifiTrackerCallback listener, 326 String tag) { 327 mInjector = injector; 328 mActivityManager = context.getSystemService(ActivityManager.class); 329 mLifecycleObserver = new LifecycleObserver() { 330 @OnLifecycleEvent(Lifecycle.Event.ON_START) 331 @MainThread 332 public void onStart() { 333 BaseWifiTracker.this.onStart(); 334 } 335 336 @OnLifecycleEvent(Lifecycle.Event.ON_STOP) 337 @MainThread 338 public void onStop() { 339 BaseWifiTracker.this.onStop(); 340 } 341 342 @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) 343 @MainThread 344 public void onDestroy() { 345 BaseWifiTracker.this.onDestroy(); 346 } 347 }; 348 if (lifecycle != null) { 349 lifecycle.addObserver(mLifecycleObserver); 350 } 351 mContext = context; 352 mWifiManager = wifiManager; 353 mConnectivityManager = connectivityManager; 354 mConnectivityDiagnosticsManager = 355 context.getSystemService(ConnectivityDiagnosticsManager.class); 356 if (mInjector.isSharedConnectivityFeatureEnabled() && BuildCompat.isAtLeastU()) { 357 mSharedConnectivityManager = context.getSystemService(SharedConnectivityManager.class); 358 mSharedConnectivityCallback = createSharedConnectivityCallback(); 359 } 360 mMainHandler = mainHandler; 361 mWorkerHandler = workerHandler; 362 mMaxScanAgeMillis = maxScanAgeMillis; 363 mScanIntervalMillis = scanIntervalMillis; 364 mListener = listener; 365 mTag = tag; 366 367 mScanResultUpdater = new ScanResultUpdater(clock, 368 maxScanAgeMillis + scanIntervalMillis); 369 mScanner = new BaseWifiTracker.Scanner(workerHandler.getLooper()); 370 if (mContext.getResources().getBoolean( 371 R.bool.wifitrackerlib_enable_verbose_logging_for_userdebug) 372 && Build.TYPE.equals("userdebug")) { 373 sVerboseLogging = true; 374 } else { 375 sVerboseLogging = mWifiManager.isVerboseLoggingEnabled(); 376 } 377 } 378 379 /** 380 * Disable the scanning mechanism permanently. 381 */ 382 public void disableScanning() { 383 mIsScanningDisabled = true; 384 // This method indicates SystemUI usage, which shouldn't output verbose logs since it's 385 // always up. 386 sVerboseLogging = false; 387 } 388 389 /** 390 * Returns the LifecycleObserver to listen on the app's lifecycle state. 391 */ 392 @AnyThread 393 public LifecycleObserver getLifecycleObserver() { 394 return mLifecycleObserver; 395 } 396 397 /** 398 * Registers the broadcast receiver and network callbacks and starts the scanning mechanism. 399 */ 400 @MainThread 401 public void onStart() { 402 if (isVerboseLoggingEnabled()) { 403 Log.v(mTag, "onStart"); 404 } 405 mScanner.onStart(); 406 mWorkerHandler.post(() -> { 407 IntentFilter filter = new IntentFilter(); 408 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 409 filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 410 filter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 411 filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 412 if (isVerboseLoggingEnabled()) { 413 filter.addAction(WifiManager.RSSI_CHANGED_ACTION); 414 } 415 filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); 416 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 417 mContext.registerReceiver(mBroadcastReceiver, filter, 418 /* broadcastPermission */ null, mWorkerHandler); 419 mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback, 420 mWorkerHandler); 421 mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, 422 mWorkerHandler); 423 mConnectivityDiagnosticsManager.registerConnectivityDiagnosticsCallback(mNetworkRequest, 424 mConnectivityDiagnosticsExecutor, mConnectivityDiagnosticsCallback); 425 if (mSharedConnectivityManager != null && mSharedConnectivityCallback != null 426 && BuildCompat.isAtLeastU()) { 427 mSharedConnectivityManager.registerCallback(mSharedConnectivityExecutor, 428 mSharedConnectivityCallback); 429 } 430 handleOnStart(); 431 mIsInitialized = true; 432 }); 433 } 434 435 /** 436 * Unregisters the broadcast receiver, network callbacks, and pauses the scanning mechanism. 437 */ 438 @MainThread 439 public void onStop() { 440 if (isVerboseLoggingEnabled()) { 441 Log.v(mTag, "onStop"); 442 } 443 mScanner.onStop(); 444 mWorkerHandler.post(() -> { 445 try { 446 mContext.unregisterReceiver(mBroadcastReceiver); 447 mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); 448 mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback); 449 mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback( 450 mConnectivityDiagnosticsCallback); 451 if (mSharedConnectivityManager != null && mSharedConnectivityCallback != null 452 && BuildCompat.isAtLeastU()) { 453 boolean result = 454 mSharedConnectivityManager.unregisterCallback( 455 mSharedConnectivityCallback); 456 if (!result) { 457 Log.e(mTag, "onStop: unregisterCallback failed"); 458 } 459 } 460 } catch (IllegalArgumentException e) { 461 // Already unregistered in onDestroyed(). 462 } 463 }); 464 } 465 466 /** 467 * Unregisters the broadcast receiver network callbacks in case the Activity is destroyed before 468 * the worker thread runnable posted in onStop() runs. 469 */ 470 @MainThread 471 public void onDestroy() { 472 try { 473 mContext.unregisterReceiver(mBroadcastReceiver); 474 mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); 475 mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback); 476 mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback( 477 mConnectivityDiagnosticsCallback); 478 if (mSharedConnectivityManager != null && mSharedConnectivityCallback != null 479 && BuildCompat.isAtLeastU()) { 480 boolean result = 481 mSharedConnectivityManager.unregisterCallback( 482 mSharedConnectivityCallback); 483 if (!result) { 484 Log.e(mTag, "onDestroyed: unregisterCallback failed"); 485 } 486 } 487 } catch (IllegalArgumentException e) { 488 // Already unregistered in onStop() worker thread runnable. 489 } 490 } 491 492 /** 493 * Returns true if this WifiTracker has already been initialized in the worker thread via 494 * handleOnStart() 495 */ 496 @AnyThread 497 boolean isInitialized() { 498 return mIsInitialized; 499 } 500 501 /** 502 * Returns the state of Wi-Fi as one of the following values. 503 * 504 * <li>{@link WifiManager#WIFI_STATE_DISABLED}</li> 505 * <li>{@link WifiManager#WIFI_STATE_ENABLED}</li> 506 * <li>{@link WifiManager#WIFI_STATE_DISABLING}</li> 507 * <li>{@link WifiManager#WIFI_STATE_ENABLING}</li> 508 * <li>{@link WifiManager#WIFI_STATE_UNKNOWN}</li> 509 */ 510 @AnyThread 511 public int getWifiState() { 512 return mWifiState; 513 } 514 515 /** 516 * Method to run on the worker thread when onStart is invoked. 517 * Data that can be updated immediately after onStart should be populated here. 518 */ 519 @WorkerThread 520 protected void handleOnStart() { 521 // Do nothing. 522 } 523 524 /** 525 * Handle receiving the WifiManager.WIFI_STATE_CHANGED_ACTION broadcast 526 */ 527 @WorkerThread 528 protected void handleWifiStateChangedAction() { 529 // Do nothing. 530 } 531 532 /** 533 * Handle receiving the WifiManager.SCAN_RESULTS_AVAILABLE_ACTION broadcast 534 */ 535 @WorkerThread 536 protected void handleScanResultsAvailableAction(@NonNull Intent intent) { 537 // Do nothing. 538 } 539 540 /** 541 * Handle receiving the WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION broadcast 542 */ 543 @WorkerThread 544 protected void handleConfiguredNetworksChangedAction(@NonNull Intent intent) { 545 // Do nothing. 546 } 547 548 /** 549 * Handle receiving the WifiManager.NETWORK_STATE_CHANGED_ACTION broadcast 550 */ 551 @WorkerThread 552 protected void handleNetworkStateChangedAction(@NonNull Intent intent) { 553 // Do nothing. 554 } 555 556 /** 557 * Handle receiving the WifiManager.NETWORK_STATE_CHANGED_ACTION broadcast 558 */ 559 @WorkerThread 560 protected void handleRssiChangedAction(@NonNull Intent intent) { 561 // Do nothing. 562 } 563 564 /** 565 * Handle link property changes for the given network. 566 */ 567 @WorkerThread 568 protected void handleLinkPropertiesChanged( 569 @NonNull Network network, @Nullable LinkProperties linkProperties) { 570 // Do nothing. 571 } 572 573 /** 574 * Handle network capability changes for the current connected Wifi network. 575 */ 576 @WorkerThread 577 protected void handleNetworkCapabilitiesChanged( 578 @NonNull Network network, @NonNull NetworkCapabilities capabilities) { 579 // Do nothing. 580 } 581 582 /** 583 * Handle the loss of a network. 584 */ 585 @WorkerThread 586 protected void handleNetworkLost(@NonNull Network network) { 587 // Do nothing. 588 } 589 590 /** 591 * Handle receiving a connectivity report. 592 */ 593 @WorkerThread 594 protected void handleConnectivityReportAvailable( 595 @NonNull ConnectivityDiagnosticsManager.ConnectivityReport connectivityReport) { 596 // Do nothing. 597 } 598 599 /** 600 * Handle default network capabilities changed. 601 */ 602 @WorkerThread 603 protected void handleDefaultNetworkCapabilitiesChanged(@NonNull Network network, 604 @NonNull NetworkCapabilities networkCapabilities) { 605 // Do nothing. 606 } 607 608 /** 609 * Handle default network loss. 610 */ 611 @WorkerThread 612 protected void handleDefaultNetworkLost() { 613 // Do nothing. 614 } 615 616 /** 617 * Handle updates to the default data subscription id from SubscriptionManager. 618 */ 619 @WorkerThread 620 protected void handleDefaultSubscriptionChanged(int defaultSubId) { 621 // Do nothing. 622 } 623 624 /** 625 * Handle updates to the list of tether networks from SharedConnectivityManager. 626 */ 627 @TargetApi(VERSION_CODES.UPSIDE_DOWN_CAKE) 628 @WorkerThread 629 protected void handleHotspotNetworksUpdated(List<HotspotNetwork> networks) { 630 // Do nothing. 631 } 632 633 /** 634 * Handle updates to the list of known networks from SharedConnectivityManager. 635 */ 636 @TargetApi(VERSION_CODES.UPSIDE_DOWN_CAKE) 637 @WorkerThread 638 protected void handleKnownNetworksUpdated(List<KnownNetwork> networks) { 639 // Do nothing. 640 } 641 642 /** 643 * Handle changes to the shared connectivity settings from SharedConnectivityManager. 644 */ 645 @TargetApi(VERSION_CODES.UPSIDE_DOWN_CAKE) 646 @WorkerThread 647 protected void handleSharedConnectivitySettingsChanged( 648 @NonNull SharedConnectivitySettingsState state) { 649 // Do nothing. 650 } 651 652 /** 653 * Handle changes to the shared connectivity settings from SharedConnectivityManager. 654 */ 655 @TargetApi(VERSION_CODES.UPSIDE_DOWN_CAKE) 656 @WorkerThread 657 protected void handleHotspotNetworkConnectionStatusChanged( 658 @NonNull HotspotNetworkConnectionStatus status) { 659 // Do nothing. 660 } 661 662 /** 663 * Handle changes to the shared connectivity settings from SharedConnectivityManager. 664 */ 665 @TargetApi(VERSION_CODES.UPSIDE_DOWN_CAKE) 666 @WorkerThread 667 protected void handleKnownNetworkConnectionStatusChanged( 668 @NonNull KnownNetworkConnectionStatus status) { 669 // Do nothing. 670 } 671 672 /** 673 * Handle service connected callback from SharedConnectivityManager. 674 */ 675 @TargetApi(VERSION_CODES.UPSIDE_DOWN_CAKE) 676 @WorkerThread 677 protected void handleServiceConnected() { 678 // Do nothing. 679 } 680 681 /** 682 * Handle service disconnected callback from SharedConnectivityManager. 683 */ 684 @TargetApi(VERSION_CODES.UPSIDE_DOWN_CAKE) 685 @WorkerThread 686 protected void handleServiceDisconnected() { 687 // Do nothing. 688 } 689 690 /** 691 * Handle register callback failed callback from SharedConnectivityManager. 692 */ 693 @TargetApi(VERSION_CODES.UPSIDE_DOWN_CAKE) 694 @WorkerThread 695 protected void handleRegisterCallbackFailed(Exception exception) { 696 // Do nothing. 697 } 698 699 /** 700 * Helper class to handle starting scans every SCAN_INTERVAL_MILLIS. 701 * 702 * Scanning is only done when the activity is in the Started state and Wi-Fi is enabled. 703 */ 704 private class Scanner extends Handler { 705 private boolean mIsStartedState = false; 706 private boolean mIsWifiEnabled = false; 707 private final WifiScanner.ScanListener mFirstScanListener = new WifiScanner.ScanListener() { 708 @Override 709 @MainThread 710 public void onPeriodChanged(int periodInMs) { 711 // No-op. 712 } 713 714 @Override 715 @MainThread 716 public void onResults(WifiScanner.ScanData[] results) { 717 mWorkerHandler.post(() -> { 718 if (!shouldScan()) { 719 return; 720 } 721 if (isVerboseLoggingEnabled()) { 722 Log.v(mTag, "Received scan results from first scan request."); 723 } 724 List<ScanResult> scanResults = new ArrayList<>(); 725 if (results != null) { 726 for (WifiScanner.ScanData scanData : results) { 727 scanResults.addAll(List.of(scanData.getResults())); 728 } 729 } 730 // Fake a SCAN_RESULTS_AVAILABLE_ACTION. The results should already be populated 731 // in mScanResultUpdater, which is the source of truth for the child classes. 732 mScanResultUpdater.update(scanResults); 733 handleScanResultsAvailableAction( 734 new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) 735 .putExtra(WifiManager.EXTRA_RESULTS_UPDATED, true)); 736 // Now start scanning via WifiManager.startScan(). 737 scanLoop(); 738 }); 739 } 740 741 @Override 742 @MainThread 743 public void onFullResult(ScanResult fullScanResult) { 744 // No-op. 745 } 746 747 @Override 748 @MainThread 749 public void onSuccess() { 750 // No-op. 751 } 752 753 @Override 754 @MainThread 755 public void onFailure(int reason, String description) { 756 mWorkerHandler.post(() -> { 757 if (!mIsWifiEnabled) { 758 return; 759 } 760 Log.e(mTag, "Failed to scan! Reason: " + reason + ", "); 761 // First scan failed, start scanning normally anyway. 762 scanLoop(); 763 }); 764 } 765 }; 766 767 private Scanner(Looper looper) { 768 super(looper); 769 } 770 771 /** 772 * Called when the activity enters the Started state. 773 * When this happens, evaluate if we need to start scanning. 774 */ 775 @MainThread 776 private void onStart() { 777 mIsStartedState = true; 778 mWorkerHandler.post(this::possiblyStartScanning); 779 } 780 781 /** 782 * Called when the activity exits the Started state. 783 * When this happens, stop scanning. 784 */ 785 @MainThread 786 private void onStop() { 787 mIsStartedState = false; 788 mWorkerHandler.post(this::stopScanning); 789 } 790 791 /** 792 * Called whenever the Wi-Fi state changes. If the new state differs from the old state, 793 * then re-evaluate whether we need to start or stop scanning. 794 * @param enabled Whether Wi-Fi is enabled or not. 795 */ 796 @WorkerThread 797 private void onWifiStateChanged(boolean enabled) { 798 boolean oldEnabled = mIsWifiEnabled; 799 mIsWifiEnabled = enabled; 800 if (mIsWifiEnabled != oldEnabled) { 801 if (mIsWifiEnabled) { 802 possiblyStartScanning(); 803 } else { 804 stopScanning(); 805 } 806 } 807 } 808 809 /** 810 * Returns true if we should be scanning and false if not. 811 * Scanning should only happen when Wi-Fi is enabled and the activity is started. 812 */ 813 private boolean shouldScan() { 814 return mIsWifiEnabled && mIsStartedState && !mIsScanningDisabled; 815 } 816 817 @WorkerThread 818 private void possiblyStartScanning() { 819 if (!shouldScan()) { 820 return; 821 } 822 Log.i(mTag, "Scanning started"); 823 if (BuildCompat.isAtLeastU()) { 824 // Start off with a fast scan of 2.4GHz, 5GHz, and 6GHz RNR using WifiScanner. 825 // After this is done, fall back to WifiManager.startScan() to get the rest of 826 // the bands and hidden networks. 827 // TODO(b/274177966): Move to using WifiScanner exclusively once we have 828 // permission to use ScanSettings.hiddenNetworks. 829 WifiScanner.ScanSettings scanSettings = new WifiScanner.ScanSettings(); 830 scanSettings.band = WifiScanner.WIFI_BAND_BOTH; 831 scanSettings.setRnrSetting(WifiScanner.WIFI_RNR_ENABLED); 832 scanSettings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT 833 | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 834 WifiScanner wifiScanner = mContext.getSystemService(WifiScanner.class); 835 if (wifiScanner != null) { 836 wifiScanner.stopScan(mFirstScanListener); 837 if (isVerboseLoggingEnabled()) { 838 Log.v(mTag, "Issuing scan request from WifiScanner"); 839 } 840 wifiScanner.startScan(scanSettings, mFirstScanListener); 841 return; 842 } else { 843 Log.e(mTag, "Failed to retrieve WifiScanner!"); 844 } 845 } 846 scanLoop(); 847 } 848 849 @WorkerThread 850 private void stopScanning() { 851 Log.i(mTag, "Scanning stopped"); 852 removeCallbacksAndMessages(null); 853 } 854 855 @WorkerThread 856 private void scanLoop() { 857 if (!shouldScan()) { 858 Log.wtf(mTag, "Scan loop called even though we shouldn't be scanning!" 859 + " mIsWifiEnabled=" + mIsWifiEnabled 860 + " mIsStartedState=" + mIsStartedState); 861 return; 862 } 863 if (!isAppVisible()) { 864 Log.wtf(mTag, "Scan loop called even though app isn't visible anymore!" 865 + " mIsWifiEnabled=" + mIsWifiEnabled 866 + " mIsStartedState=" + mIsStartedState); 867 return; 868 } 869 if (isVerboseLoggingEnabled()) { 870 Log.v(mTag, "Issuing scan request from WifiManager"); 871 } 872 // Remove any pending scanLoops in case possiblyStartScanning was called more than once. 873 removeCallbacksAndMessages(null); 874 mWifiManager.startScan(); 875 postDelayed(this::scanLoop, mScanIntervalMillis); 876 } 877 } 878 879 private boolean isAppVisible() { 880 ActivityManager.RunningAppProcessInfo processInfo = 881 new ActivityManager.RunningAppProcessInfo(); 882 ActivityManager.getMyMemoryState(processInfo); 883 return processInfo.importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; 884 } 885 886 /** 887 * Posts onWifiStateChanged callback on the main thread. 888 */ 889 @WorkerThread 890 private void notifyOnWifiStateChanged() { 891 if (mListener != null) { 892 mMainHandler.post(mListener::onWifiStateChanged); 893 } 894 } 895 896 /** 897 * Base callback handling Wi-Fi state changes 898 * 899 * Subclasses should extend this for their own needs. 900 */ 901 protected interface BaseWifiTrackerCallback { 902 /** 903 * Called when the value for {@link #getWifiState() has changed. 904 */ 905 @MainThread 906 void onWifiStateChanged(); 907 } 908 } 909