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