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