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