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