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