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