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