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