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.app.AppOpsManager.MODE_IGNORED; 20 import static android.app.AppOpsManager.OPSTR_CHANGE_WIFI_STATE; 21 import static android.net.wifi.WifiManager.ACTION_REMOVE_SUGGESTION_DISCONNECT; 22 import static android.net.wifi.WifiManager.ACTION_REMOVE_SUGGESTION_LINGER; 23 24 import android.annotation.IntDef; 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.annotation.SuppressLint; 28 import android.app.ActivityManager; 29 import android.app.AppOpsManager; 30 import android.app.Notification; 31 import android.app.PendingIntent; 32 import android.content.BroadcastReceiver; 33 import android.content.Context; 34 import android.content.Intent; 35 import android.content.IntentFilter; 36 import android.content.res.Resources; 37 import android.graphics.drawable.Icon; 38 import android.net.MacAddress; 39 import android.net.wifi.ISuggestionConnectionStatusListener; 40 import android.net.wifi.ISuggestionUserApprovalStatusListener; 41 import android.net.wifi.ScanResult; 42 import android.net.wifi.SecurityParams; 43 import android.net.wifi.WifiConfiguration; 44 import android.net.wifi.WifiContext; 45 import android.net.wifi.WifiEnterpriseConfig; 46 import android.net.wifi.WifiManager; 47 import android.net.wifi.WifiNetworkSuggestion; 48 import android.net.wifi.WifiScanner; 49 import android.net.wifi.WifiSsid; 50 import android.net.wifi.hotspot2.PasspointConfiguration; 51 import android.os.Process; 52 import android.os.RemoteCallbackList; 53 import android.os.RemoteException; 54 import android.os.SystemProperties; 55 import android.os.UserHandle; 56 import android.telephony.SubscriptionManager; 57 import android.telephony.TelephonyManager; 58 import android.text.TextUtils; 59 import android.util.ArrayMap; 60 import android.util.ArraySet; 61 import android.util.EventLog; 62 import android.util.Log; 63 import android.util.Pair; 64 65 import com.android.internal.annotations.VisibleForTesting; 66 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; 67 import com.android.modules.utils.build.SdkLevel; 68 import com.android.server.wifi.util.LruConnectionTracker; 69 import com.android.server.wifi.util.WifiPermissionsUtil; 70 import com.android.wifi.resources.R; 71 72 import java.io.FileDescriptor; 73 import java.io.PrintWriter; 74 import java.lang.annotation.Retention; 75 import java.lang.annotation.RetentionPolicy; 76 import java.util.ArrayList; 77 import java.util.BitSet; 78 import java.util.Collection; 79 import java.util.Comparator; 80 import java.util.HashMap; 81 import java.util.HashSet; 82 import java.util.Iterator; 83 import java.util.LinkedHashSet; 84 import java.util.List; 85 import java.util.Map; 86 import java.util.Objects; 87 import java.util.Optional; 88 import java.util.Set; 89 import java.util.stream.Collectors; 90 91 import javax.annotation.concurrent.NotThreadSafe; 92 93 /** 94 * Network Suggestions Manager. 95 * NOTE: This class should always be invoked from the main wifi service thread. 96 */ 97 @NotThreadSafe 98 @SuppressLint("LongLogTag") 99 public class WifiNetworkSuggestionsManager { 100 private static final String TAG = "WifiNetworkSuggestionsManager"; 101 102 /** Intent when user tapped action button to allow the app. */ 103 @VisibleForTesting 104 public static final String NOTIFICATION_USER_ALLOWED_APP_INTENT_ACTION = 105 "com.android.server.wifi.action.NetworkSuggestion.USER_ALLOWED_APP"; 106 /** Intent when user tapped action button to disallow the app. */ 107 @VisibleForTesting 108 public static final String NOTIFICATION_USER_DISALLOWED_APP_INTENT_ACTION = 109 "com.android.server.wifi.action.NetworkSuggestion.USER_DISALLOWED_APP"; 110 /** Intent when user dismissed the notification. */ 111 @VisibleForTesting 112 public static final String NOTIFICATION_USER_DISMISSED_INTENT_ACTION = 113 "com.android.server.wifi.action.NetworkSuggestion.USER_DISMISSED"; 114 @VisibleForTesting 115 public static final String EXTRA_PACKAGE_NAME = 116 "com.android.server.wifi.extra.NetworkSuggestion.PACKAGE_NAME"; 117 @VisibleForTesting 118 public static final String EXTRA_UID = 119 "com.android.server.wifi.extra.NetworkSuggestion.UID"; 120 121 public static final int APP_TYPE_CARRIER_PRIVILEGED = 1; 122 public static final int APP_TYPE_NETWORK_PROVISIONING = 2; 123 public static final int APP_TYPE_NON_PRIVILEGED = 3; 124 125 public static final int ACTION_USER_ALLOWED_APP = 1; 126 public static final int ACTION_USER_DISALLOWED_APP = 2; 127 public static final int ACTION_USER_DISMISS = 3; 128 129 public static final int DEFAULT_PRIORITY_GROUP = 0; 130 131 @IntDef(prefix = { "ACTION_USER_" }, value = { 132 ACTION_USER_ALLOWED_APP, 133 ACTION_USER_DISALLOWED_APP, 134 ACTION_USER_DISMISS 135 }) 136 @Retention(RetentionPolicy.SOURCE) 137 public @interface UserActionCode { } 138 139 /** 140 * Limit number of hidden networks attach to scan 141 */ 142 private static final int NUMBER_OF_HIDDEN_NETWORK_FOR_ONE_SCAN = 100; 143 /** 144 * Expiration timeout for user notification in milliseconds. (15 min) 145 */ 146 private static final long NOTIFICATION_EXPIRY_MILLS = 15 * 60 * 1000; 147 /** 148 * Notification update delay in milliseconds. (10 min) 149 */ 150 private static final long NOTIFICATION_UPDATE_DELAY_MILLS = 10 * 60 * 1000; 151 152 /** 153 * Modifiable only for testing. 154 */ 155 private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger"; 156 /** 157 * Default to 30s linger time-out. Should be same as ConnectivityService#DEFAULT_LINGER_DELAY_MS 158 */ 159 @VisibleForTesting 160 public static final int DEFAULT_LINGER_DELAY_MS = 30_000; 161 162 private final WifiContext mContext; 163 private final Resources mResources; 164 private final RunnerHandler mHandler; 165 private final AppOpsManager mAppOps; 166 private final ActivityManager mActivityManager; 167 private final WifiNotificationManager mNotificationManager; 168 private final WifiPermissionsUtil mWifiPermissionsUtil; 169 private final WifiConfigManager mWifiConfigManager; 170 private final WifiMetrics mWifiMetrics; 171 private final WifiInjector mWifiInjector; 172 private final FrameworkFacade mFrameworkFacade; 173 private final WifiCarrierInfoManager mWifiCarrierInfoManager; 174 private final WifiKeyStore mWifiKeyStore; 175 private final Clock mClock; 176 // Keep order of network connection. 177 private final LruConnectionTracker mLruConnectionTracker; 178 179 private class OnNetworkUpdateListener implements 180 WifiConfigManager.OnNetworkUpdateListener { 181 182 @Override onConnectChoiceSet(@onNull List<WifiConfiguration> networks, String choiceKey, int rssi)183 public void onConnectChoiceSet(@NonNull List<WifiConfiguration> networks, 184 String choiceKey, int rssi) { 185 onUserConnectChoiceSetForSuggestion(networks, choiceKey, rssi); 186 } 187 188 @Override onConnectChoiceRemoved(@onNull String choiceKey)189 public void onConnectChoiceRemoved(@NonNull String choiceKey) { 190 if (choiceKey == null) { 191 return; 192 } 193 onUserConnectChoiceRemoveForSuggestion(choiceKey); 194 } 195 196 @Override onSecurityParamsUpdate(@onNull WifiConfiguration configuration, @NonNull List<SecurityParams> securityParams)197 public void onSecurityParamsUpdate(@NonNull WifiConfiguration configuration, 198 @NonNull List<SecurityParams> securityParams) { 199 if (configuration == null || securityParams == null || securityParams.isEmpty()) { 200 Log.e(TAG, "onSecurityParamsUpdate: must have valid config and " 201 + "securityParams"); 202 return; 203 } 204 onSecurityParamsUpdateForSuggestion(configuration, securityParams); 205 } 206 } 207 208 /** 209 * Per app meta data to store network suggestions, status, etc for each app providing network 210 * suggestions on the device. 211 */ 212 public static class PerAppInfo { 213 /** 214 * UID of the app. 215 */ 216 public int uid; 217 /** 218 * Package Name of the app. 219 */ 220 public final String packageName; 221 /** 222 * First Feature in the package that registered the suggestion 223 */ 224 public final String featureId; 225 /** 226 97 * Map of active network suggestions provided by the app keyed by hashcode. 227 */ 228 public final Map<Integer, ExtendedWifiNetworkSuggestion> extNetworkSuggestions = 229 new ArrayMap<>(); 230 /** 231 * Whether we have shown the user a notification for this app. 232 */ 233 public boolean hasUserApproved = false; 234 /** 235 * Carrier Id of SIM which give app carrier privileges. 236 */ 237 public int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID; 238 239 /** Stores the max size of the {@link #extNetworkSuggestions} list ever for this app */ 240 public int maxSize = 0; 241 PerAppInfo(int uid, @NonNull String packageName, @Nullable String featureId)242 public PerAppInfo(int uid, @NonNull String packageName, @Nullable String featureId) { 243 this.uid = uid; 244 this.packageName = packageName; 245 this.featureId = featureId; 246 } 247 248 /** 249 * Needed for migration of config store data. 250 */ setUid(int uid)251 public void setUid(int uid) { 252 if (this.uid == Process.INVALID_UID) { 253 this.uid = uid; 254 } 255 // else ignored. 256 } 257 258 /** 259 * Needed when a normal App became carrier privileged when SIM insert 260 */ setCarrierId(int carrierId)261 public void setCarrierId(int carrierId) { 262 if (this.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) { 263 this.carrierId = carrierId; 264 } 265 // else ignored. 266 } 267 268 /** 269 * Returns true if this app has the necessary approvals to place network suggestions. 270 */ isApproved()271 private boolean isApproved() { 272 return hasUserApproved || carrierId != TelephonyManager.UNKNOWN_CARRIER_ID; 273 } 274 275 // This is only needed for comparison in unit tests. 276 @Override equals(Object other)277 public boolean equals(Object other) { 278 if (other == null) return false; 279 if (!(other instanceof PerAppInfo)) return false; 280 PerAppInfo otherPerAppInfo = (PerAppInfo) other; 281 return uid == otherPerAppInfo.uid 282 && TextUtils.equals(packageName, otherPerAppInfo.packageName) 283 && Objects.equals(extNetworkSuggestions, otherPerAppInfo.extNetworkSuggestions) 284 && hasUserApproved == otherPerAppInfo.hasUserApproved; 285 } 286 287 // This is only needed for comparison in unit tests. 288 @Override hashCode()289 public int hashCode() { 290 return Objects.hash(uid, packageName, extNetworkSuggestions, hasUserApproved); 291 } 292 293 @Override toString()294 public String toString() { 295 return new StringBuilder("PerAppInfo[ ") 296 .append("uid=").append(uid) 297 .append(", packageName=").append(packageName) 298 .append(", hasUserApproved=").append(hasUserApproved) 299 .append(", suggestions=").append(extNetworkSuggestions) 300 .append(" ]") 301 .toString(); 302 } 303 } 304 305 /** 306 * Internal container class which holds a network suggestion and a pointer to the 307 * {@link PerAppInfo} entry from {@link #mActiveNetworkSuggestionsPerApp} corresponding to the 308 * app that made the suggestion. 309 */ 310 public static class ExtendedWifiNetworkSuggestion { 311 public final WifiNetworkSuggestion wns; 312 // Store the pointer to the corresponding app's meta data. 313 public final PerAppInfo perAppInfo; 314 public boolean isAutojoinEnabled; 315 public String anonymousIdentity = null; 316 public String connectChoice = null; 317 public int connectChoiceRssi = 0; 318 ExtendedWifiNetworkSuggestion(@onNull WifiNetworkSuggestion wns, @NonNull PerAppInfo perAppInfo, boolean isAutoJoinEnabled)319 public ExtendedWifiNetworkSuggestion(@NonNull WifiNetworkSuggestion wns, 320 @NonNull PerAppInfo perAppInfo, 321 boolean isAutoJoinEnabled) { 322 this.wns = wns; 323 this.perAppInfo = perAppInfo; 324 this.isAutojoinEnabled = isAutoJoinEnabled; 325 this.wns.wifiConfiguration.fromWifiNetworkSuggestion = true; 326 this.wns.wifiConfiguration.ephemeral = true; 327 this.wns.wifiConfiguration.creatorName = perAppInfo.packageName; 328 this.wns.wifiConfiguration.creatorUid = perAppInfo.uid; 329 if (perAppInfo.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) { 330 return; 331 } 332 // If App is carrier privileged, set carrier Id to the profile. 333 this.wns.wifiConfiguration.carrierId = perAppInfo.carrierId; 334 if (this.wns.passpointConfiguration != null) { 335 this.wns.passpointConfiguration.setCarrierId(perAppInfo.carrierId); 336 } 337 } 338 339 @Override hashCode()340 public int hashCode() { 341 return Objects.hash(wns, perAppInfo.uid, perAppInfo.packageName); 342 } 343 344 @Override equals(Object obj)345 public boolean equals(Object obj) { 346 if (this == obj) { 347 return true; 348 } 349 if (!(obj instanceof ExtendedWifiNetworkSuggestion)) { 350 return false; 351 } 352 ExtendedWifiNetworkSuggestion other = (ExtendedWifiNetworkSuggestion) obj; 353 return wns.equals(other.wns) 354 && perAppInfo.uid == other.perAppInfo.uid 355 && TextUtils.equals(perAppInfo.packageName, other.perAppInfo.packageName); 356 } 357 358 @Override toString()359 public String toString() { 360 return new StringBuilder(wns.toString()) 361 .append(", isAutoJoinEnabled=").append(isAutojoinEnabled) 362 .toString(); 363 } 364 365 /** 366 * Convert from {@link WifiNetworkSuggestion} to a new instance of 367 * {@link ExtendedWifiNetworkSuggestion}. 368 */ fromWns(@onNull WifiNetworkSuggestion wns, @NonNull PerAppInfo perAppInfo, boolean isAutoJoinEnabled)369 public static ExtendedWifiNetworkSuggestion fromWns(@NonNull WifiNetworkSuggestion wns, 370 @NonNull PerAppInfo perAppInfo, boolean isAutoJoinEnabled) { 371 return new ExtendedWifiNetworkSuggestion(wns, perAppInfo, isAutoJoinEnabled); 372 } 373 374 /** 375 * Create a {@link WifiConfiguration} from suggestion for framework internal use. 376 */ createInternalWifiConfiguration( @ullable WifiCarrierInfoManager carrierInfoManager)377 public WifiConfiguration createInternalWifiConfiguration( 378 @Nullable WifiCarrierInfoManager carrierInfoManager) { 379 WifiConfiguration config = new WifiConfiguration(wns.getWifiConfiguration()); 380 config.shared = false; 381 config.allowAutojoin = isAutojoinEnabled; 382 if (config.enterpriseConfig 383 != null && config.enterpriseConfig.isAuthenticationSimBased() 384 && !TextUtils.isEmpty(anonymousIdentity)) { 385 config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity); 386 } 387 config.getNetworkSelectionStatus().setConnectChoice(connectChoice); 388 config.getNetworkSelectionStatus().setConnectChoiceRssi(connectChoiceRssi); 389 if (carrierInfoManager != null) { 390 config.subscriptionId = carrierInfoManager.getBestMatchSubscriptionId(config); 391 // shouldDisableMacRandomization checks if the SSID matches with a SSID configured 392 // in CarrierConfigManger for the provided subscriptionId. 393 if (carrierInfoManager.shouldDisableMacRandomization(config.SSID, 394 config.carrierId, config.subscriptionId)) { 395 Log.i(TAG, "Disabling MAC randomization on " + config.SSID 396 + " due to CarrierConfig override"); 397 config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE; 398 } 399 } 400 return config; 401 } 402 } 403 404 /** 405 * Map of package name of an app to the set of active network suggestions provided by the app. 406 */ 407 private final Map<String, PerAppInfo> mActiveNetworkSuggestionsPerApp = new HashMap<>(); 408 /** 409 * Map of package name of an app to the app ops changed listener for the app. 410 */ 411 private final Map<String, AppOpsChangedListener> mAppOpsChangedListenerPerApp = new HashMap<>(); 412 /** 413 * Map maintained to help lookup all the network suggestions (with no bssid) that match a 414 * provided scan result. 415 * Note: 416 * <li>There could be multiple suggestions (provided by different apps) that match a single 417 * scan result.</li> 418 * <li>Adding/Removing to this set for scan result lookup is expensive. But, we expect scan 419 * result lookup to happen much more often than apps modifying network suggestions.</li> 420 */ 421 private final Map<ScanResultMatchInfo, Set<ExtendedWifiNetworkSuggestion>> 422 mActiveScanResultMatchInfoWithNoBssid = new HashMap<>(); 423 /** 424 * Map maintained to help lookup all the network suggestions (with bssid) that match a provided 425 * scan result. 426 * Note: 427 * <li>There could be multiple suggestions (provided by different apps) that match a single 428 * scan result.</li> 429 * <li>Adding/Removing to this set for scan result lookup is expensive. But, we expect scan 430 * result lookup to happen much more often than apps modifying network suggestions.</li> 431 */ 432 private final Map<Pair<ScanResultMatchInfo, MacAddress>, Set<ExtendedWifiNetworkSuggestion>> 433 mActiveScanResultMatchInfoWithBssid = new HashMap<>(); 434 435 private final Map<String, Set<ExtendedWifiNetworkSuggestion>> 436 mPasspointInfo = new HashMap<>(); 437 438 private final HashMap<String, RemoteCallbackList<ISuggestionConnectionStatusListener>> 439 mSuggestionStatusListenerPerApp = new HashMap<>(); 440 441 private final HashMap<String, RemoteCallbackList<ISuggestionUserApprovalStatusListener>> 442 mSuggestionUserApprovalStatusListenerPerApp = new HashMap<>(); 443 444 /** 445 * Store the suggestion update listeners. 446 */ 447 private final List<OnSuggestionUpdateListener> mListeners = new ArrayList<>(); 448 449 /** 450 * Intent filter for processing notification actions. 451 */ 452 private final IntentFilter mIntentFilter; 453 454 /** 455 * Verbose logging flag. 456 */ 457 private boolean mVerboseLoggingEnabled = false; 458 /** 459 * Indicates that we have new data to serialize. 460 */ 461 private boolean mHasNewDataToSerialize = false; 462 /** 463 * The {@link Clock#getElapsedSinceBootMillis()} must be at least this value for us 464 * to update/show the notification. 465 */ 466 private long mNotificationUpdateTime; 467 468 private boolean mIsLastUserApprovalUiDialog = false; 469 470 private boolean mUserDataLoaded = false; 471 private boolean mIsDeviceShuttingDown = false; 472 473 /** 474 * Keep a set of packageNames which is treated as carrier provider. 475 */ 476 private final Set<String> mCrossCarrierProvidersSet = new ArraySet<>(); 477 478 /** 479 * Listener for app-ops changes for active suggestor apps. 480 */ 481 private final class AppOpsChangedListener implements AppOpsManager.OnOpChangedListener { 482 private final String mPackageName; 483 private final int mUid; 484 AppOpsChangedListener(@onNull String packageName, int uid)485 AppOpsChangedListener(@NonNull String packageName, int uid) { 486 mPackageName = packageName; 487 mUid = uid; 488 } 489 490 @Override onOpChanged(String op, String packageName)491 public void onOpChanged(String op, String packageName) { 492 mHandler.post(() -> { 493 if (!mPackageName.equals(packageName)) return; 494 if (!OPSTR_CHANGE_WIFI_STATE.equals(op)) return; 495 496 // Ensure the uid to package mapping is still correct. 497 try { 498 mAppOps.checkPackage(mUid, mPackageName); 499 } catch (SecurityException e) { 500 Log.wtf(TAG, "Invalid uid/package" + packageName); 501 return; 502 } 503 504 if (mAppOps.unsafeCheckOpNoThrow(OPSTR_CHANGE_WIFI_STATE, mUid, mPackageName) 505 == AppOpsManager.MODE_IGNORED) { 506 Log.i(TAG, "User disallowed change wifi state for " + packageName); 507 // User disabled the app, remove app from database. We want the notification 508 // again if the user enabled the app-op back. 509 removeApp(mPackageName); 510 mWifiMetrics.incrementNetworkSuggestionUserRevokePermission(); 511 } 512 }); 513 } 514 }; 515 516 /** 517 * Module to interact with the wifi config store. 518 */ 519 private class NetworkSuggestionDataSource implements NetworkSuggestionStoreData.DataSource { 520 @Override toSerialize()521 public Map<String, PerAppInfo> toSerialize() { 522 for (Map.Entry<String, PerAppInfo> entry : mActiveNetworkSuggestionsPerApp.entrySet()) { 523 for (ExtendedWifiNetworkSuggestion ewns : entry.getValue().extNetworkSuggestions 524 .values()) { 525 if (ewns.wns.passpointConfiguration != null) { 526 continue; 527 } 528 ewns.wns.wifiConfiguration.isMostRecentlyConnected = mLruConnectionTracker 529 .isMostRecentlyConnected(ewns.createInternalWifiConfiguration( 530 mWifiCarrierInfoManager)); 531 } 532 } 533 // Clear the flag after writing to disk. 534 // TODO(b/115504887): Don't reset the flag on write failure. 535 mHasNewDataToSerialize = false; 536 return mActiveNetworkSuggestionsPerApp; 537 } 538 539 @Override fromDeserialized(Map<String, PerAppInfo> networkSuggestionsMap)540 public void fromDeserialized(Map<String, PerAppInfo> networkSuggestionsMap) { 541 mActiveNetworkSuggestionsPerApp.clear(); 542 mActiveNetworkSuggestionsPerApp.putAll(networkSuggestionsMap); 543 // Build the scan cache. 544 for (Map.Entry<String, PerAppInfo> entry : networkSuggestionsMap.entrySet()) { 545 String packageName = entry.getKey(); 546 Collection<ExtendedWifiNetworkSuggestion> extNetworkSuggestions = 547 entry.getValue().extNetworkSuggestions.values(); 548 // Start tracking app-op changes from for all the app in the database 549 startTrackingAppOpsChange(packageName, entry.getValue().uid); 550 for (ExtendedWifiNetworkSuggestion ewns : extNetworkSuggestions) { 551 if (ewns.wns.passpointConfiguration != null) { 552 addToPasspointInfoMap(ewns); 553 } else { 554 if (ewns.wns.wifiConfiguration.isMostRecentlyConnected) { 555 mLruConnectionTracker 556 .addNetwork(ewns.createInternalWifiConfiguration( 557 mWifiCarrierInfoManager)); 558 } 559 addToScanResultMatchInfoMap(ewns); 560 } 561 } 562 } 563 mUserDataLoaded = true; 564 } 565 566 @Override reset()567 public void reset() { 568 mUserDataLoaded = false; 569 mActiveNetworkSuggestionsPerApp.clear(); 570 mActiveScanResultMatchInfoWithBssid.clear(); 571 mActiveScanResultMatchInfoWithNoBssid.clear(); 572 mPasspointInfo.clear(); 573 } 574 575 @Override hasNewDataToSerialize()576 public boolean hasNewDataToSerialize() { 577 return mHasNewDataToSerialize; 578 } 579 } 580 handleUserAllowAction(int uid, String packageName)581 private void handleUserAllowAction(int uid, String packageName) { 582 Log.i(TAG, "User clicked to allow app"); 583 // Set the user approved flag. 584 setHasUserApprovedForApp(true, uid, packageName); 585 mNotificationUpdateTime = 0; 586 mWifiMetrics.addUserApprovalSuggestionAppUiReaction( 587 ACTION_USER_ALLOWED_APP, 588 mIsLastUserApprovalUiDialog); 589 } 590 handleUserDisallowAction(int uid, String packageName)591 private void handleUserDisallowAction(int uid, String packageName) { 592 Log.i(TAG, "User clicked to disallow app"); 593 // Take away CHANGE_WIFI_STATE app-ops from the app. 594 mAppOps.setMode(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, uid, packageName, 595 MODE_IGNORED); 596 // Set the user approved flag. 597 setHasUserApprovedForApp(false, uid, packageName); 598 mNotificationUpdateTime = 0; 599 mWifiMetrics.addUserApprovalSuggestionAppUiReaction( 600 ACTION_USER_DISALLOWED_APP, 601 mIsLastUserApprovalUiDialog); 602 } 603 handleUserDismissAction()604 private void handleUserDismissAction() { 605 Log.i(TAG, "User dismissed the notification"); 606 mNotificationUpdateTime = 0; 607 mWifiMetrics.addUserApprovalSuggestionAppUiReaction( 608 ACTION_USER_DISMISS, 609 mIsLastUserApprovalUiDialog); 610 } 611 612 private final BroadcastReceiver mBroadcastReceiver = 613 new BroadcastReceiver() { 614 @Override 615 public void onReceive(Context context, Intent intent) { 616 String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME); 617 int uid = intent.getIntExtra(EXTRA_UID, -1); 618 if (packageName == null || uid == -1) { 619 Log.e(TAG, "No package name or uid found in intent"); 620 return; 621 } 622 switch (intent.getAction()) { 623 case NOTIFICATION_USER_ALLOWED_APP_INTENT_ACTION: 624 handleUserAllowAction(uid, packageName); 625 break; 626 case NOTIFICATION_USER_DISALLOWED_APP_INTENT_ACTION: 627 handleUserDisallowAction(uid, packageName); 628 break; 629 case NOTIFICATION_USER_DISMISSED_INTENT_ACTION: 630 handleUserDismissAction(); 631 return; // no need to cancel a dismissed notification, return. 632 default: 633 Log.e(TAG, "Unknown action " + intent.getAction()); 634 return; 635 } 636 // Clear notification once the user interacts with it. 637 mNotificationManager.cancel(SystemMessage.NOTE_NETWORK_SUGGESTION_AVAILABLE); 638 } 639 }; 640 641 /** 642 * Interface for other modules to listen to the suggestion updated events. 643 */ 644 public interface OnSuggestionUpdateListener { 645 /** 646 * Invoked on suggestion being added or updated. 647 */ onSuggestionsAddedOrUpdated(@onNull List<WifiNetworkSuggestion> addedSuggestions)648 void onSuggestionsAddedOrUpdated(@NonNull List<WifiNetworkSuggestion> addedSuggestions); 649 /** 650 * Invoked on suggestion being removed. 651 */ onSuggestionsRemoved(@onNull List<WifiNetworkSuggestion> removedSuggestions)652 void onSuggestionsRemoved(@NonNull List<WifiNetworkSuggestion> removedSuggestions); 653 } 654 655 private final class ImsiProtectedOrUserApprovedListener implements 656 WifiCarrierInfoManager.OnImsiProtectedOrUserApprovedListener { 657 658 @Override onImsiProtectedOrUserApprovalChanged(int carrierId, boolean allowAutoJoin)659 public void onImsiProtectedOrUserApprovalChanged(int carrierId, boolean allowAutoJoin) { 660 restoreInitialAutojoinForCarrierId(carrierId, allowAutoJoin); 661 } 662 } 663 WifiNetworkSuggestionsManager(WifiContext context, RunnerHandler handler, WifiInjector wifiInjector, WifiPermissionsUtil wifiPermissionsUtil, WifiConfigManager wifiConfigManager, WifiConfigStore wifiConfigStore, WifiMetrics wifiMetrics, WifiCarrierInfoManager wifiCarrierInfoManager, WifiKeyStore keyStore, LruConnectionTracker lruConnectionTracker, Clock clock)664 public WifiNetworkSuggestionsManager(WifiContext context, RunnerHandler handler, 665 WifiInjector wifiInjector, WifiPermissionsUtil wifiPermissionsUtil, 666 WifiConfigManager wifiConfigManager, WifiConfigStore wifiConfigStore, 667 WifiMetrics wifiMetrics, WifiCarrierInfoManager wifiCarrierInfoManager, 668 WifiKeyStore keyStore, LruConnectionTracker lruConnectionTracker, 669 Clock clock) { 670 mContext = context; 671 mResources = context.getResources(); 672 mHandler = handler; 673 mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 674 mActivityManager = context.getSystemService(ActivityManager.class); 675 mWifiInjector = wifiInjector; 676 mFrameworkFacade = mWifiInjector.getFrameworkFacade(); 677 mWifiPermissionsUtil = wifiPermissionsUtil; 678 mWifiConfigManager = wifiConfigManager; 679 mWifiMetrics = wifiMetrics; 680 mWifiCarrierInfoManager = wifiCarrierInfoManager; 681 mWifiKeyStore = keyStore; 682 mNotificationManager = mWifiInjector.getWifiNotificationManager(); 683 mClock = clock; 684 685 // register the data store for serializing/deserializing data. 686 wifiConfigStore.registerStoreData( 687 wifiInjector.makeNetworkSuggestionStoreData(new NetworkSuggestionDataSource())); 688 689 mWifiCarrierInfoManager.addImsiProtectedOrUserApprovedListener( 690 new ImsiProtectedOrUserApprovedListener()); 691 692 // Register broadcast receiver for UI interactions. 693 mIntentFilter = new IntentFilter(); 694 mIntentFilter.addAction(NOTIFICATION_USER_ALLOWED_APP_INTENT_ACTION); 695 mIntentFilter.addAction(NOTIFICATION_USER_DISALLOWED_APP_INTENT_ACTION); 696 mIntentFilter.addAction(NOTIFICATION_USER_DISMISSED_INTENT_ACTION); 697 698 mContext.registerReceiver(mBroadcastReceiver, mIntentFilter, null, handler); 699 mLruConnectionTracker = lruConnectionTracker; 700 mHandler.postToFront(() -> mWifiConfigManager.addOnNetworkUpdateListener( 701 new WifiNetworkSuggestionsManager.OnNetworkUpdateListener())); 702 } 703 704 /** 705 * Enable verbose logging. 706 */ enableVerboseLogging(boolean verboseEnabled)707 public void enableVerboseLogging(boolean verboseEnabled) { 708 mVerboseLoggingEnabled = verboseEnabled; 709 } 710 saveToStore()711 private void saveToStore() { 712 // Set the flag to let WifiConfigStore that we have new data to write. 713 mHasNewDataToSerialize = true; 714 if (!mWifiConfigManager.saveToStore()) { 715 Log.w(TAG, "Failed to save to store"); 716 } 717 } 718 addToScanResultMatchInfoMap( @onNull ExtendedWifiNetworkSuggestion extNetworkSuggestion)719 private void addToScanResultMatchInfoMap( 720 @NonNull ExtendedWifiNetworkSuggestion extNetworkSuggestion) { 721 ScanResultMatchInfo scanResultMatchInfo = 722 ScanResultMatchInfo.fromWifiConfiguration( 723 extNetworkSuggestion.wns.wifiConfiguration); 724 Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestionsForScanResultMatchInfo; 725 if (!TextUtils.isEmpty(extNetworkSuggestion.wns.wifiConfiguration.BSSID)) { 726 Pair<ScanResultMatchInfo, MacAddress> lookupPair = 727 Pair.create(scanResultMatchInfo, 728 MacAddress.fromString( 729 extNetworkSuggestion.wns.wifiConfiguration.BSSID)); 730 extNetworkSuggestionsForScanResultMatchInfo = 731 mActiveScanResultMatchInfoWithBssid.get(lookupPair); 732 if (extNetworkSuggestionsForScanResultMatchInfo == null) { 733 extNetworkSuggestionsForScanResultMatchInfo = new HashSet<>(); 734 mActiveScanResultMatchInfoWithBssid.put( 735 lookupPair, extNetworkSuggestionsForScanResultMatchInfo); 736 } 737 } else { 738 extNetworkSuggestionsForScanResultMatchInfo = 739 mActiveScanResultMatchInfoWithNoBssid.get(scanResultMatchInfo); 740 if (extNetworkSuggestionsForScanResultMatchInfo == null) { 741 extNetworkSuggestionsForScanResultMatchInfo = new HashSet<>(); 742 mActiveScanResultMatchInfoWithNoBssid.put( 743 scanResultMatchInfo, extNetworkSuggestionsForScanResultMatchInfo); 744 } 745 } 746 extNetworkSuggestionsForScanResultMatchInfo.remove(extNetworkSuggestion); 747 extNetworkSuggestionsForScanResultMatchInfo.add(extNetworkSuggestion); 748 } 749 removeFromScanResultMatchInfoMapAndRemoveRelatedScoreCard( @onNull ExtendedWifiNetworkSuggestion extNetworkSuggestion, boolean removeScoreCard)750 private void removeFromScanResultMatchInfoMapAndRemoveRelatedScoreCard( 751 @NonNull ExtendedWifiNetworkSuggestion extNetworkSuggestion, boolean removeScoreCard) { 752 ScanResultMatchInfo scanResultMatchInfo = 753 ScanResultMatchInfo.fromWifiConfiguration( 754 extNetworkSuggestion.wns.wifiConfiguration); 755 Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestionsForScanResultMatchInfo; 756 if (!TextUtils.isEmpty(extNetworkSuggestion.wns.wifiConfiguration.BSSID)) { 757 Pair<ScanResultMatchInfo, MacAddress> lookupPair = 758 Pair.create(scanResultMatchInfo, 759 MacAddress.fromString( 760 extNetworkSuggestion.wns.wifiConfiguration.BSSID)); 761 extNetworkSuggestionsForScanResultMatchInfo = 762 mActiveScanResultMatchInfoWithBssid.get(lookupPair); 763 // This should never happen because we should have done necessary error checks in 764 // the parent method. 765 if (extNetworkSuggestionsForScanResultMatchInfo == null) { 766 Log.wtf(TAG, "No scan result match info found."); 767 return; 768 } 769 extNetworkSuggestionsForScanResultMatchInfo.remove(extNetworkSuggestion); 770 // Remove the set from map if empty. 771 if (extNetworkSuggestionsForScanResultMatchInfo.isEmpty()) { 772 mActiveScanResultMatchInfoWithBssid.remove(lookupPair); 773 if (!mActiveScanResultMatchInfoWithNoBssid.containsKey(scanResultMatchInfo)) { 774 if (removeScoreCard) { 775 removeNetworkFromScoreCard(extNetworkSuggestion.wns.wifiConfiguration); 776 } 777 mLruConnectionTracker.removeNetwork( 778 extNetworkSuggestion.wns.wifiConfiguration); 779 } 780 } 781 } else { 782 extNetworkSuggestionsForScanResultMatchInfo = 783 mActiveScanResultMatchInfoWithNoBssid.get(scanResultMatchInfo); 784 // This should never happen because we should have done necessary error checks in 785 // the parent method. 786 if (extNetworkSuggestionsForScanResultMatchInfo == null) { 787 Log.wtf(TAG, "No scan result match info found."); 788 return; 789 } 790 extNetworkSuggestionsForScanResultMatchInfo.remove(extNetworkSuggestion); 791 // Remove the set from map if empty. 792 if (extNetworkSuggestionsForScanResultMatchInfo.isEmpty()) { 793 mActiveScanResultMatchInfoWithNoBssid.remove(scanResultMatchInfo); 794 if (removeScoreCard) { 795 removeNetworkFromScoreCard(extNetworkSuggestion.wns.wifiConfiguration); 796 } 797 mLruConnectionTracker.removeNetwork( 798 extNetworkSuggestion.wns.wifiConfiguration); 799 } 800 } 801 } 802 removeNetworkFromScoreCard(WifiConfiguration wifiConfiguration)803 private void removeNetworkFromScoreCard(WifiConfiguration wifiConfiguration) { 804 WifiConfiguration existing = 805 mWifiConfigManager.getConfiguredNetwork(wifiConfiguration.getProfileKey()); 806 // If there is a saved network, do not remove from the score card. 807 if (existing != null && !existing.fromWifiNetworkSuggestion) { 808 return; 809 } 810 mWifiInjector.getWifiScoreCard().removeNetwork(wifiConfiguration.SSID); 811 } 812 addToPasspointInfoMap(ExtendedWifiNetworkSuggestion ewns)813 private void addToPasspointInfoMap(ExtendedWifiNetworkSuggestion ewns) { 814 Set<ExtendedWifiNetworkSuggestion> extendedWifiNetworkSuggestions = 815 mPasspointInfo.get(ewns.wns.wifiConfiguration.FQDN); 816 if (extendedWifiNetworkSuggestions == null) { 817 extendedWifiNetworkSuggestions = new HashSet<>(); 818 } 819 extendedWifiNetworkSuggestions.remove(ewns); 820 extendedWifiNetworkSuggestions.add(ewns); 821 mPasspointInfo.put(ewns.wns.wifiConfiguration.FQDN, extendedWifiNetworkSuggestions); 822 } 823 removeFromPassPointInfoMap(ExtendedWifiNetworkSuggestion ewns)824 private void removeFromPassPointInfoMap(ExtendedWifiNetworkSuggestion ewns) { 825 Set<ExtendedWifiNetworkSuggestion> extendedWifiNetworkSuggestions = 826 mPasspointInfo.get(ewns.wns.wifiConfiguration.FQDN); 827 if (extendedWifiNetworkSuggestions == null 828 || !extendedWifiNetworkSuggestions.contains(ewns)) { 829 Log.wtf(TAG, "No Passpoint info found."); 830 return; 831 } 832 extendedWifiNetworkSuggestions.remove(ewns); 833 if (extendedWifiNetworkSuggestions.isEmpty()) { 834 mPasspointInfo.remove(ewns.wns.wifiConfiguration.FQDN); 835 } 836 } 837 startTrackingAppOpsChange(@onNull String packageName, int uid)838 private void startTrackingAppOpsChange(@NonNull String packageName, int uid) { 839 AppOpsChangedListener appOpsChangedListener = 840 new AppOpsChangedListener(packageName, uid); 841 mAppOps.startWatchingMode(OPSTR_CHANGE_WIFI_STATE, packageName, appOpsChangedListener); 842 mAppOpsChangedListenerPerApp.put(packageName, appOpsChangedListener); 843 } 844 845 /** 846 * Helper method to convert the incoming collection of public {@link WifiNetworkSuggestion} 847 * objects to a set of corresponding internal wrapper 848 * {@link ExtendedWifiNetworkSuggestion} objects. 849 */ convertToExtendedWnsSet( final Collection<WifiNetworkSuggestion> networkSuggestions, final PerAppInfo perAppInfo)850 private Set<ExtendedWifiNetworkSuggestion> convertToExtendedWnsSet( 851 final Collection<WifiNetworkSuggestion> networkSuggestions, 852 final PerAppInfo perAppInfo) { 853 return networkSuggestions 854 .stream() 855 .map(n -> ExtendedWifiNetworkSuggestion.fromWns(n, perAppInfo, 856 n.isInitialAutoJoinEnabled)) 857 .collect(Collectors.toSet()); 858 } 859 860 /** 861 * Helper method to convert the incoming collection of internal wrapper 862 * {@link ExtendedWifiNetworkSuggestion} objects to a set of corresponding public 863 * {@link WifiNetworkSuggestion} objects. 864 */ convertToWnsSet( final Collection<ExtendedWifiNetworkSuggestion> extNetworkSuggestions)865 private Set<WifiNetworkSuggestion> convertToWnsSet( 866 final Collection<ExtendedWifiNetworkSuggestion> extNetworkSuggestions) { 867 return extNetworkSuggestions 868 .stream() 869 .map(n -> n.wns) 870 .collect(Collectors.toSet()); 871 } 872 updateWifiConfigInWcmIfPresent( WifiConfiguration newConfig, int uid, String packageName)873 private void updateWifiConfigInWcmIfPresent( 874 WifiConfiguration newConfig, int uid, String packageName) { 875 WifiConfiguration configInWcm = 876 mWifiConfigManager.getConfiguredNetwork(newConfig.getProfileKey()); 877 if (configInWcm == null) return; 878 // !suggestion 879 if (!configInWcm.fromWifiNetworkSuggestion) return; 880 // is suggestion from same app. 881 if (configInWcm.creatorUid != uid 882 || !TextUtils.equals(configInWcm.creatorName, packageName)) { 883 return; 884 } 885 NetworkUpdateResult result = mWifiConfigManager.addOrUpdateNetwork( 886 newConfig, uid, packageName, false); 887 if (!result.isSuccess()) { 888 Log.e(TAG, "Failed to update config in WifiConfigManager"); 889 return; 890 } 891 if (mVerboseLoggingEnabled) { 892 Log.v(TAG, "Updated config in WifiConfigManager"); 893 } 894 } 895 896 /** 897 * Add the provided list of network suggestions from the corresponding app's active list. 898 */ add( List<WifiNetworkSuggestion> networkSuggestions, int uid, String packageName, @Nullable String featureId)899 public @WifiManager.NetworkSuggestionsStatusCode int add( 900 List<WifiNetworkSuggestion> networkSuggestions, int uid, String packageName, 901 @Nullable String featureId) { 902 if (!mWifiPermissionsUtil.doesUidBelongToCurrentUserOrDeviceOwner(uid)) { 903 Log.e(TAG, "UID " + uid + " not visible to the current user"); 904 return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL; 905 } 906 if (!mUserDataLoaded || mIsDeviceShuttingDown) { 907 Log.e(TAG, "Add Network suggestion before boot complete or when device is " 908 + "shutting down is not allowed."); 909 return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL; 910 } 911 if (networkSuggestions == null || networkSuggestions.isEmpty()) { 912 Log.w(TAG, "Empty list of network suggestions for " + packageName + ". Ignoring"); 913 return WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS; 914 } 915 if (mVerboseLoggingEnabled) { 916 Log.v(TAG, "Adding " + networkSuggestions.size() + " networks from " + packageName); 917 } 918 if (!validateNetworkSuggestions(networkSuggestions, packageName, uid)) { 919 Log.e(TAG, "Invalid suggestion add from app: " + packageName); 920 return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_INVALID; 921 } 922 int carrierId = mWifiCarrierInfoManager 923 .getCarrierIdForPackageWithCarrierPrivileges(packageName); 924 if (!validateCarrierNetworkSuggestions(networkSuggestions, uid, packageName, carrierId)) { 925 Log.e(TAG, "bad wifi suggestion from app: " + packageName); 926 return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_NOT_ALLOWED; 927 } 928 for (WifiNetworkSuggestion wns : networkSuggestions) { 929 wns.wifiConfiguration.convertLegacyFieldsToSecurityParamsIfNeeded(); 930 if (!WifiConfigurationUtil.addUpgradableSecurityTypeIfNecessary( 931 wns.wifiConfiguration)) { 932 Log.e(TAG, "Invalid suggestion add from app: " + packageName); 933 return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_INVALID; 934 } 935 } 936 937 PerAppInfo perAppInfo = mActiveNetworkSuggestionsPerApp.get(packageName); 938 if (perAppInfo == null) { 939 perAppInfo = new PerAppInfo(uid, packageName, featureId); 940 mActiveNetworkSuggestionsPerApp.put(packageName, perAppInfo); 941 if (mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(uid)) { 942 Log.i(TAG, "Setting the carrier provisioning app approved"); 943 perAppInfo.hasUserApproved = true; 944 mWifiMetrics.incrementNetworkSuggestionApiUsageNumOfAppInType( 945 APP_TYPE_NETWORK_PROVISIONING); 946 } else if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid) 947 || isAppWorkingAsCrossCarrierProvider(packageName)) { 948 // Bypass added for tests & shell commands. 949 Log.i(TAG, "Setting the test app approved"); 950 perAppInfo.hasUserApproved = true; 951 } else if (carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) { 952 Log.i(TAG, "Setting the carrier privileged app approved"); 953 perAppInfo.setCarrierId(carrierId); 954 mWifiMetrics.incrementNetworkSuggestionApiUsageNumOfAppInType( 955 APP_TYPE_CARRIER_PRIVILEGED); 956 } else { 957 if (isSuggestionFromForegroundApp(packageName)) { 958 sendUserApprovalDialog(packageName, uid); 959 } else { 960 sendUserApprovalNotificationIfNotApproved(packageName, uid); 961 } 962 mWifiMetrics.incrementNetworkSuggestionApiUsageNumOfAppInType( 963 APP_TYPE_NON_PRIVILEGED); 964 } 965 onSuggestionUserApprovalStatusChanged(uid, packageName); 966 startTrackingAppOpsChange(packageName, uid); 967 } 968 // If PerAppInfo is upgrade from pre-R, uid may not be set. 969 perAppInfo.setUid(uid); 970 // If App became carrier privileged, set the carrier Id. 971 perAppInfo.setCarrierId(carrierId); 972 Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestions = 973 convertToExtendedWnsSet(networkSuggestions, perAppInfo); 974 boolean isLowRamDevice = mActivityManager.isLowRamDevice(); 975 int networkSuggestionsMaxPerApp = 976 WifiManager.getMaxNumberOfNetworkSuggestionsPerApp(isLowRamDevice); 977 if (perAppInfo.extNetworkSuggestions.size() + extNetworkSuggestions.size() 978 > networkSuggestionsMaxPerApp) { 979 Set<Integer> keySet = extNetworkSuggestions 980 .stream() 981 .map(ExtendedWifiNetworkSuggestion::hashCode) 982 .collect(Collectors.toSet()); 983 Set<Integer> savedKeySet = new HashSet<>(perAppInfo.extNetworkSuggestions.keySet()); 984 savedKeySet.addAll(keySet); 985 if (savedKeySet.size() > networkSuggestionsMaxPerApp) { 986 Log.e(TAG, "Failed to add network suggestions for " + packageName 987 + ". Exceeds max per app, current list size: " 988 + perAppInfo.extNetworkSuggestions.size() 989 + ", new list size: " 990 + extNetworkSuggestions.size()); 991 return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP; 992 } 993 } 994 995 for (ExtendedWifiNetworkSuggestion ewns: extNetworkSuggestions) { 996 ExtendedWifiNetworkSuggestion oldEwns = perAppInfo.extNetworkSuggestions 997 .get(ewns.hashCode()); 998 // Keep the user connect choice and AnonymousIdentity 999 if (oldEwns != null) { 1000 ewns.connectChoice = oldEwns.connectChoice; 1001 ewns.connectChoiceRssi = oldEwns.connectChoiceRssi; 1002 ewns.anonymousIdentity = oldEwns.anonymousIdentity; 1003 // If user change the auto-join, keep the user choice. 1004 if (oldEwns.isAutojoinEnabled != oldEwns.wns.isInitialAutoJoinEnabled) { 1005 ewns.isAutojoinEnabled = oldEwns.isAutojoinEnabled; 1006 } 1007 } 1008 // If network has no IMSI protection and user didn't approve exemption, make it initial 1009 // auto join disabled 1010 if (isSimBasedPhase1Suggestion(ewns)) { 1011 int carrierIdFromSuggestion = getCarrierIdFromSuggestion(ewns); 1012 int subId = ewns.wns.wifiConfiguration.subscriptionId; 1013 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 1014 if (ewns.wns.wifiConfiguration.getSubscriptionGroup() != null) { 1015 subId = mWifiCarrierInfoManager.getActiveSubscriptionIdInGroup( 1016 ewns.wns.wifiConfiguration.getSubscriptionGroup()); 1017 } else { 1018 subId = mWifiCarrierInfoManager.getMatchingSubId(carrierIdFromSuggestion); 1019 } 1020 } 1021 if (!(mWifiCarrierInfoManager.requiresImsiEncryption(subId) 1022 || mWifiCarrierInfoManager.hasUserApprovedImsiPrivacyExemptionForCarrier( 1023 carrierIdFromSuggestion) 1024 || mWifiCarrierInfoManager.isOobPseudonymFeatureEnabled( 1025 carrierIdFromSuggestion))) { 1026 ewns.isAutojoinEnabled = false; 1027 } 1028 } 1029 mWifiMetrics.addNetworkSuggestionPriorityGroup(ewns.wns.priorityGroup); 1030 if (ewns.wns.passpointConfiguration == null) { 1031 if (ewns.wns.wifiConfiguration.isEnterprise()) { 1032 if (!mWifiKeyStore.updateNetworkKeys(ewns.wns.wifiConfiguration, null)) { 1033 Log.e(TAG, "Enterprise network install failure for SSID: " 1034 + ewns.wns.wifiConfiguration.SSID); 1035 continue; 1036 } 1037 } 1038 // If we have a config in WifiConfigManager for this suggestion, update 1039 // WifiConfigManager with the latest WifiConfig. 1040 // Note: Similar logic is present in PasspointManager for passpoint networks. 1041 updateWifiConfigInWcmIfPresent(ewns.createInternalWifiConfiguration( 1042 mWifiCarrierInfoManager), uid, packageName); 1043 addToScanResultMatchInfoMap(ewns); 1044 } else { 1045 ewns.wns.passpointConfiguration.setAutojoinEnabled(ewns.isAutojoinEnabled); 1046 // Install Passpoint config, if failure, ignore that suggestion 1047 if (!mWifiInjector.getPasspointManager().addOrUpdateProvider( 1048 ewns.wns.passpointConfiguration, uid, 1049 packageName, true, !ewns.wns.isUntrusted(), 1050 ewns.wns.isRestricted())) { 1051 Log.e(TAG, "Passpoint profile install failure for FQDN: " 1052 + ewns.wns.wifiConfiguration.FQDN); 1053 continue; 1054 } 1055 addToPasspointInfoMap(ewns); 1056 } 1057 perAppInfo.extNetworkSuggestions.remove(ewns.hashCode()); 1058 perAppInfo.extNetworkSuggestions.put(ewns.hashCode(), ewns); 1059 } 1060 for (OnSuggestionUpdateListener listener : mListeners) { 1061 listener.onSuggestionsAddedOrUpdated(networkSuggestions); 1062 } 1063 // Update the max size for this app. 1064 perAppInfo.maxSize = Math.max(perAppInfo.extNetworkSuggestions.size(), perAppInfo.maxSize); 1065 try { 1066 saveToStore(); 1067 } catch (OutOfMemoryError e) { 1068 Optional<PerAppInfo> appInfo = mActiveNetworkSuggestionsPerApp.values() 1069 .stream() 1070 .max(Comparator.comparingInt(a -> a.extNetworkSuggestions.size())); 1071 if (appInfo.isPresent()) { 1072 EventLog.writeEvent(0x534e4554, "245299920", appInfo.get().uid, 1073 "Trying to add large number of suggestion, num=" 1074 + appInfo.get().extNetworkSuggestions.size()); 1075 } else { 1076 Log.e(TAG, "serialize out of memory but no app has suggestion!"); 1077 } 1078 // Remove the most recently added suggestions, which should cause the failure. 1079 remove(networkSuggestions, uid, packageName, ACTION_REMOVE_SUGGESTION_DISCONNECT); 1080 return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL; 1081 } 1082 mWifiMetrics.incrementNetworkSuggestionApiNumModification(); 1083 mWifiMetrics.noteNetworkSuggestionApiListSizeHistogram(getAllMaxSizes()); 1084 return WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS; 1085 } 1086 getCarrierIdFromSuggestion(ExtendedWifiNetworkSuggestion ewns)1087 private int getCarrierIdFromSuggestion(ExtendedWifiNetworkSuggestion ewns) { 1088 if (ewns.wns.passpointConfiguration == null) { 1089 return ewns.wns.wifiConfiguration.carrierId; 1090 } 1091 return ewns.wns.passpointConfiguration.getCarrierId(); 1092 } 1093 isSimBasedPhase1Suggestion(ExtendedWifiNetworkSuggestion ewns)1094 private boolean isSimBasedPhase1Suggestion(ExtendedWifiNetworkSuggestion ewns) { 1095 if (ewns.wns.passpointConfiguration == null) { 1096 return ewns.wns.wifiConfiguration.enterpriseConfig != null 1097 && ewns.wns.wifiConfiguration.enterpriseConfig.isAuthenticationSimBased() 1098 && !ewns.wns.wifiConfiguration.enterpriseConfig.isEapMethodServerCertUsed(); 1099 } else { 1100 return ewns.wns.passpointConfiguration.getCredential().getSimCredential() != null; 1101 } 1102 } 1103 checkNetworkSuggestionsNoNulls(List<WifiNetworkSuggestion> networkSuggestions)1104 private boolean checkNetworkSuggestionsNoNulls(List<WifiNetworkSuggestion> networkSuggestions) { 1105 for (WifiNetworkSuggestion wns : networkSuggestions) { 1106 if (wns == null || wns.wifiConfiguration == null) { 1107 return false; 1108 } 1109 } 1110 return true; 1111 } 1112 validateNetworkSuggestions( List<WifiNetworkSuggestion> networkSuggestions, String packageName, int uid)1113 private boolean validateNetworkSuggestions( 1114 List<WifiNetworkSuggestion> networkSuggestions, String packageName, int uid) { 1115 if (!checkNetworkSuggestionsNoNulls(networkSuggestions)) { 1116 return false; 1117 } 1118 1119 BitSet supportedFeatures = mWifiInjector.getActiveModeWarden() 1120 .getPrimaryClientModeManager().getSupportedFeaturesBitSet(); 1121 1122 for (WifiNetworkSuggestion wns : networkSuggestions) { 1123 if (wns.passpointConfiguration == null) { 1124 WifiConfiguration config = wns.wifiConfiguration; 1125 if (!WifiConfigurationUtil.validate(config, supportedFeatures, 1126 WifiConfigurationUtil.VALIDATE_FOR_ADD)) { 1127 return false; 1128 } 1129 if (config.macRandomizationSetting != WifiConfiguration.RANDOMIZATION_PERSISTENT 1130 && config.macRandomizationSetting 1131 != WifiConfiguration.RANDOMIZATION_NON_PERSISTENT) { 1132 Log.w(TAG, "MAC randomization setting is invalid. Automatically setting" 1133 + " config to use persistent random MAC address."); 1134 config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; 1135 } 1136 if (config.isEnterprise()) { 1137 final WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; 1138 if (enterpriseConfig.isEapMethodServerCertUsed() 1139 && !enterpriseConfig.isMandatoryParameterSetForServerCertValidation()) { 1140 Log.e(TAG, "Insecure enterprise suggestion is invalid."); 1141 return false; 1142 } 1143 final String alias = enterpriseConfig.getClientKeyPairAliasInternal(); 1144 if (alias != null && !mWifiKeyStore.validateKeyChainAlias(alias, uid)) { 1145 Log.e(TAG, "Invalid client key pair KeyChain alias: " + alias); 1146 return false; 1147 } 1148 } 1149 1150 } else { 1151 if (!wns.passpointConfiguration.validate()) { 1152 EventLog.writeEvent(0x534e4554, "245299920", uid, 1153 "Trying to add invalid passpoint suggestion"); 1154 return false; 1155 } 1156 if (!wns.passpointConfiguration.isMacRandomizationEnabled()) { 1157 Log.w(TAG, "MAC randomization must be enabled on Passpoint suggestion." 1158 + " Defaulting to use persistent MAC randomization for invalid" 1159 + " configuration."); 1160 wns.passpointConfiguration.setMacRandomizationEnabled(true); 1161 wns.passpointConfiguration.setNonPersistentMacRandomizationEnabled(false); 1162 } 1163 } 1164 if (!isAppWorkingAsCrossCarrierProvider(packageName) 1165 && !isValidCarrierMergedNetworkSuggestion(wns)) { 1166 Log.e(TAG, "Merged carrier network must be a metered, enterprise config with a " 1167 + "valid subscription Id"); 1168 return false; 1169 } 1170 if (!SdkLevel.isAtLeastS()) { 1171 if (wns.wifiConfiguration.oemPaid) { 1172 Log.e(TAG, "OEM paid suggestions are only allowed from Android S."); 1173 return false; 1174 } 1175 if (wns.wifiConfiguration.oemPrivate) { 1176 Log.e(TAG, "OEM private suggestions are only allowed from Android S."); 1177 return false; 1178 } 1179 if (wns.wifiConfiguration.subscriptionId 1180 != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 1181 Log.e(TAG, "Setting Subscription Id is only allowed from Android S."); 1182 return false; 1183 } 1184 if (wns.priorityGroup != 0) { 1185 Log.e(TAG, "Setting Priority group is only allowed from Android S."); 1186 return false; 1187 } 1188 if (wns.wifiConfiguration.carrierMerged) { 1189 Log.e(TAG, "Setting carrier merged network is only allowed from Android S."); 1190 return false; 1191 } 1192 } 1193 if (!SdkLevel.isAtLeastT()) { 1194 if (wns.wifiConfiguration.getSubscriptionGroup() != null) { 1195 Log.e(TAG, "Setting subscription group is only allowed from Android T."); 1196 return false; 1197 } 1198 } 1199 if (wns.wifiConfiguration.getSubscriptionGroup() != null 1200 && wns.wifiConfiguration.subscriptionId 1201 != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 1202 Log.e(TAG, "Setting both subscription group and subscription id are not " 1203 + "allowed."); 1204 return false; 1205 } 1206 } 1207 return true; 1208 } 1209 isValidCarrierMergedNetworkSuggestion(WifiNetworkSuggestion wns)1210 private boolean isValidCarrierMergedNetworkSuggestion(WifiNetworkSuggestion wns) { 1211 if (!wns.wifiConfiguration.carrierMerged) { 1212 // Not carrier merged. 1213 return true; 1214 } 1215 if (!wns.wifiConfiguration.isEnterprise() && wns.passpointConfiguration == null) { 1216 // Carrier merged network must be a enterprise network. 1217 return false; 1218 } 1219 if (!WifiConfiguration.isMetered(wns.wifiConfiguration, null)) { 1220 // Carrier merged network must be metered. 1221 return false; 1222 } 1223 if (wns.wifiConfiguration.subscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID 1224 && wns.wifiConfiguration.getSubscriptionGroup() == null) { 1225 // Carrier merged network must have a valid subscription Id. 1226 return false; 1227 } 1228 return true; 1229 } 1230 validateCarrierNetworkSuggestions( List<WifiNetworkSuggestion> networkSuggestions, int uid, String packageName, int provisionerCarrierId)1231 private boolean validateCarrierNetworkSuggestions( 1232 List<WifiNetworkSuggestion> networkSuggestions, int uid, String packageName, 1233 int provisionerCarrierId) { 1234 boolean isAppWorkingAsCrossCarrierProvider = isAppWorkingAsCrossCarrierProvider( 1235 packageName); 1236 boolean isCrossCarrierProvisioner = 1237 mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(uid) 1238 || isAppWorkingAsCrossCarrierProvider; 1239 1240 for (WifiNetworkSuggestion suggestion : networkSuggestions) { 1241 WifiConfiguration wifiConfiguration = suggestion.wifiConfiguration; 1242 PasspointConfiguration passpointConfiguration = suggestion.passpointConfiguration; 1243 if (wifiConfiguration.carrierMerged && !areCarrierMergedSuggestionsAllowed( 1244 wifiConfiguration, packageName)) { 1245 // Carrier must be explicitly configured as merged carrier offload enabled 1246 return false; 1247 } 1248 if (!isCrossCarrierProvisioner && provisionerCarrierId 1249 == TelephonyManager.UNKNOWN_CARRIER_ID) { 1250 // If an app doesn't have carrier privileges or carrier provisioning permission, 1251 // suggests SIM-based network, sets CarrierId and sets SubscriptionId are illegal. 1252 if (wifiConfiguration.carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) { 1253 return false; 1254 } 1255 if (wifiConfiguration.subscriptionId 1256 != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 1257 return false; 1258 } 1259 if (wifiConfiguration.getSubscriptionGroup() != null) { 1260 return false; 1261 } 1262 if (passpointConfiguration == null) { 1263 if (wifiConfiguration.enterpriseConfig != null 1264 && wifiConfiguration.enterpriseConfig.isAuthenticationSimBased()) { 1265 return false; 1266 } 1267 } else { 1268 if (passpointConfiguration.getCredential() != null 1269 && passpointConfiguration.getCredential().getSimCredential() != null) { 1270 return false; 1271 } 1272 } 1273 } else { 1274 int carrierId = isCrossCarrierProvisioner ? wifiConfiguration.carrierId 1275 : provisionerCarrierId; 1276 int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 1277 if (wifiConfiguration.getSubscriptionGroup() != null) { 1278 subId = mWifiCarrierInfoManager.getActiveSubscriptionIdInGroup( 1279 wifiConfiguration.getSubscriptionGroup()); 1280 } else { 1281 subId = wifiConfiguration.subscriptionId; 1282 } 1283 if (!mWifiCarrierInfoManager 1284 .isSubIdMatchingCarrierId(subId, carrierId)) { 1285 Log.e(TAG, "Subscription ID doesn't match the carrier. CarrierId:" 1286 + carrierId + ", subscriptionId:" + subId + ", NetworkSuggestion:" 1287 + suggestion); 1288 return false; 1289 } 1290 } 1291 } 1292 return true; 1293 } 1294 stopTrackingAppOpsChange(@onNull String packageName)1295 private void stopTrackingAppOpsChange(@NonNull String packageName) { 1296 AppOpsChangedListener appOpsChangedListener = 1297 mAppOpsChangedListenerPerApp.remove(packageName); 1298 if (appOpsChangedListener == null) { 1299 Log.wtf(TAG, "No app ops listener found for " + packageName); 1300 return; 1301 } 1302 mAppOps.stopWatchingMode(appOpsChangedListener); 1303 } 1304 1305 /** 1306 * Remove provided list from that App active list. If provided list is empty, will remove all. 1307 * Will disconnect network if current connected network is in the remove list. 1308 */ removeInternal( @onNull Collection<ExtendedWifiNetworkSuggestion> extNetworkSuggestions, @NonNull String packageName, @NonNull PerAppInfo perAppInfo, @WifiManager.ActionAfterRemovingSuggestion int action)1309 private void removeInternal( 1310 @NonNull Collection<ExtendedWifiNetworkSuggestion> extNetworkSuggestions, 1311 @NonNull String packageName, 1312 @NonNull PerAppInfo perAppInfo, @WifiManager.ActionAfterRemovingSuggestion int action) { 1313 // Get internal suggestions 1314 Set<ExtendedWifiNetworkSuggestion> removingExtSuggestions = 1315 new HashSet<>(perAppInfo.extNetworkSuggestions.values()); 1316 if (!extNetworkSuggestions.isEmpty()) { 1317 // Keep the internal suggestions need to remove. 1318 removingExtSuggestions.retainAll(extNetworkSuggestions); 1319 perAppInfo.extNetworkSuggestions.values().removeAll(extNetworkSuggestions); 1320 } else { 1321 // empty list is used to clear everything for the app. Store a copy for use below. 1322 perAppInfo.extNetworkSuggestions.clear(); 1323 } 1324 if (perAppInfo.extNetworkSuggestions.isEmpty()) { 1325 // Note: We don't remove the app entry even if there is no active suggestions because 1326 // we want to keep the notification state for all apps that have ever provided 1327 // suggestions. 1328 if (mVerboseLoggingEnabled) Log.v(TAG, "No active suggestions for " + packageName); 1329 } 1330 // Clear the cache. 1331 WifiConfiguration connected = mWifiInjector.getActiveModeWarden() 1332 .getPrimaryClientModeManager().getConnectedWifiConfiguration(); 1333 List<WifiNetworkSuggestion> removingSuggestions = new ArrayList<>(); 1334 for (ExtendedWifiNetworkSuggestion ewns : removingExtSuggestions) { 1335 removeNetworkSuggestionCache(ewns); 1336 removingSuggestions.add(ewns.wns); 1337 WifiConfiguration removing = ewns 1338 .createInternalWifiConfiguration(mWifiCarrierInfoManager); 1339 WifiConfiguration cached = mWifiConfigManager.getConfiguredNetwork( 1340 removing.getProfileKey()); 1341 if (connected != null && cached != null && cached.networkId == connected.networkId 1342 && action == ACTION_REMOVE_SUGGESTION_LINGER) { 1343 mWifiInjector.getActiveModeWarden().getPrimaryClientModeManager() 1344 .setShouldReduceNetworkScore(true); 1345 // Execute when linger time out clean up the cache in WifiConfigManager. 1346 mHandler.postDelayed(() -> removeSuggestionFromWifiConfigManager(ewns), 1347 getLingerDelayMs()); 1348 } else { 1349 // Remove the config from WifiConfigManager. If current connected suggestion is 1350 // remove, would trigger a disconnect. 1351 mWifiConfigManager.removeSuggestionConfiguredNetwork(removing); 1352 } 1353 } 1354 for (OnSuggestionUpdateListener listener : mListeners) { 1355 listener.onSuggestionsRemoved(removingSuggestions); 1356 } 1357 } 1358 removeNetworkSuggestionCache(ExtendedWifiNetworkSuggestion ewns)1359 private void removeNetworkSuggestionCache(ExtendedWifiNetworkSuggestion ewns) { 1360 if (ewns.wns.passpointConfiguration != null) { 1361 // Clear the Passpoint config. 1362 mWifiInjector.getPasspointManager().removeProvider( 1363 ewns.perAppInfo.uid, 1364 false, 1365 ewns.wns.passpointConfiguration.getUniqueId(), null); 1366 removeFromPassPointInfoMap(ewns); 1367 } else { 1368 if (ewns.wns.wifiConfiguration.isEnterprise()) { 1369 mWifiKeyStore.removeKeys(ewns.wns.wifiConfiguration.enterpriseConfig, false); 1370 } 1371 removeFromScanResultMatchInfoMapAndRemoveRelatedScoreCard(ewns, true); 1372 mWifiConfigManager.removeConnectChoiceFromAllNetworks(ewns 1373 .createInternalWifiConfiguration(mWifiCarrierInfoManager) 1374 .getProfileKey()); 1375 } 1376 } 1377 removeSuggestionFromWifiConfigManager( ExtendedWifiNetworkSuggestion extendedWifiNetworkSuggestion)1378 private void removeSuggestionFromWifiConfigManager( 1379 ExtendedWifiNetworkSuggestion extendedWifiNetworkSuggestion) { 1380 PerAppInfo perAppInfo = extendedWifiNetworkSuggestion.perAppInfo; 1381 if (perAppInfo.extNetworkSuggestions.containsValue(extendedWifiNetworkSuggestion)) { 1382 // If the suggestion is added by app again, do not remove it from WifiConfigManager. 1383 return; 1384 } 1385 mWifiConfigManager.removeSuggestionConfiguredNetwork(extendedWifiNetworkSuggestion 1386 .createInternalWifiConfiguration(mWifiCarrierInfoManager)); 1387 } 1388 1389 /** 1390 * Remove the provided list of network suggestions from the corresponding app's active list. 1391 */ remove( List<WifiNetworkSuggestion> networkSuggestions, int uid, String packageName, @WifiManager.ActionAfterRemovingSuggestion int action)1392 public @WifiManager.NetworkSuggestionsStatusCode int remove( 1393 List<WifiNetworkSuggestion> networkSuggestions, int uid, String packageName, 1394 @WifiManager.ActionAfterRemovingSuggestion int action) { 1395 if (!mWifiPermissionsUtil.doesUidBelongToCurrentUserOrDeviceOwner(uid)) { 1396 Log.e(TAG, "UID " + uid + " not visible to the current user"); 1397 return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL; 1398 } 1399 if (!mUserDataLoaded || mIsDeviceShuttingDown) { 1400 Log.e(TAG, "Remove Network suggestion before boot complete or when device is " 1401 + "shutting down is not allowed."); 1402 return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL; 1403 } 1404 if (networkSuggestions == null) { 1405 Log.w(TAG, "Null list of network suggestions for " + packageName + ". Ignoring"); 1406 return WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS; 1407 } 1408 if (mVerboseLoggingEnabled) { 1409 Log.v(TAG, "Removing " + networkSuggestions.size() + " networks from " + packageName); 1410 } 1411 if (!checkNetworkSuggestionsNoNulls(networkSuggestions)) { 1412 Log.e(TAG, "Null in suggestion remove from app: " + packageName); 1413 return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID; 1414 } 1415 PerAppInfo perAppInfo = mActiveNetworkSuggestionsPerApp.get(packageName); 1416 if (perAppInfo == null) { 1417 Log.e(TAG, "Failed to remove network suggestions for " + packageName 1418 + ". No network suggestions found"); 1419 return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID; 1420 } 1421 Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestions = 1422 convertToExtendedWnsSet(networkSuggestions, perAppInfo); 1423 Set<Integer> keySet = extNetworkSuggestions 1424 .stream() 1425 .map(ExtendedWifiNetworkSuggestion::hashCode) 1426 .collect(Collectors.toSet()); 1427 // check if all the request network suggestions are present in the active list. 1428 if (!extNetworkSuggestions.isEmpty() 1429 && !perAppInfo.extNetworkSuggestions.keySet().containsAll(keySet)) { 1430 Log.e(TAG, "Failed to remove network suggestions for " + packageName 1431 + ". Network suggestions not found in active network suggestions"); 1432 return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID; 1433 } 1434 removeInternal(extNetworkSuggestions, packageName, perAppInfo, action); 1435 saveToStore(); 1436 mWifiMetrics.incrementNetworkSuggestionApiNumModification(); 1437 mWifiMetrics.noteNetworkSuggestionApiListSizeHistogram(getAllMaxSizes()); 1438 return WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS; 1439 } 1440 1441 /** 1442 * Remove all tracking of the app that has been uninstalled. 1443 */ removeApp(@onNull String packageName)1444 public void removeApp(@NonNull String packageName) { 1445 PerAppInfo perAppInfo = mActiveNetworkSuggestionsPerApp.get(packageName); 1446 if (perAppInfo == null) return; 1447 removeInternal(List.of(), packageName, perAppInfo, ACTION_REMOVE_SUGGESTION_DISCONNECT); 1448 // Stop tracking app-op changes when the App is removed from suggestion database 1449 stopTrackingAppOpsChange(packageName); 1450 // Remove the package fully from the internal database. 1451 mActiveNetworkSuggestionsPerApp.remove(packageName); 1452 RemoteCallbackList<ISuggestionConnectionStatusListener> listenerTracker = 1453 mSuggestionStatusListenerPerApp.remove(packageName); 1454 if (listenerTracker != null) listenerTracker.kill(); 1455 saveToStore(); 1456 Log.i(TAG, "Removed " + packageName); 1457 } 1458 1459 /** 1460 * Get all network suggestion for target App 1461 * @return List of WifiNetworkSuggestions 1462 */ get(@onNull String packageName, int uid)1463 public @NonNull List<WifiNetworkSuggestion> get(@NonNull String packageName, int uid) { 1464 List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<>(); 1465 if (!mWifiPermissionsUtil.doesUidBelongToCurrentUserOrDeviceOwner(uid)) { 1466 Log.e(TAG, "UID " + uid + " not visible to the current user"); 1467 return networkSuggestionList; 1468 } 1469 if (!mUserDataLoaded) { 1470 Log.e(TAG, "Get Network suggestion before boot complete is not allowed."); 1471 return networkSuggestionList; 1472 } 1473 PerAppInfo perAppInfo = mActiveNetworkSuggestionsPerApp.get(packageName); 1474 // if App never suggested return empty list. 1475 if (perAppInfo == null) return networkSuggestionList; 1476 for (ExtendedWifiNetworkSuggestion extendedSuggestion : perAppInfo.extNetworkSuggestions 1477 .values()) { 1478 networkSuggestionList.add(extendedSuggestion.wns); 1479 } 1480 return networkSuggestionList; 1481 } 1482 1483 /** 1484 * Clear all internal state (for network settings reset). 1485 */ clear()1486 public void clear() { 1487 Iterator<Map.Entry<String, PerAppInfo>> iter = 1488 mActiveNetworkSuggestionsPerApp.entrySet().iterator(); 1489 while (iter.hasNext()) { 1490 Map.Entry<String, PerAppInfo> entry = iter.next(); 1491 removeInternal(List.of(), entry.getKey(), entry.getValue(), 1492 ACTION_REMOVE_SUGGESTION_DISCONNECT); 1493 // Stop tracking app-op changes when the App is removed from suggestion database 1494 stopTrackingAppOpsChange(entry.getKey()); 1495 iter.remove(); 1496 } 1497 mSuggestionStatusListenerPerApp.clear(); 1498 mSuggestionUserApprovalStatusListenerPerApp.clear(); 1499 resetNotification(); 1500 saveToStore(); 1501 Log.i(TAG, "Cleared all internal state"); 1502 } 1503 1504 /** 1505 * Check if network suggestions are enabled or disabled for the app. 1506 */ hasUserApprovedForApp(String packageName)1507 public boolean hasUserApprovedForApp(String packageName) { 1508 PerAppInfo perAppInfo = mActiveNetworkSuggestionsPerApp.get(packageName); 1509 if (perAppInfo == null) return false; 1510 1511 return perAppInfo.hasUserApproved; 1512 } 1513 1514 /** 1515 * Enable or Disable network suggestions for the app. 1516 */ setHasUserApprovedForApp(boolean approved, int uid, String packageName)1517 public void setHasUserApprovedForApp(boolean approved, int uid, String packageName) { 1518 PerAppInfo perAppInfo = mActiveNetworkSuggestionsPerApp.get(packageName); 1519 if (perAppInfo == null) return; 1520 1521 if (mVerboseLoggingEnabled) { 1522 Log.v(TAG, "Setting the app " + packageName 1523 + (approved ? " approved" : " not approved")); 1524 } 1525 perAppInfo.hasUserApproved = approved; 1526 onSuggestionUserApprovalStatusChanged(uid, packageName); 1527 saveToStore(); 1528 } 1529 1530 /** 1531 * When user approve the IMSI protection exemption for carrier or the IMSI protection is 1532 * enabled, restore the initial auto join configure. If user already change it to enabled, 1533 * keep that choice. 1534 */ restoreInitialAutojoinForCarrierId(int carrierId, boolean allowAutoJoin)1535 private void restoreInitialAutojoinForCarrierId(int carrierId, boolean allowAutoJoin) { 1536 for (PerAppInfo appInfo : mActiveNetworkSuggestionsPerApp.values()) { 1537 for (ExtendedWifiNetworkSuggestion ewns : appInfo.extNetworkSuggestions.values()) { 1538 if (!(isSimBasedPhase1Suggestion(ewns) 1539 && getCarrierIdFromSuggestion(ewns) == carrierId)) { 1540 continue; 1541 } 1542 if (ewns.isAutojoinEnabled == allowAutoJoin) { 1543 continue; 1544 } 1545 if (mVerboseLoggingEnabled) { 1546 Log.v(TAG, "Restore auto-join for suggestion: " + ewns); 1547 } 1548 if (allowAutoJoin) { 1549 ewns.isAutojoinEnabled |= ewns.wns.isInitialAutoJoinEnabled; 1550 } else { 1551 ewns.isAutojoinEnabled = false; 1552 } 1553 // Restore passpoint provider auto join. 1554 if (ewns.wns.passpointConfiguration != null) { 1555 mWifiInjector.getPasspointManager() 1556 .enableAutojoin(ewns.wns.passpointConfiguration.getUniqueId(), 1557 null, ewns.isAutojoinEnabled); 1558 } else { 1559 // Update WifiConfigManager to sync auto-join. 1560 updateWifiConfigInWcmIfPresent(ewns.createInternalWifiConfiguration( 1561 mWifiCarrierInfoManager), 1562 ewns.perAppInfo.uid, ewns.perAppInfo.packageName); 1563 } 1564 } 1565 } 1566 saveToStore(); 1567 } 1568 1569 /** 1570 * Returns a set of all network suggestions across all apps. 1571 */ 1572 @VisibleForTesting getAllNetworkSuggestions()1573 public Set<WifiNetworkSuggestion> getAllNetworkSuggestions() { 1574 return mActiveNetworkSuggestionsPerApp.values() 1575 .stream() 1576 .flatMap(e -> convertToWnsSet(e.extNetworkSuggestions.values()) 1577 .stream()) 1578 .collect(Collectors.toSet()); 1579 } 1580 1581 /** 1582 * Returns a set of all network suggestions across all apps that have been approved by user. 1583 */ getAllApprovedNetworkSuggestions()1584 public Set<WifiNetworkSuggestion> getAllApprovedNetworkSuggestions() { 1585 return mActiveNetworkSuggestionsPerApp.values() 1586 .stream() 1587 .filter(e -> e.isApproved()) 1588 .flatMap(e -> convertToWnsSet(e.extNetworkSuggestions.values()) 1589 .stream()) 1590 .collect(Collectors.toSet()); 1591 } 1592 1593 /** 1594 * Get all user approved, non-passpoint networks from suggestion. 1595 */ getAllScanOptimizationSuggestionNetworks()1596 public List<WifiConfiguration> getAllScanOptimizationSuggestionNetworks() { 1597 List<WifiConfiguration> networks = new ArrayList<>(); 1598 for (PerAppInfo info : mActiveNetworkSuggestionsPerApp.values()) { 1599 if (!info.isApproved()) { 1600 continue; 1601 } 1602 for (ExtendedWifiNetworkSuggestion ewns : info.extNetworkSuggestions.values()) { 1603 if (ewns.wns.getPasspointConfig() != null) { 1604 continue; 1605 } 1606 WifiConfiguration network = mWifiConfigManager 1607 .getConfiguredNetwork(ewns.wns.getWifiConfiguration() 1608 .getProfileKey()); 1609 if (network == null) { 1610 network = ewns.createInternalWifiConfiguration(mWifiCarrierInfoManager); 1611 } 1612 networks.add(network); 1613 } 1614 } 1615 return networks; 1616 } 1617 1618 /** 1619 * Get all user-approved Passpoint networks from suggestion. 1620 * 1621 * @param requireSsid If true, this method will only return Passpoint suggestions that include 1622 * an SSID. If false, this method will return all Passpoint suggestions, including those 1623 * which do not include an SSID. 1624 * <p>Note: Passpoint SSIDs are recorded upon successful connection to a network. Having an 1625 * SSID indicates that a Passpoint network has connected since the last reboot. 1626 */ getAllPasspointScanOptimizationSuggestionNetworks( boolean requireSsid)1627 public List<WifiConfiguration> getAllPasspointScanOptimizationSuggestionNetworks( 1628 boolean requireSsid) { 1629 List<WifiConfiguration> networks = new ArrayList<>(); 1630 for (PerAppInfo info : mActiveNetworkSuggestionsPerApp.values()) { 1631 if (!info.isApproved()) { 1632 continue; 1633 } 1634 for (ExtendedWifiNetworkSuggestion ewns : info.extNetworkSuggestions.values()) { 1635 if (ewns.wns.getPasspointConfig() == null) { 1636 continue; 1637 } 1638 WifiConfiguration network = mWifiConfigManager 1639 .getConfiguredNetwork(ewns.wns.getWifiConfiguration() 1640 .getProfileKey()); 1641 if (network == null) { 1642 network = ewns.createInternalWifiConfiguration(mWifiCarrierInfoManager); 1643 } 1644 network.SSID = mWifiInjector.getPasspointManager() 1645 .getMostRecentSsidForProfile(network.getPasspointUniqueId()); 1646 if (requireSsid && network.SSID == null) { 1647 continue; 1648 } 1649 networks.add(network); 1650 } 1651 } 1652 return networks; 1653 } 1654 getAllMaxSizes()1655 private List<Integer> getAllMaxSizes() { 1656 return mActiveNetworkSuggestionsPerApp.values() 1657 .stream() 1658 .map(e -> e.maxSize) 1659 .collect(Collectors.toList()); 1660 } 1661 getPrivateBroadcast(@onNull String action, @NonNull Pair<String, String> extra1, @NonNull Pair<String, Integer> extra2)1662 private PendingIntent getPrivateBroadcast(@NonNull String action, 1663 @NonNull Pair<String, String> extra1, @NonNull Pair<String, Integer> extra2) { 1664 Intent intent = new Intent(action) 1665 .setPackage(mContext.getServiceWifiPackageName()) 1666 .putExtra(extra1.first, extra1.second) 1667 .putExtra(extra2.first, extra2.second); 1668 return mFrameworkFacade.getBroadcast(mContext, 0, intent, 1669 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); 1670 } 1671 1672 /** 1673 * Check if the request came from foreground app. 1674 */ isSuggestionFromForegroundApp(@onNull String packageName)1675 private boolean isSuggestionFromForegroundApp(@NonNull String packageName) { 1676 try { 1677 return mActivityManager.getPackageImportance(packageName) 1678 <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; 1679 } catch (SecurityException e) { 1680 Log.e(TAG, "Failed to check the app state", e); 1681 return false; 1682 } 1683 } 1684 sendUserApprovalDialog(@onNull String packageName, int uid)1685 private void sendUserApprovalDialog(@NonNull String packageName, int uid) { 1686 CharSequence appName = mFrameworkFacade.getAppName(mContext, packageName, uid); 1687 mWifiInjector.getWifiDialogManager().createSimpleDialog( 1688 mResources.getString(R.string.wifi_suggestion_title), 1689 mResources.getString(R.string.wifi_suggestion_content, appName), 1690 mResources.getString(R.string.wifi_suggestion_action_allow_app), 1691 mResources.getString(R.string.wifi_suggestion_action_disallow_app), 1692 null /* neutralButtonText */, 1693 new WifiDialogManager.SimpleDialogCallback() { 1694 @Override 1695 public void onPositiveButtonClicked() { 1696 handleUserAllowAction(uid, packageName); 1697 } 1698 1699 @Override 1700 public void onNegativeButtonClicked() { 1701 handleUserDisallowAction(uid, packageName); 1702 } 1703 1704 @Override 1705 public void onNeutralButtonClicked() { 1706 // Not used. 1707 handleUserDismissAction(); 1708 } 1709 1710 @Override 1711 public void onCancelled() { 1712 handleUserDismissAction(); 1713 } 1714 }, 1715 new WifiThreadRunner(mHandler)).launchDialog(); 1716 mNotificationUpdateTime = mClock.getElapsedSinceBootMillis() 1717 + NOTIFICATION_UPDATE_DELAY_MILLS; 1718 mIsLastUserApprovalUiDialog = true; 1719 } 1720 sendUserApprovalNotification(@onNull String packageName, int uid)1721 private void sendUserApprovalNotification(@NonNull String packageName, int uid) { 1722 Notification.Action userAllowAppNotificationAction = 1723 new Notification.Action.Builder(null, 1724 mResources.getText(R.string.wifi_suggestion_action_allow_app), 1725 getPrivateBroadcast(NOTIFICATION_USER_ALLOWED_APP_INTENT_ACTION, 1726 Pair.create(EXTRA_PACKAGE_NAME, packageName), 1727 Pair.create(EXTRA_UID, uid))) 1728 .build(); 1729 Notification.Action userDisallowAppNotificationAction = 1730 new Notification.Action.Builder(null, 1731 mResources.getText(R.string.wifi_suggestion_action_disallow_app), 1732 getPrivateBroadcast(NOTIFICATION_USER_DISALLOWED_APP_INTENT_ACTION, 1733 Pair.create(EXTRA_PACKAGE_NAME, packageName), 1734 Pair.create(EXTRA_UID, uid))) 1735 .build(); 1736 1737 CharSequence appName = mFrameworkFacade.getAppName(mContext, packageName, uid); 1738 Notification notification = mFrameworkFacade.makeNotificationBuilder( 1739 mContext, WifiService.NOTIFICATION_NETWORK_STATUS) 1740 .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(), 1741 com.android.wifi.resources.R.drawable.stat_notify_wifi_in_range)) 1742 .setTicker(mResources.getString(R.string.wifi_suggestion_title)) 1743 .setContentTitle(mResources.getString(R.string.wifi_suggestion_title)) 1744 .setStyle(new Notification.BigTextStyle() 1745 .bigText(mResources.getString(R.string.wifi_suggestion_content, appName))) 1746 .setDeleteIntent(getPrivateBroadcast(NOTIFICATION_USER_DISMISSED_INTENT_ACTION, 1747 Pair.create(EXTRA_PACKAGE_NAME, packageName), Pair.create(EXTRA_UID, uid))) 1748 .setShowWhen(false) 1749 .setLocalOnly(true) 1750 .setColor(mResources.getColor(android.R.color.system_notification_accent_color, 1751 mContext.getTheme())) 1752 .addAction(userAllowAppNotificationAction) 1753 .addAction(userDisallowAppNotificationAction) 1754 .setTimeoutAfter(NOTIFICATION_EXPIRY_MILLS) 1755 .build(); 1756 1757 // Post the notification. 1758 mNotificationManager.notify(SystemMessage.NOTE_NETWORK_SUGGESTION_AVAILABLE, notification); 1759 mNotificationUpdateTime = mClock.getElapsedSinceBootMillis() 1760 + NOTIFICATION_UPDATE_DELAY_MILLS; 1761 mIsLastUserApprovalUiDialog = false; 1762 } 1763 1764 /** 1765 * Send user approval notification if the app is not approved 1766 * @param packageName app package name 1767 * @param uid app UID 1768 * @return true if app is not approved and send notification. 1769 */ sendUserApprovalNotificationIfNotApproved( @onNull String packageName, @NonNull int uid)1770 private boolean sendUserApprovalNotificationIfNotApproved( 1771 @NonNull String packageName, @NonNull int uid) { 1772 if (!mActiveNetworkSuggestionsPerApp.containsKey(packageName)) { 1773 Log.wtf(TAG, "AppInfo is missing for " + packageName); 1774 return false; 1775 } 1776 if (mActiveNetworkSuggestionsPerApp.get(packageName).hasUserApproved) { 1777 return false; // already approved. 1778 } 1779 1780 if (mNotificationUpdateTime > mClock.getElapsedSinceBootMillis()) { 1781 return false; // Active notification is still available, do not update. 1782 } 1783 Log.i(TAG, "Sending user approval notification for " + packageName); 1784 sendUserApprovalNotification(packageName, uid); 1785 return true; 1786 } 1787 1788 private @Nullable Set<ExtendedWifiNetworkSuggestion> getNetworkSuggestionsForScanResultMatchInfo( @onNull ScanResultMatchInfo scanResultMatchInfo, @Nullable MacAddress bssid)1789 getNetworkSuggestionsForScanResultMatchInfo( 1790 @NonNull ScanResultMatchInfo scanResultMatchInfo, @Nullable MacAddress bssid) { 1791 Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestions = new HashSet<>(); 1792 if (bssid != null) { 1793 Set<ExtendedWifiNetworkSuggestion> matchingExtNetworkSuggestionsWithBssid = 1794 mActiveScanResultMatchInfoWithBssid.get( 1795 Pair.create(scanResultMatchInfo, bssid)); 1796 if (matchingExtNetworkSuggestionsWithBssid != null) { 1797 extNetworkSuggestions.addAll(matchingExtNetworkSuggestionsWithBssid); 1798 } 1799 } 1800 Set<ExtendedWifiNetworkSuggestion> matchingNetworkSuggestionsWithNoBssid = 1801 mActiveScanResultMatchInfoWithNoBssid.get(scanResultMatchInfo); 1802 if (matchingNetworkSuggestionsWithNoBssid != null) { 1803 extNetworkSuggestions.addAll(matchingNetworkSuggestionsWithNoBssid); 1804 } 1805 if (extNetworkSuggestions.isEmpty()) { 1806 return null; 1807 } 1808 return extNetworkSuggestions; 1809 } 1810 getNetworkSuggestionsForFqdnMatch( @ullable String fqdn)1811 private @Nullable Set<ExtendedWifiNetworkSuggestion> getNetworkSuggestionsForFqdnMatch( 1812 @Nullable String fqdn) { 1813 if (TextUtils.isEmpty(fqdn)) { 1814 return null; 1815 } 1816 return mPasspointInfo.get(fqdn); 1817 } 1818 1819 /** 1820 * Returns a set of all network suggestions matching the provided FQDN. 1821 */ getNetworkSuggestionsForFqdn(String fqdn)1822 public @NonNull Set<ExtendedWifiNetworkSuggestion> getNetworkSuggestionsForFqdn(String fqdn) { 1823 Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestions = 1824 getNetworkSuggestionsForFqdnMatch(fqdn); 1825 if (extNetworkSuggestions == null) { 1826 return Set.of(); 1827 } 1828 Set<ExtendedWifiNetworkSuggestion> approvedExtNetworkSuggestions = new HashSet<>(); 1829 for (ExtendedWifiNetworkSuggestion ewns : extNetworkSuggestions) { 1830 if (!ewns.perAppInfo.isApproved()) { 1831 sendUserApprovalNotificationIfNotApproved(ewns.perAppInfo.packageName, 1832 ewns.perAppInfo.uid); 1833 continue; 1834 } 1835 if (ewns.wns.wifiConfiguration.carrierMerged && !areCarrierMergedSuggestionsAllowed( 1836 ewns.wns.wifiConfiguration, ewns.perAppInfo.packageName)) { 1837 continue; 1838 } 1839 if (isSimBasedPhase1Suggestion(ewns)) { 1840 mWifiCarrierInfoManager.sendImsiProtectionExemptionNotificationIfRequired( 1841 getCarrierIdFromSuggestion(ewns)); 1842 } 1843 approvedExtNetworkSuggestions.add(ewns); 1844 } 1845 1846 if (approvedExtNetworkSuggestions.isEmpty()) { 1847 return Set.of(); 1848 } 1849 if (mVerboseLoggingEnabled) { 1850 Log.v(TAG, "getNetworkSuggestionsForFqdn Found " 1851 + approvedExtNetworkSuggestions + " for " + fqdn); 1852 } 1853 return approvedExtNetworkSuggestions; 1854 } 1855 1856 /** 1857 * Returns a set of all network suggestions matching the provided scan detail. 1858 */ getNetworkSuggestionsForScanDetail( @onNull ScanDetail scanDetail)1859 public @NonNull Set<ExtendedWifiNetworkSuggestion> getNetworkSuggestionsForScanDetail( 1860 @NonNull ScanDetail scanDetail) { 1861 ScanResult scanResult = scanDetail.getScanResult(); 1862 if (scanResult == null) { 1863 Log.e(TAG, "No scan result found in scan detail"); 1864 return Set.of(); 1865 } 1866 Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestions = null; 1867 try { 1868 ScanResultMatchInfo scanResultMatchInfo = 1869 ScanResultMatchInfo.fromScanResult(scanResult); 1870 extNetworkSuggestions = getNetworkSuggestionsForScanResultMatchInfo( 1871 scanResultMatchInfo, MacAddress.fromString(scanResult.BSSID)); 1872 } catch (IllegalArgumentException e) { 1873 Log.e(TAG, "Failed to lookup network from scan result match info map", e); 1874 } 1875 if (extNetworkSuggestions == null) { 1876 return Set.of(); 1877 } 1878 Set<ExtendedWifiNetworkSuggestion> approvedExtNetworkSuggestions = new HashSet<>(); 1879 for (ExtendedWifiNetworkSuggestion ewns : extNetworkSuggestions) { 1880 if (!ewns.perAppInfo.isApproved()) { 1881 sendUserApprovalNotificationIfNotApproved(ewns.perAppInfo.packageName, 1882 ewns.perAppInfo.uid); 1883 continue; 1884 } 1885 if (ewns.wns.wifiConfiguration.carrierMerged && !areCarrierMergedSuggestionsAllowed( 1886 ewns.wns.wifiConfiguration, ewns.perAppInfo.packageName)) { 1887 continue; 1888 } 1889 if (isSimBasedPhase1Suggestion(ewns)) { 1890 mWifiCarrierInfoManager.sendImsiProtectionExemptionNotificationIfRequired( 1891 getCarrierIdFromSuggestion(ewns)); 1892 } 1893 approvedExtNetworkSuggestions.add(ewns); 1894 } 1895 1896 if (approvedExtNetworkSuggestions.isEmpty()) { 1897 return Set.of(); 1898 } 1899 if (mVerboseLoggingEnabled) { 1900 Log.v(TAG, "getNetworkSuggestionsForScanDetail Found " 1901 + approvedExtNetworkSuggestions + " for " + scanResult.SSID 1902 + "[" + scanResult.capabilities + "]"); 1903 } 1904 return approvedExtNetworkSuggestions; 1905 } 1906 1907 /** 1908 * Returns a set of all network suggestions matching the provided the WifiConfiguration. 1909 */ getNetworkSuggestionsForWifiConfiguration( @onNull WifiConfiguration wifiConfiguration, @Nullable String bssid)1910 public @Nullable Set<ExtendedWifiNetworkSuggestion> getNetworkSuggestionsForWifiConfiguration( 1911 @NonNull WifiConfiguration wifiConfiguration, @Nullable String bssid) { 1912 Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestions = null; 1913 if (wifiConfiguration.isPasspoint()) { 1914 extNetworkSuggestions = getNetworkSuggestionsForFqdnMatch(wifiConfiguration.FQDN); 1915 } else { 1916 try { 1917 ScanResultMatchInfo scanResultMatchInfo = 1918 ScanResultMatchInfo.fromWifiConfiguration(wifiConfiguration); 1919 extNetworkSuggestions = getNetworkSuggestionsForScanResultMatchInfo( 1920 scanResultMatchInfo, bssid == null ? null : MacAddress.fromString(bssid)); 1921 } catch (IllegalArgumentException e) { 1922 Log.e(TAG, "Failed to lookup network from scan result match info map", e); 1923 } 1924 } 1925 if (extNetworkSuggestions == null || extNetworkSuggestions.isEmpty()) { 1926 return null; 1927 } 1928 Set<ExtendedWifiNetworkSuggestion> approvedExtNetworkSuggestions = 1929 extNetworkSuggestions 1930 .stream() 1931 .filter(n -> n.perAppInfo.isApproved()) 1932 .collect(Collectors.toSet()); 1933 if (approvedExtNetworkSuggestions.isEmpty()) { 1934 return null; 1935 } 1936 if (mVerboseLoggingEnabled) { 1937 Log.v(TAG, "getNetworkSuggestionsForWifiConfiguration Found " 1938 + approvedExtNetworkSuggestions + " for " + wifiConfiguration.SSID 1939 + wifiConfiguration.FQDN + "[" + wifiConfiguration.allowedKeyManagement + "]"); 1940 } 1941 return approvedExtNetworkSuggestions; 1942 } 1943 1944 /** 1945 * Retrieve the WifiConfigurations for all matched suggestions which allow user manually connect 1946 * and user already approved for non-open networks. 1947 */ getWifiConfigForMatchedNetworkSuggestionsSharedWithUser( List<ScanResult> scanResults)1948 public @NonNull List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser( 1949 List<ScanResult> scanResults) { 1950 // Create a HashSet to avoid return multiple result for duplicate ScanResult. 1951 Set<String> networkKeys = new HashSet<>(); 1952 List<WifiConfiguration> sharedWifiConfigs = new ArrayList<>(); 1953 for (ScanResult scanResult : scanResults) { 1954 ScanResultMatchInfo scanResultMatchInfo = 1955 ScanResultMatchInfo.fromScanResult(scanResult); 1956 if (scanResultMatchInfo.securityParamsList.size() == 0) continue; 1957 // Only filter legacy Open network. 1958 if (scanResultMatchInfo.securityParamsList.size() == 1 1959 && scanResultMatchInfo.getDefaultSecurityParams().getSecurityType() 1960 == WifiConfiguration.SECURITY_TYPE_OPEN) { 1961 continue; 1962 } 1963 Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestions = 1964 getNetworkSuggestionsForScanResultMatchInfo( 1965 scanResultMatchInfo, MacAddress.fromString(scanResult.BSSID)); 1966 if (extNetworkSuggestions == null || extNetworkSuggestions.isEmpty()) { 1967 continue; 1968 } 1969 Set<ExtendedWifiNetworkSuggestion> sharedNetworkSuggestions = extNetworkSuggestions 1970 .stream() 1971 .filter(ewns -> ewns.perAppInfo.hasUserApproved 1972 && ewns.wns.isUserAllowedToManuallyConnect) 1973 .collect(Collectors.toSet()); 1974 if (sharedNetworkSuggestions.isEmpty()) { 1975 continue; 1976 } 1977 for (ExtendedWifiNetworkSuggestion ewns : sharedNetworkSuggestions) { 1978 if (mVerboseLoggingEnabled) { 1979 Log.v(TAG, "getWifiConfigForMatchedNetworkSuggestionsSharedWithUser Found " 1980 + ewns + " for " + scanResult.SSID 1981 + "[" + scanResult.capabilities + "]"); 1982 } 1983 WifiConfiguration config = ewns.createInternalWifiConfiguration( 1984 mWifiCarrierInfoManager); 1985 if (config.carrierId != TelephonyManager.UNKNOWN_CARRIER_ID 1986 && !mWifiCarrierInfoManager.isSimReady(config.subscriptionId)) { 1987 continue; 1988 } 1989 if (config.carrierMerged && !areCarrierMergedSuggestionsAllowed( 1990 config, ewns.perAppInfo.packageName)) { 1991 continue; 1992 } 1993 WifiConfiguration wCmWifiConfig = mWifiConfigManager 1994 .getConfiguredNetwork(config.getProfileKey()); 1995 if (wCmWifiConfig == null) { 1996 continue; 1997 } 1998 if (networkKeys.add(wCmWifiConfig.getProfileKey())) { 1999 sharedWifiConfigs.add(wCmWifiConfig); 2000 } 2001 } 2002 } 2003 return sharedWifiConfigs; 2004 } 2005 2006 /** 2007 * Check if the given passpoint suggestion has user approval and allow user manually connect. 2008 */ isPasspointSuggestionSharedWithUser(WifiConfiguration config)2009 public boolean isPasspointSuggestionSharedWithUser(WifiConfiguration config) { 2010 if (WifiConfiguration.isMetered(config, null) 2011 && mWifiCarrierInfoManager.isCarrierNetworkFromNonDefaultDataSim(config)) { 2012 return false; 2013 } 2014 if (config.carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) { 2015 int subId = mWifiCarrierInfoManager.getBestMatchSubscriptionId(config); 2016 if (!mWifiCarrierInfoManager.isSimReady(subId)) { 2017 return false; 2018 } 2019 } 2020 Set<ExtendedWifiNetworkSuggestion> extendedWifiNetworkSuggestions = 2021 getNetworkSuggestionsForFqdnMatch(config.FQDN); 2022 Set<ExtendedWifiNetworkSuggestion> matchedSuggestions = 2023 extendedWifiNetworkSuggestions == null ? null : extendedWifiNetworkSuggestions 2024 .stream().filter(ewns -> ewns.perAppInfo.uid == config.creatorUid) 2025 .collect(Collectors.toSet()); 2026 if (matchedSuggestions == null || matchedSuggestions.isEmpty()) { 2027 Log.e(TAG, "Matched network suggestion is missing for FQDN:" + config.FQDN); 2028 return false; 2029 } 2030 ExtendedWifiNetworkSuggestion suggestion = matchedSuggestions 2031 .stream().findAny().get(); 2032 return suggestion.wns.isUserAllowedToManuallyConnect 2033 && suggestion.perAppInfo.hasUserApproved; 2034 } 2035 2036 /** 2037 * Get hidden network from active network suggestions. 2038 * Todo(): Now limit by a fixed number, maybe we can try rotation? 2039 * @param autoJoinOnly retrieve hidden network autojoin enabled only. 2040 * @return list of HiddenNetwork 2041 */ retrieveHiddenNetworkList( boolean autoJoinOnly)2042 public List<WifiScanner.ScanSettings.HiddenNetwork> retrieveHiddenNetworkList( 2043 boolean autoJoinOnly) { 2044 List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworks = new ArrayList<>(); 2045 Set<WifiSsid> ssidSet = new LinkedHashSet<>(); 2046 for (PerAppInfo appInfo : mActiveNetworkSuggestionsPerApp.values()) { 2047 if (!appInfo.hasUserApproved) continue; 2048 for (ExtendedWifiNetworkSuggestion ewns : appInfo.extNetworkSuggestions.values()) { 2049 if (!ewns.wns.wifiConfiguration.hiddenSSID) continue; 2050 if (autoJoinOnly && !ewns.isAutojoinEnabled) continue; 2051 ssidSet.addAll(mWifiInjector.getSsidTranslator().getAllPossibleOriginalSsids( 2052 WifiSsid.fromString(ewns.wns.wifiConfiguration.SSID))); 2053 if (ssidSet.size() >= NUMBER_OF_HIDDEN_NETWORK_FOR_ONE_SCAN) { 2054 break; 2055 } 2056 } 2057 } 2058 for (WifiSsid ssid : ssidSet) { 2059 hiddenNetworks.add(new WifiScanner.ScanSettings.HiddenNetwork(ssid.toString())); 2060 if (hiddenNetworks.size() >= NUMBER_OF_HIDDEN_NETWORK_FOR_ONE_SCAN) { 2061 break; 2062 } 2063 } 2064 return hiddenNetworks; 2065 } 2066 2067 /** 2068 * Helper method to send the post connection broadcast to specified package. 2069 */ sendPostConnectionBroadcast( ExtendedWifiNetworkSuggestion extSuggestion)2070 private void sendPostConnectionBroadcast( 2071 ExtendedWifiNetworkSuggestion extSuggestion) { 2072 Intent intent = new Intent(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION); 2073 intent.putExtra(WifiManager.EXTRA_NETWORK_SUGGESTION, extSuggestion.wns); 2074 // Intended to wakeup the receiving app so set the specific package name. 2075 intent.setPackage(extSuggestion.perAppInfo.packageName); 2076 mContext.sendBroadcastAsUser( 2077 intent, UserHandle.getUserHandleForUid(extSuggestion.perAppInfo.uid)); 2078 } 2079 2080 /** 2081 * Helper method to send the post connection broadcast to specified package. 2082 */ sendPostConnectionBroadcastIfAllowed( ExtendedWifiNetworkSuggestion matchingExtSuggestion, @NonNull String message)2083 private void sendPostConnectionBroadcastIfAllowed( 2084 ExtendedWifiNetworkSuggestion matchingExtSuggestion, @NonNull String message) { 2085 try { 2086 mWifiPermissionsUtil.enforceCanAccessScanResults( 2087 matchingExtSuggestion.perAppInfo.packageName, 2088 matchingExtSuggestion.perAppInfo.featureId, 2089 matchingExtSuggestion.perAppInfo.uid, message); 2090 } catch (SecurityException se) { 2091 Log.w(TAG, "Permission denied for sending post connection broadcast to " 2092 + matchingExtSuggestion.perAppInfo.packageName); 2093 return; 2094 } 2095 if (mVerboseLoggingEnabled) { 2096 Log.v(TAG, "Sending post connection broadcast to " 2097 + matchingExtSuggestion.perAppInfo.packageName); 2098 } 2099 sendPostConnectionBroadcast(matchingExtSuggestion); 2100 } 2101 2102 /** 2103 * Send out the {@link WifiManager#ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION} to the 2104 * network suggestion that provided credential for the current connection network. 2105 * If current connection network is open user saved network, broadcast will be only sent out to 2106 * one of the carrier apps that suggested matched network suggestions. 2107 * 2108 * @param connectedNetwork {@link WifiConfiguration} representing the network connected to. 2109 * @param connectedBssid BSSID of the network connected to. 2110 */ handleConnectionSuccess( @onNull WifiConfiguration connectedNetwork, @NonNull String connectedBssid)2111 private void handleConnectionSuccess( 2112 @NonNull WifiConfiguration connectedNetwork, @NonNull String connectedBssid) { 2113 if (!(connectedNetwork.fromWifiNetworkSuggestion || connectedNetwork.isOpenNetwork())) { 2114 return; 2115 } 2116 Set<ExtendedWifiNetworkSuggestion> matchingExtNetworkSuggestions = 2117 getNetworkSuggestionsForWifiConfiguration(connectedNetwork, connectedBssid); 2118 2119 if (mVerboseLoggingEnabled) { 2120 Log.v(TAG, "Network suggestions matching the connection " 2121 + matchingExtNetworkSuggestions); 2122 } 2123 if (matchingExtNetworkSuggestions == null 2124 || matchingExtNetworkSuggestions.isEmpty()) return; 2125 2126 Set<ExtendedWifiNetworkSuggestion> matchingExtNetworkSuggestionsFromTargetApp; 2127 if (connectedNetwork.fromWifiNetworkSuggestion) { 2128 matchingExtNetworkSuggestionsFromTargetApp = 2129 getMatchedSuggestionsWithSameProfileKey(matchingExtNetworkSuggestions, 2130 connectedNetwork); 2131 if (matchingExtNetworkSuggestionsFromTargetApp.isEmpty()) { 2132 Log.wtf(TAG, "Current connected network suggestion is missing!"); 2133 return; 2134 } 2135 } else { 2136 // If not suggestion, the connected network is open network. 2137 // For saved open network, found the matching suggestion from carrier privileged 2138 // apps. As we only expect one suggestor app to take action on post connection, if 2139 // multiple apps suggested matched suggestions, framework will randomly pick one. 2140 matchingExtNetworkSuggestionsFromTargetApp = matchingExtNetworkSuggestions.stream() 2141 .filter(x -> x.perAppInfo.carrierId != TelephonyManager.UNKNOWN_CARRIER_ID 2142 || mWifiPermissionsUtil 2143 .checkNetworkCarrierProvisioningPermission(x.perAppInfo.uid)) 2144 .limit(1).collect(Collectors.toSet()); 2145 if (matchingExtNetworkSuggestionsFromTargetApp.isEmpty()) { 2146 if (mVerboseLoggingEnabled) { 2147 Log.v(TAG, "No suggestion matched connected user saved open network."); 2148 } 2149 return; 2150 } 2151 } 2152 2153 mWifiMetrics.incrementNetworkSuggestionApiNumConnectSuccess(); 2154 // Find subset of network suggestions have set |isAppInteractionRequired|. 2155 Set<ExtendedWifiNetworkSuggestion> matchingExtNetworkSuggestionsWithReqAppInteraction = 2156 matchingExtNetworkSuggestionsFromTargetApp.stream() 2157 .filter(x -> x.wns.isAppInteractionRequired) 2158 .collect(Collectors.toSet()); 2159 if (matchingExtNetworkSuggestionsWithReqAppInteraction.isEmpty()) return; 2160 2161 // Iterate over the matching network suggestions list: 2162 // a) Ensure that these apps have the necessary location permissions. 2163 // b) Send directed broadcast to the app with their corresponding network suggestion. 2164 for (ExtendedWifiNetworkSuggestion matchingExtNetworkSuggestion 2165 : matchingExtNetworkSuggestionsWithReqAppInteraction) { 2166 sendPostConnectionBroadcastIfAllowed( 2167 matchingExtNetworkSuggestion, 2168 "Connected to " + matchingExtNetworkSuggestion.wns.wifiConfiguration.SSID 2169 + ". featureId is first feature of the app using network suggestions"); 2170 } 2171 } 2172 2173 /** 2174 * Handle connection failure. 2175 * 2176 * @param network {@link WifiConfiguration} representing the network that connection failed to. 2177 * @param bssid BSSID of the network connection failed to if known, else null. 2178 * @param failureCode failure reason code. 2179 */ handleConnectionFailure(@onNull WifiConfiguration network, @Nullable String bssid, int failureCode)2180 private void handleConnectionFailure(@NonNull WifiConfiguration network, 2181 @Nullable String bssid, int failureCode) { 2182 if (!network.fromWifiNetworkSuggestion) { 2183 return; 2184 } 2185 Set<ExtendedWifiNetworkSuggestion> matchingExtNetworkSuggestions = 2186 getNetworkSuggestionsForWifiConfiguration(network, bssid); 2187 if (mVerboseLoggingEnabled) { 2188 Log.v(TAG, "Network suggestions matching the connection failure " 2189 + matchingExtNetworkSuggestions); 2190 } 2191 if (matchingExtNetworkSuggestions == null 2192 || matchingExtNetworkSuggestions.isEmpty()) return; 2193 2194 mWifiMetrics.incrementNetworkSuggestionApiNumConnectFailure(); 2195 // TODO (b/115504887, b/112196799): Blocklist the corresponding network suggestion if 2196 // the connection failed. 2197 2198 // Find subset of network suggestions which suggested the connection failure network. 2199 Set<ExtendedWifiNetworkSuggestion> matchingExtNetworkSuggestionsFromTargetApp = 2200 getMatchedSuggestionsWithSameProfileKey(matchingExtNetworkSuggestions, network); 2201 if (matchingExtNetworkSuggestionsFromTargetApp.isEmpty()) { 2202 Log.wtf(TAG, "Current connection failure network suggestion is missing!"); 2203 return; 2204 } 2205 2206 for (ExtendedWifiNetworkSuggestion matchingExtNetworkSuggestion 2207 : matchingExtNetworkSuggestionsFromTargetApp) { 2208 sendConnectionFailureIfAllowed(matchingExtNetworkSuggestion.perAppInfo.packageName, 2209 matchingExtNetworkSuggestion.perAppInfo.featureId, 2210 matchingExtNetworkSuggestion.perAppInfo.uid, 2211 matchingExtNetworkSuggestion.wns, failureCode); 2212 } 2213 } 2214 getMatchedSuggestionsWithSameProfileKey( Set<ExtendedWifiNetworkSuggestion> matchingSuggestions, WifiConfiguration network)2215 private Set<ExtendedWifiNetworkSuggestion> getMatchedSuggestionsWithSameProfileKey( 2216 Set<ExtendedWifiNetworkSuggestion> matchingSuggestions, WifiConfiguration network) { 2217 if (matchingSuggestions == null || matchingSuggestions.isEmpty()) { 2218 return Set.of(); 2219 } 2220 Set<ExtendedWifiNetworkSuggestion> matchingExtNetworkSuggestionsWithSameProfileKey = 2221 new HashSet<>(); 2222 for (ExtendedWifiNetworkSuggestion ewns : matchingSuggestions) { 2223 WifiConfiguration config = ewns 2224 .createInternalWifiConfiguration(mWifiCarrierInfoManager); 2225 if (config.getProfileKey().equals(network.getProfileKey()) 2226 && config.creatorName.equals(network.creatorName)) { 2227 matchingExtNetworkSuggestionsWithSameProfileKey.add(ewns); 2228 } 2229 } 2230 return matchingExtNetworkSuggestionsWithSameProfileKey; 2231 } 2232 2233 /** 2234 * Invoked by {@link ClientModeImpl} on end of connection attempt to a network. 2235 * 2236 * @param failureCode Failure codes representing {@link WifiMetrics.ConnectionEvent} codes. 2237 * @param network WifiConfiguration corresponding to the current network. 2238 * @param bssid BSSID of the current network. 2239 */ handleConnectionAttemptEnded( int failureCode, @NonNull WifiConfiguration network, @Nullable String bssid)2240 public void handleConnectionAttemptEnded( 2241 int failureCode, @NonNull WifiConfiguration network, @Nullable String bssid) { 2242 if (mVerboseLoggingEnabled) { 2243 Log.v(TAG, "handleConnectionAttemptEnded " + failureCode + ", " + network); 2244 } 2245 if (failureCode == WifiMetrics.ConnectionEvent.FAILURE_NONE) { 2246 handleConnectionSuccess(network, bssid); 2247 } else { 2248 handleConnectionFailure(network, bssid, failureCode); 2249 } 2250 } 2251 2252 /** 2253 * Send network connection failure event to app when an connection attempt failure. 2254 * @param packageName package name to send event 2255 * @param featureId The feature in the package 2256 * @param uid uid of the app. 2257 * @param matchingSuggestion suggestion on this connection failure 2258 * @param connectionEvent connection failure code 2259 */ sendConnectionFailureIfAllowed(String packageName, @Nullable String featureId, int uid, @NonNull WifiNetworkSuggestion matchingSuggestion, int connectionEvent)2260 private void sendConnectionFailureIfAllowed(String packageName, @Nullable String featureId, 2261 int uid, @NonNull WifiNetworkSuggestion matchingSuggestion, int connectionEvent) { 2262 RemoteCallbackList<ISuggestionConnectionStatusListener> listenersTracker = 2263 mSuggestionStatusListenerPerApp.get(packageName); 2264 if (listenersTracker == null || listenersTracker.getRegisteredCallbackCount() == 0) { 2265 return; 2266 } 2267 try { 2268 mWifiPermissionsUtil.enforceCanAccessScanResults( 2269 packageName, featureId, uid, "Connection failure"); 2270 } catch (SecurityException se) { 2271 Log.w(TAG, "Permission denied for sending connection failure event to " + packageName); 2272 return; 2273 } 2274 if (mVerboseLoggingEnabled) { 2275 Log.v(TAG, "Sending connection failure event to " + packageName); 2276 } 2277 int itemCount = listenersTracker.beginBroadcast(); 2278 for (int i = 0; i < itemCount; i++) { 2279 try { 2280 listenersTracker.getBroadcastItem(i).onConnectionStatus(matchingSuggestion, 2281 internalConnectionEventToSuggestionFailureCode(connectionEvent)); 2282 } catch (RemoteException e) { 2283 Log.e(TAG, "sendNetworkCallback: remote exception -- " + e); 2284 } 2285 } 2286 listenersTracker.finishBroadcast(); 2287 } 2288 2289 private @WifiManager.SuggestionConnectionStatusCode internalConnectionEventToSuggestionFailureCode(int connectionEvent)2290 int internalConnectionEventToSuggestionFailureCode(int connectionEvent) { 2291 switch (connectionEvent) { 2292 case WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION: 2293 case WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT: 2294 return WifiManager.STATUS_SUGGESTION_CONNECTION_FAILURE_ASSOCIATION; 2295 case WifiMetrics.ConnectionEvent.FAILURE_SSID_TEMP_DISABLED: 2296 case WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE: 2297 return WifiManager.STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION; 2298 case WifiMetrics.ConnectionEvent.FAILURE_DHCP: 2299 return WifiManager.STATUS_SUGGESTION_CONNECTION_FAILURE_IP_PROVISIONING; 2300 default: 2301 return WifiManager.STATUS_SUGGESTION_CONNECTION_FAILURE_UNKNOWN; 2302 } 2303 } 2304 2305 /** 2306 * Register a SuggestionUserApprovalStatusListener on user approval status changes. 2307 * @param listener ISuggestionUserApprovalStatusListener instance to add. 2308 * @param uid uid of the app. 2309 */ addSuggestionUserApprovalStatusListener( @onNull ISuggestionUserApprovalStatusListener listener, String packageName, int uid)2310 public void addSuggestionUserApprovalStatusListener( 2311 @NonNull ISuggestionUserApprovalStatusListener listener, String packageName, int uid) { 2312 RemoteCallbackList<ISuggestionUserApprovalStatusListener> listenersTracker = 2313 mSuggestionUserApprovalStatusListenerPerApp.get(packageName); 2314 if (listenersTracker == null) { 2315 listenersTracker = new RemoteCallbackList<>(); 2316 } 2317 listenersTracker.register(listener); 2318 mSuggestionUserApprovalStatusListenerPerApp.put(packageName, listenersTracker); 2319 try { 2320 listener.onUserApprovalStatusChange( 2321 getNetworkSuggestionUserApprovalStatus(uid, packageName)); 2322 } catch (RemoteException e) { 2323 Log.e(TAG, "sendUserApprovalStatusChange: remote exception -- " + e); 2324 } 2325 } 2326 2327 /** 2328 * Unregister a listener on on user approval status changes. 2329 * @param listener ISuggestionUserApprovalStatusListener instance to remove. 2330 * @param uid uid of the app. 2331 */ removeSuggestionUserApprovalStatusListener( @onNull ISuggestionUserApprovalStatusListener listener, String packageName, int uid)2332 public void removeSuggestionUserApprovalStatusListener( 2333 @NonNull ISuggestionUserApprovalStatusListener listener, String packageName, int uid) { 2334 RemoteCallbackList<ISuggestionUserApprovalStatusListener> listenersTracker = 2335 mSuggestionUserApprovalStatusListenerPerApp.get(packageName); 2336 if (listenersTracker == null || !listenersTracker.unregister(listener)) { 2337 Log.w(TAG, "removeSuggestionUserApprovalStatusListener: Listener from " + packageName 2338 + " already removed."); 2339 return; 2340 } 2341 if (listenersTracker != null && listenersTracker.getRegisteredCallbackCount() == 0) { 2342 mSuggestionUserApprovalStatusListenerPerApp.remove(packageName); 2343 } 2344 } 2345 2346 /** 2347 * Register a SuggestionConnectionStatusListener on network connection failure. 2348 * @param listener ISuggestionNetworkCallback instance to add. 2349 * @param uid uid of the app. 2350 * @return true if succeed otherwise false. 2351 */ registerSuggestionConnectionStatusListener( @onNull ISuggestionConnectionStatusListener listener, String packageName, int uid)2352 public boolean registerSuggestionConnectionStatusListener( 2353 @NonNull ISuggestionConnectionStatusListener listener, 2354 String packageName, int uid) { 2355 if (!mWifiPermissionsUtil.doesUidBelongToCurrentUserOrDeviceOwner(uid)) { 2356 Log.e(TAG, "UID " + uid + " not visible to the current user"); 2357 return false; 2358 } 2359 RemoteCallbackList<ISuggestionConnectionStatusListener> listenersTracker = 2360 mSuggestionStatusListenerPerApp.get(packageName); 2361 if (listenersTracker == null) { 2362 listenersTracker = new RemoteCallbackList<>(); 2363 } 2364 listenersTracker.register(listener); 2365 mSuggestionStatusListenerPerApp.put(packageName, listenersTracker); 2366 return true; 2367 } 2368 2369 /** 2370 * Unregister a listener on network connection failure. 2371 * @param listener ISuggestionNetworkCallback instance to remove. 2372 * @param uid uid of the app. 2373 */ unregisterSuggestionConnectionStatusListener( @onNull ISuggestionConnectionStatusListener listener, String packageName, int uid)2374 public void unregisterSuggestionConnectionStatusListener( 2375 @NonNull ISuggestionConnectionStatusListener listener, String packageName, int uid) { 2376 if (!mWifiPermissionsUtil.doesUidBelongToCurrentUserOrDeviceOwner(uid)) { 2377 Log.e(TAG, "UID " + uid + " not visible to the current user"); 2378 return; 2379 } 2380 RemoteCallbackList<ISuggestionConnectionStatusListener> listenersTracker = 2381 mSuggestionStatusListenerPerApp.get(packageName); 2382 if (listenersTracker == null || !listenersTracker.unregister(listener)) { 2383 Log.w(TAG, "unregisterSuggestionConnectionStatusListener: Listener from " + packageName 2384 + " already unregister."); 2385 } 2386 if (listenersTracker != null && listenersTracker.getRegisteredCallbackCount() == 0) { 2387 mSuggestionStatusListenerPerApp.remove(packageName); 2388 } 2389 } 2390 2391 /** 2392 * When SIM state changes, check if carrier privileges changes for app. 2393 * If app changes from privileged to not privileged, remove all suggestions and reset state. 2394 * If app changes from not privileges to privileged, set target carrier id for all suggestions. 2395 */ updateCarrierPrivilegedApps()2396 public void updateCarrierPrivilegedApps() { 2397 if (SdkLevel.isAtLeastT()) { 2398 return; 2399 } 2400 Log.w(TAG, "SIM state is changed!"); 2401 Iterator<Map.Entry<String, PerAppInfo>> iter = 2402 mActiveNetworkSuggestionsPerApp.entrySet().iterator(); 2403 while (iter.hasNext()) { 2404 PerAppInfo appInfo = iter.next().getValue(); 2405 int carrierId = mWifiCarrierInfoManager 2406 .getCarrierIdForPackageWithCarrierPrivileges(appInfo.packageName); 2407 if (carrierId == appInfo.carrierId) { 2408 continue; 2409 } 2410 if (carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) { 2411 Log.i(TAG, "Carrier privilege revoked for " + appInfo.packageName); 2412 removeInternal(List.of(), appInfo.packageName, appInfo, 2413 ACTION_REMOVE_SUGGESTION_DISCONNECT); 2414 // Stop tracking app-op changes when the App is removed from suggestion database 2415 stopTrackingAppOpsChange(appInfo.packageName); 2416 iter.remove(); 2417 continue; 2418 } 2419 Log.i(TAG, "Carrier privilege granted for " + appInfo.packageName); 2420 appInfo.carrierId = carrierId; 2421 for (ExtendedWifiNetworkSuggestion ewns : appInfo.extNetworkSuggestions.values()) { 2422 ewns.wns.wifiConfiguration.carrierId = carrierId; 2423 } 2424 } 2425 saveToStore(); 2426 } 2427 2428 /** 2429 * When carrier privileged packages list changes, handle the apps which privileged state changed 2430 * - If app changes from privileged to not privileged, remove all suggestions and reset state 2431 * - If app changes from not privileges to privileged, set target carrier id for all suggestions 2432 */ updateCarrierPrivilegedApps(Set<String> privilegedApps)2433 public void updateCarrierPrivilegedApps(Set<String> privilegedApps) { 2434 if (!SdkLevel.isAtLeastT()) { 2435 return; 2436 } 2437 if (mVerboseLoggingEnabled) { 2438 StringBuilder stringBuilder = new StringBuilder(); 2439 stringBuilder.append("Carrier privileged packages changed, privileged apps=["); 2440 for (String packagesName : privilegedApps) { 2441 stringBuilder.append(packagesName).append(", "); 2442 } 2443 stringBuilder.append("]"); 2444 Log.d(TAG, stringBuilder.toString()); 2445 } 2446 Iterator<Map.Entry<String, PerAppInfo>> iter = 2447 mActiveNetworkSuggestionsPerApp.entrySet().iterator(); 2448 while (iter.hasNext()) { 2449 PerAppInfo appInfo = iter.next().getValue(); 2450 if (privilegedApps.contains(appInfo.packageName)) { 2451 if (appInfo.carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) { 2452 // Already privileged before, no change. 2453 continue; 2454 } 2455 // for (newly) privileged packages: update carrier ID 2456 int carrierId = mWifiCarrierInfoManager 2457 .getCarrierIdForPackageWithCarrierPrivileges(appInfo.packageName); 2458 Log.i(TAG, "Carrier privilege granted for " + appInfo.packageName); 2459 appInfo.carrierId = carrierId; 2460 for (ExtendedWifiNetworkSuggestion ewns : appInfo.extNetworkSuggestions.values()) { 2461 ewns.wns.wifiConfiguration.carrierId = carrierId; 2462 } 2463 continue; 2464 } 2465 if (appInfo.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) { 2466 // Apps never got privileged, no change. 2467 continue; 2468 } 2469 // Carrier privilege revoked, remove. 2470 Log.i(TAG, "Carrier privilege revoked for " + appInfo.packageName); 2471 removeInternal(List.of(), appInfo.packageName, appInfo, 2472 ACTION_REMOVE_SUGGESTION_DISCONNECT); 2473 // Stop tracking app-op changes when the App is removed from suggestion database 2474 stopTrackingAppOpsChange(appInfo.packageName); 2475 iter.remove(); 2476 } 2477 saveToStore(); 2478 } 2479 2480 /** 2481 * Resets all sim networks state. 2482 */ resetSimNetworkSuggestions()2483 public void resetSimNetworkSuggestions() { 2484 mActiveNetworkSuggestionsPerApp.values().stream() 2485 .flatMap(e -> e.extNetworkSuggestions.values().stream()) 2486 .forEach(ewns -> ewns.anonymousIdentity = null); 2487 saveToStore(); 2488 } 2489 2490 /** 2491 * Set auto-join enable/disable for suggestion network 2492 * @param config WifiConfiguration which is to change. 2493 * @param choice true to enable auto-join, false to disable. 2494 * @return true on success, false otherwise (e.g. if no match suggestion exists). 2495 */ allowNetworkSuggestionAutojoin(WifiConfiguration config, boolean choice)2496 public boolean allowNetworkSuggestionAutojoin(WifiConfiguration config, boolean choice) { 2497 if (!config.fromWifiNetworkSuggestion) { 2498 Log.e(TAG, "allowNetworkSuggestionAutojoin: on non-suggestion network: " 2499 + config); 2500 return false; 2501 } 2502 2503 if (config.isPasspoint()) { 2504 if (!mWifiInjector.getPasspointManager().enableAutojoin(config.getProfileKey(), 2505 null, choice)) { 2506 return false; 2507 } 2508 } 2509 2510 Set<ExtendedWifiNetworkSuggestion> matchingExtendedWifiNetworkSuggestions = 2511 getMatchedSuggestionsWithSameProfileKey( 2512 getNetworkSuggestionsForWifiConfiguration(config, config.BSSID), config); 2513 if (matchingExtendedWifiNetworkSuggestions.isEmpty()) { 2514 Log.e(TAG, "allowNetworkSuggestionAutojoin: network is missing: " 2515 + config); 2516 return false; 2517 } 2518 for (ExtendedWifiNetworkSuggestion ewns : matchingExtendedWifiNetworkSuggestions) { 2519 ewns.isAutojoinEnabled = choice; 2520 } 2521 saveToStore(); 2522 return true; 2523 } 2524 2525 /** 2526 * Get the filtered ScanResults which may be authenticated by the suggested configurations. 2527 * @param wifiNetworkSuggestions The list of {@link WifiNetworkSuggestion} 2528 * @param scanResults The list of {@link ScanResult} 2529 * @return The filtered ScanResults 2530 */ 2531 @NonNull getMatchingScanResults( @onNull List<WifiNetworkSuggestion> wifiNetworkSuggestions, @NonNull List<ScanResult> scanResults)2532 public Map<WifiNetworkSuggestion, List<ScanResult>> getMatchingScanResults( 2533 @NonNull List<WifiNetworkSuggestion> wifiNetworkSuggestions, 2534 @NonNull List<ScanResult> scanResults) { 2535 Map<WifiNetworkSuggestion, List<ScanResult>> filteredScanResults = new HashMap<>(); 2536 if (wifiNetworkSuggestions == null || wifiNetworkSuggestions.isEmpty() 2537 || scanResults == null || scanResults.isEmpty()) { 2538 return filteredScanResults; 2539 } 2540 for (WifiNetworkSuggestion suggestion : wifiNetworkSuggestions) { 2541 if (suggestion == null || suggestion.wifiConfiguration == null) { 2542 continue; 2543 } 2544 filteredScanResults.put(suggestion, 2545 getMatchingScanResultsForSuggestion(suggestion, scanResults)); 2546 } 2547 2548 return filteredScanResults; 2549 } 2550 getMatchingScanResultsForSuggestion(WifiNetworkSuggestion suggestion, List<ScanResult> scanResults)2551 private List<ScanResult> getMatchingScanResultsForSuggestion(WifiNetworkSuggestion suggestion, 2552 List<ScanResult> scanResults) { 2553 if (suggestion.passpointConfiguration != null) { 2554 return mWifiInjector.getPasspointManager().getMatchingScanResults( 2555 suggestion.passpointConfiguration, scanResults); 2556 } else { 2557 return getMatchingScanResults(suggestion.wifiConfiguration, scanResults); 2558 } 2559 } 2560 2561 /** 2562 * Get the filtered ScanResults which may be authenticated by the {@link WifiConfiguration}. 2563 * @param wifiConfiguration The instance of {@link WifiConfiguration} 2564 * @param scanResults The list of {@link ScanResult} 2565 * @return The filtered ScanResults 2566 */ 2567 @NonNull getMatchingScanResults( @onNull WifiConfiguration wifiConfiguration, @NonNull List<ScanResult> scanResults)2568 private List<ScanResult> getMatchingScanResults( 2569 @NonNull WifiConfiguration wifiConfiguration, 2570 @NonNull List<ScanResult> scanResults) { 2571 ScanResultMatchInfo matchInfoFromConfigration = 2572 ScanResultMatchInfo.fromWifiConfiguration(wifiConfiguration); 2573 if (matchInfoFromConfigration == null) { 2574 return new ArrayList<>(); 2575 } 2576 List<ScanResult> filteredScanResult = new ArrayList<>(); 2577 for (ScanResult scanResult : scanResults) { 2578 if (matchInfoFromConfigration.equals(ScanResultMatchInfo.fromScanResult(scanResult))) { 2579 filteredScanResult.add(scanResult); 2580 } 2581 } 2582 2583 return filteredScanResult; 2584 } 2585 2586 /** 2587 * Add the suggestion update event listener 2588 */ addOnSuggestionUpdateListener(OnSuggestionUpdateListener listener)2589 public void addOnSuggestionUpdateListener(OnSuggestionUpdateListener listener) { 2590 mListeners.add(listener); 2591 } 2592 2593 /** 2594 * When a saved open network has a same network suggestion which is from app has 2595 * NETWORK_CARRIER_PROVISIONING permission, also that app suggested secure network suggestion 2596 * for same carrier with higher or equal priority and Auto-Join enabled, also that secure 2597 * network is in the range. The saved open network will be ignored during the network selection. 2598 * TODO (b/142035508): revert all these changes once we build infra needed to solve this. 2599 * @param configuration Saved open network to check if it should be ignored. 2600 * @param scanDetails Available ScanDetail nearby. 2601 * @return True if the open network should be ignored, false otherwise. 2602 */ shouldBeIgnoredBySecureSuggestionFromSameCarrier( @onNull WifiConfiguration configuration, List<ScanDetail> scanDetails)2603 public boolean shouldBeIgnoredBySecureSuggestionFromSameCarrier( 2604 @NonNull WifiConfiguration configuration, List<ScanDetail> scanDetails) { 2605 if (!mResources.getBoolean( 2606 R.bool.config_wifiIgnoreOpenSavedNetworkWhenSecureSuggestionAvailable)) { 2607 return false; 2608 } 2609 if (configuration == null || scanDetails == null || !configuration.isOpenNetwork()) { 2610 return false; 2611 } 2612 Set<ExtendedWifiNetworkSuggestion> matchedExtSuggestions = 2613 getNetworkSuggestionsForWifiConfiguration(configuration, null); 2614 if (matchedExtSuggestions == null || matchedExtSuggestions.isEmpty()) { 2615 return false; 2616 } 2617 matchedExtSuggestions = matchedExtSuggestions.stream().filter(ewns -> 2618 mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(ewns.perAppInfo.uid)) 2619 .collect(Collectors.toSet()); 2620 if (matchedExtSuggestions.isEmpty()) { 2621 return false; 2622 } 2623 for (ExtendedWifiNetworkSuggestion ewns : matchedExtSuggestions) { 2624 if (hasSecureSuggestionFromSameCarrierAvailable(ewns, scanDetails)) { 2625 return true; 2626 } 2627 } 2628 return false; 2629 } 2630 2631 /** 2632 * Check the suggestion user approval status. 2633 */ getNetworkSuggestionUserApprovalStatus( int uid, String packageName)2634 private @WifiManager.SuggestionUserApprovalStatus int getNetworkSuggestionUserApprovalStatus( 2635 int uid, String packageName) { 2636 if (mAppOps.unsafeCheckOpNoThrow(OPSTR_CHANGE_WIFI_STATE, uid, packageName) 2637 == AppOpsManager.MODE_IGNORED) { 2638 return WifiManager.STATUS_SUGGESTION_APPROVAL_REJECTED_BY_USER; 2639 } 2640 if (!mActiveNetworkSuggestionsPerApp.containsKey(packageName)) { 2641 return WifiManager.STATUS_SUGGESTION_APPROVAL_UNKNOWN; 2642 } 2643 PerAppInfo info = mActiveNetworkSuggestionsPerApp.get(packageName); 2644 if (info.hasUserApproved) { 2645 return WifiManager.STATUS_SUGGESTION_APPROVAL_APPROVED_BY_USER; 2646 } 2647 if (info.carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) { 2648 return WifiManager.STATUS_SUGGESTION_APPROVAL_APPROVED_BY_CARRIER_PRIVILEGE; 2649 } 2650 return WifiManager.STATUS_SUGGESTION_APPROVAL_PENDING; 2651 } 2652 hasSecureSuggestionFromSameCarrierAvailable( ExtendedWifiNetworkSuggestion extendedWifiNetworkSuggestion, List<ScanDetail> scanDetails)2653 private boolean hasSecureSuggestionFromSameCarrierAvailable( 2654 ExtendedWifiNetworkSuggestion extendedWifiNetworkSuggestion, 2655 List<ScanDetail> scanDetails) { 2656 boolean isOpenSuggestionMetered = WifiConfiguration.isMetered( 2657 extendedWifiNetworkSuggestion.wns.wifiConfiguration, null); 2658 Set<ExtendedWifiNetworkSuggestion> secureExtSuggestions = new HashSet<>(); 2659 for (ExtendedWifiNetworkSuggestion ewns : extendedWifiNetworkSuggestion.perAppInfo 2660 .extNetworkSuggestions.values()) { 2661 // Open network and auto-join disable suggestion, ignore. 2662 if (isOpenSuggestion(ewns) || !ewns.isAutojoinEnabled) { 2663 continue; 2664 } 2665 // From different carrier as open suggestion, ignore. 2666 if (getCarrierIdFromSuggestion(ewns) 2667 != getCarrierIdFromSuggestion(extendedWifiNetworkSuggestion)) { 2668 continue; 2669 } 2670 // Secure and open has different meterness, ignore 2671 if (WifiConfiguration.isMetered(ewns.wns.wifiConfiguration, null) 2672 != isOpenSuggestionMetered) { 2673 continue; 2674 } 2675 // Low priority than open suggestion, ignore. 2676 if (ewns.wns.wifiConfiguration.priority 2677 < extendedWifiNetworkSuggestion.wns.wifiConfiguration.priority) { 2678 continue; 2679 } 2680 WifiConfiguration wcmConfig = mWifiConfigManager 2681 .getConfiguredNetwork(ewns.wns.wifiConfiguration.getProfileKey()); 2682 // Network selection is disabled, ignore. 2683 if (wcmConfig != null && !wcmConfig.getNetworkSelectionStatus().isNetworkEnabled()) { 2684 continue; 2685 } 2686 secureExtSuggestions.add(ewns); 2687 } 2688 2689 if (secureExtSuggestions.isEmpty()) { 2690 return false; 2691 } 2692 List<ScanResult> scanResults = scanDetails.stream().map(ScanDetail::getScanResult) 2693 .collect(Collectors.toList()); 2694 // Check if the secure suggestion is in the range. 2695 for (ExtendedWifiNetworkSuggestion ewns : secureExtSuggestions) { 2696 if (!getMatchingScanResultsForSuggestion(ewns.wns, scanResults).isEmpty()) { 2697 return true; 2698 } 2699 } 2700 return false; 2701 } 2702 2703 /** 2704 * Set the app treated as cross carrier provider. That can suggest for any carrier 2705 * @param packageName App name to set. 2706 * @param enabled True to set app treated as cross carrier provider, false otherwise. 2707 */ setAppWorkingAsCrossCarrierProvider(String packageName, boolean enabled)2708 public void setAppWorkingAsCrossCarrierProvider(String packageName, boolean enabled) { 2709 if (enabled) { 2710 mCrossCarrierProvidersSet.add(packageName); 2711 } else { 2712 mCrossCarrierProvidersSet.remove(packageName); 2713 } 2714 } 2715 2716 /** 2717 * Check whether the app is treated as a cross carrier provider or not. 2718 * @param packageName App name to check 2719 * @return True for app is treated as a carrier provider, false otherwise. 2720 */ isAppWorkingAsCrossCarrierProvider(String packageName)2721 public boolean isAppWorkingAsCrossCarrierProvider(String packageName) { 2722 return mCrossCarrierProvidersSet.contains(packageName); 2723 } 2724 2725 /** 2726 * Store Anonymous Identity for SIM based suggestion after connection. 2727 */ setAnonymousIdentity(WifiConfiguration config)2728 public void setAnonymousIdentity(WifiConfiguration config) { 2729 if (config.isPasspoint() || !config.fromWifiNetworkSuggestion) { 2730 return; 2731 } 2732 if (config.enterpriseConfig == null 2733 || !config.enterpriseConfig.isAuthenticationSimBased()) { 2734 Log.e(TAG, "Network is not SIM based, AnonymousIdentity is invalid"); 2735 } 2736 Set<ExtendedWifiNetworkSuggestion> matchedSuggestionSet = 2737 getMatchedSuggestionsWithSameProfileKey( 2738 getNetworkSuggestionsForWifiConfiguration(config, config.BSSID), config); 2739 if (matchedSuggestionSet.isEmpty()) { 2740 Log.wtf(TAG, "Current connected SIM based network suggestion is missing!"); 2741 return; 2742 } 2743 for (ExtendedWifiNetworkSuggestion ewns : matchedSuggestionSet) { 2744 ewns.anonymousIdentity = config.enterpriseConfig.getAnonymousIdentity(); 2745 if (TextUtils.isEmpty(ewns.anonymousIdentity)) { 2746 // Update WifiConfig with App set AnonymousIdentity 2747 updateWifiConfigInWcmIfPresent(ewns.createInternalWifiConfiguration( 2748 mWifiCarrierInfoManager), ewns.perAppInfo.uid, ewns.perAppInfo.packageName); 2749 } 2750 } 2751 saveToStore(); 2752 } 2753 isOpenSuggestion(ExtendedWifiNetworkSuggestion extendedWifiNetworkSuggestion)2754 private boolean isOpenSuggestion(ExtendedWifiNetworkSuggestion extendedWifiNetworkSuggestion) { 2755 if (extendedWifiNetworkSuggestion.wns.passpointConfiguration != null) { 2756 return false; 2757 } 2758 return extendedWifiNetworkSuggestion.wns.wifiConfiguration.isOpenNetwork(); 2759 } 2760 onUserConnectChoiceSetForSuggestion(Collection<WifiConfiguration> networks, String choiceKey, int rssi)2761 private void onUserConnectChoiceSetForSuggestion(Collection<WifiConfiguration> networks, 2762 String choiceKey, int rssi) { 2763 Set<String> networkKeys = networks.stream() 2764 .filter(config -> config.fromWifiNetworkSuggestion) 2765 .map(WifiConfiguration::getProfileKey) 2766 .collect(Collectors.toSet()); 2767 mActiveNetworkSuggestionsPerApp.values().stream() 2768 .flatMap(e -> e.extNetworkSuggestions.values().stream()) 2769 .forEach(ewns -> { 2770 String profileKey = ewns 2771 .createInternalWifiConfiguration(mWifiCarrierInfoManager) 2772 .getProfileKey(); 2773 if (TextUtils.equals(profileKey, choiceKey)) { 2774 ewns.connectChoice = null; 2775 ewns.connectChoiceRssi = 0; 2776 } else if (networkKeys.contains(profileKey)) { 2777 ewns.connectChoice = choiceKey; 2778 ewns.connectChoiceRssi = rssi; 2779 } 2780 }); 2781 saveToStore(); 2782 } 2783 onUserConnectChoiceRemoveForSuggestion(String choiceKey)2784 private void onUserConnectChoiceRemoveForSuggestion(String choiceKey) { 2785 if (mActiveNetworkSuggestionsPerApp.values().stream() 2786 .flatMap(e -> e.extNetworkSuggestions.values().stream()) 2787 .filter(ewns -> TextUtils.equals(ewns.connectChoice, choiceKey)) 2788 .peek(ewns -> { 2789 ewns.connectChoice = null; 2790 ewns.connectChoiceRssi = 0; 2791 }).count() == 0) { 2792 return; 2793 } 2794 saveToStore(); 2795 } 2796 onSuggestionUserApprovalStatusChanged(int uid, String packageName)2797 private void onSuggestionUserApprovalStatusChanged(int uid, String packageName) { 2798 RemoteCallbackList<ISuggestionUserApprovalStatusListener> listenersTracker = 2799 mSuggestionUserApprovalStatusListenerPerApp.get(packageName); 2800 if (listenersTracker == null || listenersTracker.getRegisteredCallbackCount() == 0) { 2801 return; 2802 } 2803 2804 if (mVerboseLoggingEnabled) { 2805 Log.v(TAG, "Sending user approval status change event to " + packageName); 2806 } 2807 int itemCount = listenersTracker.beginBroadcast(); 2808 for (int i = 0; i < itemCount; i++) { 2809 try { 2810 listenersTracker.getBroadcastItem(i).onUserApprovalStatusChange( 2811 getNetworkSuggestionUserApprovalStatus(uid, packageName)); 2812 } catch (RemoteException e) { 2813 Log.e(TAG, "sendUserApprovalStatusChange: remote exception -- " + e); 2814 } 2815 } 2816 listenersTracker.finishBroadcast(); 2817 } 2818 areCarrierMergedSuggestionsAllowed(WifiConfiguration config, String packageName)2819 private boolean areCarrierMergedSuggestionsAllowed(WifiConfiguration config, 2820 String packageName) { 2821 if (isAppWorkingAsCrossCarrierProvider(packageName)) { 2822 return true; 2823 } 2824 int subId = config.subscriptionId; 2825 if (config.getSubscriptionGroup() != null) { 2826 subId = mWifiCarrierInfoManager.getActiveSubscriptionIdInGroup( 2827 config.getSubscriptionGroup()); 2828 } 2829 2830 return mWifiCarrierInfoManager.areMergedCarrierWifiNetworksAllowed(subId); 2831 } 2832 2833 /** 2834 * Dump of {@link WifiNetworkSuggestionsManager}. 2835 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)2836 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2837 pw.println("Dump of WifiNetworkSuggestionsManager"); 2838 pw.println("WifiNetworkSuggestionsManager - Networks Begin ----"); 2839 for (Map.Entry<String, PerAppInfo> networkSuggestionsEntry 2840 : mActiveNetworkSuggestionsPerApp.entrySet()) { 2841 pw.println("Package Name: " + networkSuggestionsEntry.getKey()); 2842 PerAppInfo appInfo = networkSuggestionsEntry.getValue(); 2843 pw.println("Has user approved: " + appInfo.hasUserApproved); 2844 pw.println("Has carrier privileges: " 2845 + (appInfo.carrierId != TelephonyManager.UNKNOWN_CARRIER_ID)); 2846 for (ExtendedWifiNetworkSuggestion extNetworkSuggestion 2847 : appInfo.extNetworkSuggestions.values()) { 2848 pw.println("Network: " + extNetworkSuggestion); 2849 } 2850 } 2851 pw.println("WifiNetworkSuggestionsManager - Networks End ----"); 2852 } 2853 resetNotification()2854 public void resetNotification() { 2855 mNotificationManager.cancel(SystemMessage.NOTE_NETWORK_SUGGESTION_AVAILABLE); 2856 mNotificationUpdateTime = 0; 2857 } 2858 getLingerDelayMs()2859 private int getLingerDelayMs() { 2860 return SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS); 2861 } 2862 onSecurityParamsUpdateForSuggestion(WifiConfiguration config, List<SecurityParams> securityParams)2863 private void onSecurityParamsUpdateForSuggestion(WifiConfiguration config, 2864 List<SecurityParams> securityParams) { 2865 Set<ExtendedWifiNetworkSuggestion> matchingExtendedWifiNetworkSuggestions = 2866 getNetworkSuggestionsForWifiConfiguration(config, config.BSSID); 2867 if (matchingExtendedWifiNetworkSuggestions == null 2868 || matchingExtendedWifiNetworkSuggestions.isEmpty()) { 2869 if (mVerboseLoggingEnabled) { 2870 Log.w(TAG, "onSecurityParamsUpdateForSuggestion: no network matches: " + config); 2871 } 2872 return; 2873 } 2874 for (ExtendedWifiNetworkSuggestion ewns : matchingExtendedWifiNetworkSuggestions) { 2875 removeFromScanResultMatchInfoMapAndRemoveRelatedScoreCard(ewns, false); 2876 ewns.wns.wifiConfiguration.setSecurityParams(securityParams); 2877 addToScanResultMatchInfoMap(ewns); 2878 } 2879 saveToStore(); 2880 } 2881 2882 /** 2883 * Handle device shut down 2884 */ handleShutDown()2885 public void handleShutDown() { 2886 mIsDeviceShuttingDown = true; 2887 } 2888 } 2889