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