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