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