• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.server.wifi;
18 
19 import static android.net.wifi.WifiScanner.WIFI_BAND_ALL;
20 import static android.net.wifi.WifiScanner.WIFI_BAND_UNSPECIFIED;
21 
22 import static com.android.internal.util.Preconditions.checkNotNull;
23 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_LOCAL_ONLY;
24 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY;
25 import static com.android.server.wifi.WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE;
26 import static com.android.server.wifi.proto.nano.WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_WRONG_PSWD;
27 import static com.android.server.wifi.proto.nano.WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN;
28 import static com.android.server.wifi.util.NativeUtil.addEnclosingQuotes;
29 
30 import static java.lang.Math.toIntExact;
31 
32 import android.annotation.NonNull;
33 import android.annotation.Nullable;
34 import android.app.ActivityManager;
35 import android.app.AlarmManager;
36 import android.app.AppOpsManager;
37 import android.companion.CompanionDeviceManager;
38 import android.content.BroadcastReceiver;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.IntentFilter;
42 import android.content.pm.ApplicationInfo;
43 import android.content.pm.PackageManager;
44 import android.net.MacAddress;
45 import android.net.NetworkCapabilities;
46 import android.net.NetworkFactory;
47 import android.net.NetworkRequest;
48 import android.net.NetworkSpecifier;
49 import android.net.wifi.IActionListener;
50 import android.net.wifi.ILocalOnlyConnectionStatusListener;
51 import android.net.wifi.INetworkRequestMatchCallback;
52 import android.net.wifi.INetworkRequestUserSelectionCallback;
53 import android.net.wifi.ScanResult;
54 import android.net.wifi.SecurityParams;
55 import android.net.wifi.WifiConfiguration;
56 import android.net.wifi.WifiConfiguration.SecurityType;
57 import android.net.wifi.WifiContext;
58 import android.net.wifi.WifiManager;
59 import android.net.wifi.WifiNetworkSpecifier;
60 import android.net.wifi.WifiScanner;
61 import android.net.wifi.util.ScanResultUtil;
62 import android.os.Build;
63 import android.os.Handler;
64 import android.os.Looper;
65 import android.os.PatternMatcher;
66 import android.os.PowerManager;
67 import android.os.Process;
68 import android.os.RemoteCallbackList;
69 import android.os.RemoteException;
70 import android.os.UserHandle;
71 import android.os.WorkSource;
72 import android.text.TextUtils;
73 import android.util.ArraySet;
74 import android.util.Log;
75 import android.util.Pair;
76 
77 import com.android.internal.annotations.VisibleForTesting;
78 import com.android.modules.utils.HandlerExecutor;
79 import com.android.modules.utils.build.SdkLevel;
80 import com.android.server.wifi.proto.nano.WifiMetricsProto;
81 import com.android.server.wifi.util.ActionListenerWrapper;
82 import com.android.server.wifi.util.WifiPermissionsUtil;
83 import com.android.wifi.resources.R;
84 
85 import java.io.FileDescriptor;
86 import java.io.PrintWriter;
87 import java.util.ArrayList;
88 import java.util.Collection;
89 import java.util.Collections;
90 import java.util.Comparator;
91 import java.util.HashMap;
92 import java.util.HashSet;
93 import java.util.Iterator;
94 import java.util.LinkedHashSet;
95 import java.util.List;
96 import java.util.Map;
97 import java.util.Objects;
98 import java.util.Set;
99 import java.util.concurrent.TimeUnit;
100 import java.util.stream.Collectors;
101 
102 /**
103  * Network factory to handle trusted wifi network requests.
104  */
105 public class WifiNetworkFactory extends NetworkFactory {
106     private static final String TAG = "WifiNetworkFactory";
107     @VisibleForTesting
108     private static final int SCORE_FILTER = 60;
109     @VisibleForTesting
110     public static final int CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS = 30 * 1000;
111     @VisibleForTesting
112     public static final int PERIODIC_SCAN_INTERVAL_MS = 10 * 1000; // 10 seconds
113     @VisibleForTesting
114     public static final int NETWORK_CONNECTION_TIMEOUT_MS = 30 * 1000; // 30 seconds
115     @VisibleForTesting
116     public static final int USER_SELECTED_NETWORK_CONNECT_RETRY_MAX = 3; // max of 3 retries.
117     @VisibleForTesting
118     public static final String UI_START_INTENT_ACTION =
119             "com.android.settings.wifi.action.NETWORK_REQUEST";
120     @VisibleForTesting
121     public static final String UI_START_INTENT_CATEGORY = "android.intent.category.DEFAULT";
122     @VisibleForTesting
123     public static final String UI_START_INTENT_EXTRA_APP_NAME =
124             "com.android.settings.wifi.extra.APP_NAME";
125     @VisibleForTesting
126     public static final String UI_START_INTENT_EXTRA_REQUEST_IS_FOR_SINGLE_NETWORK =
127             "com.android.settings.wifi.extra.REQUEST_IS_FOR_SINGLE_NETWORK";
128     // Capacity limit of approved Access Point per App
129     @VisibleForTesting
130     public static final int NUM_OF_ACCESS_POINT_LIMIT_PER_APP = 50;
131 
132     private final WifiContext mContext;
133     private final ActivityManager mActivityManager;
134     private final AlarmManager mAlarmManager;
135     private final AppOpsManager mAppOpsManager;
136     private final Clock mClock;
137     private final Handler mHandler;
138     private final WifiInjector mWifiInjector;
139     private final WifiConnectivityManager mWifiConnectivityManager;
140     private final WifiConfigManager mWifiConfigManager;
141     private final WifiConfigStore mWifiConfigStore;
142     private final WifiPermissionsUtil mWifiPermissionsUtil;
143     private final WifiMetrics mWifiMetrics;
144     private final WifiNative mWifiNative;
145     private final ActiveModeWarden mActiveModeWarden;
146     private final WifiScanner.ScanSettings mScanSettings;
147     private final NetworkFactoryScanListener mScanListener;
148     private final PeriodicScanAlarmListener mPeriodicScanTimerListener;
149     private final ConnectionTimeoutAlarmListener mConnectionTimeoutAlarmListener;
150     private final ConnectHelper mConnectHelper;
151     private final ClientModeImplMonitor mClientModeImplMonitor;
152     private final FrameworkFacade mFacade;
153     private final MultiInternetManager mMultiInternetManager;
154     private RemoteCallbackList<INetworkRequestMatchCallback> mRegisteredCallbacks;
155     // Store all user approved access points for apps.
156     @VisibleForTesting
157     public final Map<String, LinkedHashSet<AccessPoint>> mUserApprovedAccessPointMap;
158     private WifiScanner mWifiScanner;
159     @Nullable private ClientModeManager mClientModeManager;
160     @Nullable private ActiveModeManager.ClientRole mClientModeManagerRole;
161     private CompanionDeviceManager mCompanionDeviceManager;
162     // Temporary approval set by shell commands.
163     @Nullable private String mApprovedApp = null;
164 
165     private int mGenericConnectionReqCount = 0;
166     // Request that is being actively processed. All new requests start out as an "active" request
167     // because we're processing it & handling all the user interactions associated with it. Once we
168     // successfully connect to the network, we transition that request to "connected".
169     @Nullable private NetworkRequest mActiveSpecificNetworkRequest;
170     @Nullable private WifiNetworkSpecifier mActiveSpecificNetworkRequestSpecifier;
171     private boolean mSkipUserDialogue;
172     // Request corresponding to the the network that the device is currently connected to.
173     @Nullable private NetworkRequest mConnectedSpecificNetworkRequest;
174     @Nullable private WifiNetworkSpecifier mConnectedSpecificNetworkRequestSpecifier;
175     @Nullable private WifiConfiguration mUserSelectedNetwork;
176     private boolean mShouldHaveInternetCapabilities = false;
177     private Set<Integer> mConnectedUids = new ArraySet<>();
178     private int mUserSelectedNetworkConnectRetryCount;
179     // Map of bssid to latest scan results for all scan results matching a request. Will be
180     //  - null, if there are no active requests.
181     //  - empty, if there are no matching scan results received for the active request.
182     @Nullable private Map<String, ScanResult> mActiveMatchedScanResults;
183     /** Connection start time to keep track of connection duration */
184     private long mConnectionStartTimeMillis = -1L;
185     /**
186      * CMI listener used for concurrent connection metrics collection.
187      * Not used when the connection is on primary STA (i.e not STA + STA).
188      */
189     @Nullable private CmiListener mCmiListener;
190     // Verbose logging flag.
191     private boolean mVerboseLoggingEnabled = false;
192     private boolean mPeriodicScanTimerSet = false;
193     private boolean mConnectionTimeoutSet = false;
194     private boolean mIsPeriodicScanEnabled = false;
195     private boolean mIsPeriodicScanPaused = false;
196     // We sent a new connection request and are waiting for connection success.
197     private boolean mPendingConnectionSuccess = false;
198     /**
199      * Indicates that we have new data to serialize.
200      */
201     private boolean mHasNewDataToSerialize = false;
202 
203     private final HashMap<String, RemoteCallbackList<ILocalOnlyConnectionStatusListener>>
204             mLocalOnlyStatusListenerPerApp = new HashMap<>();
205     private final HashMap<String, String> mFeatureIdPerApp = new HashMap<>();
206 
207     /**
208      * Helper class to store an access point that the user previously approved for a specific app.
209      * TODO(b/123014687): Move to a common util class.
210      */
211     public static class AccessPoint {
212         public final String ssid;
213         public final MacAddress bssid;
214         public final @SecurityType int networkType;
215 
AccessPoint(@onNull String ssid, @NonNull MacAddress bssid, @SecurityType int networkType)216         AccessPoint(@NonNull String ssid, @NonNull MacAddress bssid,
217                 @SecurityType int networkType) {
218             this.ssid = ssid;
219             this.bssid = bssid;
220             this.networkType = networkType;
221         }
222 
223         @Override
hashCode()224         public int hashCode() {
225             return Objects.hash(ssid, bssid, networkType);
226         }
227 
228         @Override
equals(Object obj)229         public boolean equals(Object obj) {
230             if (this == obj) {
231                 return true;
232             }
233             if (!(obj instanceof AccessPoint)) {
234                 return false;
235             }
236             AccessPoint other = (AccessPoint) obj;
237             return TextUtils.equals(this.ssid, other.ssid)
238                     && Objects.equals(this.bssid, other.bssid)
239                     && this.networkType == other.networkType;
240         }
241 
242         @Override
toString()243         public String toString() {
244             StringBuilder sb = new StringBuilder("AccessPoint: ");
245             return sb.append(ssid)
246                     .append(", ")
247                     .append(bssid)
248                     .append(", ")
249                     .append(networkType)
250                     .toString();
251         }
252     }
253 
254     // Scan listener for scan requests.
255     private class NetworkFactoryScanListener implements WifiScanner.ScanListener {
256         @Override
onSuccess()257         public void onSuccess() {
258             // Scan request succeeded, wait for results to report to external clients.
259             if (mVerboseLoggingEnabled) {
260                 Log.d(TAG, "Scan request succeeded");
261             }
262         }
263 
264         @Override
onFailure(int reason, String description)265         public void onFailure(int reason, String description) {
266             Log.e(TAG, "Scan failure received. reason: " + reason
267                     + ", description: " + description);
268             // TODO(b/113878056): Retry scan to workaround any transient scan failures.
269             scheduleNextPeriodicScan();
270         }
271 
272         @Override
onResults(WifiScanner.ScanData[] scanDatas)273         public void onResults(WifiScanner.ScanData[] scanDatas) {
274             if (mVerboseLoggingEnabled) {
275                 Log.d(TAG, "Scan results received");
276             }
277             // For single scans, the array size should always be 1.
278             if (scanDatas.length != 1) {
279                 Log.wtf(TAG, "Found more than 1 batch of scan results, Ignoring...");
280                 return;
281             }
282             WifiScanner.ScanData scanData = scanDatas[0];
283             ScanResult[] scanResults = scanData.getResults();
284             if (mVerboseLoggingEnabled) {
285                 Log.v(TAG, "Received " + scanResults.length + " scan results");
286             }
287             handleScanResults(scanResults);
288             if (!mSkipUserDialogue && mActiveMatchedScanResults != null) {
289                 sendNetworkRequestMatchCallbacksForActiveRequest(
290                         mActiveMatchedScanResults.values());
291             }
292             scheduleNextPeriodicScan();
293         }
294 
295         @Override
onFullResult(ScanResult fullScanResult)296         public void onFullResult(ScanResult fullScanResult) {
297             // Ignore for single scans.
298         }
299 
300         @Override
onPeriodChanged(int periodInMs)301         public void onPeriodChanged(int periodInMs) {
302             // Ignore for single scans.
303         }
304     };
305 
306     private class PeriodicScanAlarmListener implements AlarmManager.OnAlarmListener {
307         @Override
onAlarm()308         public void onAlarm() {
309             // Trigger the next scan.
310             startScan();
311             mPeriodicScanTimerSet = false;
312         }
313     }
314 
315     private class ConnectionTimeoutAlarmListener implements AlarmManager.OnAlarmListener {
316         @Override
onAlarm()317         public void onAlarm() {
318             Log.e(TAG, "Timed-out connecting to network");
319             if (mUserSelectedNetwork != null) {
320                 handleNetworkConnectionFailure(mUserSelectedNetwork, mUserSelectedNetwork.BSSID,
321                         WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT,
322                         FAILURE_REASON_UNKNOWN);
323             } else {
324                 Log.wtf(TAG, "mUserSelectedNetwork is null, when connection time out");
325             }
326             mConnectionTimeoutSet = false;
327         }
328     }
329 
330     // Callback result from settings UI.
331     private class NetworkFactoryUserSelectionCallback extends
332             INetworkRequestUserSelectionCallback.Stub {
333         private final NetworkRequest mNetworkRequest;
334 
NetworkFactoryUserSelectionCallback(NetworkRequest networkRequest)335         NetworkFactoryUserSelectionCallback(NetworkRequest networkRequest) {
336             mNetworkRequest = networkRequest;
337         }
338 
339         @Override
select(WifiConfiguration wifiConfiguration)340         public void select(WifiConfiguration wifiConfiguration) {
341             mHandler.post(() -> {
342                 Log.i(TAG, "select configuration " + wifiConfiguration);
343                 if (mActiveSpecificNetworkRequest != mNetworkRequest) {
344                     Log.e(TAG, "Stale callback select received");
345                     return;
346                 }
347                 handleConnectToNetworkUserSelection(wifiConfiguration, true);
348             });
349         }
350 
351         @Override
reject()352         public void reject() {
353             mHandler.post(() -> {
354                 if (mActiveSpecificNetworkRequest != mNetworkRequest) {
355                     Log.e(TAG, "Stale callback reject received");
356                     return;
357                 }
358                 handleRejectUserSelection();
359             });
360         }
361     }
362 
363     private final class ConnectActionListener extends IActionListener.Stub {
364         @Override
onSuccess()365         public void onSuccess() {
366             if (mVerboseLoggingEnabled) {
367                 Log.v(TAG, "Triggered network connection");
368             }
369         }
370 
371         @Override
onFailure(int reason)372         public void onFailure(int reason) {
373             Log.e(TAG, "Failed to trigger network connection");
374             if (mUserSelectedNetwork == null) {
375                 Log.e(TAG, "mUserSelectedNetwork is null, when connection failure");
376                 return;
377             }
378             handleNetworkConnectionFailure(mUserSelectedNetwork, mUserSelectedNetwork.BSSID,
379                     reason, FAILURE_REASON_UNKNOWN);
380         }
381     }
382 
383     private final class ClientModeManagerRequestListener implements
384             ActiveModeWarden.ExternalClientModeManagerRequestListener {
385         @Override
onAnswer(@ullable ClientModeManager modeManager)386         public void onAnswer(@Nullable ClientModeManager modeManager) {
387             if (modeManager != null) {
388                 // Remove the mode manager if the associated request is no longer active.
389                 if (mActiveSpecificNetworkRequest == null
390                         && mConnectedSpecificNetworkRequest == null) {
391                     Log.w(TAG, "Client mode manager request answer received with no active and "
392                             + "connected requests, remove the manager");
393                     mActiveModeWarden.removeClientModeManager(modeManager);
394                     return;
395                 }
396                 if (mActiveSpecificNetworkRequest == null) {
397                     Log.w(TAG, "Client mode manager request answer received with no active"
398                             + " requests, but has connected request. ");
399                     if (modeManager != mClientModeManager) {
400                         // If clientModeManager changes, teardown the current connection
401                         mActiveModeWarden.removeClientModeManager(modeManager);
402                     }
403                     return;
404                 }
405                 if (modeManager != mClientModeManager) {
406                     // If clientModeManager changes, teardown the current connection
407                     removeClientModeManagerIfNecessary();
408                 }
409                 mClientModeManager = modeManager;
410                 mClientModeManagerRole = modeManager.getRole();
411                 if (mVerboseLoggingEnabled) {
412                     Log.v(TAG, "retrieve CMM: " + mClientModeManager.toString());
413                 }
414                 handleClientModeManagerRetrieval();
415             } else {
416                 handleClientModeManagerRemovalOrFailure();
417             }
418         }
419     }
420 
421     private class ModeChangeCallback implements ActiveModeWarden.ModeChangeCallback {
422         @Override
onActiveModeManagerAdded(@onNull ActiveModeManager activeModeManager)423         public void onActiveModeManagerAdded(@NonNull ActiveModeManager activeModeManager) {
424             // ignored.
425             // Will get a dedicated ClientModeManager instance for our request via
426             // ClientModeManagerRequestListener.
427         }
428 
429         @Override
onActiveModeManagerRemoved(@onNull ActiveModeManager activeModeManager)430         public void onActiveModeManagerRemoved(@NonNull ActiveModeManager activeModeManager) {
431             if (!(activeModeManager instanceof ClientModeManager)) return;
432             if (mVerboseLoggingEnabled) {
433                 Log.v(TAG, "ModeManager removed " + activeModeManager.getInterfaceName());
434             }
435             // Mode manager removed. Cleanup any ongoing requests.
436             if (activeModeManager == mClientModeManager
437                     || !mActiveModeWarden.hasPrimaryClientModeManager()) {
438                 handleClientModeManagerRemovalOrFailure();
439             }
440         }
441 
442         @Override
onActiveModeManagerRoleChanged(@onNull ActiveModeManager activeModeManager)443         public void onActiveModeManagerRoleChanged(@NonNull ActiveModeManager activeModeManager) {
444             if (!(activeModeManager instanceof ClientModeManager)) return;
445             if (mVerboseLoggingEnabled) {
446                 Log.v(TAG, "ModeManager role changed " + activeModeManager.getInterfaceName());
447             }
448             // Mode manager role changed. Cleanup any ongoing requests.
449             if (activeModeManager == mClientModeManager
450                     || !mActiveModeWarden.hasPrimaryClientModeManager()) {
451                 handleClientModeManagerRemovalOrFailure();
452             }
453         }
454     }
455 
456     /**
457      * Module to interact with the wifi config store.
458      */
459     private class NetworkRequestDataSource implements NetworkRequestStoreData.DataSource {
460         @Override
toSerialize()461         public Map<String, Set<AccessPoint>> toSerialize() {
462             // Clear the flag after writing to disk.
463             mHasNewDataToSerialize = false;
464             return new HashMap<>(mUserApprovedAccessPointMap);
465         }
466 
467         @Override
fromDeserialized(Map<String, Set<AccessPoint>> approvedAccessPointMap)468         public void fromDeserialized(Map<String, Set<AccessPoint>> approvedAccessPointMap) {
469             approvedAccessPointMap.forEach((key, value) ->
470                     mUserApprovedAccessPointMap.put(key, new LinkedHashSet<>(value)));
471         }
472 
473         @Override
reset()474         public void reset() {
475             mUserApprovedAccessPointMap.clear();
476         }
477 
478         @Override
hasNewDataToSerialize()479         public boolean hasNewDataToSerialize() {
480             return mHasNewDataToSerialize;
481         }
482     }
483 
484     /**
485      * To keep track of concurrent connections using this API surface (for metrics collection only).
486      *
487      * Only used if the connection is initiated on secondary STA.
488      */
489     private class CmiListener implements ClientModeImplListener {
490         /** Concurrent connection start time to keep track of connection duration */
491         private long mConcurrentConnectionStartTimeMillis = -1L;
492         /** Whether we have already indicated the presence of concurrent connection */
493         private boolean mHasAlreadyIncrementedConcurrentConnectionCount = false;
494 
isLocalOnlyOrPrimary(@onNull ClientModeManager cmm)495         private boolean isLocalOnlyOrPrimary(@NonNull ClientModeManager cmm) {
496             return cmm.getRole() == ROLE_CLIENT_PRIMARY
497                     || cmm.getRole() == ROLE_CLIENT_LOCAL_ONLY;
498         }
499 
checkForConcurrencyStartAndIncrementMetrics()500         private void checkForConcurrencyStartAndIncrementMetrics() {
501             int numLocalOnlyOrPrimaryConnectedCmms = 0;
502             for (ClientModeManager cmm : mActiveModeWarden.getClientModeManagers()) {
503                 if (isLocalOnlyOrPrimary(cmm) && cmm.isConnected()) {
504                     numLocalOnlyOrPrimaryConnectedCmms++;
505                 }
506             }
507             if (numLocalOnlyOrPrimaryConnectedCmms > 1) {
508                 mConcurrentConnectionStartTimeMillis = mClock.getElapsedSinceBootMillis();
509                 // Note: We could have multiple connect/disconnect of the primary connection
510                 // while remaining connected to the local only connection. We want to keep track
511                 // of the connection durations accurately across those disconnects. However, we do
512                 // not want to increment the connection count metric since that should be a 1:1
513                 // mapping with the number of requests processed (i.e don't indicate 2 concurrent
514                 // connection count if the primary disconnected & connected back while processing
515                 // the same local only request).
516                 if (!mHasAlreadyIncrementedConcurrentConnectionCount) {
517                     mWifiMetrics.incrementNetworkRequestApiNumConcurrentConnection();
518                     mHasAlreadyIncrementedConcurrentConnectionCount = true;
519                 }
520             }
521         }
522 
checkForConcurrencyEndAndIncrementMetrics()523         public void checkForConcurrencyEndAndIncrementMetrics() {
524             if (mConcurrentConnectionStartTimeMillis != -1L) {
525                 mWifiMetrics.incrementNetworkRequestApiConcurrentConnectionDurationSecHistogram(
526                         toIntExact(TimeUnit.MILLISECONDS.toSeconds(
527                                 mClock.getElapsedSinceBootMillis()
528                                         - mConcurrentConnectionStartTimeMillis)));
529                 mConcurrentConnectionStartTimeMillis = -1L;
530             }
531         }
532 
CmiListener()533         CmiListener() {
534             checkForConcurrencyStartAndIncrementMetrics();
535         }
536 
537         @Override
onL3Connected(@onNull ConcreteClientModeManager clientModeManager)538         public void onL3Connected(@NonNull ConcreteClientModeManager clientModeManager) {
539             if (isLocalOnlyOrPrimary(clientModeManager)) {
540                 checkForConcurrencyStartAndIncrementMetrics();
541             }
542         }
543 
544         @Override
onConnectionEnd(@onNull ConcreteClientModeManager clientModeManager)545         public void onConnectionEnd(@NonNull ConcreteClientModeManager clientModeManager) {
546             if (isLocalOnlyOrPrimary(clientModeManager)) {
547                 checkForConcurrencyEndAndIncrementMetrics();
548             }
549         }
550     }
551 
WifiNetworkFactory(Looper looper, WifiContext context, NetworkCapabilities nc, ActivityManager activityManager, AlarmManager alarmManager, AppOpsManager appOpsManager, Clock clock, WifiInjector wifiInjector, WifiConnectivityManager connectivityManager, WifiConfigManager configManager, WifiConfigStore configStore, WifiPermissionsUtil wifiPermissionsUtil, WifiMetrics wifiMetrics, WifiNative wifiNative, ActiveModeWarden activeModeWarden, ConnectHelper connectHelper, ClientModeImplMonitor clientModeImplMonitor, FrameworkFacade facade, MultiInternetManager multiInternetManager)552     public WifiNetworkFactory(Looper looper, WifiContext context, NetworkCapabilities nc,
553             ActivityManager activityManager, AlarmManager alarmManager,
554             AppOpsManager appOpsManager,
555             Clock clock, WifiInjector wifiInjector,
556             WifiConnectivityManager connectivityManager,
557             WifiConfigManager configManager,
558             WifiConfigStore configStore,
559             WifiPermissionsUtil wifiPermissionsUtil,
560             WifiMetrics wifiMetrics,
561             WifiNative wifiNative,
562             ActiveModeWarden activeModeWarden,
563             ConnectHelper connectHelper,
564             ClientModeImplMonitor clientModeImplMonitor,
565             FrameworkFacade facade,
566             MultiInternetManager multiInternetManager) {
567         super(looper, context, TAG, nc);
568         mContext = context;
569         mActivityManager = activityManager;
570         mAlarmManager = alarmManager;
571         mAppOpsManager = appOpsManager;
572         mClock = clock;
573         mHandler = new Handler(looper);
574         mWifiInjector = wifiInjector;
575         mWifiConnectivityManager = connectivityManager;
576         mWifiConfigManager = configManager;
577         mWifiConfigStore = configStore;
578         mWifiPermissionsUtil = wifiPermissionsUtil;
579         mWifiMetrics = wifiMetrics;
580         mWifiNative = wifiNative;
581         mActiveModeWarden = activeModeWarden;
582         mConnectHelper = connectHelper;
583         mClientModeImplMonitor = clientModeImplMonitor;
584         // Create the scan settings.
585         mScanSettings = new WifiScanner.ScanSettings();
586         mScanSettings.type = WifiScanner.SCAN_TYPE_HIGH_ACCURACY;
587         mScanSettings.band = WifiScanner.WIFI_BAND_ALL;
588         mScanSettings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
589         mScanListener = new NetworkFactoryScanListener();
590         mPeriodicScanTimerListener = new PeriodicScanAlarmListener();
591         mConnectionTimeoutAlarmListener = new ConnectionTimeoutAlarmListener();
592         mUserApprovedAccessPointMap = new HashMap<>();
593         mFacade = facade;
594         mMultiInternetManager = multiInternetManager;
595 
596         IntentFilter filter = new IntentFilter();
597         filter.addAction(Intent.ACTION_SCREEN_ON);
598         filter.addAction(Intent.ACTION_SCREEN_OFF);
599         context.registerReceiver(
600                 new BroadcastReceiver() {
601                     @Override
602                     public void onReceive(Context context, Intent intent) {
603                         String action = intent.getAction();
604                         if (action.equals(Intent.ACTION_SCREEN_ON)) {
605                             handleScreenStateChanged(true);
606                         } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
607                             handleScreenStateChanged(false);
608                         }
609                     }
610                 }, filter, null, mHandler);
611         handleScreenStateChanged(context.getSystemService(PowerManager.class).isInteractive());
612 
613         // register the data store for serializing/deserializing data.
614         configStore.registerStoreData(
615                 wifiInjector.makeNetworkRequestStoreData(new NetworkRequestDataSource()));
616 
617         activeModeWarden.registerModeChangeCallback(new ModeChangeCallback());
618 
619         setScoreFilter(SCORE_FILTER);
620     }
621 
saveToStore()622     private void saveToStore() {
623         // Set the flag to let WifiConfigStore that we have new data to write.
624         mHasNewDataToSerialize = true;
625         if (!mWifiConfigManager.saveToStore(true)) {
626             Log.w(TAG, "Failed to save to store");
627         }
628     }
629 
630     /**
631      * Enable verbose logging.
632      */
enableVerboseLogging(boolean verbose)633     public void enableVerboseLogging(boolean verbose) {
634         mVerboseLoggingEnabled = verbose;
635     }
636 
637     /**
638      * Add a new callback for network request match handling.
639      */
addCallback(INetworkRequestMatchCallback callback)640     public void addCallback(INetworkRequestMatchCallback callback) {
641         if (mActiveSpecificNetworkRequest == null) {
642             Log.wtf(TAG, "No valid network request. Ignoring callback registration");
643             try {
644                 callback.onAbort();
645             } catch (RemoteException e) {
646                 Log.e(TAG, "Unable to invoke network request abort callback " + callback, e);
647             }
648             return;
649         }
650         if (mRegisteredCallbacks == null) {
651             mRegisteredCallbacks = new RemoteCallbackList<>();
652         }
653         if (!mRegisteredCallbacks.register(callback)) {
654             Log.e(TAG, "Failed to add callback");
655             return;
656         }
657         if (mVerboseLoggingEnabled) {
658             Log.v(TAG, "Adding callback. Num callbacks: "
659                     + mRegisteredCallbacks.getRegisteredCallbackCount());
660         }
661         // Register our user selection callback.
662         try {
663             callback.onUserSelectionCallbackRegistration(
664                     new NetworkFactoryUserSelectionCallback(mActiveSpecificNetworkRequest));
665         } catch (RemoteException e) {
666             Log.e(TAG, "Unable to invoke user selection registration callback " + callback, e);
667             return;
668         }
669 
670         // If we are already in the midst of processing a request, send matching callbacks
671         // immediately on registering the callback.
672         if (mActiveMatchedScanResults != null) {
673             sendNetworkRequestMatchCallbacksForActiveRequest(
674                     mActiveMatchedScanResults.values());
675         }
676     }
677 
678     /**
679      * Remove an existing callback for network request match handling.
680      */
removeCallback(INetworkRequestMatchCallback callback)681     public void removeCallback(INetworkRequestMatchCallback callback) {
682         if (mRegisteredCallbacks == null) return;
683         mRegisteredCallbacks.unregister(callback);
684         if (mVerboseLoggingEnabled) {
685             Log.v(TAG, "Removing callback. Num callbacks: "
686                     + mRegisteredCallbacks.getRegisteredCallbackCount());
687         }
688     }
689 
canNewRequestOverrideExistingRequest( NetworkRequest newRequest, NetworkRequest existingRequest)690     private boolean canNewRequestOverrideExistingRequest(
691             NetworkRequest newRequest, NetworkRequest existingRequest) {
692         if (existingRequest == null) return true;
693         // Request from app with NETWORK_SETTINGS can override any existing requests.
694         if (mWifiPermissionsUtil.checkNetworkSettingsPermission(newRequest.getRequestorUid())) {
695             return true;
696         }
697         // Request from fg app can override any existing requests.
698         if (mFacade.isRequestFromForegroundApp(mContext, newRequest.getRequestorPackageName())) {
699             return true;
700         }
701         // Request from fg service can override only if the existing request is not from a fg app.
702         if (!mFacade.isRequestFromForegroundApp(mContext,
703                 existingRequest.getRequestorPackageName())) {
704             return true;
705         }
706         Log.e(TAG, "Already processing request from a foreground app "
707                 + existingRequest.getRequestorPackageName() + ". Rejecting request from "
708                 + newRequest.getRequestorPackageName());
709         return false;
710     }
711 
isRequestWithWifiNetworkSpecifierValid(NetworkRequest networkRequest)712     boolean isRequestWithWifiNetworkSpecifierValid(NetworkRequest networkRequest) {
713         WifiNetworkSpecifier wns = (WifiNetworkSpecifier) networkRequest.getNetworkSpecifier();
714         // Request cannot have internet capability since such a request can never be fulfilled.
715         // (NetworkAgent for connection with WifiNetworkSpecifier will not have internet capability)
716         if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
717             Log.e(TAG, "Request with wifi network specifier cannot contain "
718                     + "NET_CAPABILITY_INTERNET. Rejecting");
719             return false;
720         }
721         if (networkRequest.getRequestorUid() == Process.INVALID_UID) {
722             Log.e(TAG, "Request with wifi network specifier should contain valid uid. Rejecting");
723             return false;
724         }
725         if (TextUtils.isEmpty(networkRequest.getRequestorPackageName())) {
726             Log.e(TAG, "Request with wifi network specifier should contain valid package name."
727                     + "Rejecting");
728             return false;
729         }
730         try {
731             mAppOpsManager.checkPackage(
732                     networkRequest.getRequestorUid(), networkRequest.getRequestorPackageName());
733         } catch (SecurityException e) {
734             Log.e(TAG, "Invalid uid/package name " + networkRequest.getRequestorUid() + ", "
735                     + networkRequest.getRequestorPackageName() + ". Rejecting", e);
736             return false;
737         }
738 
739         if (wns.getBand() != ScanResult.UNSPECIFIED) {
740             Log.e(TAG, "Requesting specific frequency bands is not yet supported. Rejecting");
741             return false;
742         }
743         if (!WifiConfigurationUtil.validateNetworkSpecifier(wns, mContext.getResources()
744                 .getInteger(R.integer.config_wifiNetworkSpecifierMaxPreferredChannels))) {
745             Log.e(TAG, "Invalid wifi network specifier: " + wns + ". Rejecting ");
746             return false;
747         }
748         return true;
749     }
750 
751     /**
752      * Check whether to accept the new network connection request.
753      *
754      * All the validation of the incoming request is done in this method.
755      */
756     @Override
acceptRequest(NetworkRequest networkRequest)757     public boolean acceptRequest(NetworkRequest networkRequest) {
758         NetworkSpecifier ns = networkRequest.getNetworkSpecifier();
759         boolean isFromSetting = mWifiPermissionsUtil.checkNetworkSettingsPermission(
760                 networkRequest.getRequestorUid());
761         if (ns == null) {
762             // Generic wifi request. Always accept.
763         } else {
764             // Unsupported network specifier.
765             if (!(ns instanceof WifiNetworkSpecifier)) {
766                 Log.e(TAG, "Unsupported network specifier: " + ns + ". Rejecting");
767                 return false;
768             }
769             // MultiInternet Request to be handled by MultiInternetWifiNetworkFactory.
770             if (mMultiInternetManager.isStaConcurrencyForMultiInternetEnabled()
771                     && MultiInternetWifiNetworkFactory.isWifiMultiInternetRequest(networkRequest,
772                     isFromSetting)) {
773                 return false;
774             }
775             // Invalid request with wifi network specifier.
776             if (!isRequestWithWifiNetworkSpecifierValid(networkRequest)) {
777                 Log.e(TAG, "Invalid network specifier: " + ns + ". Rejecting");
778                 releaseRequestAsUnfulfillableByAnyFactory(networkRequest);
779                 return false;
780             }
781             if (mWifiPermissionsUtil.isGuestUser()) {
782                 Log.e(TAG, "network specifier from guest user, reject");
783                 releaseRequestAsUnfulfillableByAnyFactory(networkRequest);
784                 return false;
785             }
786             if (Objects.equals(mActiveSpecificNetworkRequest, networkRequest)
787                     || Objects.equals(mConnectedSpecificNetworkRequest, networkRequest)) {
788                 Log.e(TAG, "acceptRequest: Already processing the request " + networkRequest);
789                 return true;
790             }
791             // Only allow specific wifi network request from foreground app/service.
792             if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(
793                     networkRequest.getRequestorUid())
794                     && !mFacade.isRequestFromForegroundAppOrService(mContext,
795                     networkRequest.getRequestorPackageName())) {
796                 Log.e(TAG, "Request not from foreground app or service."
797                         + " Rejecting request from " + networkRequest.getRequestorPackageName());
798                 releaseRequestAsUnfulfillableByAnyFactory(networkRequest);
799                 return false;
800             }
801             // If there is an active request, only proceed if the new request is from a foreground
802             // app.
803             if (!canNewRequestOverrideExistingRequest(
804                     networkRequest, mActiveSpecificNetworkRequest)) {
805                 Log.e(TAG, "Request cannot override active request."
806                         + " Rejecting request from " + networkRequest.getRequestorPackageName());
807                 releaseRequestAsUnfulfillableByAnyFactory(networkRequest);
808                 return false;
809             }
810             // If there is a connected request, only proceed if the new request is from a foreground
811             // app.
812             if (!canNewRequestOverrideExistingRequest(
813                     networkRequest, mConnectedSpecificNetworkRequest)) {
814                 Log.e(TAG, "Request cannot override connected request."
815                         + " Rejecting request from " + networkRequest.getRequestorPackageName());
816                 releaseRequestAsUnfulfillableByAnyFactory(networkRequest);
817                 return false;
818             }
819             if (mVerboseLoggingEnabled) {
820                 Log.v(TAG, "Accepted network request with specifier from fg "
821                         + (mFacade.isRequestFromForegroundApp(mContext,
822                                 networkRequest.getRequestorPackageName())
823                         ? "app" : "service"));
824             }
825         }
826         if (mVerboseLoggingEnabled) {
827             Log.v(TAG, "Accepted network request " + networkRequest);
828         }
829         return true;
830     }
831 
832     /**
833      * Handle new network connection requests.
834      *
835      * The assumption here is that {@link #acceptRequest(NetworkRequest)} has already sanitized
836      * the incoming request.
837      */
838     @Override
needNetworkFor(NetworkRequest networkRequest)839     protected void needNetworkFor(NetworkRequest networkRequest) {
840         NetworkSpecifier ns = networkRequest.getNetworkSpecifier();
841         boolean isFromSetting = mWifiPermissionsUtil.checkNetworkSettingsPermission(
842                 networkRequest.getRequestorUid());
843         if (ns == null) {
844             // Generic wifi request. Turn on auto-join if necessary.
845             if (++mGenericConnectionReqCount == 1) {
846                 mWifiConnectivityManager.setTrustedConnectionAllowed(true);
847             }
848         } else {
849             // Unsupported network specifier.
850             if (!(ns instanceof WifiNetworkSpecifier)) {
851                 Log.e(TAG, "Unsupported network specifier: " + ns + ". Ignoring");
852                 return;
853             }
854             // MultiInternet Request to be handled by MultiInternetWifiNetworkFactory.
855             if (mMultiInternetManager.isStaConcurrencyForMultiInternetEnabled()
856                     && MultiInternetWifiNetworkFactory.isWifiMultiInternetRequest(networkRequest,
857                     isFromSetting)) {
858                 return;
859             }
860             // Invalid request with wifi network specifier.
861             if (!isRequestWithWifiNetworkSpecifierValid(networkRequest)) {
862                 Log.e(TAG, "Invalid network specifier: " + ns + ". Rejecting");
863                 releaseRequestAsUnfulfillableByAnyFactory(networkRequest);
864                 return;
865             }
866             if (mWifiPermissionsUtil.isGuestUser()) {
867                 Log.e(TAG, "network specifier from guest user, reject");
868                 releaseRequestAsUnfulfillableByAnyFactory(networkRequest);
869                 return;
870             }
871             // Wifi-off abort early.
872             if (!mActiveModeWarden.hasPrimaryClientModeManager()) {
873                 Log.e(TAG, "Request with wifi network specifier when wifi is off."
874                         + "Rejecting");
875                 releaseRequestAsUnfulfillableByAnyFactory(networkRequest);
876                 return;
877             }
878             if (Objects.equals(mActiveSpecificNetworkRequest, networkRequest)
879                     || Objects.equals(mConnectedSpecificNetworkRequest, networkRequest)) {
880                 Log.e(TAG, "needNetworkFor: Already processing the request " + networkRequest);
881                 return;
882             }
883 
884             retrieveWifiScanner();
885             // Reset state from any previous request.
886             setupForActiveRequest();
887             // Store the active network request.
888             mActiveSpecificNetworkRequest = networkRequest;
889             WifiNetworkSpecifier wns = (WifiNetworkSpecifier) ns;
890             mActiveSpecificNetworkRequestSpecifier = new WifiNetworkSpecifier(
891                     wns.ssidPatternMatcher, wns.bssidPatternMatcher, wns.getBand(),
892                     wns.wifiConfiguration, wns.getPreferredChannelFrequenciesMhz());
893             mSkipUserDialogue = false;
894             mWifiMetrics.incrementNetworkRequestApiNumRequest();
895 
896             // special case for STA+STA: since we are not allowed to replace the primary STA we
897             // should check if we are able to get an interface for a secondary STA. If not - we
898             // want to escalate and display the dialog to the user EVEN if we have a normal bypass
899             // (normal == user approved before, if the app has full UI bypass we won't override it)
900             boolean revokeNormalBypass = false;
901             if (mContext.getResources().getBoolean(
902                     R.bool.config_wifiMultiStaLocalOnlyConcurrencyEnabled)
903                     && !mWifiPermissionsUtil.isTargetSdkLessThan(
904                     mActiveSpecificNetworkRequest.getRequestorPackageName(), Build.VERSION_CODES.S,
905                     mActiveSpecificNetworkRequest.getRequestorUid())
906                     && mClientModeManager == null) {
907                 revokeNormalBypass = !mWifiNative.isItPossibleToCreateStaIface(
908                         new WorkSource(mActiveSpecificNetworkRequest.getRequestorUid(),
909                                 mActiveSpecificNetworkRequest.getRequestorPackageName()));
910             }
911 
912 
913             if (!triggerConnectIfUserApprovedMatchFound(revokeNormalBypass)) {
914                 // Didn't find an approved match, send the matching results to UI and trigger
915                 // periodic scans for finding a network in the request.
916                 // Fetch the latest cached scan results to speed up network matching.
917                 ScanResult[] cachedScanResults = getFilteredCachedScanResults();
918                 if (mVerboseLoggingEnabled) {
919                     Log.v(TAG, "Using cached " + cachedScanResults.length + " scan results");
920                 }
921                 handleScanResults(cachedScanResults);
922                 // Start UI to let the user grant/disallow this request from the app.
923                 if (!mSkipUserDialogue) {
924                     startUi();
925                     if (mActiveMatchedScanResults != null) {
926                         sendNetworkRequestMatchCallbacksForActiveRequest(
927                                 mActiveMatchedScanResults.values());
928                     }
929                 }
930                 startPeriodicScans();
931             }
932         }
933     }
934 
935     @Override
releaseNetworkFor(NetworkRequest networkRequest)936     protected void releaseNetworkFor(NetworkRequest networkRequest) {
937         NetworkSpecifier ns = networkRequest.getNetworkSpecifier();
938         if (ns == null) {
939             // Generic wifi request. Turn off auto-join if necessary.
940             if (mGenericConnectionReqCount == 0) {
941                 Log.e(TAG, "No valid network request to release");
942                 return;
943             }
944             if (--mGenericConnectionReqCount == 0) {
945                 mWifiConnectivityManager.setTrustedConnectionAllowed(false);
946             }
947         } else {
948             // Unsupported network specifier.
949             if (!(ns instanceof WifiNetworkSpecifier)) {
950                 Log.e(TAG, "Unsupported network specifier mentioned. Ignoring");
951                 return;
952             }
953             if (mActiveSpecificNetworkRequest == null && mConnectedSpecificNetworkRequest == null) {
954                 Log.e(TAG, "Network release received with no active/connected request."
955                         + " Ignoring");
956                 return;
957             }
958             if (Objects.equals(mActiveSpecificNetworkRequest, networkRequest)) {
959                 Log.i(TAG, "App released active request, cancelling "
960                         + mActiveSpecificNetworkRequest);
961                 teardownForActiveRequest();
962             } else if (Objects.equals(mConnectedSpecificNetworkRequest, networkRequest)) {
963                 Log.i(TAG, "App released connected request, cancelling "
964                         + mConnectedSpecificNetworkRequest);
965                 teardownForConnectedNetwork();
966             } else {
967                 Log.e(TAG, "Network specifier does not match the active/connected request."
968                         + " Ignoring");
969             }
970         }
971     }
972 
973     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)974     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
975         super.dump(fd, pw, args);
976         pw.println(TAG + ": mGenericConnectionReqCount " + mGenericConnectionReqCount);
977         pw.println(TAG + ": mActiveSpecificNetworkRequest " + mActiveSpecificNetworkRequest);
978         pw.println(TAG + ": mUserApprovedAccessPointMap " + mUserApprovedAccessPointMap);
979     }
980 
981     /**
982      * Check if there is at least one connection request.
983      */
hasConnectionRequests()984     public boolean hasConnectionRequests() {
985         return mGenericConnectionReqCount > 0 || mActiveSpecificNetworkRequest != null
986                 || mConnectedSpecificNetworkRequest != null;
987     }
988 
989     /**
990      * Return the uid of the specific network request being processed if connected to the requested
991      * network.
992      *
993      * @param connectedNetwork WifiConfiguration corresponding to the connected network.
994      * @return Pair of uid & package name of the specific request (if any), else <-1, "">.
995      */
getSpecificNetworkRequestUidAndPackageName( @onNull WifiConfiguration connectedNetwork, @NonNull String connectedBssid)996     public Pair<Integer, String> getSpecificNetworkRequestUidAndPackageName(
997             @NonNull WifiConfiguration connectedNetwork, @NonNull String connectedBssid) {
998         if (mUserSelectedNetwork == null || connectedNetwork == null) {
999             return Pair.create(Process.INVALID_UID, "");
1000         }
1001         if (!isUserSelectedNetwork(connectedNetwork, connectedBssid)) {
1002             Log.w(TAG, "Connected to unknown network " + connectedNetwork + ":" + connectedBssid
1003                     + ". Ignoring...");
1004             return Pair.create(Process.INVALID_UID, "");
1005         }
1006         if (mConnectedSpecificNetworkRequestSpecifier != null) {
1007             return Pair.create(mConnectedSpecificNetworkRequest.getRequestorUid(),
1008                     mConnectedSpecificNetworkRequest.getRequestorPackageName());
1009         }
1010         if (mActiveSpecificNetworkRequestSpecifier != null) {
1011             return Pair.create(mActiveSpecificNetworkRequest.getRequestorUid(),
1012                     mActiveSpecificNetworkRequest.getRequestorPackageName());
1013         }
1014         return Pair.create(Process.INVALID_UID, "");
1015     }
1016 
1017     /**
1018      * Return the uids of the specific network request being processed if connected to the requested
1019      * network.
1020      *
1021      * @param connectedNetwork WifiConfiguration corresponding to the connected network.
1022      * @return Set of uids which request this network
1023      */
getSpecificNetworkRequestUids( @onNull WifiConfiguration connectedNetwork, @NonNull String connectedBssid)1024     public Set<Integer> getSpecificNetworkRequestUids(
1025             @NonNull WifiConfiguration connectedNetwork, @NonNull String connectedBssid) {
1026         if (mUserSelectedNetwork == null || connectedNetwork == null) {
1027             return Collections.emptySet();
1028         }
1029         if (!isUserSelectedNetwork(connectedNetwork, connectedBssid)) {
1030             Log.w(TAG, "Connected to unknown network " + connectedNetwork + ":" + connectedBssid
1031                     + ". Ignoring...");
1032             return Collections.emptySet();
1033         }
1034         if (mConnectedSpecificNetworkRequestSpecifier != null) {
1035             return mConnectedUids;
1036         }
1037         if (mActiveSpecificNetworkRequestSpecifier != null) {
1038             return Set.of(mActiveSpecificNetworkRequest.getRequestorUid());
1039         }
1040         return Collections.emptySet();
1041     }
1042 
1043     /**
1044      * Return whether if current network request should have the internet capabilities due to a
1045      * same saved/suggestion network is present.
1046      */
shouldHaveInternetCapabilities()1047     public boolean shouldHaveInternetCapabilities() {
1048         return mShouldHaveInternetCapabilities;
1049     }
1050 
1051     // Helper method to add the provided network configuration to WifiConfigManager, if it does not
1052     // already exist & return the allocated network ID. This ID will be used in the CONNECT_NETWORK
1053     // request to ClientModeImpl.
1054     // If the network already exists, just return the network ID of the existing network.
addNetworkToWifiConfigManager(@onNull WifiConfiguration network)1055     private int addNetworkToWifiConfigManager(@NonNull WifiConfiguration network) {
1056         WifiConfiguration existingSavedNetwork =
1057                 mWifiConfigManager.getConfiguredNetwork(network.getProfileKey());
1058         if (existingSavedNetwork != null) {
1059             if (WifiConfigurationUtil.hasCredentialChanged(existingSavedNetwork, network)) {
1060                 // TODO (b/142035508): What if the user has a saved network with different
1061                 // credentials?
1062                 Log.w(TAG, "Network config already present in config manager, reusing");
1063             }
1064             return existingSavedNetwork.networkId;
1065         }
1066         NetworkUpdateResult networkUpdateResult =
1067                 mWifiConfigManager.addOrUpdateNetwork(
1068                         network, mActiveSpecificNetworkRequest.getRequestorUid(),
1069                         mActiveSpecificNetworkRequest.getRequestorPackageName(), false);
1070         if (mVerboseLoggingEnabled) {
1071             Log.v(TAG, "Added network to config manager " + networkUpdateResult.getNetworkId());
1072         }
1073         return networkUpdateResult.getNetworkId();
1074     }
1075 
1076     // Helper method to remove the provided network configuration from WifiConfigManager, if it was
1077     // added by an app's specifier request.
disconnectAndRemoveNetworkFromWifiConfigManager( @ullable WifiConfiguration network)1078     private void disconnectAndRemoveNetworkFromWifiConfigManager(
1079             @Nullable WifiConfiguration network) {
1080         // Trigger a disconnect first.
1081         if (mClientModeManager != null) mClientModeManager.disconnect();
1082 
1083         if (network == null) return;
1084         WifiConfiguration wcmNetwork =
1085                 mWifiConfigManager.getConfiguredNetwork(network.getProfileKey());
1086         if (wcmNetwork == null) {
1087             Log.e(TAG, "Network not present in config manager");
1088             return;
1089         }
1090         // Remove the network if it was added previously by an app's specifier request.
1091         if (wcmNetwork.ephemeral && wcmNetwork.fromWifiNetworkSpecifier) {
1092             boolean success =
1093                     mWifiConfigManager.removeNetwork(
1094                             wcmNetwork.networkId, wcmNetwork.creatorUid, wcmNetwork.creatorName);
1095             if (!success) {
1096                 Log.e(TAG, "Failed to remove network from config manager");
1097             } else if (mVerboseLoggingEnabled) {
1098                 Log.v(TAG, "Removed network from config manager " + wcmNetwork.networkId);
1099             }
1100         }
1101     }
1102 
1103     // Helper method to trigger a connection request & schedule a timeout alarm to track the
1104     // connection request.
connectToNetwork(@onNull WifiConfiguration network)1105     private void connectToNetwork(@NonNull WifiConfiguration network) {
1106         // Cancel connection timeout alarm for any previous connection attempts.
1107         cancelConnectionTimeout();
1108 
1109         // First add the network to WifiConfigManager and then use the obtained networkId
1110         // in the CONNECT_NETWORK request.
1111         // Note: We don't do any error checks on the networkId because ClientModeImpl will do the
1112         // necessary checks when processing CONNECT_NETWORK.
1113         int networkId = addNetworkToWifiConfigManager(network);
1114 
1115         mWifiMetrics.setNominatorForNetwork(networkId,
1116                 WifiMetricsProto.ConnectionEvent.NOMINATOR_SPECIFIER);
1117         if (mClientModeManagerRole == ROLE_CLIENT_PRIMARY) {
1118             mWifiMetrics.incrementNetworkRequestApiNumConnectOnPrimaryIface();
1119         } else {
1120             mWifiMetrics.incrementNetworkRequestApiNumConnectOnSecondaryIface();
1121         }
1122 
1123         // Send the connect request to ClientModeImpl.
1124         // TODO(b/117601161): Refactor this.
1125         ConnectActionListener listener = new ConnectActionListener();
1126         mConnectHelper.connectToNetwork(
1127                 mClientModeManager,
1128                 new NetworkUpdateResult(networkId),
1129                 new ActionListenerWrapper(listener),
1130                 mActiveSpecificNetworkRequest.getRequestorUid(),
1131                 mActiveSpecificNetworkRequest.getRequestorPackageName());
1132 
1133         // Post an alarm to handle connection timeout.
1134         scheduleConnectionTimeout();
1135     }
1136 
handleConnectToNetworkUserSelectionInternal(WifiConfiguration network, boolean didUserSeeUi)1137     private void handleConnectToNetworkUserSelectionInternal(WifiConfiguration network,
1138             boolean didUserSeeUi) {
1139         // Copy over the credentials from the app's request and then copy the ssid from user
1140         // selection.
1141         WifiConfiguration networkToConnect =
1142                 new WifiConfiguration(mActiveSpecificNetworkRequestSpecifier.wifiConfiguration);
1143         networkToConnect.SSID = network.SSID;
1144         // Set the WifiConfiguration.BSSID field to prevent roaming.
1145         if (network.BSSID != null) {
1146             // If pre-approved, use the bssid from the request.
1147             networkToConnect.BSSID = network.BSSID;
1148         } else {
1149             // If not pre-approved, find the best bssid matching the request.
1150             networkToConnect.BSSID =
1151                     findBestBssidFromActiveMatchedScanResultsForNetwork(
1152                             ScanResultMatchInfo.fromWifiConfiguration(networkToConnect));
1153         }
1154         networkToConnect.ephemeral = true;
1155         // Mark it user private to avoid conflicting with any saved networks the user might have.
1156         // TODO (b/142035508): Use a more generic mechanism to fix this.
1157         networkToConnect.shared = false;
1158         networkToConnect.fromWifiNetworkSpecifier = true;
1159 
1160         // TODO(b/188021807): Implement the band request from the specifier on the network to
1161         // connect.
1162 
1163         // Store the user selected network.
1164         mUserSelectedNetwork = networkToConnect;
1165 
1166         // Request a new CMM for the connection processing.
1167         if (mVerboseLoggingEnabled) {
1168             Log.v(TAG,
1169                     "Requesting new ClientModeManager instance - didUserSeeUi = " + didUserSeeUi);
1170         }
1171         mShouldHaveInternetCapabilities = false;
1172         ClientModeManagerRequestListener listener = new ClientModeManagerRequestListener();
1173         if (mWifiPermissionsUtil.checkEnterCarModePrioritized(mActiveSpecificNetworkRequest
1174                 .getRequestorUid())) {
1175             mShouldHaveInternetCapabilities = hasNetworkForInternet(mUserSelectedNetwork);
1176             if (mShouldHaveInternetCapabilities) {
1177                 listener.onAnswer(mActiveModeWarden.getPrimaryClientModeManager());
1178                 return;
1179             }
1180         }
1181         WorkSource ws = new WorkSource(mActiveSpecificNetworkRequest.getRequestorUid(),
1182                 mActiveSpecificNetworkRequest.getRequestorPackageName());
1183         mActiveModeWarden.requestLocalOnlyClientModeManager(new ClientModeManagerRequestListener(),
1184                 ws, networkToConnect.SSID, networkToConnect.BSSID, didUserSeeUi);
1185     }
1186 
hasNetworkForInternet(WifiConfiguration network)1187     private boolean hasNetworkForInternet(WifiConfiguration network) {
1188         List<WifiConfiguration> networks = mWifiConfigManager.getConfiguredNetworksWithPasswords();
1189         return networks.stream().anyMatch(a -> Objects.equals(a.SSID, network.SSID)
1190                 && !WifiConfigurationUtil.hasCredentialChanged(a, network)
1191                 && !a.fromWifiNetworkSpecifier
1192                 && !a.noInternetAccessExpected);
1193     }
1194 
handleConnectToNetworkUserSelection(WifiConfiguration network, boolean didUserSeeUi)1195     private void handleConnectToNetworkUserSelection(WifiConfiguration network,
1196             boolean didUserSeeUi) {
1197         Log.d(TAG, "User initiated connect to network: " + network.SSID);
1198 
1199         // Cancel the ongoing scans after user selection.
1200         cancelPeriodicScans();
1201         mIsPeriodicScanEnabled = false;
1202 
1203         // Trigger connection attempts.
1204         handleConnectToNetworkUserSelectionInternal(network, didUserSeeUi);
1205 
1206         // Add the network to the approved access point map for the app.
1207         addNetworkToUserApprovedAccessPointMap(mUserSelectedNetwork);
1208     }
1209 
handleRejectUserSelection()1210     private void handleRejectUserSelection() {
1211         Log.w(TAG, "User dismissed notification, cancelling " + mActiveSpecificNetworkRequest);
1212         teardownForActiveRequest();
1213         mWifiMetrics.incrementNetworkRequestApiNumUserReject();
1214     }
1215 
isUserSelectedNetwork(WifiConfiguration config, String bssid)1216     private boolean isUserSelectedNetwork(WifiConfiguration config, String bssid) {
1217         if (!TextUtils.equals(mUserSelectedNetwork.SSID, config.SSID)) {
1218             return false;
1219         }
1220         if (!Objects.equals(
1221                 mUserSelectedNetwork.allowedKeyManagement, config.allowedKeyManagement)) {
1222             return false;
1223         }
1224         if (!TextUtils.equals(mUserSelectedNetwork.BSSID, bssid)) {
1225             return false;
1226         }
1227         return true;
1228     }
1229 
1230     /**
1231      * Invoked by {@link ClientModeImpl} on end of connection attempt to a network.
1232      */
handleConnectionAttemptEnded( int failureCode, @NonNull WifiConfiguration network, @NonNull String bssid, int failureReason)1233     public void handleConnectionAttemptEnded(
1234             int failureCode, @NonNull WifiConfiguration network, @NonNull String bssid,
1235             int failureReason) {
1236         if (failureCode == WifiMetrics.ConnectionEvent.FAILURE_NONE) {
1237             handleNetworkConnectionSuccess(network, bssid);
1238         } else {
1239             handleNetworkConnectionFailure(network, bssid, failureCode, failureReason);
1240         }
1241     }
1242 
1243     /**
1244      * Invoked by {@link ClientModeImpl} on successful connection to a network.
1245      */
handleNetworkConnectionSuccess(@onNull WifiConfiguration connectedNetwork, @NonNull String connectedBssid)1246     private void handleNetworkConnectionSuccess(@NonNull WifiConfiguration connectedNetwork,
1247             @NonNull String connectedBssid) {
1248         if (mUserSelectedNetwork == null || connectedNetwork == null
1249                 || !mPendingConnectionSuccess) {
1250             return;
1251         }
1252         if (!isUserSelectedNetwork(connectedNetwork, connectedBssid)) {
1253             Log.w(TAG, "Connected to unknown network " + connectedNetwork + ":" + connectedBssid
1254                     + ". Ignoring...");
1255             return;
1256         }
1257         Log.d(TAG, "Connected to network " + mUserSelectedNetwork);
1258 
1259         // transition the request from "active" to "connected".
1260         setupForConnectedRequest(true);
1261     }
1262 
1263     /**
1264      * Invoked by {@link ClientModeImpl} on failure to connect to a network.
1265      */
handleNetworkConnectionFailure(@onNull WifiConfiguration failedNetwork, @NonNull String failedBssid, int failureCode, int failureReason)1266     private void handleNetworkConnectionFailure(@NonNull WifiConfiguration failedNetwork,
1267             @NonNull String failedBssid, int failureCode, int failureReason) {
1268         if (mUserSelectedNetwork == null || failedNetwork == null) {
1269             return;
1270         }
1271         if (!isUserSelectedNetwork(failedNetwork, failedBssid)) {
1272             Log.w(TAG, "Connection failed to unknown network " + failedNetwork + ":" + failedBssid
1273                     + ". Ignoring...");
1274             return;
1275         }
1276 
1277         if (!mPendingConnectionSuccess) {
1278             if (mConnectedSpecificNetworkRequest != null) {
1279                 Log.w(TAG, "Connection is terminated, cancelling "
1280                         + mConnectedSpecificNetworkRequest);
1281                 teardownForConnectedNetwork();
1282             }
1283             return;
1284         }
1285         boolean isCredentialWrong = failureCode == FAILURE_AUTHENTICATION_FAILURE
1286                 && failureReason == AUTH_FAILURE_WRONG_PSWD;
1287         Log.w(TAG, "Failed to connect to network " + mUserSelectedNetwork);
1288         if (!isCredentialWrong && mUserSelectedNetworkConnectRetryCount++
1289                 < USER_SELECTED_NETWORK_CONNECT_RETRY_MAX) {
1290             Log.i(TAG, "Retrying connection attempt, attempt# "
1291                     + mUserSelectedNetworkConnectRetryCount);
1292             connectToNetwork(mUserSelectedNetwork);
1293             return;
1294         }
1295         Log.e(TAG, "Connection failures, cancelling " + mUserSelectedNetwork);
1296         if (mRegisteredCallbacks != null) {
1297             int itemCount = mRegisteredCallbacks.beginBroadcast();
1298             for (int i = 0; i < itemCount; i++) {
1299                 try {
1300                     mRegisteredCallbacks.getBroadcastItem(i).onUserSelectionConnectFailure(
1301                             mUserSelectedNetwork);
1302                 } catch (RemoteException e) {
1303                     Log.e(TAG, "Unable to invoke network request connect failure callback ", e);
1304                 }
1305             }
1306             mRegisteredCallbacks.finishBroadcast();
1307         }
1308         sendConnectionFailureIfAllowed(mActiveSpecificNetworkRequest.getRequestorPackageName(),
1309                 mActiveSpecificNetworkRequest.getRequestorUid(),
1310                 mActiveSpecificNetworkRequestSpecifier, failureCode);
1311         teardownForActiveRequest();
1312     }
1313 
1314     /**
1315      * Invoked by {@link ClientModeImpl} to indicate screen state changes.
1316      */
handleScreenStateChanged(boolean screenOn)1317     private void handleScreenStateChanged(boolean screenOn) {
1318         // If there is no active request or if the user has already selected a network,
1319         // ignore screen state changes.
1320         if (mActiveSpecificNetworkRequest == null || !mIsPeriodicScanEnabled) return;
1321 
1322         // Pause periodic scans when the screen is off & resume when the screen is on.
1323         if (screenOn) {
1324             if (mVerboseLoggingEnabled) Log.v(TAG, "Resuming scans on screen on");
1325             mIsPeriodicScanPaused = false;
1326             startScan();
1327         } else {
1328             if (mVerboseLoggingEnabled) Log.v(TAG, "Pausing scans on screen off");
1329             cancelPeriodicScans();
1330             mIsPeriodicScanPaused = true;
1331         }
1332     }
1333 
1334     // Common helper method for start/end of active request processing.
cleanupActiveRequest()1335     private void cleanupActiveRequest() {
1336         if (mVerboseLoggingEnabled) Log.v(TAG, "cleanupActiveRequest");
1337         // Send the abort to the UI for the current active request.
1338         if (mRegisteredCallbacks != null) {
1339             int itemCount = mRegisteredCallbacks.beginBroadcast();
1340             for (int i = 0; i < itemCount; i++) {
1341                 try {
1342                     mRegisteredCallbacks.getBroadcastItem(i).onAbort();
1343                 } catch (RemoteException e) {
1344                     Log.e(TAG, "Unable to invoke network request abort callback ", e);
1345                 }
1346             }
1347             mRegisteredCallbacks.finishBroadcast();
1348         }
1349         // Force-release the network request to let the app know early that the attempt failed.
1350         if (mActiveSpecificNetworkRequest != null) {
1351             releaseRequestAsUnfulfillableByAnyFactory(mActiveSpecificNetworkRequest);
1352         }
1353         // Cancel periodic scan, connection timeout alarm.
1354         cancelPeriodicScans();
1355         cancelConnectionTimeout();
1356         // Reset the active network request.
1357         mActiveSpecificNetworkRequest = null;
1358         mActiveSpecificNetworkRequestSpecifier = null;
1359         mSkipUserDialogue = false;
1360         mUserSelectedNetworkConnectRetryCount = 0;
1361         mIsPeriodicScanEnabled = false;
1362         mIsPeriodicScanPaused = false;
1363         mActiveMatchedScanResults = null;
1364         mPendingConnectionSuccess = false;
1365         // Remove any callbacks registered for the request.
1366         if (mRegisteredCallbacks != null) mRegisteredCallbacks.kill();
1367         mRegisteredCallbacks = null;
1368     }
1369 
1370     // Invoked at the start of new active request processing.
setupForActiveRequest()1371     private void setupForActiveRequest() {
1372         if (mActiveSpecificNetworkRequest != null) {
1373             cleanupActiveRequest();
1374         }
1375     }
1376 
removeClientModeManagerIfNecessary()1377     private void removeClientModeManagerIfNecessary() {
1378         if (mClientModeManager != null) {
1379             // Set to false anyway, because no network request is active.
1380             mWifiConnectivityManager.setSpecificNetworkRequestInProgress(false);
1381             if (mContext.getResources().getBoolean(R.bool.config_wifiUseHalApiToDisableFwRoaming)) {
1382                 mClientModeManager.enableRoaming(true); // Re-enable roaming.
1383             }
1384             if (mVerboseLoggingEnabled) {
1385                 Log.v(TAG, "removeClientModeManager, role: " + mClientModeManagerRole);
1386             }
1387             mActiveModeWarden.removeClientModeManager(mClientModeManager);
1388             // For every connection attempt, get the appropriate client mode impl to use.
1389             mClientModeManager = null;
1390             mClientModeManagerRole = null;
1391         }
1392     }
1393 
1394     // Invoked at the termination of current active request processing.
teardownForActiveRequest()1395     private void teardownForActiveRequest() {
1396         if (mPendingConnectionSuccess) {
1397             Log.i(TAG, "Disconnecting from network on reset");
1398             disconnectAndRemoveNetworkFromWifiConfigManager(mUserSelectedNetwork);
1399         }
1400         cleanupActiveRequest();
1401         // ensure there is no connected request in progress.
1402         if (mConnectedSpecificNetworkRequest == null) {
1403             removeClientModeManagerIfNecessary();
1404         }
1405     }
1406 
1407     // Invoked at the start of new connected request processing.
setupForConnectedRequest(boolean newConnection)1408     private void setupForConnectedRequest(boolean newConnection) {
1409         if (mRegisteredCallbacks != null) {
1410             int itemCount = mRegisteredCallbacks.beginBroadcast();
1411             for (int i = 0; i < itemCount; i++) {
1412                 try {
1413                     mRegisteredCallbacks.getBroadcastItem(i).onUserSelectionConnectSuccess(
1414                             mUserSelectedNetwork);
1415                 } catch (RemoteException e) {
1416                     Log.e(TAG, "Unable to invoke network request connect failure callback ", e);
1417                 }
1418             }
1419             mRegisteredCallbacks.finishBroadcast();
1420         }
1421         if (newConnection) {
1422             mConnectedSpecificNetworkRequest = mActiveSpecificNetworkRequest;
1423             mConnectedSpecificNetworkRequestSpecifier = mActiveSpecificNetworkRequestSpecifier;
1424             mConnectedUids.clear();
1425         }
1426         if (mActiveSpecificNetworkRequest.getRequestorUid() == 0) {
1427             // For shell test call from root
1428             mConnectedUids.add(Process.SYSTEM_UID);
1429         } else {
1430             mConnectedUids.add(mActiveSpecificNetworkRequest.getRequestorUid());
1431         }
1432         mActiveSpecificNetworkRequest = null;
1433         mActiveSpecificNetworkRequestSpecifier = null;
1434         mSkipUserDialogue = false;
1435         mActiveMatchedScanResults = null;
1436         mPendingConnectionSuccess = false;
1437         if (!newConnection) {
1438             mClientModeManager.updateCapabilities();
1439             return;
1440         }
1441         // Cancel connection timeout alarm.
1442         cancelConnectionTimeout();
1443 
1444         mConnectionStartTimeMillis = mClock.getElapsedSinceBootMillis();
1445         if (mClientModeManagerRole == ROLE_CLIENT_PRIMARY) {
1446             mWifiMetrics.incrementNetworkRequestApiNumConnectSuccessOnPrimaryIface();
1447         } else {
1448             mWifiMetrics.incrementNetworkRequestApiNumConnectSuccessOnSecondaryIface();
1449             // secondary STA being used, register CMI listener for concurrent connection metrics
1450             // collection.
1451             mCmiListener = new CmiListener();
1452             mClientModeImplMonitor.registerListener(mCmiListener);
1453         }
1454         // Disable roaming.
1455         if (mContext.getResources().getBoolean(R.bool.config_wifiUseHalApiToDisableFwRoaming)) {
1456             // Note: This is an old HAL API, but since it wasn't being exercised before, we are
1457             // being extra cautious and only using it on devices running >= S.
1458             if (!mClientModeManager.enableRoaming(false)) {
1459                 Log.w(TAG, "Failed to disable roaming");
1460             }
1461         }
1462     }
1463 
1464     // Invoked at the termination of current connected request processing.
teardownForConnectedNetwork()1465     private void teardownForConnectedNetwork() {
1466         Log.i(TAG, "Disconnecting from network on reset");
1467         disconnectAndRemoveNetworkFromWifiConfigManager(mUserSelectedNetwork);
1468         mConnectedSpecificNetworkRequest = null;
1469         mConnectedSpecificNetworkRequestSpecifier = null;
1470         mConnectedUids.clear();
1471 
1472         if (mConnectionStartTimeMillis != -1) {
1473             int connectionDurationSec = toIntExact(TimeUnit.MILLISECONDS.toSeconds(
1474                     mClock.getElapsedSinceBootMillis() - mConnectionStartTimeMillis));
1475             if (mClientModeManagerRole == ROLE_CLIENT_PRIMARY) {
1476                 mWifiMetrics.incrementNetworkRequestApiConnectionDurationSecOnPrimaryIfaceHistogram(
1477                         connectionDurationSec);
1478 
1479             } else {
1480                 mWifiMetrics
1481                         .incrementNetworkRequestApiConnectionDurationSecOnSecondaryIfaceHistogram(
1482                                 connectionDurationSec);
1483             }
1484             mConnectionStartTimeMillis = -1L;
1485         }
1486         if (mCmiListener != null) {
1487             mCmiListener.checkForConcurrencyEndAndIncrementMetrics();
1488             mClientModeImplMonitor.unregisterListener(mCmiListener);
1489             mCmiListener = null;
1490         }
1491         // ensure there is no active request in progress.
1492         if (mActiveSpecificNetworkRequest == null) {
1493             removeClientModeManagerIfNecessary();
1494         }
1495     }
1496 
1497     /**
1498      * Helper method to populate WifiScanner handle. This is done lazily because
1499      * WifiScanningService is started after WifiService.
1500      */
retrieveWifiScanner()1501     private void retrieveWifiScanner() {
1502         if (mWifiScanner != null) return;
1503         mWifiScanner = mWifiInjector.getWifiScanner();
1504         checkNotNull(mWifiScanner);
1505     }
1506 
handleClientModeManagerRetrieval()1507     private void handleClientModeManagerRetrieval() {
1508         if (mVerboseLoggingEnabled) {
1509             Log.v(TAG, "ClientModeManager retrieved: " + mClientModeManager);
1510         }
1511         if (mUserSelectedNetwork == null) {
1512             Log.e(TAG, "No user selected network to connect to. Ignoring ClientModeManager"
1513                     + "retrieval..");
1514             return;
1515         }
1516         // TODO(230795804): remove the car mode check when we can smooth switch the ownership of the
1517         //  network and attribute to the right App with correct package name.
1518         if (SdkLevel.isAtLeastS() && ActiveModeWarden
1519                 .isClientModeManagerConnectedOrConnectingToBssid(mClientModeManager,
1520                 mUserSelectedNetwork.SSID, mUserSelectedNetwork.BSSID)
1521                 && mConnectedSpecificNetworkRequest != null
1522                 && !WifiConfigurationUtil.hasCredentialChanged(
1523                         mConnectedSpecificNetworkRequestSpecifier.wifiConfiguration,
1524                 mActiveSpecificNetworkRequestSpecifier.wifiConfiguration)
1525                 && !mWifiPermissionsUtil.checkEnterCarModePrioritized(
1526                         mActiveSpecificNetworkRequest.getRequestorUid())) {
1527             // Already connected to the same network.
1528             setupForConnectedRequest(false);
1529             return;
1530         }
1531 
1532         // If using primary STA, disable Auto-join so that NetworkFactory can take control of the
1533         // network connection.
1534         if (mClientModeManagerRole == ROLE_CLIENT_PRIMARY) {
1535             mWifiConnectivityManager.setSpecificNetworkRequestInProgress(true);
1536         }
1537 
1538         // Disconnect from the current network before issuing a new connect request.
1539         disconnectAndRemoveNetworkFromWifiConfigManager(mUserSelectedNetwork);
1540 
1541         // Trigger connection to the network.
1542         connectToNetwork(mUserSelectedNetwork);
1543         // Triggered connection to network, now wait for the connection status.
1544         mPendingConnectionSuccess = true;
1545     }
1546 
handleClientModeManagerRemovalOrFailure()1547     private void handleClientModeManagerRemovalOrFailure() {
1548         if (mActiveSpecificNetworkRequest != null) {
1549             Log.w(TAG, "ClientModeManager retrieval failed or removed, cancelling "
1550                     + mActiveSpecificNetworkRequest);
1551             teardownForActiveRequest();
1552         }
1553         if (mConnectedSpecificNetworkRequest != null) {
1554             Log.w(TAG, "ClientModeManager retrieval failed or removed, cancelling "
1555                     + mConnectedSpecificNetworkRequest);
1556             teardownForConnectedNetwork();
1557         }
1558     }
1559 
startPeriodicScans()1560     private void startPeriodicScans() {
1561         if (mActiveSpecificNetworkRequestSpecifier == null) {
1562             Log.e(TAG, "Periodic scan triggered when there is no active network request. "
1563                     + "Ignoring...");
1564             return;
1565         }
1566         WifiNetworkSpecifier wns = mActiveSpecificNetworkRequestSpecifier;
1567         WifiConfiguration wifiConfiguration = wns.wifiConfiguration;
1568         if (wifiConfiguration.hiddenSSID) {
1569             // Can't search for SSID pattern in hidden networks.
1570             mScanSettings.hiddenNetworks.clear();
1571             mScanSettings.hiddenNetworks.add(new WifiScanner.ScanSettings.HiddenNetwork(
1572                     addEnclosingQuotes(wns.ssidPatternMatcher.getPath())));
1573         }
1574         int[] channelFreqs = wns.getPreferredChannelFrequenciesMhz();
1575         if (channelFreqs.length > 0) {
1576             int index = 0;
1577             mScanSettings.channels = new WifiScanner.ChannelSpec[channelFreqs.length];
1578             for (int freq : channelFreqs) {
1579                 mScanSettings.channels[index++] = new WifiScanner.ChannelSpec(freq);
1580             }
1581             mScanSettings.band = WIFI_BAND_UNSPECIFIED;
1582         }
1583         mIsPeriodicScanEnabled = true;
1584         startScan();
1585         // Clear the channel settings to perform a full band scan.
1586         mScanSettings.channels = new WifiScanner.ChannelSpec[0];
1587         mScanSettings.band = WIFI_BAND_ALL;
1588     }
1589 
cancelPeriodicScans()1590     private void cancelPeriodicScans() {
1591         if (mPeriodicScanTimerSet) {
1592             mAlarmManager.cancel(mPeriodicScanTimerListener);
1593             mPeriodicScanTimerSet = false;
1594         }
1595         // Clear the hidden networks field after each request.
1596         mScanSettings.hiddenNetworks.clear();
1597     }
1598 
scheduleNextPeriodicScan()1599     private void scheduleNextPeriodicScan() {
1600         if (mIsPeriodicScanPaused) {
1601             Log.e(TAG, "Scan triggered when periodic scanning paused. Ignoring...");
1602             return;
1603         }
1604         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1605                 mClock.getElapsedSinceBootMillis() + PERIODIC_SCAN_INTERVAL_MS,
1606                 TAG, mPeriodicScanTimerListener, mHandler);
1607         mPeriodicScanTimerSet = true;
1608     }
1609 
startScan()1610     private void startScan() {
1611         if (mActiveSpecificNetworkRequestSpecifier == null) {
1612             Log.e(TAG, "Scan triggered when there is no active network request. Ignoring...");
1613             return;
1614         }
1615         if (!mIsPeriodicScanEnabled) {
1616             Log.e(TAG, "Scan triggered after user selected network. Ignoring...");
1617             return;
1618         }
1619         if (mVerboseLoggingEnabled) {
1620             Log.v(TAG, "Starting the next scan for " + mActiveSpecificNetworkRequestSpecifier);
1621         }
1622         // Create a worksource using the caller's UID.
1623         WorkSource workSource = new WorkSource(mActiveSpecificNetworkRequest.getRequestorUid());
1624         mWifiScanner.startScan(
1625                 mScanSettings, new HandlerExecutor(mHandler), mScanListener, workSource);
1626     }
1627 
doesScanResultMatchWifiNetworkSpecifier( WifiNetworkSpecifier wns, ScanResult scanResult)1628     private boolean doesScanResultMatchWifiNetworkSpecifier(
1629             WifiNetworkSpecifier wns, ScanResult scanResult) {
1630         if (!wns.ssidPatternMatcher.match(scanResult.SSID)) {
1631             return false;
1632         }
1633         MacAddress bssid = MacAddress.fromString(scanResult.BSSID);
1634         MacAddress matchBaseAddress = wns.bssidPatternMatcher.first;
1635         MacAddress matchMask = wns.bssidPatternMatcher.second;
1636         if (!bssid.matches(matchBaseAddress, matchMask)) {
1637             return false;
1638         }
1639         ScanResultMatchInfo fromScanResult = ScanResultMatchInfo.fromScanResult(scanResult);
1640         ScanResultMatchInfo fromWifiConfiguration =
1641                 ScanResultMatchInfo.fromWifiConfiguration(wns.wifiConfiguration);
1642         return fromScanResult.networkTypeEquals(fromWifiConfiguration);
1643     }
1644 
1645     // Loops through the scan results and finds scan results matching the active network
1646     // request.
getNetworksMatchingActiveNetworkRequest( ScanResult[] scanResults)1647     private List<ScanResult> getNetworksMatchingActiveNetworkRequest(
1648             ScanResult[] scanResults) {
1649         if (mActiveSpecificNetworkRequestSpecifier == null) {
1650             Log.e(TAG, "Scan results received with no active network request. Ignoring...");
1651             return new ArrayList<>();
1652         }
1653         List<ScanResult> matchedScanResults = new ArrayList<>();
1654         WifiNetworkSpecifier wns = mActiveSpecificNetworkRequestSpecifier;
1655 
1656         for (ScanResult scanResult : scanResults) {
1657             if (doesScanResultMatchWifiNetworkSpecifier(wns, scanResult)) {
1658                 matchedScanResults.add(scanResult);
1659             }
1660         }
1661         if (mVerboseLoggingEnabled) {
1662             Log.v(TAG, "List of scan results matching the active request "
1663                     + matchedScanResults);
1664         }
1665         return matchedScanResults;
1666     }
1667 
sendNetworkRequestMatchCallbacksForActiveRequest( @onNull Collection<ScanResult> matchedScanResults)1668     private void sendNetworkRequestMatchCallbacksForActiveRequest(
1669             @NonNull Collection<ScanResult> matchedScanResults) {
1670         if (matchedScanResults.isEmpty()) return;
1671         if (mRegisteredCallbacks == null
1672                 || mRegisteredCallbacks.getRegisteredCallbackCount() == 0) {
1673             Log.e(TAG, "No callback registered for sending network request matches. "
1674                     + "Ignoring...");
1675             return;
1676         }
1677         int itemCount = mRegisteredCallbacks.beginBroadcast();
1678         for (int i = 0; i < itemCount; i++) {
1679             try {
1680                 mRegisteredCallbacks.getBroadcastItem(i).onMatch(
1681                         new ArrayList<>(matchedScanResults));
1682             } catch (RemoteException e) {
1683                 Log.e(TAG, "Unable to invoke network request match callback ", e);
1684             }
1685         }
1686         mRegisteredCallbacks.finishBroadcast();
1687     }
1688 
cancelConnectionTimeout()1689     private void cancelConnectionTimeout() {
1690         if (mConnectionTimeoutSet) {
1691             mAlarmManager.cancel(mConnectionTimeoutAlarmListener);
1692             mConnectionTimeoutSet = false;
1693         }
1694     }
1695 
scheduleConnectionTimeout()1696     private void scheduleConnectionTimeout() {
1697         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1698                 mClock.getElapsedSinceBootMillis() + NETWORK_CONNECTION_TIMEOUT_MS,
1699                 TAG, mConnectionTimeoutAlarmListener, mHandler);
1700         mConnectionTimeoutSet = true;
1701     }
1702 
getAppName(@onNull String packageName, int uid)1703     private @NonNull CharSequence getAppName(@NonNull String packageName, int uid) {
1704         ApplicationInfo applicationInfo = null;
1705         try {
1706             applicationInfo = mContext.getPackageManager().getApplicationInfoAsUser(
1707                     packageName, 0, UserHandle.getUserHandleForUid(uid));
1708         } catch (PackageManager.NameNotFoundException e) {
1709             Log.e(TAG, "Failed to find app name for " + packageName);
1710             return "";
1711         }
1712         CharSequence appName = mContext.getPackageManager().getApplicationLabel(applicationInfo);
1713         return (appName != null) ? appName : "";
1714     }
1715 
startUi()1716     private void startUi() {
1717         Intent intent = new Intent();
1718         intent.setAction(UI_START_INTENT_ACTION);
1719         intent.addCategory(UI_START_INTENT_CATEGORY);
1720         intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
1721         intent.putExtra(UI_START_INTENT_EXTRA_APP_NAME,
1722                 getAppName(mActiveSpecificNetworkRequest.getRequestorPackageName(),
1723                         mActiveSpecificNetworkRequest.getRequestorUid()));
1724         intent.putExtra(UI_START_INTENT_EXTRA_REQUEST_IS_FOR_SINGLE_NETWORK,
1725                 isActiveRequestForSingleNetwork());
1726         mContext.startActivityAsUser(intent, UserHandle.getUserHandleForUid(
1727                 mActiveSpecificNetworkRequest.getRequestorUid()));
1728     }
1729 
1730     // Helper method to determine if the specifier does not contain any patterns and matches
1731     // a single access point.
isActiveRequestForSingleAccessPoint()1732     private boolean isActiveRequestForSingleAccessPoint() {
1733         if (mActiveSpecificNetworkRequestSpecifier == null) return false;
1734 
1735         if (mActiveSpecificNetworkRequestSpecifier.ssidPatternMatcher.getType()
1736                 != PatternMatcher.PATTERN_LITERAL) {
1737             return false;
1738         }
1739         if (!Objects.equals(
1740                 mActiveSpecificNetworkRequestSpecifier.bssidPatternMatcher.second,
1741                 MacAddress.BROADCAST_ADDRESS)) {
1742             return false;
1743         }
1744         return true;
1745     }
1746 
1747     // Helper method to determine if the specifier does not contain any patterns and matches
1748     // a single network.
isActiveRequestForSingleNetwork()1749     private boolean isActiveRequestForSingleNetwork() {
1750         if (mActiveSpecificNetworkRequestSpecifier == null) return false;
1751 
1752         if (mActiveSpecificNetworkRequestSpecifier.ssidPatternMatcher.getType()
1753                 == PatternMatcher.PATTERN_LITERAL) {
1754             return true;
1755         }
1756         if (Objects.equals(
1757                 mActiveSpecificNetworkRequestSpecifier.bssidPatternMatcher.second,
1758                 MacAddress.BROADCAST_ADDRESS)) {
1759             return true;
1760         }
1761         return false;
1762     }
1763 
1764     // Will return the best bssid to use for the current request's connection.
1765     //
1766     // Note: This will never return null, unless there is some internal error.
1767     // For ex:
1768     // i) The latest scan results were empty.
1769     // ii) The latest scan result did not contain any BSSID for the SSID user chose.
findBestBssidFromActiveMatchedScanResultsForNetwork( @onNull ScanResultMatchInfo scanResultMatchInfo)1770     private @Nullable String findBestBssidFromActiveMatchedScanResultsForNetwork(
1771             @NonNull ScanResultMatchInfo scanResultMatchInfo) {
1772         if (mActiveSpecificNetworkRequestSpecifier == null
1773                 || mActiveMatchedScanResults == null) return null;
1774         ScanResult selectedScanResult = mActiveMatchedScanResults
1775                 .values()
1776                 .stream()
1777                 .filter(scanResult -> Objects.equals(
1778                         ScanResultMatchInfo.fromScanResult(scanResult),
1779                         scanResultMatchInfo))
1780                 .max(Comparator.comparing(scanResult -> scanResult.level))
1781                 .orElse(null);
1782         if (selectedScanResult == null) { // Should never happen.
1783             Log.wtf(TAG, "Expected to find at least one matching scan result");
1784             return null;
1785         }
1786         if (mVerboseLoggingEnabled) {
1787             Log.v(TAG, "Best bssid selected for the request " + selectedScanResult);
1788         }
1789         return selectedScanResult.BSSID;
1790     }
1791 
isAccessPointApprovedInInternalApprovalList( @onNull String ssid, @NonNull MacAddress bssid, @SecurityType int networkType, @NonNull String requestorPackageName)1792     private boolean isAccessPointApprovedInInternalApprovalList(
1793             @NonNull String ssid, @NonNull MacAddress bssid, @SecurityType int networkType,
1794             @NonNull String requestorPackageName) {
1795         Set<AccessPoint> approvedAccessPoints =
1796                 mUserApprovedAccessPointMap.get(requestorPackageName);
1797         if (approvedAccessPoints == null) return false;
1798         AccessPoint accessPoint =
1799                 new AccessPoint(ssid, bssid, networkType);
1800         if (approvedAccessPoints.contains(accessPoint)) {
1801             // keep the most recently used AP in the end
1802             approvedAccessPoints.remove(accessPoint);
1803             approvedAccessPoints.add(accessPoint);
1804             if (mVerboseLoggingEnabled) {
1805                 Log.v(TAG, "Found " + bssid
1806                         + " in internal user approved access point for " + requestorPackageName);
1807             }
1808             return true;
1809         }
1810         // AP does not match, but check if SSID + security type match
1811         if (networkType == WifiConfiguration.SECURITY_TYPE_OPEN
1812                 || networkType == WifiConfiguration.SECURITY_TYPE_OWE) {
1813             // require exact BSSID match for open networks
1814             return false;
1815         }
1816         // Only require SSID and SecurityType match for non-open networks.
1817         if (approvedAccessPoints.stream()
1818                 .anyMatch((ap) -> ap.ssid.equals(ssid) && ap.networkType == networkType)) {
1819             if (mVerboseLoggingEnabled) {
1820                 Log.v(TAG, "Found SSID=" + ssid
1821                         + " in internal user approved access point for " + requestorPackageName);
1822             }
1823             return true;
1824         }
1825         return false;
1826     }
1827 
isAccessPointApprovedInCompanionDeviceManager( @onNull MacAddress bssid, @NonNull UserHandle requestorUserHandle, @NonNull String requestorPackageName)1828     private boolean isAccessPointApprovedInCompanionDeviceManager(
1829             @NonNull MacAddress bssid,
1830             @NonNull UserHandle requestorUserHandle,
1831             @NonNull String requestorPackageName) {
1832         if (mCompanionDeviceManager == null) {
1833             mCompanionDeviceManager = mContext.getSystemService(CompanionDeviceManager.class);
1834         }
1835         boolean approved = mCompanionDeviceManager.isDeviceAssociatedForWifiConnection(
1836                 requestorPackageName, bssid, requestorUserHandle);
1837         if (!approved) return false;
1838         if (mVerboseLoggingEnabled) {
1839             Log.v(TAG, "Found " + bssid
1840                     + " in CompanionDeviceManager approved access point for "
1841                     + requestorPackageName);
1842         }
1843         return true;
1844     }
1845 
isAccessPointApprovedForActiveRequest(@onNull String ssid, @NonNull MacAddress bssid, @SecurityType int networkType, boolean revokeNormalBypass)1846     private boolean isAccessPointApprovedForActiveRequest(@NonNull String ssid,
1847             @NonNull MacAddress bssid, @SecurityType int networkType, boolean revokeNormalBypass) {
1848         String requestorPackageName = mActiveSpecificNetworkRequest.getRequestorPackageName();
1849         UserHandle requestorUserHandle =
1850                 UserHandle.getUserHandleForUid(mActiveSpecificNetworkRequest.getRequestorUid());
1851         // Check if access point is approved via CompanionDeviceManager first.
1852         if (isAccessPointApprovedInCompanionDeviceManager(
1853                 bssid, requestorUserHandle, requestorPackageName)) {
1854             return true;
1855         }
1856         // Check if access point is approved in internal approval list next.
1857         if (!revokeNormalBypass && isAccessPointApprovedInInternalApprovalList(
1858                 ssid, bssid, networkType, requestorPackageName)) {
1859             return true;
1860         }
1861         // Shell approved app
1862         if (TextUtils.equals(mApprovedApp, requestorPackageName)) {
1863             return true;
1864         }
1865         // no bypass approvals, show UI.
1866         return false;
1867     }
1868 
1869 
1870     // Helper method to store the all the BSSIDs matching the network from the matched scan results
addNetworkToUserApprovedAccessPointMap(@onNull WifiConfiguration network)1871     private void addNetworkToUserApprovedAccessPointMap(@NonNull WifiConfiguration network) {
1872         if (mActiveSpecificNetworkRequestSpecifier == null
1873                 || mActiveMatchedScanResults == null) return;
1874         // Note: This hopefully is a list of size 1, because we want to store a 1:1 mapping
1875         // from user selection and the AP that was approved. But, since we get a WifiConfiguration
1876         // object representing an entire network from UI, we need to ensure that all the visible
1877         // BSSIDs matching the original request and the selected network are stored.
1878         Set<AccessPoint> newUserApprovedAccessPoints = new HashSet<>();
1879 
1880         ScanResultMatchInfo fromWifiConfiguration =
1881                 ScanResultMatchInfo.fromWifiConfiguration(network);
1882         for (ScanResult scanResult : mActiveMatchedScanResults.values()) {
1883             ScanResultMatchInfo fromScanResult = ScanResultMatchInfo.fromScanResult(scanResult);
1884             SecurityParams params = fromScanResult.matchForNetworkSelection(fromWifiConfiguration);
1885             if (null != params) {
1886                 AccessPoint approvedAccessPoint =
1887                         new AccessPoint(scanResult.SSID, MacAddress.fromString(scanResult.BSSID),
1888                                 params.getSecurityType());
1889                 newUserApprovedAccessPoints.add(approvedAccessPoint);
1890             }
1891         }
1892         if (newUserApprovedAccessPoints.isEmpty()) return;
1893 
1894         String requestorPackageName = mActiveSpecificNetworkRequest.getRequestorPackageName();
1895         LinkedHashSet<AccessPoint> approvedAccessPoints =
1896                 mUserApprovedAccessPointMap.get(requestorPackageName);
1897         if (approvedAccessPoints == null) {
1898             approvedAccessPoints = new LinkedHashSet<>();
1899             mUserApprovedAccessPointMap.put(requestorPackageName, approvedAccessPoints);
1900             // Note the new app in metrics.
1901             mWifiMetrics.incrementNetworkRequestApiNumApps();
1902         }
1903         if (mVerboseLoggingEnabled) {
1904             Log.v(TAG, "Adding " + newUserApprovedAccessPoints
1905                     + " to user approved access point for " + requestorPackageName);
1906         }
1907         // keep the most recently added APs in the end
1908         approvedAccessPoints.removeAll(newUserApprovedAccessPoints);
1909         approvedAccessPoints.addAll(newUserApprovedAccessPoints);
1910         cleanUpLRUAccessPoints(approvedAccessPoints);
1911         saveToStore();
1912     }
1913 
1914     /**
1915      * 1) If the request is for a single bssid, check if the matching ScanResult was pre-approved
1916      * by the user.
1917      * 2) If yes to (b), trigger a connect immediately and returns true. Else, returns false.
1918      *
1919      * @return true if a pre-approved network was found for connection, false otherwise.
1920      */
triggerConnectIfUserApprovedMatchFound(boolean revokeNormalBypass)1921     private boolean triggerConnectIfUserApprovedMatchFound(boolean revokeNormalBypass) {
1922         if (mActiveSpecificNetworkRequestSpecifier == null) return false;
1923         boolean requestForSingleAccessPoint = isActiveRequestForSingleAccessPoint();
1924         if (!requestForSingleAccessPoint && !isActiveRequestForSingleNetwork()) {
1925             Log.i(TAG, "ActiveRequest not for single access point or network.");
1926             return false;
1927         }
1928 
1929         String ssid = mActiveSpecificNetworkRequestSpecifier.ssidPatternMatcher.getPath();
1930         MacAddress bssid = mActiveSpecificNetworkRequestSpecifier.bssidPatternMatcher.first;
1931         SecurityParams params =
1932                 ScanResultMatchInfo.fromWifiConfiguration(
1933                         mActiveSpecificNetworkRequestSpecifier.wifiConfiguration)
1934                                 .getFirstAvailableSecurityParams();
1935         if (null == params) return false;
1936         int networkType = params.getSecurityType();
1937 
1938         if (!isAccessPointApprovedForActiveRequest(ssid, bssid, networkType, revokeNormalBypass)
1939                 || mWifiConfigManager.isNetworkTemporarilyDisabledByUser(
1940                 ScanResultUtil.createQuotedSsid(ssid))) {
1941             if (mVerboseLoggingEnabled) {
1942                 Log.v(TAG, "No approved access point found");
1943             }
1944             return false;
1945         }
1946         if (requestForSingleAccessPoint) {
1947             Log.v(TAG, "Approved access point found in matching scan results. "
1948                     + "Triggering connect " + ssid + "/" + bssid);
1949             // Request is for a single AP which is already approved. Connect directly.
1950             WifiConfiguration config = mActiveSpecificNetworkRequestSpecifier.wifiConfiguration;
1951             config.SSID = "\"" + ssid + "\"";
1952             config.BSSID = bssid.toString();
1953             handleConnectToNetworkUserSelectionInternal(config, false);
1954             mWifiMetrics.incrementNetworkRequestApiNumUserApprovalBypass();
1955             return true;
1956         }
1957         // request is for a single network (but not a particular AP) that's already approved.
1958         // Scanning is still needed to select the best BSSID, but allow skipping the UI.
1959         Log.v(TAG, "Approved network found. Allowing user dialogue to get bypassed.");
1960         mSkipUserDialogue = true;
1961         return false;
1962     }
1963 
1964     /**
1965      * Handle scan results
1966      *
1967      * @param scanResults Array of {@link ScanResult} to be processed.
1968      */
handleScanResults(ScanResult[] scanResults)1969     private void handleScanResults(ScanResult[] scanResults) {
1970         List<ScanResult> matchedScanResults =
1971                 getNetworksMatchingActiveNetworkRequest(scanResults);
1972         if ((mActiveMatchedScanResults == null || mActiveMatchedScanResults.isEmpty())
1973                 && !matchedScanResults.isEmpty()) {
1974             // only note the first match size in metrics (chances of this changing in further
1975             // scans is pretty low)
1976             mWifiMetrics.incrementNetworkRequestApiMatchSizeHistogram(
1977                     matchedScanResults.size());
1978         }
1979         // First set of scan results for this request.
1980         if (mActiveMatchedScanResults == null) mActiveMatchedScanResults = new HashMap<>();
1981         // Coalesce the new set of scan results with previous scan results received for request.
1982         mActiveMatchedScanResults.putAll(matchedScanResults
1983                 .stream()
1984                 .collect(Collectors.toMap(
1985                         scanResult -> scanResult.BSSID, scanResult -> scanResult, (a, b) -> a)));
1986         // Weed out any stale cached scan results.
1987         long currentTimeInMillis = mClock.getElapsedSinceBootMillis();
1988         mActiveMatchedScanResults.entrySet().removeIf(
1989                 e -> ((currentTimeInMillis - (e.getValue().timestamp / 1000))
1990                         >= CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS));
1991         if (!mActiveMatchedScanResults.isEmpty() && mSkipUserDialogue) {
1992             WifiConfiguration config = mActiveSpecificNetworkRequestSpecifier.wifiConfiguration;
1993             config.SSID = "\""
1994                     + mActiveSpecificNetworkRequestSpecifier.ssidPatternMatcher.getPath() + "\"";
1995             config.BSSID = findBestBssidFromActiveMatchedScanResultsForNetwork(
1996                     ScanResultMatchInfo.fromWifiConfiguration(config));
1997             Log.v(TAG, "Bypassing user dialog for connection to SSID="
1998                     + config.SSID + ", BSSID=" + config.BSSID);
1999             handleConnectToNetworkUserSelection(config, false);
2000         }
2001     }
2002 
2003     /**
2004      * Retrieve the latest cached scan results from wifi scanner and filter out any
2005      * {@link ScanResult} older than {@link #CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS}.
2006      */
getFilteredCachedScanResults()2007     private @NonNull ScanResult[] getFilteredCachedScanResults() {
2008         List<ScanResult> cachedScanResults = mWifiScanner.getSingleScanResults();
2009         if (cachedScanResults == null || cachedScanResults.isEmpty()) return new ScanResult[0];
2010         long currentTimeInMillis = mClock.getElapsedSinceBootMillis();
2011         return cachedScanResults.stream()
2012                 .filter(scanResult
2013                         -> ((currentTimeInMillis - (scanResult.timestamp / 1000))
2014                         < CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS))
2015                 .toArray(ScanResult[]::new);
2016     }
2017 
2018     /**
2019      * Clean up least recently used Access Points if specified app reach the limit.
2020      */
cleanUpLRUAccessPoints(Set<AccessPoint> approvedAccessPoints)2021     private static void cleanUpLRUAccessPoints(Set<AccessPoint> approvedAccessPoints) {
2022         if (approvedAccessPoints.size() <= NUM_OF_ACCESS_POINT_LIMIT_PER_APP) {
2023             return;
2024         }
2025         Iterator iter = approvedAccessPoints.iterator();
2026         while (iter.hasNext() && approvedAccessPoints.size() > NUM_OF_ACCESS_POINT_LIMIT_PER_APP) {
2027             iter.next();
2028             iter.remove();
2029         }
2030     }
2031 
2032     /**
2033      * Sets all access points approved for the specified app.
2034      * Used by shell commands.
2035      */
setUserApprovedApp(@onNull String packageName, boolean approved)2036     public void setUserApprovedApp(@NonNull String packageName, boolean approved) {
2037         if (approved) {
2038             mApprovedApp = packageName;
2039         } else if (TextUtils.equals(packageName, mApprovedApp)) {
2040             mApprovedApp = null;
2041         }
2042     }
2043 
2044     /**
2045      * Whether all access points are approved for the specified app.
2046      * Used by shell commands.
2047      */
hasUserApprovedApp(@onNull String packageName)2048     public boolean hasUserApprovedApp(@NonNull String packageName) {
2049         return TextUtils.equals(packageName, mApprovedApp);
2050     }
2051 
2052     /**
2053      * Remove all user approved access points and listener for the specified app.
2054      */
removeApp(@onNull String packageName)2055     public void removeApp(@NonNull String packageName) {
2056         if (mUserApprovedAccessPointMap.remove(packageName) != null) {
2057             Log.i(TAG, "Removing all approved access points for " + packageName);
2058         }
2059         RemoteCallbackList<ILocalOnlyConnectionStatusListener> listenerTracker =
2060                 mLocalOnlyStatusListenerPerApp.remove(packageName);
2061         if (listenerTracker != null) listenerTracker.kill();
2062         mFeatureIdPerApp.remove(packageName);
2063         saveToStore();
2064     }
2065 
2066     /**
2067      * Add a listener to get the connection failure of the local-only conncetion
2068      */
addLocalOnlyConnectionStatusListener( @onNull ILocalOnlyConnectionStatusListener listener, String packageName, String featureId)2069     public void addLocalOnlyConnectionStatusListener(
2070             @NonNull ILocalOnlyConnectionStatusListener listener, String packageName,
2071             String featureId) {
2072         RemoteCallbackList<ILocalOnlyConnectionStatusListener> listenersTracker =
2073                 mLocalOnlyStatusListenerPerApp.get(packageName);
2074         if (listenersTracker == null) {
2075             listenersTracker = new RemoteCallbackList<>();
2076         }
2077         listenersTracker.register(listener);
2078         mLocalOnlyStatusListenerPerApp.put(packageName, listenersTracker);
2079         if (!mFeatureIdPerApp.containsKey(packageName)) {
2080             mFeatureIdPerApp.put(packageName, featureId);
2081         }
2082     }
2083 
2084     /**
2085      * Remove a listener which added before
2086      */
removeLocalOnlyConnectionStatusListener( @onNull ILocalOnlyConnectionStatusListener listener, String packageName)2087     public void removeLocalOnlyConnectionStatusListener(
2088             @NonNull ILocalOnlyConnectionStatusListener listener, String packageName) {
2089         RemoteCallbackList<ILocalOnlyConnectionStatusListener> listenersTracker =
2090                 mLocalOnlyStatusListenerPerApp.get(packageName);
2091         if (listenersTracker == null || !listenersTracker.unregister(listener)) {
2092             Log.w(TAG, "removeLocalOnlyConnectionFailureListener: Listener from " + packageName
2093                     + " already unregister.");
2094         }
2095         if (listenersTracker != null && listenersTracker.getRegisteredCallbackCount() == 0) {
2096             mLocalOnlyStatusListenerPerApp.remove(packageName);
2097             mFeatureIdPerApp.remove(packageName);
2098         }
2099     }
2100 
sendConnectionFailureIfAllowed(String packageName, int uid, @NonNull WifiNetworkSpecifier networkSpecifier, int connectionEvent)2101     private void sendConnectionFailureIfAllowed(String packageName,
2102             int uid, @NonNull WifiNetworkSpecifier networkSpecifier, int connectionEvent) {
2103         RemoteCallbackList<ILocalOnlyConnectionStatusListener> listenersTracker =
2104                 mLocalOnlyStatusListenerPerApp.get(packageName);
2105         if (listenersTracker == null || listenersTracker.getRegisteredCallbackCount() == 0) {
2106             return;
2107         }
2108 
2109         if (mVerboseLoggingEnabled) {
2110             Log.v(TAG, "Sending connection failure event to " + packageName);
2111         }
2112         final int n = listenersTracker.beginBroadcast();
2113         for (int i = 0; i < n; i++) {
2114             try {
2115                 listenersTracker.getBroadcastItem(i).onConnectionStatus(networkSpecifier,
2116                         internalConnectionEventToLocalOnlyFailureCode(connectionEvent));
2117             } catch (RemoteException e) {
2118                 Log.e(TAG, "sendNetworkCallback: remote exception -- " + e);
2119             }
2120         }
2121         listenersTracker.finishBroadcast();
2122     }
2123 
2124     private @WifiManager.LocalOnlyConnectionStatusCode int
internalConnectionEventToLocalOnlyFailureCode(int connectionEvent)2125             internalConnectionEventToLocalOnlyFailureCode(int connectionEvent) {
2126         switch (connectionEvent) {
2127             case WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION:
2128             case WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT:
2129                 return WifiManager.STATUS_LOCAL_ONLY_CONNECTION_FAILURE_ASSOCIATION;
2130             case WifiMetrics.ConnectionEvent.FAILURE_SSID_TEMP_DISABLED:
2131             case FAILURE_AUTHENTICATION_FAILURE:
2132                 return WifiManager.STATUS_LOCAL_ONLY_CONNECTION_FAILURE_AUTHENTICATION;
2133             case WifiMetrics.ConnectionEvent.FAILURE_DHCP:
2134                 return WifiManager.STATUS_LOCAL_ONLY_CONNECTION_FAILURE_IP_PROVISIONING;
2135             case WifiMetrics.ConnectionEvent.FAILURE_NETWORK_NOT_FOUND:
2136                 return WifiManager.STATUS_LOCAL_ONLY_CONNECTION_FAILURE_NOT_FOUND;
2137             case WifiMetrics.ConnectionEvent.FAILURE_NO_RESPONSE:
2138                 return WifiManager.STATUS_LOCAL_ONLY_CONNECTION_FAILURE_NO_RESPONSE;
2139             default:
2140                 return WifiManager.STATUS_LOCAL_ONLY_CONNECTION_FAILURE_UNKNOWN;
2141         }
2142     }
2143 
2144     /**
2145      * Clear all internal state (for network settings reset).
2146      */
clear()2147     public void clear() {
2148         mUserApprovedAccessPointMap.clear();
2149         mApprovedApp = null;
2150         for (RemoteCallbackList<ILocalOnlyConnectionStatusListener> listenerTracker
2151                 : mLocalOnlyStatusListenerPerApp.values()) {
2152             listenerTracker.kill();
2153         }
2154         mLocalOnlyStatusListenerPerApp.clear();
2155         mFeatureIdPerApp.clear();
2156         Log.i(TAG, "Cleared all internal state");
2157         saveToStore();
2158     }
2159 }
2160