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