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