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