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