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 android.net.wifi.WifiScanner.WIFI_BAND_ALL; 20 import static android.net.wifi.WifiScanner.WIFI_BAND_UNSPECIFIED; 21 22 import static com.android.internal.util.Preconditions.checkNotNull; 23 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_LOCAL_ONLY; 24 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY; 25 import static com.android.server.wifi.WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE; 26 import static com.android.server.wifi.proto.nano.WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_WRONG_PSWD; 27 import static com.android.server.wifi.proto.nano.WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN; 28 import static com.android.server.wifi.util.NativeUtil.addEnclosingQuotes; 29 30 import static java.lang.Math.toIntExact; 31 32 import android.annotation.NonNull; 33 import android.annotation.Nullable; 34 import android.annotation.TargetApi; 35 import android.app.ActivityManager; 36 import android.app.AlarmManager; 37 import android.app.AppOpsManager; 38 import android.companion.CompanionDeviceManager; 39 import android.content.Intent; 40 import android.content.pm.ApplicationInfo; 41 import android.content.pm.PackageManager; 42 import android.net.MacAddress; 43 import android.net.NetworkCapabilities; 44 import android.net.NetworkFactory; 45 import android.net.NetworkRequest; 46 import android.net.NetworkSpecifier; 47 import android.net.wifi.IActionListener; 48 import android.net.wifi.ILocalOnlyConnectionStatusListener; 49 import android.net.wifi.INetworkRequestMatchCallback; 50 import android.net.wifi.INetworkRequestUserSelectionCallback; 51 import android.net.wifi.ScanResult; 52 import android.net.wifi.SecurityParams; 53 import android.net.wifi.WifiConfiguration; 54 import android.net.wifi.WifiConfiguration.SecurityType; 55 import android.net.wifi.WifiContext; 56 import android.net.wifi.WifiManager; 57 import android.net.wifi.WifiNetworkSpecifier; 58 import android.net.wifi.WifiScanner; 59 import android.net.wifi.util.ScanResultUtil; 60 import android.os.Build; 61 import android.os.Handler; 62 import android.os.Looper; 63 import android.os.PatternMatcher; 64 import android.os.Process; 65 import android.os.RemoteCallbackList; 66 import android.os.RemoteException; 67 import android.os.UserHandle; 68 import android.os.WorkSource; 69 import android.text.TextUtils; 70 import android.util.ArraySet; 71 import android.util.Log; 72 import android.util.Pair; 73 74 import com.android.internal.annotations.VisibleForTesting; 75 import com.android.modules.utils.HandlerExecutor; 76 import com.android.modules.utils.build.SdkLevel; 77 import com.android.server.wifi.proto.nano.WifiMetricsProto; 78 import com.android.server.wifi.util.ActionListenerWrapper; 79 import com.android.server.wifi.util.WifiPermissionsUtil; 80 import com.android.wifi.resources.R; 81 82 import java.io.FileDescriptor; 83 import java.io.PrintWriter; 84 import java.util.ArrayList; 85 import java.util.Collection; 86 import java.util.Collections; 87 import java.util.Comparator; 88 import java.util.HashMap; 89 import java.util.HashSet; 90 import java.util.Iterator; 91 import java.util.LinkedHashSet; 92 import java.util.List; 93 import java.util.Map; 94 import java.util.Objects; 95 import java.util.Set; 96 import java.util.concurrent.TimeUnit; 97 import java.util.stream.Collectors; 98 99 /** 100 * Network factory to handle trusted wifi network requests. 101 */ 102 public class WifiNetworkFactory extends NetworkFactory { 103 private static final String TAG = "WifiNetworkFactory"; 104 @VisibleForTesting 105 private static final int SCORE_FILTER = 60; 106 @VisibleForTesting 107 public static final int CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS = 30 * 1000; 108 @VisibleForTesting 109 public static final int PERIODIC_SCAN_INTERVAL_MS = 10 * 1000; // 10 seconds 110 @VisibleForTesting 111 public static final int NETWORK_CONNECTION_TIMEOUT_MS = 30 * 1000; // 30 seconds 112 @VisibleForTesting 113 public static final int USER_SELECTED_NETWORK_CONNECT_RETRY_MAX = 3; // max of 3 retries. 114 @VisibleForTesting 115 public static final int USER_APPROVED_SCAN_RETRY_MAX = 3; // max of 3 retries. 116 @VisibleForTesting 117 public static final String UI_START_INTENT_ACTION = 118 "com.android.settings.wifi.action.NETWORK_REQUEST"; 119 @VisibleForTesting 120 public static final String UI_START_INTENT_CATEGORY = "android.intent.category.DEFAULT"; 121 @VisibleForTesting 122 public static final String UI_START_INTENT_EXTRA_APP_NAME = 123 "com.android.settings.wifi.extra.APP_NAME"; 124 @VisibleForTesting 125 public static final String UI_START_INTENT_EXTRA_REQUEST_IS_FOR_SINGLE_NETWORK = 126 "com.android.settings.wifi.extra.REQUEST_IS_FOR_SINGLE_NETWORK"; 127 // Capacity limit of approved Access Point per App 128 @VisibleForTesting 129 public static final int NUM_OF_ACCESS_POINT_LIMIT_PER_APP = 50; 130 131 private final WifiContext mContext; 132 private final ActivityManager mActivityManager; 133 private final AlarmManager mAlarmManager; 134 private final AppOpsManager mAppOpsManager; 135 private final Clock mClock; 136 private final Handler mHandler; 137 private final WifiInjector mWifiInjector; 138 private final WifiConnectivityManager mWifiConnectivityManager; 139 private final WifiConfigManager mWifiConfigManager; 140 private final WifiConfigStore mWifiConfigStore; 141 private final WifiPermissionsUtil mWifiPermissionsUtil; 142 private final WifiMetrics mWifiMetrics; 143 private final WifiNative mWifiNative; 144 private final ActiveModeWarden mActiveModeWarden; 145 private final WifiScanner.ScanSettings mScanSettings; 146 private final NetworkFactoryScanListener mScanListener; 147 private final PeriodicScanAlarmListener mPeriodicScanTimerListener; 148 private final ConnectionTimeoutAlarmListener mConnectionTimeoutAlarmListener; 149 private final ConnectHelper mConnectHelper; 150 private final ClientModeImplMonitor mClientModeImplMonitor; 151 private final FrameworkFacade mFacade; 152 private final MultiInternetManager mMultiInternetManager; 153 private final NetworkCapabilities mCapabilitiesFilter; 154 private RemoteCallbackList<INetworkRequestMatchCallback> mRegisteredCallbacks; 155 // Store all user approved access points for apps. 156 @VisibleForTesting 157 public final Map<String, LinkedHashSet<AccessPoint>> mUserApprovedAccessPointMap; 158 private WifiScanner mWifiScanner; 159 @Nullable private ClientModeManager mClientModeManager; 160 @Nullable private ActiveModeManager.ClientRole mClientModeManagerRole; 161 private CompanionDeviceManager mCompanionDeviceManager; 162 // Temporary approval set by shell commands. 163 @Nullable private String mApprovedApp = null; 164 165 private int mGenericConnectionReqCount = 0; 166 // Request that is being actively processed. All new requests start out as an "active" request 167 // because we're processing it & handling all the user interactions associated with it. Once we 168 // successfully connect to the network, we transition that request to "connected". 169 @Nullable private NetworkRequest mActiveSpecificNetworkRequest; 170 @Nullable private WifiNetworkSpecifier mActiveSpecificNetworkRequestSpecifier; 171 private boolean mSkipUserDialogue; 172 // Request corresponding to the the network that the device is currently connected to. 173 @Nullable private NetworkRequest mConnectedSpecificNetworkRequest; 174 @Nullable private WifiNetworkSpecifier mConnectedSpecificNetworkRequestSpecifier; 175 @Nullable private WifiConfiguration mUserSelectedNetwork; 176 private boolean mShouldHaveInternetCapabilities = false; 177 private Set<Integer> mConnectedUids = new ArraySet<>(); 178 private int mUserSelectedNetworkConnectRetryCount; 179 private int mUserApprovedScanRetryCount; 180 // Map of bssid to latest scan results for all scan results matching a request. Will be 181 // - null, if there are no active requests. 182 // - empty, if there are no matching scan results received for the active request. 183 @Nullable private Map<String, ScanResult> mActiveMatchedScanResults; 184 /** Connection start time to keep track of connection duration */ 185 private long mConnectionStartTimeMillis = -1L; 186 /** 187 * CMI listener used for concurrent connection metrics collection. 188 * Not used when the connection is on primary STA (i.e not STA + STA). 189 */ 190 @Nullable private CmiListener mCmiListener; 191 // Verbose logging flag. 192 private boolean mVerboseLoggingEnabled = false; 193 private boolean mPeriodicScanTimerSet = false; 194 private boolean mConnectionTimeoutSet = false; 195 private boolean mIsPeriodicScanEnabled = false; 196 private boolean mIsPeriodicScanPaused = false; 197 // We sent a new connection request and are waiting for connection success. 198 private boolean mPendingConnectionSuccess = false; 199 /** 200 * Indicates that we have new data to serialize. 201 */ 202 private boolean mHasNewDataToSerialize = false; 203 204 private final HashMap<String, RemoteCallbackList<ILocalOnlyConnectionStatusListener>> 205 mLocalOnlyStatusListenerPerApp = new HashMap<>(); 206 private final HashMap<String, String> mFeatureIdPerApp = new HashMap<>(); 207 208 /** 209 * Helper class to store an access point that the user previously approved for a specific app. 210 * TODO(b/123014687): Move to a common util class. 211 */ 212 public static class AccessPoint { 213 public final String ssid; 214 public final MacAddress bssid; 215 public final @SecurityType int networkType; 216 AccessPoint(@onNull String ssid, @NonNull MacAddress bssid, @SecurityType int networkType)217 AccessPoint(@NonNull String ssid, @NonNull MacAddress bssid, 218 @SecurityType int networkType) { 219 this.ssid = ssid; 220 this.bssid = bssid; 221 this.networkType = networkType; 222 } 223 224 @Override hashCode()225 public int hashCode() { 226 return Objects.hash(ssid, bssid, networkType); 227 } 228 229 @Override equals(Object obj)230 public boolean equals(Object obj) { 231 if (this == obj) { 232 return true; 233 } 234 if (!(obj instanceof AccessPoint)) { 235 return false; 236 } 237 AccessPoint other = (AccessPoint) obj; 238 return TextUtils.equals(this.ssid, other.ssid) 239 && Objects.equals(this.bssid, other.bssid) 240 && this.networkType == other.networkType; 241 } 242 243 @Override toString()244 public String toString() { 245 StringBuilder sb = new StringBuilder("AccessPoint: "); 246 return sb.append(ssid) 247 .append(", ") 248 .append(bssid) 249 .append(", ") 250 .append(networkType) 251 .toString(); 252 } 253 } 254 255 // Scan listener for scan requests. 256 private class NetworkFactoryScanListener implements WifiScanner.ScanListener { 257 @Override onSuccess()258 public void onSuccess() { 259 // Scan request succeeded, wait for results to report to external clients. 260 if (mVerboseLoggingEnabled) { 261 Log.d(TAG, "Scan request succeeded"); 262 } 263 } 264 265 @Override onFailure(int reason, String description)266 public void onFailure(int reason, String description) { 267 Log.e(TAG, "Scan failure received. reason: " + reason 268 + ", description: " + description); 269 // TODO(b/113878056): Retry scan to workaround any transient scan failures. 270 scheduleNextPeriodicScan(); 271 } 272 273 @Override onResults(WifiScanner.ScanData[] scanDatas)274 public void onResults(WifiScanner.ScanData[] scanDatas) { 275 if (mVerboseLoggingEnabled) { 276 Log.d(TAG, "Scan results received"); 277 } 278 // For single scans, the array size should always be 1. 279 if (scanDatas.length != 1) { 280 Log.wtf(TAG, "Found more than 1 batch of scan results, Ignoring..."); 281 return; 282 } 283 WifiScanner.ScanData scanData = scanDatas[0]; 284 ScanResult[] scanResults = scanData.getResults(); 285 if (mVerboseLoggingEnabled) { 286 Log.v(TAG, "Received " + scanResults.length + " scan results"); 287 } 288 handleScanResults(scanResults); 289 if (!mSkipUserDialogue && mActiveMatchedScanResults != null) { 290 sendNetworkRequestMatchCallbacksForActiveRequest( 291 mActiveMatchedScanResults.values()); 292 } 293 scheduleNextPeriodicScan(); 294 } 295 296 @Override onFullResult(ScanResult fullScanResult)297 public void onFullResult(ScanResult fullScanResult) { 298 // Ignore for single scans. 299 } 300 301 @Override onPeriodChanged(int periodInMs)302 public void onPeriodChanged(int periodInMs) { 303 // Ignore for single scans. 304 } 305 }; 306 307 private class PeriodicScanAlarmListener implements AlarmManager.OnAlarmListener { 308 @Override onAlarm()309 public void onAlarm() { 310 // Trigger the next scan. 311 startScan(); 312 mPeriodicScanTimerSet = false; 313 } 314 } 315 316 private class ConnectionTimeoutAlarmListener implements AlarmManager.OnAlarmListener { 317 @Override onAlarm()318 public void onAlarm() { 319 Log.e(TAG, "Timed-out connecting to network"); 320 if (mUserSelectedNetwork != null) { 321 handleNetworkConnectionFailure(mUserSelectedNetwork, mUserSelectedNetwork.BSSID, 322 WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT, 323 FAILURE_REASON_UNKNOWN); 324 } else { 325 Log.wtf(TAG, "mUserSelectedNetwork is null, when connection time out"); 326 } 327 mConnectionTimeoutSet = false; 328 } 329 } 330 331 // Callback result from settings UI. 332 private class NetworkFactoryUserSelectionCallback extends 333 INetworkRequestUserSelectionCallback.Stub { 334 private final NetworkRequest mNetworkRequest; 335 NetworkFactoryUserSelectionCallback(NetworkRequest networkRequest)336 NetworkFactoryUserSelectionCallback(NetworkRequest networkRequest) { 337 mNetworkRequest = networkRequest; 338 } 339 340 @Override select(WifiConfiguration wifiConfiguration)341 public void select(WifiConfiguration wifiConfiguration) { 342 if (wifiConfiguration == null) { 343 Log.wtf(TAG, "User select null config, seems a settings UI issue"); 344 return; 345 } 346 mHandler.post(() -> { 347 Log.i(TAG, "select configuration " + wifiConfiguration); 348 if (mActiveSpecificNetworkRequest != mNetworkRequest) { 349 Log.e(TAG, "Stale callback select received"); 350 return; 351 } 352 handleConnectToNetworkUserSelection(wifiConfiguration, true); 353 }); 354 } 355 356 @Override reject()357 public void reject() { 358 mHandler.post(() -> { 359 if (mActiveSpecificNetworkRequest != mNetworkRequest) { 360 Log.e(TAG, "Stale callback reject received"); 361 return; 362 } 363 handleRejectUserSelection(); 364 }); 365 } 366 } 367 368 private final class ConnectActionListener extends IActionListener.Stub { 369 @Override onSuccess()370 public void onSuccess() { 371 if (mVerboseLoggingEnabled) { 372 Log.v(TAG, "Triggered network connection"); 373 } 374 } 375 376 @Override onFailure(int reason)377 public void onFailure(int reason) { 378 Log.e(TAG, "Failed to trigger network connection"); 379 if (mUserSelectedNetwork == null) { 380 Log.e(TAG, "mUserSelectedNetwork is null, when connection failure"); 381 return; 382 } 383 handleNetworkConnectionFailure(mUserSelectedNetwork, mUserSelectedNetwork.BSSID, 384 reason, FAILURE_REASON_UNKNOWN); 385 } 386 } 387 388 private final class ClientModeManagerRequestListener implements 389 ActiveModeWarden.ExternalClientModeManagerRequestListener { 390 @Override onAnswer(@ullable ClientModeManager modeManager)391 public void onAnswer(@Nullable ClientModeManager modeManager) { 392 if (modeManager != null) { 393 // Remove the mode manager if the associated request is no longer active. 394 if (mActiveSpecificNetworkRequest == null 395 && mConnectedSpecificNetworkRequest == null) { 396 Log.w(TAG, "Client mode manager request answer received with no active and " 397 + "connected requests, remove the manager"); 398 mActiveModeWarden.removeClientModeManager(modeManager); 399 return; 400 } 401 if (mActiveSpecificNetworkRequest == null) { 402 Log.w(TAG, "Client mode manager request answer received with no active" 403 + " requests, but has connected request. "); 404 if (modeManager != mClientModeManager) { 405 // If clientModeManager changes, teardown the current connection 406 mActiveModeWarden.removeClientModeManager(modeManager); 407 } 408 return; 409 } 410 if (modeManager != mClientModeManager) { 411 // If clientModeManager changes, teardown the current connection 412 removeClientModeManagerIfNecessary(); 413 } 414 mClientModeManager = modeManager; 415 mClientModeManagerRole = modeManager.getRole(); 416 if (mVerboseLoggingEnabled) { 417 Log.v(TAG, "retrieve CMM: " + mClientModeManager.toString()); 418 } 419 handleClientModeManagerRetrieval(); 420 } else { 421 handleClientModeManagerRemovalOrFailure(); 422 } 423 } 424 } 425 426 private class ModeChangeCallback implements ActiveModeWarden.ModeChangeCallback { 427 @Override onActiveModeManagerAdded(@onNull ActiveModeManager activeModeManager)428 public void onActiveModeManagerAdded(@NonNull ActiveModeManager activeModeManager) { 429 // ignored. 430 // Will get a dedicated ClientModeManager instance for our request via 431 // ClientModeManagerRequestListener. 432 } 433 434 @Override onActiveModeManagerRemoved(@onNull ActiveModeManager activeModeManager)435 public void onActiveModeManagerRemoved(@NonNull ActiveModeManager activeModeManager) { 436 if (!(activeModeManager instanceof ClientModeManager)) return; 437 if (mVerboseLoggingEnabled) { 438 Log.v(TAG, "ModeManager removed " + activeModeManager.getInterfaceName()); 439 } 440 // Mode manager removed. Cleanup any ongoing requests. 441 if (activeModeManager == mClientModeManager 442 || !mActiveModeWarden.hasPrimaryClientModeManager()) { 443 handleClientModeManagerRemovalOrFailure(); 444 } 445 } 446 447 @Override onActiveModeManagerRoleChanged(@onNull ActiveModeManager activeModeManager)448 public void onActiveModeManagerRoleChanged(@NonNull ActiveModeManager activeModeManager) { 449 if (!(activeModeManager instanceof ClientModeManager)) return; 450 if (mVerboseLoggingEnabled) { 451 Log.v(TAG, "ModeManager role changed " + activeModeManager.getInterfaceName()); 452 } 453 // Mode manager role changed. Cleanup any ongoing requests. 454 if (activeModeManager == mClientModeManager 455 || !mActiveModeWarden.hasPrimaryClientModeManager()) { 456 handleClientModeManagerRemovalOrFailure(); 457 } 458 } 459 } 460 461 /** 462 * Module to interact with the wifi config store. 463 */ 464 private class NetworkRequestDataSource implements NetworkRequestStoreData.DataSource { 465 @Override toSerialize()466 public Map<String, Set<AccessPoint>> toSerialize() { 467 // Clear the flag after writing to disk. 468 mHasNewDataToSerialize = false; 469 return new HashMap<>(mUserApprovedAccessPointMap); 470 } 471 472 @Override fromDeserialized(Map<String, Set<AccessPoint>> approvedAccessPointMap)473 public void fromDeserialized(Map<String, Set<AccessPoint>> approvedAccessPointMap) { 474 approvedAccessPointMap.forEach((key, value) -> 475 mUserApprovedAccessPointMap.put(key, new LinkedHashSet<>(value))); 476 } 477 478 @Override reset()479 public void reset() { 480 mUserApprovedAccessPointMap.clear(); 481 } 482 483 @Override hasNewDataToSerialize()484 public boolean hasNewDataToSerialize() { 485 return mHasNewDataToSerialize; 486 } 487 } 488 489 /** 490 * To keep track of concurrent connections using this API surface (for metrics collection only). 491 * 492 * Only used if the connection is initiated on secondary STA. 493 */ 494 private class CmiListener implements ClientModeImplListener { 495 /** Concurrent connection start time to keep track of connection duration */ 496 private long mConcurrentConnectionStartTimeMillis = -1L; 497 /** Whether we have already indicated the presence of concurrent connection */ 498 private boolean mHasAlreadyIncrementedConcurrentConnectionCount = false; 499 isLocalOnlyOrPrimary(@onNull ClientModeManager cmm)500 private boolean isLocalOnlyOrPrimary(@NonNull ClientModeManager cmm) { 501 return cmm.getRole() == ROLE_CLIENT_PRIMARY 502 || cmm.getRole() == ROLE_CLIENT_LOCAL_ONLY; 503 } 504 checkForConcurrencyStartAndIncrementMetrics()505 private void checkForConcurrencyStartAndIncrementMetrics() { 506 int numLocalOnlyOrPrimaryConnectedCmms = 0; 507 for (ClientModeManager cmm : mActiveModeWarden.getClientModeManagers()) { 508 if (isLocalOnlyOrPrimary(cmm) && cmm.isConnected()) { 509 numLocalOnlyOrPrimaryConnectedCmms++; 510 } 511 } 512 if (numLocalOnlyOrPrimaryConnectedCmms > 1) { 513 mConcurrentConnectionStartTimeMillis = mClock.getElapsedSinceBootMillis(); 514 // Note: We could have multiple connect/disconnect of the primary connection 515 // while remaining connected to the local only connection. We want to keep track 516 // of the connection durations accurately across those disconnects. However, we do 517 // not want to increment the connection count metric since that should be a 1:1 518 // mapping with the number of requests processed (i.e don't indicate 2 concurrent 519 // connection count if the primary disconnected & connected back while processing 520 // the same local only request). 521 if (!mHasAlreadyIncrementedConcurrentConnectionCount) { 522 mWifiMetrics.incrementNetworkRequestApiNumConcurrentConnection(); 523 mHasAlreadyIncrementedConcurrentConnectionCount = true; 524 } 525 } 526 } 527 checkForConcurrencyEndAndIncrementMetrics()528 public void checkForConcurrencyEndAndIncrementMetrics() { 529 if (mConcurrentConnectionStartTimeMillis != -1L) { 530 mWifiMetrics.incrementNetworkRequestApiConcurrentConnectionDurationSecHistogram( 531 toIntExact(TimeUnit.MILLISECONDS.toSeconds( 532 mClock.getElapsedSinceBootMillis() 533 - mConcurrentConnectionStartTimeMillis))); 534 mConcurrentConnectionStartTimeMillis = -1L; 535 } 536 } 537 CmiListener()538 CmiListener() { 539 checkForConcurrencyStartAndIncrementMetrics(); 540 } 541 542 @Override onL3Connected(@onNull ConcreteClientModeManager clientModeManager)543 public void onL3Connected(@NonNull ConcreteClientModeManager clientModeManager) { 544 if (isLocalOnlyOrPrimary(clientModeManager)) { 545 checkForConcurrencyStartAndIncrementMetrics(); 546 } 547 } 548 549 @Override onConnectionEnd(@onNull ConcreteClientModeManager clientModeManager)550 public void onConnectionEnd(@NonNull ConcreteClientModeManager clientModeManager) { 551 if (isLocalOnlyOrPrimary(clientModeManager)) { 552 checkForConcurrencyEndAndIncrementMetrics(); 553 } 554 } 555 } 556 WifiNetworkFactory(Looper looper, WifiContext context, NetworkCapabilities nc, ActivityManager activityManager, AlarmManager alarmManager, AppOpsManager appOpsManager, Clock clock, WifiInjector wifiInjector, WifiConnectivityManager connectivityManager, WifiConfigManager configManager, WifiConfigStore configStore, WifiPermissionsUtil wifiPermissionsUtil, WifiMetrics wifiMetrics, WifiNative wifiNative, ActiveModeWarden activeModeWarden, ConnectHelper connectHelper, ClientModeImplMonitor clientModeImplMonitor, FrameworkFacade facade, MultiInternetManager multiInternetManager)557 public WifiNetworkFactory(Looper looper, WifiContext context, NetworkCapabilities nc, 558 ActivityManager activityManager, AlarmManager alarmManager, 559 AppOpsManager appOpsManager, 560 Clock clock, WifiInjector wifiInjector, 561 WifiConnectivityManager connectivityManager, 562 WifiConfigManager configManager, 563 WifiConfigStore configStore, 564 WifiPermissionsUtil wifiPermissionsUtil, 565 WifiMetrics wifiMetrics, 566 WifiNative wifiNative, 567 ActiveModeWarden activeModeWarden, 568 ConnectHelper connectHelper, 569 ClientModeImplMonitor clientModeImplMonitor, 570 FrameworkFacade facade, 571 MultiInternetManager multiInternetManager) { 572 super(looper, context, TAG, nc); 573 mContext = context; 574 mActivityManager = activityManager; 575 mAlarmManager = alarmManager; 576 mAppOpsManager = appOpsManager; 577 mClock = clock; 578 mHandler = new Handler(looper); 579 mWifiInjector = wifiInjector; 580 mWifiConnectivityManager = connectivityManager; 581 mWifiConfigManager = configManager; 582 mWifiConfigStore = configStore; 583 mWifiPermissionsUtil = wifiPermissionsUtil; 584 mWifiMetrics = wifiMetrics; 585 mWifiNative = wifiNative; 586 mActiveModeWarden = activeModeWarden; 587 mConnectHelper = connectHelper; 588 mClientModeImplMonitor = clientModeImplMonitor; 589 // Create the scan settings. 590 mScanSettings = new WifiScanner.ScanSettings(); 591 mScanSettings.type = WifiScanner.SCAN_TYPE_HIGH_ACCURACY; 592 mScanSettings.band = WifiScanner.WIFI_BAND_ALL; 593 mScanSettings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 594 mScanListener = new NetworkFactoryScanListener(); 595 mPeriodicScanTimerListener = new PeriodicScanAlarmListener(); 596 mConnectionTimeoutAlarmListener = new ConnectionTimeoutAlarmListener(); 597 mUserApprovedAccessPointMap = new HashMap<>(); 598 mFacade = facade; 599 mMultiInternetManager = multiInternetManager; 600 mCapabilitiesFilter = nc; 601 602 // register the data store for serializing/deserializing data. 603 configStore.registerStoreData( 604 wifiInjector.makeNetworkRequestStoreData(new NetworkRequestDataSource())); 605 606 activeModeWarden.registerModeChangeCallback(new ModeChangeCallback()); 607 608 setScoreFilter(SCORE_FILTER); 609 mWifiInjector 610 .getWifiDeviceStateChangeManager() 611 .registerStateChangeCallback( 612 new WifiDeviceStateChangeManager.StateChangeCallback() { 613 @Override 614 public void onScreenStateChanged(boolean screenOn) { 615 handleScreenStateChanged(screenOn); 616 } 617 }); 618 } 619 620 // package-private 621 @TargetApi(Build.VERSION_CODES.S) updateSubIdsInCapabilitiesFilter(Set<Integer> subIds)622 void updateSubIdsInCapabilitiesFilter(Set<Integer> subIds) { 623 // setSubscriptionIds is only available on Android S+ devices. 624 if (SdkLevel.isAtLeastS()) { 625 NetworkCapabilities newFilter = 626 new NetworkCapabilities.Builder(mCapabilitiesFilter) 627 .setSubscriptionIds(subIds).build(); 628 setCapabilityFilter(newFilter); 629 } 630 } 631 saveToStore()632 private void saveToStore() { 633 // Set the flag to let WifiConfigStore that we have new data to write. 634 mHasNewDataToSerialize = true; 635 if (!mWifiConfigManager.saveToStore()) { 636 Log.w(TAG, "Failed to save to store"); 637 } 638 } 639 640 /** 641 * Enable verbose logging. 642 */ enableVerboseLogging(boolean verbose)643 public void enableVerboseLogging(boolean verbose) { 644 mVerboseLoggingEnabled = verbose; 645 } 646 647 /** 648 * Add a new callback for network request match handling. 649 */ addCallback(INetworkRequestMatchCallback callback)650 public void addCallback(INetworkRequestMatchCallback callback) { 651 if (mActiveSpecificNetworkRequest == null) { 652 Log.wtf(TAG, "No valid network request. Ignoring callback registration"); 653 try { 654 callback.onAbort(); 655 } catch (RemoteException e) { 656 Log.e(TAG, "Unable to invoke network request abort callback " + callback, e); 657 } 658 return; 659 } 660 if (mRegisteredCallbacks == null) { 661 mRegisteredCallbacks = new RemoteCallbackList<>(); 662 } 663 if (!mRegisteredCallbacks.register(callback)) { 664 Log.e(TAG, "Failed to add callback"); 665 return; 666 } 667 if (mVerboseLoggingEnabled) { 668 Log.v(TAG, "Adding callback. Num callbacks: " 669 + mRegisteredCallbacks.getRegisteredCallbackCount()); 670 } 671 // Register our user selection callback. 672 try { 673 callback.onUserSelectionCallbackRegistration( 674 new NetworkFactoryUserSelectionCallback(mActiveSpecificNetworkRequest)); 675 } catch (RemoteException e) { 676 Log.e(TAG, "Unable to invoke user selection registration callback " + callback, e); 677 return; 678 } 679 680 // If we are already in the midst of processing a request, send matching callbacks 681 // immediately on registering the callback. 682 if (mActiveMatchedScanResults != null) { 683 sendNetworkRequestMatchCallbacksForActiveRequest( 684 mActiveMatchedScanResults.values()); 685 } 686 } 687 688 /** 689 * Remove an existing callback for network request match handling. 690 */ removeCallback(INetworkRequestMatchCallback callback)691 public void removeCallback(INetworkRequestMatchCallback callback) { 692 if (mRegisteredCallbacks == null) return; 693 mRegisteredCallbacks.unregister(callback); 694 if (mVerboseLoggingEnabled) { 695 Log.v(TAG, "Removing callback. Num callbacks: " 696 + mRegisteredCallbacks.getRegisteredCallbackCount()); 697 } 698 } 699 canNewRequestOverrideExistingRequest( NetworkRequest newRequest, NetworkRequest existingRequest)700 private boolean canNewRequestOverrideExistingRequest( 701 NetworkRequest newRequest, NetworkRequest existingRequest) { 702 if (existingRequest == null) return true; 703 // Request from app with NETWORK_SETTINGS can override any existing requests. 704 if (mWifiPermissionsUtil.checkNetworkSettingsPermission(newRequest.getRequestorUid())) { 705 return true; 706 } 707 // Request from fg app can override any existing requests. 708 if (mFacade.isRequestFromForegroundApp(mContext, newRequest.getRequestorPackageName())) { 709 return true; 710 } 711 // Request from fg service can override only if the existing request is not from a fg app. 712 if (!mFacade.isRequestFromForegroundApp(mContext, 713 existingRequest.getRequestorPackageName())) { 714 return true; 715 } 716 Log.e(TAG, "Already processing request from a foreground app " 717 + existingRequest.getRequestorPackageName() + ". Rejecting request from " 718 + newRequest.getRequestorPackageName()); 719 return false; 720 } 721 isRequestWithWifiNetworkSpecifierValid(NetworkRequest networkRequest)722 boolean isRequestWithWifiNetworkSpecifierValid(NetworkRequest networkRequest) { 723 WifiNetworkSpecifier wns = (WifiNetworkSpecifier) networkRequest.getNetworkSpecifier(); 724 // Request cannot have internet capability since such a request can never be fulfilled. 725 // (NetworkAgent for connection with WifiNetworkSpecifier will not have internet capability) 726 if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { 727 Log.e(TAG, "Request with wifi network specifier cannot contain " 728 + "NET_CAPABILITY_INTERNET. Rejecting"); 729 return false; 730 } 731 if (networkRequest.getRequestorUid() == Process.INVALID_UID) { 732 Log.e(TAG, "Request with wifi network specifier should contain valid uid. Rejecting"); 733 return false; 734 } 735 if (TextUtils.isEmpty(networkRequest.getRequestorPackageName())) { 736 Log.e(TAG, "Request with wifi network specifier should contain valid package name." 737 + "Rejecting"); 738 return false; 739 } 740 try { 741 mAppOpsManager.checkPackage( 742 networkRequest.getRequestorUid(), networkRequest.getRequestorPackageName()); 743 } catch (SecurityException e) { 744 Log.e(TAG, "Invalid uid/package name " + networkRequest.getRequestorUid() + ", " 745 + networkRequest.getRequestorPackageName() + ". Rejecting", e); 746 return false; 747 } 748 749 if (wns.getBand() != ScanResult.UNSPECIFIED) { 750 Log.e(TAG, "Requesting specific frequency bands is not yet supported. Rejecting"); 751 return false; 752 } 753 if (!WifiConfigurationUtil.validateNetworkSpecifier(wns, mContext.getResources() 754 .getInteger(R.integer.config_wifiNetworkSpecifierMaxPreferredChannels))) { 755 Log.e(TAG, "Invalid wifi network specifier: " + wns + ". Rejecting "); 756 return false; 757 } 758 if (wns.wifiConfiguration.enterpriseConfig != null 759 && wns.wifiConfiguration.enterpriseConfig.isTrustOnFirstUseEnabled()) { 760 Log.e(TAG, "Invalid wifi network specifier with TOFU enabled: " + wns + ". Rejecting "); 761 return false; 762 } 763 return true; 764 } 765 766 /** 767 * Check whether to accept the new network connection request. 768 * 769 * All the validation of the incoming request is done in this method. 770 */ 771 @Override acceptRequest(NetworkRequest networkRequest)772 public boolean acceptRequest(NetworkRequest networkRequest) { 773 NetworkSpecifier ns = networkRequest.getNetworkSpecifier(); 774 boolean isFromSetting = mWifiPermissionsUtil.checkNetworkSettingsPermission( 775 networkRequest.getRequestorUid()); 776 if (ns == null) { 777 // Generic wifi request. Always accept. 778 } else { 779 // Unsupported network specifier. 780 if (!(ns instanceof WifiNetworkSpecifier)) { 781 Log.e(TAG, "Unsupported network specifier: " + ns + ". Rejecting"); 782 return false; 783 } 784 // MultiInternet Request to be handled by MultiInternetWifiNetworkFactory. 785 if (mMultiInternetManager.isStaConcurrencyForMultiInternetEnabled() 786 && MultiInternetWifiNetworkFactory.isWifiMultiInternetRequest(networkRequest, 787 isFromSetting)) { 788 return false; 789 } 790 // Invalid request with wifi network specifier. 791 if (!isRequestWithWifiNetworkSpecifierValid(networkRequest)) { 792 Log.e(TAG, "Invalid network specifier: " + ns + ". Rejecting"); 793 releaseRequestAsUnfulfillableByAnyFactory(networkRequest); 794 return false; 795 } 796 if (mWifiPermissionsUtil.isGuestUser()) { 797 Log.e(TAG, "network specifier from guest user, reject"); 798 releaseRequestAsUnfulfillableByAnyFactory(networkRequest); 799 return false; 800 } 801 if (Objects.equals(mActiveSpecificNetworkRequest, networkRequest) 802 || Objects.equals(mConnectedSpecificNetworkRequest, networkRequest)) { 803 Log.e(TAG, "acceptRequest: Already processing the request " + networkRequest); 804 return true; 805 } 806 // Only allow specific wifi network request from foreground app/service. 807 if (!mWifiPermissionsUtil.checkNetworkSettingsPermission( 808 networkRequest.getRequestorUid()) 809 && !mFacade.isRequestFromForegroundAppOrService(mContext, 810 networkRequest.getRequestorPackageName())) { 811 Log.e(TAG, "Request not from foreground app or service." 812 + " Rejecting request from " + networkRequest.getRequestorPackageName()); 813 releaseRequestAsUnfulfillableByAnyFactory(networkRequest); 814 return false; 815 } 816 // If there is an active request, only proceed if the new request is from a foreground 817 // app. 818 if (!canNewRequestOverrideExistingRequest( 819 networkRequest, mActiveSpecificNetworkRequest)) { 820 Log.e(TAG, "Request cannot override active request." 821 + " Rejecting request from " + networkRequest.getRequestorPackageName()); 822 releaseRequestAsUnfulfillableByAnyFactory(networkRequest); 823 return false; 824 } 825 // If there is a connected request, only proceed if the new request is from a foreground 826 // app. 827 if (!canNewRequestOverrideExistingRequest( 828 networkRequest, mConnectedSpecificNetworkRequest)) { 829 Log.e(TAG, "Request cannot override connected request." 830 + " Rejecting request from " + networkRequest.getRequestorPackageName()); 831 releaseRequestAsUnfulfillableByAnyFactory(networkRequest); 832 return false; 833 } 834 if (mVerboseLoggingEnabled) { 835 Log.v(TAG, "Accepted network request with specifier from fg " 836 + (mFacade.isRequestFromForegroundApp(mContext, 837 networkRequest.getRequestorPackageName()) 838 ? "app" : "service")); 839 } 840 } 841 if (mVerboseLoggingEnabled) { 842 Log.v(TAG, "Accepted network request " + networkRequest); 843 } 844 return true; 845 } 846 847 /** 848 * Handle new network connection requests. 849 * 850 * The assumption here is that {@link #acceptRequest(NetworkRequest)} has already sanitized 851 * the incoming request. 852 */ 853 @Override needNetworkFor(NetworkRequest networkRequest)854 protected void needNetworkFor(NetworkRequest networkRequest) { 855 NetworkSpecifier ns = networkRequest.getNetworkSpecifier(); 856 boolean isFromSetting = mWifiPermissionsUtil.checkNetworkSettingsPermission( 857 networkRequest.getRequestorUid()); 858 if (ns == null) { 859 // Generic wifi request. Turn on auto-join if necessary. 860 if (++mGenericConnectionReqCount == 1) { 861 mWifiConnectivityManager.setTrustedConnectionAllowed(true); 862 } 863 } else { 864 // Unsupported network specifier. 865 if (!(ns instanceof WifiNetworkSpecifier)) { 866 Log.e(TAG, "Unsupported network specifier: " + ns + ". Ignoring"); 867 return; 868 } 869 // MultiInternet Request to be handled by MultiInternetWifiNetworkFactory. 870 if (mMultiInternetManager.isStaConcurrencyForMultiInternetEnabled() 871 && MultiInternetWifiNetworkFactory.isWifiMultiInternetRequest(networkRequest, 872 isFromSetting)) { 873 return; 874 } 875 // Invalid request with wifi network specifier. 876 if (!isRequestWithWifiNetworkSpecifierValid(networkRequest)) { 877 Log.e(TAG, "Invalid network specifier: " + ns + ". Rejecting"); 878 releaseRequestAsUnfulfillableByAnyFactory(networkRequest); 879 return; 880 } 881 if (mWifiPermissionsUtil.isGuestUser()) { 882 Log.e(TAG, "network specifier from guest user, reject"); 883 releaseRequestAsUnfulfillableByAnyFactory(networkRequest); 884 return; 885 } 886 // Wifi-off abort early. 887 if (!mActiveModeWarden.hasPrimaryClientModeManager()) { 888 Log.e(TAG, "Request with wifi network specifier when wifi is off." 889 + "Rejecting"); 890 releaseRequestAsUnfulfillableByAnyFactory(networkRequest); 891 return; 892 } 893 if (Objects.equals(mActiveSpecificNetworkRequest, networkRequest) 894 || Objects.equals(mConnectedSpecificNetworkRequest, networkRequest)) { 895 Log.e(TAG, "needNetworkFor: Already processing the request " + networkRequest); 896 return; 897 } 898 899 retrieveWifiScanner(); 900 // Reset state from any previous request. 901 setupForActiveRequest(); 902 // Store the active network request. 903 mActiveSpecificNetworkRequest = networkRequest; 904 WifiNetworkSpecifier wns = (WifiNetworkSpecifier) ns; 905 mActiveSpecificNetworkRequestSpecifier = new WifiNetworkSpecifier( 906 wns.ssidPatternMatcher, wns.bssidPatternMatcher, wns.getBand(), 907 wns.wifiConfiguration, wns.getPreferredChannelFrequenciesMhz()); 908 mSkipUserDialogue = false; 909 mWifiMetrics.incrementNetworkRequestApiNumRequest(); 910 911 // special case for STA+STA: since we are not allowed to replace the primary STA we 912 // should check if we are able to get an interface for a secondary STA. If not - we 913 // want to escalate and display the dialog to the user EVEN if we have a normal bypass 914 // (normal == user approved before, if the app has full UI bypass we won't override it) 915 boolean revokeNormalBypass = false; 916 if (mContext.getResources().getBoolean( 917 R.bool.config_wifiMultiStaLocalOnlyConcurrencyEnabled) 918 && !mWifiPermissionsUtil.isTargetSdkLessThan( 919 mActiveSpecificNetworkRequest.getRequestorPackageName(), Build.VERSION_CODES.S, 920 mActiveSpecificNetworkRequest.getRequestorUid()) 921 && mClientModeManager == null) { 922 revokeNormalBypass = !mWifiNative.isItPossibleToCreateStaIface( 923 new WorkSource(mActiveSpecificNetworkRequest.getRequestorUid(), 924 mActiveSpecificNetworkRequest.getRequestorPackageName())); 925 } 926 927 ScanResult[] cachedScanResults = getFilteredCachedScanResults(); 928 if (!triggerConnectIfUserApprovedMatchFound(revokeNormalBypass, cachedScanResults)) { 929 // Didn't find an approved match, send the matching results to UI and trigger 930 // periodic scans for finding a network in the request. 931 // Fetch the latest cached scan results to speed up network matching. 932 933 if (mVerboseLoggingEnabled) { 934 Log.v(TAG, "Using cached " + cachedScanResults.length + " scan results"); 935 } 936 handleScanResults(cachedScanResults); 937 // Start UI to let the user grant/disallow this request from the app. 938 if (!mSkipUserDialogue) { 939 startUi(); 940 if (mActiveMatchedScanResults != null) { 941 sendNetworkRequestMatchCallbacksForActiveRequest( 942 mActiveMatchedScanResults.values()); 943 } 944 } 945 mUserApprovedScanRetryCount = 0; 946 startPeriodicScans(); 947 } 948 } 949 } 950 951 @Override releaseNetworkFor(NetworkRequest networkRequest)952 protected void releaseNetworkFor(NetworkRequest networkRequest) { 953 NetworkSpecifier ns = networkRequest.getNetworkSpecifier(); 954 if (ns == null) { 955 // Generic wifi request. Turn off auto-join if necessary. 956 if (mGenericConnectionReqCount == 0) { 957 Log.e(TAG, "No valid network request to release"); 958 return; 959 } 960 if (--mGenericConnectionReqCount == 0) { 961 mWifiConnectivityManager.setTrustedConnectionAllowed(false); 962 } 963 } else { 964 // Unsupported network specifier. 965 if (!(ns instanceof WifiNetworkSpecifier)) { 966 Log.e(TAG, "Unsupported network specifier mentioned. Ignoring"); 967 return; 968 } 969 if (mActiveSpecificNetworkRequest == null && mConnectedSpecificNetworkRequest == null) { 970 Log.e(TAG, "Network release received with no active/connected request." 971 + " Ignoring"); 972 return; 973 } 974 if (Objects.equals(mActiveSpecificNetworkRequest, networkRequest)) { 975 Log.i(TAG, "App released active request, cancelling " 976 + mActiveSpecificNetworkRequest); 977 teardownForActiveRequest(); 978 } else if (Objects.equals(mConnectedSpecificNetworkRequest, networkRequest)) { 979 Log.i(TAG, "App released connected request, cancelling " 980 + mConnectedSpecificNetworkRequest); 981 teardownForConnectedNetwork(); 982 } else { 983 Log.e(TAG, "Network specifier does not match the active/connected request." 984 + " Ignoring"); 985 } 986 } 987 } 988 989 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)990 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 991 super.dump(fd, pw, args); 992 pw.println(TAG + ": mGenericConnectionReqCount " + mGenericConnectionReqCount); 993 pw.println(TAG + ": mActiveSpecificNetworkRequest " + mActiveSpecificNetworkRequest); 994 pw.println(TAG + ": mUserApprovedAccessPointMap " + mUserApprovedAccessPointMap); 995 } 996 997 /** 998 * Check if there is at least one connection request. 999 */ hasConnectionRequests()1000 public boolean hasConnectionRequests() { 1001 return mGenericConnectionReqCount > 0 || mActiveSpecificNetworkRequest != null 1002 || mConnectedSpecificNetworkRequest != null; 1003 } 1004 1005 /** 1006 * Return the uid of the specific network request being processed if connected to the requested 1007 * network. 1008 * 1009 * @param connectedNetwork WifiConfiguration corresponding to the connected network. 1010 * @return Pair of uid & package name of the specific request (if any), else <-1, "">. 1011 */ getSpecificNetworkRequestUidAndPackageName( @onNull WifiConfiguration connectedNetwork, @NonNull String connectedBssid)1012 public Pair<Integer, String> getSpecificNetworkRequestUidAndPackageName( 1013 @NonNull WifiConfiguration connectedNetwork, @NonNull String connectedBssid) { 1014 if (mUserSelectedNetwork == null || connectedNetwork == null) { 1015 return Pair.create(Process.INVALID_UID, ""); 1016 } 1017 if (!isUserSelectedNetwork(connectedNetwork, connectedBssid)) { 1018 Log.w(TAG, "Connected to unknown network " + connectedNetwork + ":" + connectedBssid 1019 + ". Ignoring..."); 1020 return Pair.create(Process.INVALID_UID, ""); 1021 } 1022 if (mConnectedSpecificNetworkRequestSpecifier != null) { 1023 return Pair.create(mConnectedSpecificNetworkRequest.getRequestorUid(), 1024 mConnectedSpecificNetworkRequest.getRequestorPackageName()); 1025 } 1026 if (mActiveSpecificNetworkRequestSpecifier != null) { 1027 return Pair.create(mActiveSpecificNetworkRequest.getRequestorUid(), 1028 mActiveSpecificNetworkRequest.getRequestorPackageName()); 1029 } 1030 return Pair.create(Process.INVALID_UID, ""); 1031 } 1032 1033 /** 1034 * Return the uids of the specific network request being processed if connected to the requested 1035 * network. 1036 * 1037 * @param connectedNetwork WifiConfiguration corresponding to the connected network. 1038 * @return Set of uids which request this network 1039 */ getSpecificNetworkRequestUids( @onNull WifiConfiguration connectedNetwork, @NonNull String connectedBssid)1040 public Set<Integer> getSpecificNetworkRequestUids( 1041 @NonNull WifiConfiguration connectedNetwork, @NonNull String connectedBssid) { 1042 if (mUserSelectedNetwork == null || connectedNetwork == null) { 1043 return Collections.emptySet(); 1044 } 1045 if (!isUserSelectedNetwork(connectedNetwork, connectedBssid)) { 1046 Log.w(TAG, "Connected to unknown network " + connectedNetwork + ":" + connectedBssid 1047 + ". Ignoring..."); 1048 return Collections.emptySet(); 1049 } 1050 if (mConnectedSpecificNetworkRequestSpecifier != null) { 1051 return mConnectedUids; 1052 } 1053 if (mActiveSpecificNetworkRequestSpecifier != null) { 1054 return Set.of(mActiveSpecificNetworkRequest.getRequestorUid()); 1055 } 1056 return Collections.emptySet(); 1057 } 1058 1059 /** 1060 * Return whether if current network request should have the internet capabilities due to a 1061 * same saved/suggestion network is present. 1062 */ shouldHaveInternetCapabilities()1063 public boolean shouldHaveInternetCapabilities() { 1064 return mShouldHaveInternetCapabilities; 1065 } 1066 1067 // Helper method to add the provided network configuration to WifiConfigManager, if it does not 1068 // already exist & return the allocated network ID. This ID will be used in the CONNECT_NETWORK 1069 // request to ClientModeImpl. 1070 // If the network already exists, just return the network ID of the existing network. addNetworkToWifiConfigManager(@onNull WifiConfiguration network)1071 private int addNetworkToWifiConfigManager(@NonNull WifiConfiguration network) { 1072 WifiConfiguration existingSavedNetwork = 1073 mWifiConfigManager.getConfiguredNetwork(network.getProfileKey()); 1074 if (existingSavedNetwork != null) { 1075 if (WifiConfigurationUtil.hasCredentialChanged(existingSavedNetwork, network)) { 1076 // TODO (b/142035508): What if the user has a saved network with different 1077 // credentials? 1078 Log.w(TAG, "Network config already present in config manager, reusing"); 1079 } 1080 return existingSavedNetwork.networkId; 1081 } 1082 NetworkUpdateResult networkUpdateResult = 1083 mWifiConfigManager.addOrUpdateNetwork( 1084 network, mActiveSpecificNetworkRequest.getRequestorUid(), 1085 mActiveSpecificNetworkRequest.getRequestorPackageName(), false); 1086 if (mVerboseLoggingEnabled) { 1087 Log.v(TAG, "Added network to config manager " + networkUpdateResult.getNetworkId()); 1088 } 1089 return networkUpdateResult.getNetworkId(); 1090 } 1091 1092 // Helper method to remove the provided network configuration from WifiConfigManager, if it was 1093 // added by an app's specifier request. disconnectAndRemoveNetworkFromWifiConfigManager( @ullable WifiConfiguration network)1094 private void disconnectAndRemoveNetworkFromWifiConfigManager( 1095 @Nullable WifiConfiguration network) { 1096 // Trigger a disconnect first. 1097 if (mClientModeManager != null) mClientModeManager.disconnect(); 1098 1099 if (network == null) return; 1100 WifiConfiguration wcmNetwork = 1101 mWifiConfigManager.getConfiguredNetwork(network.getProfileKey()); 1102 if (wcmNetwork == null) { 1103 Log.e(TAG, "Network not present in config manager"); 1104 return; 1105 } 1106 // Remove the network if it was added previously by an app's specifier request. 1107 if (wcmNetwork.ephemeral && wcmNetwork.fromWifiNetworkSpecifier) { 1108 boolean success = 1109 mWifiConfigManager.removeNetwork( 1110 wcmNetwork.networkId, wcmNetwork.creatorUid, wcmNetwork.creatorName); 1111 if (!success) { 1112 Log.e(TAG, "Failed to remove network from config manager"); 1113 } else if (mVerboseLoggingEnabled) { 1114 Log.v(TAG, "Removed network from config manager " + wcmNetwork.networkId); 1115 } 1116 } 1117 } 1118 1119 // Helper method to trigger a connection request & schedule a timeout alarm to track the 1120 // connection request. connectToNetwork(@onNull WifiConfiguration network)1121 private void connectToNetwork(@NonNull WifiConfiguration network) { 1122 // Cancel connection timeout alarm for any previous connection attempts. 1123 cancelConnectionTimeout(); 1124 1125 // First add the network to WifiConfigManager and then use the obtained networkId 1126 // in the CONNECT_NETWORK request. 1127 // Note: We don't do any error checks on the networkId because ClientModeImpl will do the 1128 // necessary checks when processing CONNECT_NETWORK. 1129 int networkId = addNetworkToWifiConfigManager(network); 1130 1131 mWifiMetrics.setNominatorForNetwork(networkId, 1132 WifiMetricsProto.ConnectionEvent.NOMINATOR_SPECIFIER); 1133 if (mClientModeManagerRole == ROLE_CLIENT_PRIMARY) { 1134 mWifiMetrics.incrementNetworkRequestApiNumConnectOnPrimaryIface(); 1135 } else { 1136 mWifiMetrics.incrementNetworkRequestApiNumConnectOnSecondaryIface(); 1137 } 1138 1139 // Send the connect request to ClientModeImpl. 1140 // TODO(b/117601161): Refactor this. 1141 ConnectActionListener listener = new ConnectActionListener(); 1142 mConnectHelper.connectToNetwork( 1143 mClientModeManager, 1144 new NetworkUpdateResult(networkId), 1145 new ActionListenerWrapper(listener), 1146 mActiveSpecificNetworkRequest.getRequestorUid(), 1147 mActiveSpecificNetworkRequest.getRequestorPackageName(), null); 1148 1149 // Post an alarm to handle connection timeout. 1150 scheduleConnectionTimeout(); 1151 } 1152 handleConnectToNetworkUserSelectionInternal(WifiConfiguration network, boolean didUserSeeUi)1153 private void handleConnectToNetworkUserSelectionInternal(WifiConfiguration network, 1154 boolean didUserSeeUi) { 1155 // Copy over the credentials from the app's request and then copy the ssid from user 1156 // selection. 1157 WifiConfiguration networkToConnect = 1158 new WifiConfiguration(mActiveSpecificNetworkRequestSpecifier.wifiConfiguration); 1159 networkToConnect.SSID = network.SSID; 1160 // Set the WifiConfiguration.BSSID field to prevent roaming. 1161 if (network.BSSID != null) { 1162 // If pre-approved, use the bssid from the request. 1163 networkToConnect.BSSID = network.BSSID; 1164 } else { 1165 // If not pre-approved, find the best bssid matching the request. 1166 networkToConnect.BSSID = 1167 findBestBssidFromActiveMatchedScanResultsForNetwork( 1168 ScanResultMatchInfo.fromWifiConfiguration(networkToConnect)); 1169 } 1170 networkToConnect.ephemeral = true; 1171 // Mark it user private to avoid conflicting with any saved networks the user might have. 1172 // TODO (b/142035508): Use a more generic mechanism to fix this. 1173 networkToConnect.shared = false; 1174 networkToConnect.fromWifiNetworkSpecifier = true; 1175 1176 // TODO(b/188021807): Implement the band request from the specifier on the network to 1177 // connect. 1178 1179 // Store the user selected network. 1180 mUserSelectedNetwork = networkToConnect; 1181 1182 // Request a new CMM for the connection processing. 1183 if (mVerboseLoggingEnabled) { 1184 Log.v(TAG, 1185 "Requesting new ClientModeManager instance - didUserSeeUi = " + didUserSeeUi); 1186 } 1187 mShouldHaveInternetCapabilities = false; 1188 ClientModeManagerRequestListener listener = new ClientModeManagerRequestListener(); 1189 if (mWifiPermissionsUtil.checkEnterCarModePrioritized(mActiveSpecificNetworkRequest 1190 .getRequestorUid())) { 1191 mShouldHaveInternetCapabilities = hasNetworkForInternet(mUserSelectedNetwork); 1192 if (mShouldHaveInternetCapabilities) { 1193 listener.onAnswer(mActiveModeWarden.getPrimaryClientModeManager()); 1194 return; 1195 } 1196 } 1197 WorkSource ws = new WorkSource(mActiveSpecificNetworkRequest.getRequestorUid(), 1198 mActiveSpecificNetworkRequest.getRequestorPackageName()); 1199 mActiveModeWarden.requestLocalOnlyClientModeManager(new ClientModeManagerRequestListener(), 1200 ws, networkToConnect.SSID, networkToConnect.BSSID, didUserSeeUi); 1201 } 1202 hasNetworkForInternet(WifiConfiguration network)1203 private boolean hasNetworkForInternet(WifiConfiguration network) { 1204 List<WifiConfiguration> networks = mWifiConfigManager.getConfiguredNetworksWithPasswords(); 1205 return networks.stream().anyMatch(a -> Objects.equals(a.SSID, network.SSID) 1206 && !WifiConfigurationUtil.hasCredentialChanged(a, network) 1207 && !a.fromWifiNetworkSpecifier 1208 && !a.noInternetAccessExpected); 1209 } 1210 handleConnectToNetworkUserSelection(WifiConfiguration network, boolean didUserSeeUi)1211 private void handleConnectToNetworkUserSelection(WifiConfiguration network, 1212 boolean didUserSeeUi) { 1213 Log.d(TAG, "User initiated connect to network: " + network.SSID); 1214 1215 // Cancel the ongoing scans after user selection. 1216 cancelPeriodicScans(); 1217 mIsPeriodicScanEnabled = false; 1218 1219 // Trigger connection attempts. 1220 handleConnectToNetworkUserSelectionInternal(network, didUserSeeUi); 1221 1222 // Add the network to the approved access point map for the app. 1223 addNetworkToUserApprovedAccessPointMap(mUserSelectedNetwork); 1224 } 1225 handleRejectUserSelection()1226 private void handleRejectUserSelection() { 1227 Log.w(TAG, "User dismissed notification, cancelling " + mActiveSpecificNetworkRequest); 1228 teardownForActiveRequest(); 1229 mWifiMetrics.incrementNetworkRequestApiNumUserReject(); 1230 } 1231 isUserSelectedNetwork(WifiConfiguration config, String bssid)1232 private boolean isUserSelectedNetwork(WifiConfiguration config, String bssid) { 1233 if (!TextUtils.equals(mUserSelectedNetwork.SSID, config.SSID)) { 1234 return false; 1235 } 1236 if (!Objects.equals( 1237 mUserSelectedNetwork.allowedKeyManagement, config.allowedKeyManagement)) { 1238 return false; 1239 } 1240 if (!TextUtils.equals(mUserSelectedNetwork.BSSID, bssid)) { 1241 return false; 1242 } 1243 return true; 1244 } 1245 1246 /** 1247 * Invoked by {@link ClientModeImpl} on end of connection attempt to a network. 1248 */ handleConnectionAttemptEnded( int failureCode, @NonNull WifiConfiguration network, @NonNull String bssid, int failureReason)1249 public void handleConnectionAttemptEnded( 1250 int failureCode, @NonNull WifiConfiguration network, @NonNull String bssid, 1251 int failureReason) { 1252 if (failureCode == WifiMetrics.ConnectionEvent.FAILURE_NONE) { 1253 handleNetworkConnectionSuccess(network, bssid); 1254 } else { 1255 handleNetworkConnectionFailure(network, bssid, failureCode, failureReason); 1256 } 1257 } 1258 1259 /** 1260 * Invoked by {@link ClientModeImpl} on successful connection to a network. 1261 */ handleNetworkConnectionSuccess(@onNull WifiConfiguration connectedNetwork, @NonNull String connectedBssid)1262 private void handleNetworkConnectionSuccess(@NonNull WifiConfiguration connectedNetwork, 1263 @NonNull String connectedBssid) { 1264 if (mUserSelectedNetwork == null || connectedNetwork == null 1265 || !mPendingConnectionSuccess) { 1266 return; 1267 } 1268 if (!isUserSelectedNetwork(connectedNetwork, connectedBssid)) { 1269 Log.w(TAG, "Connected to unknown network " + connectedNetwork + ":" + connectedBssid 1270 + ". Ignoring..."); 1271 return; 1272 } 1273 Log.d(TAG, "Connected to network " + mUserSelectedNetwork); 1274 1275 // transition the request from "active" to "connected". 1276 setupForConnectedRequest(true); 1277 } 1278 1279 /** 1280 * Invoked by {@link ClientModeImpl} on failure to connect to a network. 1281 */ handleNetworkConnectionFailure(@onNull WifiConfiguration failedNetwork, @NonNull String failedBssid, int failureCode, int failureReason)1282 private void handleNetworkConnectionFailure(@NonNull WifiConfiguration failedNetwork, 1283 @NonNull String failedBssid, int failureCode, int failureReason) { 1284 if (mUserSelectedNetwork == null || failedNetwork == null) { 1285 return; 1286 } 1287 if (!isUserSelectedNetwork(failedNetwork, failedBssid)) { 1288 Log.w(TAG, "Connection failed to unknown network " + failedNetwork + ":" + failedBssid 1289 + ". Ignoring..."); 1290 return; 1291 } 1292 1293 if (!mPendingConnectionSuccess || mActiveSpecificNetworkRequest == null) { 1294 if (mConnectedSpecificNetworkRequest != null) { 1295 Log.w(TAG, "Connection is terminated, cancelling " 1296 + mConnectedSpecificNetworkRequest); 1297 teardownForConnectedNetwork(); 1298 } 1299 return; 1300 } 1301 boolean isCredentialWrong = failureCode == FAILURE_AUTHENTICATION_FAILURE 1302 && failureReason == AUTH_FAILURE_WRONG_PSWD; 1303 Log.w(TAG, "Failed to connect to network " + mUserSelectedNetwork); 1304 if (!isCredentialWrong && mUserSelectedNetworkConnectRetryCount++ 1305 < USER_SELECTED_NETWORK_CONNECT_RETRY_MAX) { 1306 Log.i(TAG, "Retrying connection attempt, attempt# " 1307 + mUserSelectedNetworkConnectRetryCount); 1308 connectToNetwork(mUserSelectedNetwork); 1309 return; 1310 } 1311 Log.e(TAG, "Connection failures, cancelling " + mUserSelectedNetwork); 1312 if (mRegisteredCallbacks != null) { 1313 int itemCount = mRegisteredCallbacks.beginBroadcast(); 1314 for (int i = 0; i < itemCount; i++) { 1315 try { 1316 mRegisteredCallbacks.getBroadcastItem(i).onUserSelectionConnectFailure( 1317 mUserSelectedNetwork); 1318 } catch (RemoteException e) { 1319 Log.e(TAG, "Unable to invoke network request connect failure callback ", e); 1320 } 1321 } 1322 mRegisteredCallbacks.finishBroadcast(); 1323 } 1324 sendConnectionFailureIfAllowed(mActiveSpecificNetworkRequest.getRequestorPackageName(), 1325 mActiveSpecificNetworkRequest.getRequestorUid(), 1326 mActiveSpecificNetworkRequestSpecifier, failureCode); 1327 teardownForActiveRequest(); 1328 } 1329 1330 /** 1331 * Invoked by {@link ClientModeImpl} to indicate screen state changes. 1332 */ handleScreenStateChanged(boolean screenOn)1333 private void handleScreenStateChanged(boolean screenOn) { 1334 // If there is no active request or if the user has already selected a network, 1335 // ignore screen state changes. 1336 if (mActiveSpecificNetworkRequest == null || !mIsPeriodicScanEnabled) return; 1337 1338 // Pause periodic scans when the screen is off & resume when the screen is on. 1339 if (screenOn) { 1340 if (mVerboseLoggingEnabled) Log.v(TAG, "Resuming scans on screen on"); 1341 mIsPeriodicScanPaused = false; 1342 startScan(); 1343 } else { 1344 if (mVerboseLoggingEnabled) Log.v(TAG, "Pausing scans on screen off"); 1345 cancelPeriodicScans(); 1346 mIsPeriodicScanPaused = true; 1347 } 1348 } 1349 1350 // Common helper method for start/end of active request processing. cleanupActiveRequest()1351 private void cleanupActiveRequest() { 1352 if (mVerboseLoggingEnabled) Log.v(TAG, "cleanupActiveRequest"); 1353 // Send the abort to the UI for the current active request. 1354 if (mRegisteredCallbacks != null) { 1355 int itemCount = mRegisteredCallbacks.beginBroadcast(); 1356 for (int i = 0; i < itemCount; i++) { 1357 try { 1358 mRegisteredCallbacks.getBroadcastItem(i).onAbort(); 1359 } catch (RemoteException e) { 1360 Log.e(TAG, "Unable to invoke network request abort callback ", e); 1361 } 1362 } 1363 mRegisteredCallbacks.finishBroadcast(); 1364 } 1365 // Force-release the network request to let the app know early that the attempt failed. 1366 if (mActiveSpecificNetworkRequest != null) { 1367 releaseRequestAsUnfulfillableByAnyFactory(mActiveSpecificNetworkRequest); 1368 } 1369 // Cancel periodic scan, connection timeout alarm. 1370 cancelPeriodicScans(); 1371 cancelConnectionTimeout(); 1372 // Reset the active network request. 1373 mActiveSpecificNetworkRequest = null; 1374 mActiveSpecificNetworkRequestSpecifier = null; 1375 mSkipUserDialogue = false; 1376 mUserSelectedNetworkConnectRetryCount = 0; 1377 mIsPeriodicScanEnabled = false; 1378 mIsPeriodicScanPaused = false; 1379 mActiveMatchedScanResults = null; 1380 mPendingConnectionSuccess = false; 1381 // Remove any callbacks registered for the request. 1382 if (mRegisteredCallbacks != null) mRegisteredCallbacks.kill(); 1383 mRegisteredCallbacks = null; 1384 } 1385 1386 // Invoked at the start of new active request processing. setupForActiveRequest()1387 private void setupForActiveRequest() { 1388 if (mActiveSpecificNetworkRequest != null) { 1389 cleanupActiveRequest(); 1390 } 1391 } 1392 removeClientModeManagerIfNecessary()1393 private void removeClientModeManagerIfNecessary() { 1394 if (mClientModeManager != null) { 1395 // Set to false anyway, because no network request is active. 1396 mWifiConnectivityManager.setSpecificNetworkRequestInProgress(false); 1397 if (mContext.getResources().getBoolean(R.bool.config_wifiUseHalApiToDisableFwRoaming)) { 1398 mClientModeManager.enableRoaming(true); // Re-enable roaming. 1399 } 1400 if (mVerboseLoggingEnabled) { 1401 Log.v(TAG, "removeClientModeManager, role: " + mClientModeManagerRole); 1402 } 1403 mActiveModeWarden.removeClientModeManager(mClientModeManager); 1404 // For every connection attempt, get the appropriate client mode impl to use. 1405 mClientModeManager = null; 1406 mClientModeManagerRole = null; 1407 } 1408 } 1409 1410 // Invoked at the termination of current active request processing. teardownForActiveRequest()1411 private void teardownForActiveRequest() { 1412 if (mPendingConnectionSuccess) { 1413 Log.i(TAG, "Disconnecting from network on reset"); 1414 disconnectAndRemoveNetworkFromWifiConfigManager(mUserSelectedNetwork); 1415 } 1416 cleanupActiveRequest(); 1417 // ensure there is no connected request in progress. 1418 if (mConnectedSpecificNetworkRequest == null) { 1419 removeClientModeManagerIfNecessary(); 1420 } 1421 } 1422 1423 // Invoked at the start of new connected request processing. setupForConnectedRequest(boolean newConnection)1424 private void setupForConnectedRequest(boolean newConnection) { 1425 if (mRegisteredCallbacks != null) { 1426 int itemCount = mRegisteredCallbacks.beginBroadcast(); 1427 for (int i = 0; i < itemCount; i++) { 1428 try { 1429 mRegisteredCallbacks.getBroadcastItem(i).onUserSelectionConnectSuccess( 1430 mUserSelectedNetwork); 1431 } catch (RemoteException e) { 1432 Log.e(TAG, "Unable to invoke network request connect failure callback ", e); 1433 } 1434 } 1435 mRegisteredCallbacks.finishBroadcast(); 1436 } 1437 if (newConnection) { 1438 mConnectedSpecificNetworkRequest = mActiveSpecificNetworkRequest; 1439 mConnectedSpecificNetworkRequestSpecifier = mActiveSpecificNetworkRequestSpecifier; 1440 mConnectedUids.clear(); 1441 } 1442 1443 mConnectedUids.add(mActiveSpecificNetworkRequest.getRequestorUid()); 1444 mActiveSpecificNetworkRequest = null; 1445 mActiveSpecificNetworkRequestSpecifier = null; 1446 mSkipUserDialogue = false; 1447 mActiveMatchedScanResults = null; 1448 mPendingConnectionSuccess = false; 1449 if (!newConnection) { 1450 mClientModeManager.updateCapabilities(); 1451 return; 1452 } 1453 // Cancel connection timeout alarm. 1454 cancelConnectionTimeout(); 1455 1456 mConnectionStartTimeMillis = mClock.getElapsedSinceBootMillis(); 1457 if (mClientModeManagerRole == ROLE_CLIENT_PRIMARY) { 1458 mWifiMetrics.incrementNetworkRequestApiNumConnectSuccessOnPrimaryIface(); 1459 } else { 1460 mWifiMetrics.incrementNetworkRequestApiNumConnectSuccessOnSecondaryIface(); 1461 // secondary STA being used, register CMI listener for concurrent connection metrics 1462 // collection. 1463 mCmiListener = new CmiListener(); 1464 mClientModeImplMonitor.registerListener(mCmiListener); 1465 } 1466 // Disable roaming. 1467 if (mContext.getResources().getBoolean(R.bool.config_wifiUseHalApiToDisableFwRoaming)) { 1468 // Note: This is an old HAL API, but since it wasn't being exercised before, we are 1469 // being extra cautious and only using it on devices running >= S. 1470 if (!mClientModeManager.enableRoaming(false)) { 1471 Log.w(TAG, "Failed to disable roaming"); 1472 } 1473 } 1474 } 1475 1476 // Invoked at the termination of current connected request processing. teardownForConnectedNetwork()1477 private void teardownForConnectedNetwork() { 1478 Log.i(TAG, "Disconnecting from network on reset"); 1479 disconnectAndRemoveNetworkFromWifiConfigManager(mUserSelectedNetwork); 1480 mConnectedSpecificNetworkRequest = null; 1481 mConnectedSpecificNetworkRequestSpecifier = null; 1482 mConnectedUids.clear(); 1483 1484 if (mConnectionStartTimeMillis != -1) { 1485 int connectionDurationSec = toIntExact(TimeUnit.MILLISECONDS.toSeconds( 1486 mClock.getElapsedSinceBootMillis() - mConnectionStartTimeMillis)); 1487 if (mClientModeManagerRole == ROLE_CLIENT_PRIMARY) { 1488 mWifiMetrics.incrementNetworkRequestApiConnectionDurationSecOnPrimaryIfaceHistogram( 1489 connectionDurationSec); 1490 1491 } else { 1492 mWifiMetrics 1493 .incrementNetworkRequestApiConnectionDurationSecOnSecondaryIfaceHistogram( 1494 connectionDurationSec); 1495 } 1496 mConnectionStartTimeMillis = -1L; 1497 } 1498 if (mCmiListener != null) { 1499 mCmiListener.checkForConcurrencyEndAndIncrementMetrics(); 1500 mClientModeImplMonitor.unregisterListener(mCmiListener); 1501 mCmiListener = null; 1502 } 1503 // ensure there is no active request in progress. 1504 if (mActiveSpecificNetworkRequest == null) { 1505 removeClientModeManagerIfNecessary(); 1506 } 1507 } 1508 1509 /** 1510 * Helper method to populate WifiScanner handle. This is done lazily because 1511 * WifiScanningService is started after WifiService. 1512 */ retrieveWifiScanner()1513 private void retrieveWifiScanner() { 1514 if (mWifiScanner != null) return; 1515 mWifiScanner = mWifiInjector.getWifiScanner(); 1516 checkNotNull(mWifiScanner); 1517 } 1518 handleClientModeManagerRetrieval()1519 private void handleClientModeManagerRetrieval() { 1520 if (mVerboseLoggingEnabled) { 1521 Log.v(TAG, "ClientModeManager retrieved: " + mClientModeManager); 1522 } 1523 if (mUserSelectedNetwork == null) { 1524 Log.e(TAG, "No user selected network to connect to. Ignoring ClientModeManager" 1525 + "retrieval.."); 1526 return; 1527 } 1528 // TODO(230795804): remove the car mode check when we can smooth switch the ownership of the 1529 // network and attribute to the right App with correct package name. 1530 if (SdkLevel.isAtLeastS() && ActiveModeWarden 1531 .isClientModeManagerConnectedOrConnectingToBssid(mClientModeManager, 1532 mUserSelectedNetwork.SSID, mUserSelectedNetwork.BSSID) 1533 && mConnectedSpecificNetworkRequest != null 1534 && !WifiConfigurationUtil.hasCredentialChanged( 1535 mConnectedSpecificNetworkRequestSpecifier.wifiConfiguration, 1536 mActiveSpecificNetworkRequestSpecifier.wifiConfiguration) 1537 && !mWifiPermissionsUtil.checkEnterCarModePrioritized( 1538 mActiveSpecificNetworkRequest.getRequestorUid())) { 1539 // Already connected to the same network. 1540 setupForConnectedRequest(false); 1541 return; 1542 } 1543 1544 // If using primary STA, disable Auto-join so that NetworkFactory can take control of the 1545 // network connection. 1546 if (mClientModeManagerRole == ROLE_CLIENT_PRIMARY) { 1547 mWifiConnectivityManager.setSpecificNetworkRequestInProgress(true); 1548 } 1549 1550 // Disconnect from the current network before issuing a new connect request. 1551 disconnectAndRemoveNetworkFromWifiConfigManager(mUserSelectedNetwork); 1552 1553 // Trigger connection to the network. 1554 connectToNetwork(mUserSelectedNetwork); 1555 // Triggered connection to network, now wait for the connection status. 1556 mPendingConnectionSuccess = true; 1557 } 1558 handleClientModeManagerRemovalOrFailure()1559 private void handleClientModeManagerRemovalOrFailure() { 1560 if (mActiveSpecificNetworkRequest != null) { 1561 Log.w(TAG, "ClientModeManager retrieval failed or removed, cancelling " 1562 + mActiveSpecificNetworkRequest); 1563 teardownForActiveRequest(); 1564 } 1565 if (mConnectedSpecificNetworkRequest != null) { 1566 Log.w(TAG, "ClientModeManager retrieval failed or removed, cancelling " 1567 + mConnectedSpecificNetworkRequest); 1568 teardownForConnectedNetwork(); 1569 } 1570 } 1571 startPeriodicScans()1572 private void startPeriodicScans() { 1573 if (mActiveSpecificNetworkRequestSpecifier == null) { 1574 Log.e(TAG, "Periodic scan triggered when there is no active network request. " 1575 + "Ignoring..."); 1576 return; 1577 } 1578 WifiNetworkSpecifier wns = mActiveSpecificNetworkRequestSpecifier; 1579 WifiConfiguration wifiConfiguration = wns.wifiConfiguration; 1580 if (wifiConfiguration.hiddenSSID) { 1581 // Can't search for SSID pattern in hidden networks. 1582 mScanSettings.hiddenNetworks.clear(); 1583 mScanSettings.hiddenNetworks.add(new WifiScanner.ScanSettings.HiddenNetwork( 1584 addEnclosingQuotes(wns.ssidPatternMatcher.getPath()))); 1585 } 1586 int[] channelFreqs = wns.getPreferredChannelFrequenciesMhz(); 1587 if (channelFreqs.length > 0) { 1588 int index = 0; 1589 mScanSettings.channels = new WifiScanner.ChannelSpec[channelFreqs.length]; 1590 for (int freq : channelFreqs) { 1591 mScanSettings.channels[index++] = new WifiScanner.ChannelSpec(freq); 1592 } 1593 mScanSettings.band = WIFI_BAND_UNSPECIFIED; 1594 } 1595 mIsPeriodicScanEnabled = true; 1596 startScan(); 1597 // Clear the channel settings to perform a full band scan. 1598 mScanSettings.channels = new WifiScanner.ChannelSpec[0]; 1599 mScanSettings.band = WIFI_BAND_ALL; 1600 } 1601 cancelPeriodicScans()1602 private void cancelPeriodicScans() { 1603 if (mPeriodicScanTimerSet) { 1604 mAlarmManager.cancel(mPeriodicScanTimerListener); 1605 mPeriodicScanTimerSet = false; 1606 } 1607 // Clear the hidden networks field after each request. 1608 mScanSettings.hiddenNetworks.clear(); 1609 } 1610 scheduleNextPeriodicScan()1611 private void scheduleNextPeriodicScan() { 1612 if (mIsPeriodicScanPaused) { 1613 Log.e(TAG, "Scan triggered when periodic scanning paused. Ignoring..."); 1614 return; 1615 } 1616 if (mVerboseLoggingEnabled) { 1617 Log.v(TAG, "mUserSelectedScanRetryCount: " + mUserApprovedScanRetryCount); 1618 } 1619 if (mSkipUserDialogue && mUserApprovedScanRetryCount >= USER_APPROVED_SCAN_RETRY_MAX) { 1620 cleanupActiveRequest(); 1621 return; 1622 } 1623 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1624 mClock.getElapsedSinceBootMillis() + PERIODIC_SCAN_INTERVAL_MS, 1625 TAG, mPeriodicScanTimerListener, mHandler); 1626 mPeriodicScanTimerSet = true; 1627 } 1628 startScan()1629 private void startScan() { 1630 if (mActiveSpecificNetworkRequestSpecifier == null) { 1631 Log.e(TAG, "Scan triggered when there is no active network request. Ignoring..."); 1632 return; 1633 } 1634 if (!mIsPeriodicScanEnabled) { 1635 Log.e(TAG, "Scan triggered after user selected network. Ignoring..."); 1636 return; 1637 } 1638 if (mVerboseLoggingEnabled) { 1639 Log.v(TAG, "Starting the next scan for " + mActiveSpecificNetworkRequestSpecifier); 1640 } 1641 mUserApprovedScanRetryCount++; 1642 // Create a worksource using the caller's UID. 1643 WorkSource workSource = new WorkSource(mActiveSpecificNetworkRequest.getRequestorUid()); 1644 mWifiScanner.startScan( 1645 mScanSettings, new HandlerExecutor(mHandler), mScanListener, workSource); 1646 } 1647 doesScanResultMatchWifiNetworkSpecifier( WifiNetworkSpecifier wns, ScanResult scanResult)1648 private boolean doesScanResultMatchWifiNetworkSpecifier( 1649 WifiNetworkSpecifier wns, ScanResult scanResult) { 1650 if (!wns.ssidPatternMatcher.match(scanResult.SSID)) { 1651 return false; 1652 } 1653 MacAddress bssid = MacAddress.fromString(scanResult.BSSID); 1654 MacAddress matchBaseAddress = wns.bssidPatternMatcher.first; 1655 MacAddress matchMask = wns.bssidPatternMatcher.second; 1656 if (!bssid.matches(matchBaseAddress, matchMask)) { 1657 return false; 1658 } 1659 ScanResultMatchInfo fromScanResult = ScanResultMatchInfo.fromScanResult(scanResult); 1660 ScanResultMatchInfo fromWifiConfiguration = 1661 ScanResultMatchInfo.fromWifiConfiguration(wns.wifiConfiguration); 1662 return fromScanResult.networkTypeEquals(fromWifiConfiguration); 1663 } 1664 1665 // Loops through the scan results and finds scan results matching the active network 1666 // request. getNetworksMatchingActiveNetworkRequest( ScanResult[] scanResults)1667 private List<ScanResult> getNetworksMatchingActiveNetworkRequest( 1668 ScanResult[] scanResults) { 1669 if (mActiveSpecificNetworkRequestSpecifier == null) { 1670 Log.e(TAG, "Scan results received with no active network request. Ignoring..."); 1671 return Collections.emptyList(); 1672 } 1673 List<ScanResult> matchedScanResults = new ArrayList<>(); 1674 WifiNetworkSpecifier wns = mActiveSpecificNetworkRequestSpecifier; 1675 1676 for (ScanResult scanResult : scanResults) { 1677 if (doesScanResultMatchWifiNetworkSpecifier(wns, scanResult)) { 1678 matchedScanResults.add(scanResult); 1679 } 1680 } 1681 if (mVerboseLoggingEnabled) { 1682 Log.v(TAG, "List of scan results matching the active request " 1683 + matchedScanResults); 1684 } 1685 return matchedScanResults; 1686 } 1687 sendNetworkRequestMatchCallbacksForActiveRequest( @onNull Collection<ScanResult> matchedScanResults)1688 private void sendNetworkRequestMatchCallbacksForActiveRequest( 1689 @NonNull Collection<ScanResult> matchedScanResults) { 1690 if (matchedScanResults.isEmpty()) return; 1691 if (mRegisteredCallbacks == null 1692 || mRegisteredCallbacks.getRegisteredCallbackCount() == 0) { 1693 Log.e(TAG, "No callback registered for sending network request matches. " 1694 + "Ignoring..."); 1695 return; 1696 } 1697 int itemCount = mRegisteredCallbacks.beginBroadcast(); 1698 for (int i = 0; i < itemCount; i++) { 1699 try { 1700 mRegisteredCallbacks.getBroadcastItem(i).onMatch( 1701 new ArrayList<>(matchedScanResults)); 1702 } catch (RemoteException e) { 1703 Log.e(TAG, "Unable to invoke network request match callback ", e); 1704 } 1705 } 1706 mRegisteredCallbacks.finishBroadcast(); 1707 } 1708 cancelConnectionTimeout()1709 private void cancelConnectionTimeout() { 1710 if (mConnectionTimeoutSet) { 1711 mAlarmManager.cancel(mConnectionTimeoutAlarmListener); 1712 mConnectionTimeoutSet = false; 1713 } 1714 } 1715 scheduleConnectionTimeout()1716 private void scheduleConnectionTimeout() { 1717 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1718 mClock.getElapsedSinceBootMillis() + NETWORK_CONNECTION_TIMEOUT_MS, 1719 TAG, mConnectionTimeoutAlarmListener, mHandler); 1720 mConnectionTimeoutSet = true; 1721 } 1722 getAppName(@onNull String packageName, int uid)1723 private @NonNull CharSequence getAppName(@NonNull String packageName, int uid) { 1724 ApplicationInfo applicationInfo = null; 1725 try { 1726 applicationInfo = mContext.getPackageManager().getApplicationInfoAsUser( 1727 packageName, 0, UserHandle.getUserHandleForUid(uid)); 1728 } catch (PackageManager.NameNotFoundException e) { 1729 Log.e(TAG, "Failed to find app name for " + packageName); 1730 return ""; 1731 } 1732 CharSequence appName = mContext.getPackageManager().getApplicationLabel(applicationInfo); 1733 return (appName != null) ? appName : ""; 1734 } 1735 startUi()1736 private void startUi() { 1737 Intent intent = new Intent(); 1738 intent.setAction(UI_START_INTENT_ACTION); 1739 intent.addCategory(UI_START_INTENT_CATEGORY); 1740 intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); 1741 intent.putExtra(UI_START_INTENT_EXTRA_APP_NAME, 1742 getAppName(mActiveSpecificNetworkRequest.getRequestorPackageName(), 1743 mActiveSpecificNetworkRequest.getRequestorUid())); 1744 intent.putExtra(UI_START_INTENT_EXTRA_REQUEST_IS_FOR_SINGLE_NETWORK, 1745 isActiveRequestForSingleNetwork()); 1746 mContext.startActivityAsUser(intent, UserHandle.CURRENT); 1747 } 1748 1749 // Helper method to determine if the specifier does not contain any patterns and matches 1750 // a single access point. isActiveRequestForSingleAccessPoint()1751 private boolean isActiveRequestForSingleAccessPoint() { 1752 if (mActiveSpecificNetworkRequestSpecifier == null) return false; 1753 1754 if (mActiveSpecificNetworkRequestSpecifier.ssidPatternMatcher.getType() 1755 != PatternMatcher.PATTERN_LITERAL) { 1756 return false; 1757 } 1758 if (!Objects.equals( 1759 mActiveSpecificNetworkRequestSpecifier.bssidPatternMatcher.second, 1760 MacAddress.BROADCAST_ADDRESS)) { 1761 return false; 1762 } 1763 return true; 1764 } 1765 1766 // Helper method to determine if the specifier does not contain any patterns and matches 1767 // a single network. isActiveRequestForSingleNetwork()1768 private boolean isActiveRequestForSingleNetwork() { 1769 if (mActiveSpecificNetworkRequestSpecifier == null) return false; 1770 1771 if (mActiveSpecificNetworkRequestSpecifier.ssidPatternMatcher.getType() 1772 == PatternMatcher.PATTERN_LITERAL) { 1773 return true; 1774 } 1775 if (Objects.equals( 1776 mActiveSpecificNetworkRequestSpecifier.bssidPatternMatcher.second, 1777 MacAddress.BROADCAST_ADDRESS)) { 1778 return true; 1779 } 1780 return false; 1781 } 1782 1783 // Will return the best bssid to use for the current request's connection. 1784 // 1785 // Note: This will never return null, unless there is some internal error. 1786 // For ex: 1787 // i) The latest scan results were empty. 1788 // ii) The latest scan result did not contain any BSSID for the SSID user chose. findBestBssidFromActiveMatchedScanResultsForNetwork( @onNull ScanResultMatchInfo scanResultMatchInfo)1789 private @Nullable String findBestBssidFromActiveMatchedScanResultsForNetwork( 1790 @NonNull ScanResultMatchInfo scanResultMatchInfo) { 1791 if (mActiveSpecificNetworkRequestSpecifier == null 1792 || mActiveMatchedScanResults == null) return null; 1793 ScanResult selectedScanResult = mActiveMatchedScanResults 1794 .values() 1795 .stream() 1796 .filter(scanResult -> Objects.equals( 1797 ScanResultMatchInfo.fromScanResult(scanResult), 1798 scanResultMatchInfo)) 1799 .max(Comparator.comparing(scanResult -> scanResult.level)) 1800 .orElse(null); 1801 if (selectedScanResult == null) { // Should never happen. 1802 Log.wtf(TAG, "Expected to find at least one matching scan result"); 1803 return null; 1804 } 1805 if (mVerboseLoggingEnabled) { 1806 Log.v(TAG, "Best bssid selected for the request " + selectedScanResult); 1807 } 1808 return selectedScanResult.BSSID; 1809 } 1810 isAccessPointApprovedInInternalApprovalList( @onNull String ssid, @NonNull MacAddress bssid, @SecurityType int networkType, @NonNull String requestorPackageName)1811 private boolean isAccessPointApprovedInInternalApprovalList( 1812 @NonNull String ssid, @NonNull MacAddress bssid, @SecurityType int networkType, 1813 @NonNull String requestorPackageName) { 1814 Set<AccessPoint> approvedAccessPoints = 1815 mUserApprovedAccessPointMap.get(requestorPackageName); 1816 if (approvedAccessPoints == null) return false; 1817 AccessPoint accessPoint = 1818 new AccessPoint(ssid, bssid, networkType); 1819 if (approvedAccessPoints.contains(accessPoint)) { 1820 // keep the most recently used AP in the end 1821 approvedAccessPoints.remove(accessPoint); 1822 approvedAccessPoints.add(accessPoint); 1823 if (mVerboseLoggingEnabled) { 1824 Log.v(TAG, "Found " + bssid 1825 + " in internal user approved access point for " + requestorPackageName); 1826 } 1827 return true; 1828 } 1829 // AP does not match, but check if SSID + security type match 1830 if (networkType == WifiConfiguration.SECURITY_TYPE_OPEN 1831 || networkType == WifiConfiguration.SECURITY_TYPE_OWE) { 1832 // require exact BSSID match for open networks 1833 return false; 1834 } 1835 // Only require SSID and SecurityType match for non-open networks. 1836 if (approvedAccessPoints.stream() 1837 .anyMatch((ap) -> ap.ssid.equals(ssid) && ap.networkType == networkType)) { 1838 if (mVerboseLoggingEnabled) { 1839 Log.v(TAG, "Found SSID=" + ssid 1840 + " in internal user approved access point for " + requestorPackageName); 1841 } 1842 return true; 1843 } 1844 return false; 1845 } 1846 isAccessPointApprovedInCompanionDeviceManager( @onNull MacAddress bssid, @NonNull UserHandle requestorUserHandle, @NonNull String requestorPackageName)1847 private boolean isAccessPointApprovedInCompanionDeviceManager( 1848 @NonNull MacAddress bssid, 1849 @NonNull UserHandle requestorUserHandle, 1850 @NonNull String requestorPackageName) { 1851 if (mCompanionDeviceManager == null) { 1852 mCompanionDeviceManager = mContext.getSystemService(CompanionDeviceManager.class); 1853 } 1854 boolean approved = mCompanionDeviceManager.isDeviceAssociatedForWifiConnection( 1855 requestorPackageName, bssid, requestorUserHandle); 1856 if (!approved) return false; 1857 if (mVerboseLoggingEnabled) { 1858 Log.v(TAG, "Found " + bssid 1859 + " in CompanionDeviceManager approved access point for " 1860 + requestorPackageName); 1861 } 1862 return true; 1863 } 1864 isAccessPointApprovedForActiveRequest(@onNull String ssid, @NonNull MacAddress bssid, @SecurityType int networkType, boolean revokeNormalBypass)1865 private boolean isAccessPointApprovedForActiveRequest(@NonNull String ssid, 1866 @NonNull MacAddress bssid, @SecurityType int networkType, boolean revokeNormalBypass) { 1867 String requestorPackageName = mActiveSpecificNetworkRequest.getRequestorPackageName(); 1868 UserHandle requestorUserHandle = 1869 UserHandle.getUserHandleForUid(mActiveSpecificNetworkRequest.getRequestorUid()); 1870 // Check if access point is approved via CompanionDeviceManager first. 1871 if (isAccessPointApprovedInCompanionDeviceManager( 1872 bssid, requestorUserHandle, requestorPackageName)) { 1873 return true; 1874 } 1875 // Check if access point is approved in internal approval list next. 1876 if (!revokeNormalBypass && isAccessPointApprovedInInternalApprovalList( 1877 ssid, bssid, networkType, requestorPackageName)) { 1878 return true; 1879 } 1880 // Shell approved app 1881 if (TextUtils.equals(mApprovedApp, requestorPackageName)) { 1882 return true; 1883 } 1884 // no bypass approvals, show UI. 1885 return false; 1886 } 1887 1888 1889 // Helper method to store the all the BSSIDs matching the network from the matched scan results addNetworkToUserApprovedAccessPointMap(@onNull WifiConfiguration network)1890 private void addNetworkToUserApprovedAccessPointMap(@NonNull WifiConfiguration network) { 1891 if (mActiveSpecificNetworkRequestSpecifier == null 1892 || mActiveMatchedScanResults == null) return; 1893 // Note: This hopefully is a list of size 1, because we want to store a 1:1 mapping 1894 // from user selection and the AP that was approved. But, since we get a WifiConfiguration 1895 // object representing an entire network from UI, we need to ensure that all the visible 1896 // BSSIDs matching the original request and the selected network are stored. 1897 Set<AccessPoint> newUserApprovedAccessPoints = new HashSet<>(); 1898 1899 ScanResultMatchInfo fromWifiConfiguration = 1900 ScanResultMatchInfo.fromWifiConfiguration(network); 1901 for (ScanResult scanResult : mActiveMatchedScanResults.values()) { 1902 ScanResultMatchInfo fromScanResult = ScanResultMatchInfo.fromScanResult(scanResult); 1903 SecurityParams params = fromScanResult.matchForNetworkSelection(fromWifiConfiguration); 1904 if (null != params) { 1905 AccessPoint approvedAccessPoint = 1906 new AccessPoint(scanResult.SSID, MacAddress.fromString(scanResult.BSSID), 1907 params.getSecurityType()); 1908 newUserApprovedAccessPoints.add(approvedAccessPoint); 1909 } 1910 } 1911 if (newUserApprovedAccessPoints.isEmpty()) return; 1912 1913 String requestorPackageName = mActiveSpecificNetworkRequest.getRequestorPackageName(); 1914 LinkedHashSet<AccessPoint> approvedAccessPoints = 1915 mUserApprovedAccessPointMap.get(requestorPackageName); 1916 if (approvedAccessPoints == null) { 1917 approvedAccessPoints = new LinkedHashSet<>(); 1918 mUserApprovedAccessPointMap.put(requestorPackageName, approvedAccessPoints); 1919 // Note the new app in metrics. 1920 mWifiMetrics.incrementNetworkRequestApiNumApps(); 1921 } 1922 if (mVerboseLoggingEnabled) { 1923 Log.v(TAG, "Adding " + newUserApprovedAccessPoints 1924 + " to user approved access point for " + requestorPackageName); 1925 } 1926 // keep the most recently added APs in the end 1927 approvedAccessPoints.removeAll(newUserApprovedAccessPoints); 1928 approvedAccessPoints.addAll(newUserApprovedAccessPoints); 1929 cleanUpLRUAccessPoints(approvedAccessPoints); 1930 saveToStore(); 1931 } 1932 1933 /** 1934 * 1) If the request is for a single bssid, check if the matching ScanResult was pre-approved 1935 * by the user. 1936 * 2) If yes to (b), trigger a connect immediately and returns true. Else, returns false. 1937 * 1938 * @return true if a pre-approved network was found for connection, false otherwise. 1939 */ triggerConnectIfUserApprovedMatchFound(boolean revokeNormalBypass, ScanResult[] scanResults)1940 private boolean triggerConnectIfUserApprovedMatchFound(boolean revokeNormalBypass, 1941 ScanResult[] scanResults) { 1942 if (mActiveSpecificNetworkRequestSpecifier == null) return false; 1943 boolean requestForSingleAccessPoint = isActiveRequestForSingleAccessPoint(); 1944 if (!requestForSingleAccessPoint && !isActiveRequestForSingleNetwork()) { 1945 Log.i(TAG, "ActiveRequest not for single access point or network."); 1946 return false; 1947 } 1948 1949 String ssid = mActiveSpecificNetworkRequestSpecifier.ssidPatternMatcher.getPath(); 1950 MacAddress bssid = mActiveSpecificNetworkRequestSpecifier.bssidPatternMatcher.first; 1951 SecurityParams params = 1952 ScanResultMatchInfo.fromWifiConfiguration( 1953 mActiveSpecificNetworkRequestSpecifier.wifiConfiguration) 1954 .getFirstAvailableSecurityParams(); 1955 if (null == params) return false; 1956 int networkType = params.getSecurityType(); 1957 1958 if (!isAccessPointApprovedForActiveRequest(ssid, bssid, networkType, revokeNormalBypass) 1959 || mWifiConfigManager.isNetworkTemporarilyDisabledByUser( 1960 ScanResultUtil.createQuotedSsid(ssid))) { 1961 if (mVerboseLoggingEnabled) { 1962 Log.v(TAG, "No approved access point found"); 1963 } 1964 return false; 1965 } 1966 List<ScanResult> matchedScanResults = 1967 getNetworksMatchingActiveNetworkRequest(scanResults); 1968 if (requestForSingleAccessPoint && !matchedScanResults.isEmpty()) { 1969 Log.v(TAG, "Approved access point found in matching scan results. " 1970 + "Triggering connect " + ssid + "/" + bssid); 1971 // Request is for a single AP which is already approved. Connect directly. 1972 WifiConfiguration config = mActiveSpecificNetworkRequestSpecifier.wifiConfiguration; 1973 config.SSID = "\"" + ssid + "\""; 1974 config.BSSID = bssid.toString(); 1975 handleConnectToNetworkUserSelectionInternal(config, false); 1976 mWifiMetrics.incrementNetworkRequestApiNumUserApprovalBypass(); 1977 return true; 1978 } 1979 // request is for a single network (but not a particular AP) that's already approved. 1980 // Scanning is still needed to select the best BSSID, but allow skipping the UI. 1981 Log.v(TAG, "Approved network found. Allowing user dialogue to get bypassed."); 1982 mSkipUserDialogue = true; 1983 return false; 1984 } 1985 1986 /** 1987 * Handle scan results 1988 * 1989 * @param scanResults Array of {@link ScanResult} to be processed. 1990 */ handleScanResults(ScanResult[] scanResults)1991 private void handleScanResults(ScanResult[] scanResults) { 1992 List<ScanResult> matchedScanResults = 1993 getNetworksMatchingActiveNetworkRequest(scanResults); 1994 if ((mActiveMatchedScanResults == null || mActiveMatchedScanResults.isEmpty()) 1995 && !matchedScanResults.isEmpty()) { 1996 // only note the first match size in metrics (chances of this changing in further 1997 // scans is pretty low) 1998 mWifiMetrics.incrementNetworkRequestApiMatchSizeHistogram( 1999 matchedScanResults.size()); 2000 } 2001 // First set of scan results for this request. 2002 if (mActiveMatchedScanResults == null) mActiveMatchedScanResults = new HashMap<>(); 2003 // Coalesce the new set of scan results with previous scan results received for request. 2004 mActiveMatchedScanResults.putAll(matchedScanResults 2005 .stream() 2006 .collect(Collectors.toMap( 2007 scanResult -> scanResult.BSSID, scanResult -> scanResult, (a, b) -> a))); 2008 // Weed out any stale cached scan results. 2009 long currentTimeInMillis = mClock.getElapsedSinceBootMillis(); 2010 mActiveMatchedScanResults.entrySet().removeIf( 2011 e -> ((currentTimeInMillis - (e.getValue().timestamp / 1000)) 2012 >= CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS)); 2013 if (!mActiveMatchedScanResults.isEmpty() && mSkipUserDialogue) { 2014 WifiConfiguration config = mActiveSpecificNetworkRequestSpecifier.wifiConfiguration; 2015 config.SSID = "\"" 2016 + mActiveSpecificNetworkRequestSpecifier.ssidPatternMatcher.getPath() + "\""; 2017 config.BSSID = findBestBssidFromActiveMatchedScanResultsForNetwork( 2018 ScanResultMatchInfo.fromWifiConfiguration(config)); 2019 Log.v(TAG, "Bypassing user dialog for connection to SSID=" 2020 + config.SSID + ", BSSID=" + config.BSSID); 2021 handleConnectToNetworkUserSelection(config, false); 2022 } 2023 } 2024 2025 /** 2026 * Retrieve the latest cached scan results from wifi scanner and filter out any 2027 * {@link ScanResult} older than {@link #CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS}. 2028 */ getFilteredCachedScanResults()2029 private @NonNull ScanResult[] getFilteredCachedScanResults() { 2030 List<ScanResult> cachedScanResults = mWifiScanner.getSingleScanResults(); 2031 if (cachedScanResults == null || cachedScanResults.isEmpty()) return new ScanResult[0]; 2032 long currentTimeInMillis = mClock.getElapsedSinceBootMillis(); 2033 return cachedScanResults.stream() 2034 .filter(scanResult 2035 -> ((currentTimeInMillis - (scanResult.timestamp / 1000)) 2036 < CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS)) 2037 .toArray(ScanResult[]::new); 2038 } 2039 2040 /** 2041 * Clean up least recently used Access Points if specified app reach the limit. 2042 */ cleanUpLRUAccessPoints(Set<AccessPoint> approvedAccessPoints)2043 private static void cleanUpLRUAccessPoints(Set<AccessPoint> approvedAccessPoints) { 2044 if (approvedAccessPoints.size() <= NUM_OF_ACCESS_POINT_LIMIT_PER_APP) { 2045 return; 2046 } 2047 Iterator iter = approvedAccessPoints.iterator(); 2048 while (iter.hasNext() && approvedAccessPoints.size() > NUM_OF_ACCESS_POINT_LIMIT_PER_APP) { 2049 iter.next(); 2050 iter.remove(); 2051 } 2052 } 2053 2054 /** 2055 * Sets all access points approved for the specified app. 2056 * Used by shell commands. 2057 */ setUserApprovedApp(@onNull String packageName, boolean approved)2058 public void setUserApprovedApp(@NonNull String packageName, boolean approved) { 2059 if (approved) { 2060 mApprovedApp = packageName; 2061 } else if (TextUtils.equals(packageName, mApprovedApp)) { 2062 mApprovedApp = null; 2063 } 2064 } 2065 2066 /** 2067 * Whether all access points are approved for the specified app. 2068 * Used by shell commands. 2069 */ hasUserApprovedApp(@onNull String packageName)2070 public boolean hasUserApprovedApp(@NonNull String packageName) { 2071 return TextUtils.equals(packageName, mApprovedApp); 2072 } 2073 2074 /** 2075 * Remove all user approved access points and listener for the specified app. 2076 */ removeApp(@onNull String packageName)2077 public void removeApp(@NonNull String packageName) { 2078 if (mUserApprovedAccessPointMap.remove(packageName) != null) { 2079 Log.i(TAG, "Removing all approved access points for " + packageName); 2080 } 2081 RemoteCallbackList<ILocalOnlyConnectionStatusListener> listenerTracker = 2082 mLocalOnlyStatusListenerPerApp.remove(packageName); 2083 if (listenerTracker != null) listenerTracker.kill(); 2084 mFeatureIdPerApp.remove(packageName); 2085 saveToStore(); 2086 } 2087 2088 /** 2089 * Add a listener to get the connection failure of the local-only conncetion 2090 */ addLocalOnlyConnectionStatusListener( @onNull ILocalOnlyConnectionStatusListener listener, String packageName, String featureId)2091 public void addLocalOnlyConnectionStatusListener( 2092 @NonNull ILocalOnlyConnectionStatusListener listener, String packageName, 2093 String featureId) { 2094 RemoteCallbackList<ILocalOnlyConnectionStatusListener> listenersTracker = 2095 mLocalOnlyStatusListenerPerApp.get(packageName); 2096 if (listenersTracker == null) { 2097 listenersTracker = new RemoteCallbackList<>(); 2098 } 2099 listenersTracker.register(listener); 2100 mLocalOnlyStatusListenerPerApp.put(packageName, listenersTracker); 2101 if (!mFeatureIdPerApp.containsKey(packageName)) { 2102 mFeatureIdPerApp.put(packageName, featureId); 2103 } 2104 } 2105 2106 /** 2107 * Remove a listener which added before 2108 */ removeLocalOnlyConnectionStatusListener( @onNull ILocalOnlyConnectionStatusListener listener, String packageName)2109 public void removeLocalOnlyConnectionStatusListener( 2110 @NonNull ILocalOnlyConnectionStatusListener listener, String packageName) { 2111 RemoteCallbackList<ILocalOnlyConnectionStatusListener> listenersTracker = 2112 mLocalOnlyStatusListenerPerApp.get(packageName); 2113 if (listenersTracker == null || !listenersTracker.unregister(listener)) { 2114 Log.w(TAG, "removeLocalOnlyConnectionFailureListener: Listener from " + packageName 2115 + " already unregister."); 2116 } 2117 if (listenersTracker != null && listenersTracker.getRegisteredCallbackCount() == 0) { 2118 mLocalOnlyStatusListenerPerApp.remove(packageName); 2119 mFeatureIdPerApp.remove(packageName); 2120 } 2121 } 2122 sendConnectionFailureIfAllowed(String packageName, int uid, @NonNull WifiNetworkSpecifier networkSpecifier, int connectionEvent)2123 private void sendConnectionFailureIfAllowed(String packageName, 2124 int uid, @NonNull WifiNetworkSpecifier networkSpecifier, int connectionEvent) { 2125 RemoteCallbackList<ILocalOnlyConnectionStatusListener> listenersTracker = 2126 mLocalOnlyStatusListenerPerApp.get(packageName); 2127 if (listenersTracker == null || listenersTracker.getRegisteredCallbackCount() == 0) { 2128 return; 2129 } 2130 2131 if (mVerboseLoggingEnabled) { 2132 Log.v(TAG, "Sending connection failure event to " + packageName); 2133 } 2134 final int n = listenersTracker.beginBroadcast(); 2135 for (int i = 0; i < n; i++) { 2136 try { 2137 listenersTracker.getBroadcastItem(i).onConnectionStatus(networkSpecifier, 2138 internalConnectionEventToLocalOnlyFailureCode(connectionEvent)); 2139 } catch (RemoteException e) { 2140 Log.e(TAG, "sendNetworkCallback: remote exception -- " + e); 2141 } 2142 } 2143 listenersTracker.finishBroadcast(); 2144 } 2145 2146 private @WifiManager.LocalOnlyConnectionStatusCode int internalConnectionEventToLocalOnlyFailureCode(int connectionEvent)2147 internalConnectionEventToLocalOnlyFailureCode(int connectionEvent) { 2148 switch (connectionEvent) { 2149 case WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION: 2150 case WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT: 2151 return WifiManager.STATUS_LOCAL_ONLY_CONNECTION_FAILURE_ASSOCIATION; 2152 case WifiMetrics.ConnectionEvent.FAILURE_SSID_TEMP_DISABLED: 2153 case FAILURE_AUTHENTICATION_FAILURE: 2154 return WifiManager.STATUS_LOCAL_ONLY_CONNECTION_FAILURE_AUTHENTICATION; 2155 case WifiMetrics.ConnectionEvent.FAILURE_DHCP: 2156 return WifiManager.STATUS_LOCAL_ONLY_CONNECTION_FAILURE_IP_PROVISIONING; 2157 case WifiMetrics.ConnectionEvent.FAILURE_NETWORK_NOT_FOUND: 2158 return WifiManager.STATUS_LOCAL_ONLY_CONNECTION_FAILURE_NOT_FOUND; 2159 case WifiMetrics.ConnectionEvent.FAILURE_NO_RESPONSE: 2160 return WifiManager.STATUS_LOCAL_ONLY_CONNECTION_FAILURE_NO_RESPONSE; 2161 default: 2162 return WifiManager.STATUS_LOCAL_ONLY_CONNECTION_FAILURE_UNKNOWN; 2163 } 2164 } 2165 2166 /** 2167 * Clear all internal state (for network settings reset). 2168 */ clear()2169 public void clear() { 2170 mUserApprovedAccessPointMap.clear(); 2171 mApprovedApp = null; 2172 for (RemoteCallbackList<ILocalOnlyConnectionStatusListener> listenerTracker 2173 : mLocalOnlyStatusListenerPerApp.values()) { 2174 listenerTracker.kill(); 2175 } 2176 mLocalOnlyStatusListenerPerApp.clear(); 2177 mFeatureIdPerApp.clear(); 2178 Log.i(TAG, "Cleared all internal state"); 2179 saveToStore(); 2180 } 2181 } 2182