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