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