• 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_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