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