• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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