1 /* 2 * Copyright (C) 2020 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.wifitrackerlib; 18 19 import static android.net.wifi.WifiInfo.DEFAULT_MAC_ADDRESS; 20 import static android.net.wifi.WifiInfo.SECURITY_TYPE_PASSPOINT_R1_R2; 21 import static android.net.wifi.WifiInfo.SECURITY_TYPE_PASSPOINT_R3; 22 import static android.net.wifi.WifiInfo.SECURITY_TYPE_UNKNOWN; 23 import static android.net.wifi.WifiInfo.sanitizeSsid; 24 25 import static androidx.core.util.Preconditions.checkNotNull; 26 27 import static com.android.wifitrackerlib.Utils.getAutoConnectDescription; 28 import static com.android.wifitrackerlib.Utils.getAverageSpeedFromScanResults; 29 import static com.android.wifitrackerlib.Utils.getBestScanResultByLevel; 30 import static com.android.wifitrackerlib.Utils.getConnectedDescription; 31 import static com.android.wifitrackerlib.Utils.getConnectingDescription; 32 import static com.android.wifitrackerlib.Utils.getDisconnectedDescription; 33 import static com.android.wifitrackerlib.Utils.getImsiProtectionDescription; 34 import static com.android.wifitrackerlib.Utils.getMeteredDescription; 35 import static com.android.wifitrackerlib.Utils.getSpeedDescription; 36 import static com.android.wifitrackerlib.Utils.getSpeedFromWifiInfo; 37 import static com.android.wifitrackerlib.Utils.getVerboseLoggingDescription; 38 39 import android.content.Context; 40 import android.net.ConnectivityManager; 41 import android.net.NetworkCapabilities; 42 import android.net.NetworkInfo; 43 import android.net.wifi.ScanResult; 44 import android.net.wifi.WifiConfiguration; 45 import android.net.wifi.WifiInfo; 46 import android.net.wifi.WifiManager; 47 import android.net.wifi.WifiNetworkScoreCache; 48 import android.net.wifi.hotspot2.PasspointConfiguration; 49 import android.os.Handler; 50 import android.text.TextUtils; 51 import android.util.Log; 52 53 import androidx.annotation.NonNull; 54 import androidx.annotation.Nullable; 55 import androidx.annotation.WorkerThread; 56 57 import com.android.internal.annotations.VisibleForTesting; 58 59 import java.util.ArrayList; 60 import java.util.Collections; 61 import java.util.List; 62 import java.util.StringJoiner; 63 64 /** 65 * WifiEntry representation of a subscribed Passpoint network, uniquely identified by FQDN. 66 */ 67 @VisibleForTesting 68 public class PasspointWifiEntry extends WifiEntry implements WifiEntry.WifiEntryCallback { 69 static final String TAG = "PasspointWifiEntry"; 70 public static final String KEY_PREFIX = "PasspointWifiEntry:"; 71 72 private final List<ScanResult> mCurrentHomeScanResults = new ArrayList<>(); 73 private final List<ScanResult> mCurrentRoamingScanResults = new ArrayList<>(); 74 75 @NonNull private final String mKey; 76 @NonNull private final String mFqdn; 77 @NonNull private final String mFriendlyName; 78 @NonNull private final WifiTrackerInjector mInjector; 79 @NonNull private final Context mContext; 80 @Nullable 81 private PasspointConfiguration mPasspointConfig; 82 @Nullable private WifiConfiguration mWifiConfig; 83 private List<Integer> mTargetSecurityTypes = 84 List.of(SECURITY_TYPE_PASSPOINT_R1_R2, SECURITY_TYPE_PASSPOINT_R3); 85 86 private boolean mIsRoaming = false; 87 private OsuWifiEntry mOsuWifiEntry; 88 private boolean mShouldAutoOpenCaptivePortal = false; 89 90 protected long mSubscriptionExpirationTimeInMillis; 91 92 // PasspointConfiguration#setMeteredOverride(int meteredOverride) is a hide API and we can't 93 // set it in PasspointWifiEntry#setMeteredChoice(int meteredChoice). 94 // For PasspointWifiEntry#getMeteredChoice() to return correct value right after 95 // PasspointWifiEntry#setMeteredChoice(int meteredChoice), cache 96 // PasspointConfiguration#getMeteredOverride() in this variable. 97 private int mMeteredOverride = METERED_CHOICE_AUTO; 98 99 /** 100 * Create a PasspointWifiEntry with the associated PasspointConfiguration 101 */ PasspointWifiEntry( @onNull WifiTrackerInjector injector, @NonNull Context context, @NonNull Handler callbackHandler, @NonNull PasspointConfiguration passpointConfig, @NonNull WifiManager wifiManager, @NonNull WifiNetworkScoreCache scoreCache, boolean forSavedNetworksPage)102 PasspointWifiEntry( 103 @NonNull WifiTrackerInjector injector, 104 @NonNull Context context, @NonNull Handler callbackHandler, 105 @NonNull PasspointConfiguration passpointConfig, 106 @NonNull WifiManager wifiManager, 107 @NonNull WifiNetworkScoreCache scoreCache, 108 boolean forSavedNetworksPage) throws IllegalArgumentException { 109 super(callbackHandler, wifiManager, scoreCache, forSavedNetworksPage); 110 111 checkNotNull(passpointConfig, "Cannot construct with null PasspointConfiguration!"); 112 mInjector = injector; 113 mContext = context; 114 mPasspointConfig = passpointConfig; 115 mKey = uniqueIdToPasspointWifiEntryKey(passpointConfig.getUniqueId()); 116 mFqdn = passpointConfig.getHomeSp().getFqdn(); 117 checkNotNull(mFqdn, "Cannot construct with null PasspointConfiguration FQDN!"); 118 mFriendlyName = passpointConfig.getHomeSp().getFriendlyName(); 119 mSubscriptionExpirationTimeInMillis = 120 passpointConfig.getSubscriptionExpirationTimeMillis(); 121 mMeteredOverride = mPasspointConfig.getMeteredOverride(); 122 } 123 124 /** 125 * Create a PasspointWifiEntry with the associated WifiConfiguration for use with network 126 * suggestions, since WifiManager#getAllMatchingWifiConfigs() does not provide a corresponding 127 * PasspointConfiguration. 128 */ PasspointWifiEntry( @onNull WifiTrackerInjector injector, @NonNull Context context, @NonNull Handler callbackHandler, @NonNull WifiConfiguration wifiConfig, @NonNull WifiManager wifiManager, @NonNull WifiNetworkScoreCache scoreCache, boolean forSavedNetworksPage)129 PasspointWifiEntry( 130 @NonNull WifiTrackerInjector injector, 131 @NonNull Context context, @NonNull Handler callbackHandler, 132 @NonNull WifiConfiguration wifiConfig, 133 @NonNull WifiManager wifiManager, 134 @NonNull WifiNetworkScoreCache scoreCache, 135 boolean forSavedNetworksPage) throws IllegalArgumentException { 136 super(callbackHandler, wifiManager, scoreCache, forSavedNetworksPage); 137 138 checkNotNull(wifiConfig, "Cannot construct with null WifiConfiguration!"); 139 if (!wifiConfig.isPasspoint()) { 140 throw new IllegalArgumentException("Given WifiConfiguration is not for Passpoint!"); 141 } 142 mInjector = injector; 143 mContext = context; 144 mWifiConfig = wifiConfig; 145 mKey = uniqueIdToPasspointWifiEntryKey(wifiConfig.getKey()); 146 mFqdn = wifiConfig.FQDN; 147 checkNotNull(mFqdn, "Cannot construct with null WifiConfiguration FQDN!"); 148 mFriendlyName = mWifiConfig.providerFriendlyName; 149 } 150 151 @Override getKey()152 public String getKey() { 153 return mKey; 154 } 155 156 @Override 157 @ConnectedState getConnectedState()158 public synchronized int getConnectedState() { 159 if (isExpired()) { 160 if (super.getConnectedState() == CONNECTED_STATE_DISCONNECTED 161 && mOsuWifiEntry != null) { 162 return mOsuWifiEntry.getConnectedState(); 163 } 164 } 165 return super.getConnectedState(); 166 } 167 168 @Override getTitle()169 public String getTitle() { 170 return mFriendlyName; 171 } 172 173 @Override getSummary(boolean concise)174 public synchronized String getSummary(boolean concise) { 175 StringJoiner sj = new StringJoiner(mContext.getString( 176 R.string.wifitrackerlib_summary_separator)); 177 178 if (isExpired()) { 179 if (mOsuWifiEntry != null) { 180 sj.add(mOsuWifiEntry.getSummary(concise)); 181 } else { 182 sj.add(mContext.getString(R.string.wifitrackerlib_wifi_passpoint_expired)); 183 } 184 } else { 185 final String connectedStateDescription; 186 final @ConnectedState int connectedState = getConnectedState(); 187 switch (connectedState) { 188 case CONNECTED_STATE_DISCONNECTED: 189 connectedStateDescription = getDisconnectedDescription(mInjector, mContext, 190 mWifiConfig, 191 mForSavedNetworksPage, 192 concise); 193 break; 194 case CONNECTED_STATE_CONNECTING: 195 connectedStateDescription = getConnectingDescription(mContext, mNetworkInfo); 196 break; 197 case CONNECTED_STATE_CONNECTED: 198 connectedStateDescription = getConnectedDescription(mContext, 199 mWifiConfig, 200 mNetworkCapabilities, 201 null /* recommendationServiceLabel */, 202 mIsDefaultNetwork, 203 mIsLowQuality); 204 break; 205 default: 206 Log.e(TAG, "getConnectedState() returned unknown state: " + connectedState); 207 connectedStateDescription = null; 208 } 209 if (!TextUtils.isEmpty(connectedStateDescription)) { 210 sj.add(connectedStateDescription); 211 } 212 } 213 214 String speedDescription = getSpeedDescription(mContext, this); 215 if (!TextUtils.isEmpty(speedDescription)) { 216 sj.add(speedDescription); 217 } 218 219 String autoConnectDescription = getAutoConnectDescription(mContext, this); 220 if (!TextUtils.isEmpty(autoConnectDescription)) { 221 sj.add(autoConnectDescription); 222 } 223 224 String meteredDescription = getMeteredDescription(mContext, this); 225 if (!TextUtils.isEmpty(meteredDescription)) { 226 sj.add(meteredDescription); 227 } 228 229 if (!concise) { 230 String verboseLoggingDescription = getVerboseLoggingDescription(this); 231 if (!TextUtils.isEmpty(verboseLoggingDescription)) { 232 sj.add(verboseLoggingDescription); 233 } 234 } 235 236 return sj.toString(); 237 } 238 239 @Override getSecondSummary()240 public synchronized CharSequence getSecondSummary() { 241 return getConnectedState() == CONNECTED_STATE_CONNECTED 242 ? getImsiProtectionDescription(mContext, mWifiConfig) : ""; 243 } 244 245 @Override getSsid()246 public synchronized String getSsid() { 247 if (mWifiInfo != null) { 248 return sanitizeSsid(mWifiInfo.getSSID()); 249 } 250 251 return mWifiConfig != null ? sanitizeSsid(mWifiConfig.SSID) : null; 252 } 253 254 @Override getSecurityTypes()255 public synchronized List<Integer> getSecurityTypes() { 256 return new ArrayList<>(mTargetSecurityTypes); 257 } 258 259 @Override getMacAddress()260 public synchronized String getMacAddress() { 261 if (mWifiInfo != null) { 262 final String wifiInfoMac = mWifiInfo.getMacAddress(); 263 if (!TextUtils.isEmpty(wifiInfoMac) 264 && !TextUtils.equals(wifiInfoMac, DEFAULT_MAC_ADDRESS)) { 265 return wifiInfoMac; 266 } 267 } 268 if (mWifiConfig == null || getPrivacy() != PRIVACY_RANDOMIZED_MAC) { 269 final String[] factoryMacs = mWifiManager.getFactoryMacAddresses(); 270 if (factoryMacs.length > 0) { 271 return factoryMacs[0]; 272 } 273 return null; 274 } 275 return mWifiConfig.getRandomizedMacAddress().toString(); 276 } 277 278 @Override isMetered()279 public synchronized boolean isMetered() { 280 return getMeteredChoice() == METERED_CHOICE_METERED 281 || (mWifiConfig != null && mWifiConfig.meteredHint); 282 } 283 284 @Override isSuggestion()285 public synchronized boolean isSuggestion() { 286 return mWifiConfig != null && mWifiConfig.fromWifiNetworkSuggestion; 287 } 288 289 @Override isSubscription()290 public synchronized boolean isSubscription() { 291 return mPasspointConfig != null; 292 } 293 294 @Override canConnect()295 public synchronized boolean canConnect() { 296 if (isExpired()) { 297 return mOsuWifiEntry != null && mOsuWifiEntry.canConnect(); 298 } 299 300 return mLevel != WIFI_LEVEL_UNREACHABLE 301 && getConnectedState() == CONNECTED_STATE_DISCONNECTED && mWifiConfig != null; 302 } 303 304 @Override connect(@ullable ConnectCallback callback)305 public synchronized void connect(@Nullable ConnectCallback callback) { 306 if (isExpired()) { 307 if (mOsuWifiEntry != null) { 308 mOsuWifiEntry.connect(callback); 309 return; 310 } 311 } 312 // We should flag this network to auto-open captive portal since this method represents 313 // the user manually connecting to a network (i.e. not auto-join). 314 mShouldAutoOpenCaptivePortal = true; 315 mConnectCallback = callback; 316 317 if (mWifiConfig == null) { 318 // We should not be able to call connect() if mWifiConfig is null 319 new ConnectActionListener().onFailure(0); 320 } 321 mWifiManager.stopRestrictingAutoJoinToSubscriptionId(); 322 mWifiManager.connect(mWifiConfig, new ConnectActionListener()); 323 } 324 325 @Override canDisconnect()326 public boolean canDisconnect() { 327 return getConnectedState() == CONNECTED_STATE_CONNECTED; 328 } 329 330 @Override disconnect(@ullable DisconnectCallback callback)331 public synchronized void disconnect(@Nullable DisconnectCallback callback) { 332 if (canDisconnect()) { 333 mCalledDisconnect = true; 334 mDisconnectCallback = callback; 335 mCallbackHandler.postDelayed(() -> { 336 if (callback != null && mCalledDisconnect) { 337 callback.onDisconnectResult( 338 DisconnectCallback.DISCONNECT_STATUS_FAILURE_UNKNOWN); 339 } 340 }, 10_000 /* delayMillis */); 341 mWifiManager.disableEphemeralNetwork(mFqdn); 342 mWifiManager.disconnect(); 343 } 344 } 345 346 @Override canForget()347 public synchronized boolean canForget() { 348 return !isSuggestion() && mPasspointConfig != null; 349 } 350 351 @Override forget(@ullable ForgetCallback callback)352 public synchronized void forget(@Nullable ForgetCallback callback) { 353 if (!canForget()) { 354 return; 355 } 356 357 mForgetCallback = callback; 358 mWifiManager.removePasspointConfiguration(mPasspointConfig.getHomeSp().getFqdn()); 359 new ForgetActionListener().onSuccess(); 360 } 361 362 @Override 363 @MeteredChoice getMeteredChoice()364 public synchronized int getMeteredChoice() { 365 if (mMeteredOverride == WifiConfiguration.METERED_OVERRIDE_METERED) { 366 return METERED_CHOICE_METERED; 367 } else if (mMeteredOverride == WifiConfiguration.METERED_OVERRIDE_NOT_METERED) { 368 return METERED_CHOICE_UNMETERED; 369 } 370 return METERED_CHOICE_AUTO; 371 } 372 373 @Override canSetMeteredChoice()374 public synchronized boolean canSetMeteredChoice() { 375 return !isSuggestion() && mPasspointConfig != null; 376 } 377 378 @Override setMeteredChoice(int meteredChoice)379 public synchronized void setMeteredChoice(int meteredChoice) { 380 if (mPasspointConfig == null || !canSetMeteredChoice()) { 381 return; 382 } 383 384 switch (meteredChoice) { 385 case METERED_CHOICE_AUTO: 386 mMeteredOverride = WifiConfiguration.METERED_OVERRIDE_NONE; 387 break; 388 case METERED_CHOICE_METERED: 389 mMeteredOverride = WifiConfiguration.METERED_OVERRIDE_METERED; 390 break; 391 case METERED_CHOICE_UNMETERED: 392 mMeteredOverride = WifiConfiguration.METERED_OVERRIDE_NOT_METERED; 393 break; 394 default: 395 // Do nothing. 396 return; 397 } 398 mWifiManager.setPasspointMeteredOverride(mPasspointConfig.getHomeSp().getFqdn(), 399 mMeteredOverride); 400 } 401 402 @Override canSetPrivacy()403 public synchronized boolean canSetPrivacy() { 404 return !isSuggestion() && mPasspointConfig != null; 405 } 406 407 @Override 408 @Privacy getPrivacy()409 public synchronized int getPrivacy() { 410 if (mPasspointConfig == null) { 411 return PRIVACY_RANDOMIZED_MAC; 412 } 413 414 return mPasspointConfig.isMacRandomizationEnabled() 415 ? PRIVACY_RANDOMIZED_MAC : PRIVACY_DEVICE_MAC; 416 } 417 418 @Override setPrivacy(int privacy)419 public synchronized void setPrivacy(int privacy) { 420 if (mPasspointConfig == null || !canSetPrivacy()) { 421 return; 422 } 423 424 mWifiManager.setMacRandomizationSettingPasspointEnabled( 425 mPasspointConfig.getHomeSp().getFqdn(), 426 privacy == PRIVACY_DEVICE_MAC ? false : true); 427 } 428 429 @Override isAutoJoinEnabled()430 public synchronized boolean isAutoJoinEnabled() { 431 // Suggestion network; use WifiConfig instead 432 if (mPasspointConfig != null) { 433 return mPasspointConfig.isAutojoinEnabled(); 434 } 435 if (mWifiConfig != null) { 436 return mWifiConfig.allowAutojoin; 437 } 438 return false; 439 } 440 441 @Override canSetAutoJoinEnabled()442 public synchronized boolean canSetAutoJoinEnabled() { 443 return mPasspointConfig != null || mWifiConfig != null; 444 } 445 446 @Override setAutoJoinEnabled(boolean enabled)447 public synchronized void setAutoJoinEnabled(boolean enabled) { 448 if (mPasspointConfig != null) { 449 mWifiManager.allowAutojoinPasspoint(mPasspointConfig.getHomeSp().getFqdn(), enabled); 450 } else if (mWifiConfig != null) { 451 mWifiManager.allowAutojoin(mWifiConfig.networkId, enabled); 452 } 453 } 454 455 @Override getSecurityString(boolean concise)456 public String getSecurityString(boolean concise) { 457 return mContext.getString(R.string.wifitrackerlib_wifi_security_passpoint); 458 } 459 460 @Override isExpired()461 public synchronized boolean isExpired() { 462 if (mSubscriptionExpirationTimeInMillis <= 0) { 463 // Expiration time not specified. 464 return false; 465 } else { 466 return System.currentTimeMillis() >= mSubscriptionExpirationTimeInMillis; 467 } 468 } 469 470 @WorkerThread updatePasspointConfig(@ullable PasspointConfiguration passpointConfig)471 synchronized void updatePasspointConfig(@Nullable PasspointConfiguration passpointConfig) { 472 mPasspointConfig = passpointConfig; 473 if (mPasspointConfig != null) { 474 mSubscriptionExpirationTimeInMillis = 475 passpointConfig.getSubscriptionExpirationTimeMillis(); 476 mMeteredOverride = passpointConfig.getMeteredOverride(); 477 } 478 notifyOnUpdated(); 479 } 480 481 @WorkerThread updateScanResultInfo(@ullable WifiConfiguration wifiConfig, @Nullable List<ScanResult> homeScanResults, @Nullable List<ScanResult> roamingScanResults)482 synchronized void updateScanResultInfo(@Nullable WifiConfiguration wifiConfig, 483 @Nullable List<ScanResult> homeScanResults, 484 @Nullable List<ScanResult> roamingScanResults) 485 throws IllegalArgumentException { 486 mIsRoaming = false; 487 mWifiConfig = wifiConfig; 488 mCurrentHomeScanResults.clear(); 489 mCurrentRoamingScanResults.clear(); 490 if (homeScanResults != null) { 491 mCurrentHomeScanResults.addAll(homeScanResults); 492 } 493 if (roamingScanResults != null) { 494 mCurrentRoamingScanResults.addAll(roamingScanResults); 495 } 496 if (mWifiConfig != null) { 497 List<ScanResult> currentScanResults = new ArrayList<>(); 498 ScanResult bestScanResult = null; 499 if (homeScanResults != null && !homeScanResults.isEmpty()) { 500 currentScanResults.addAll(homeScanResults); 501 } else if (roamingScanResults != null && !roamingScanResults.isEmpty()) { 502 currentScanResults.addAll(roamingScanResults); 503 mIsRoaming = true; 504 } 505 bestScanResult = getBestScanResultByLevel(currentScanResults); 506 if (bestScanResult != null) { 507 mWifiConfig.SSID = "\"" + bestScanResult.SSID + "\""; 508 } 509 if (getConnectedState() == CONNECTED_STATE_DISCONNECTED) { 510 mLevel = bestScanResult != null 511 ? mWifiManager.calculateSignalLevel(bestScanResult.level) 512 : WIFI_LEVEL_UNREACHABLE; 513 // Average speed is used to prevent speed label flickering from multiple APs. 514 mSpeed = getAverageSpeedFromScanResults(mScoreCache, currentScanResults); 515 } 516 } else { 517 mLevel = WIFI_LEVEL_UNREACHABLE; 518 } 519 notifyOnUpdated(); 520 } 521 522 @Override updateSecurityTypes()523 protected synchronized void updateSecurityTypes() { 524 if (mWifiInfo != null) { 525 final int wifiInfoSecurity = mWifiInfo.getCurrentSecurityType(); 526 if (wifiInfoSecurity != SECURITY_TYPE_UNKNOWN) { 527 mTargetSecurityTypes = Collections.singletonList(wifiInfoSecurity); 528 return; 529 } 530 } 531 } 532 533 @WorkerThread onScoreCacheUpdated()534 synchronized void onScoreCacheUpdated() { 535 if (mWifiInfo != null) { 536 mSpeed = getSpeedFromWifiInfo(mScoreCache, mWifiInfo); 537 } else { 538 // Average speed is used to prevent speed label flickering from multiple APs. 539 if (!mCurrentHomeScanResults.isEmpty()) { 540 mSpeed = getAverageSpeedFromScanResults(mScoreCache, mCurrentHomeScanResults); 541 } else { 542 mSpeed = getAverageSpeedFromScanResults(mScoreCache, 543 mCurrentRoamingScanResults); 544 } 545 } 546 notifyOnUpdated(); 547 } 548 549 @WorkerThread 550 @Override connectionInfoMatches(@onNull WifiInfo wifiInfo, @NonNull NetworkInfo networkInfo)551 protected boolean connectionInfoMatches(@NonNull WifiInfo wifiInfo, 552 @NonNull NetworkInfo networkInfo) { 553 if (!wifiInfo.isPasspointAp()) { 554 return false; 555 } 556 557 // Match with FQDN until WifiInfo supports returning the passpoint uniqueID 558 return TextUtils.equals(wifiInfo.getPasspointFqdn(), mFqdn); 559 } 560 561 @WorkerThread 562 @Override updateNetworkCapabilities(@ullable NetworkCapabilities capabilities)563 synchronized void updateNetworkCapabilities(@Nullable NetworkCapabilities capabilities) { 564 super.updateNetworkCapabilities(capabilities); 565 566 // Auto-open an available captive portal if the user manually connected to this network. 567 if (canSignIn() && mShouldAutoOpenCaptivePortal) { 568 mShouldAutoOpenCaptivePortal = false; 569 signIn(null /* callback */); 570 } 571 } 572 573 @NonNull uniqueIdToPasspointWifiEntryKey(@onNull String uniqueId)574 static String uniqueIdToPasspointWifiEntryKey(@NonNull String uniqueId) { 575 checkNotNull(uniqueId, "Cannot create key with null unique id!"); 576 return KEY_PREFIX + uniqueId; 577 } 578 579 @Override getScanResultDescription()580 protected String getScanResultDescription() { 581 // TODO(b/70983952): Fill this method in. 582 return ""; 583 } 584 585 @Override getNetworkSelectionDescription()586 synchronized String getNetworkSelectionDescription() { 587 return Utils.getNetworkSelectionDescription(mWifiConfig); 588 } 589 590 /** Pass a reference to a matching OsuWifiEntry for expiration handling */ setOsuWifiEntry(OsuWifiEntry osuWifiEntry)591 synchronized void setOsuWifiEntry(OsuWifiEntry osuWifiEntry) { 592 mOsuWifiEntry = osuWifiEntry; 593 if (mOsuWifiEntry != null) { 594 mOsuWifiEntry.setListener(this); 595 } 596 } 597 598 /** Callback for updates to the linked OsuWifiEntry */ 599 @Override onUpdated()600 public void onUpdated() { 601 notifyOnUpdated(); 602 } 603 604 @Override canSignIn()605 public synchronized boolean canSignIn() { 606 return mNetworkCapabilities != null 607 && mNetworkCapabilities.hasCapability( 608 NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL); 609 } 610 611 @Override signIn(@ullable SignInCallback callback)612 public void signIn(@Nullable SignInCallback callback) { 613 if (canSignIn()) { 614 // canSignIn() implies that this WifiEntry is the currently connected network, so use 615 // getCurrentNetwork() to start the captive portal app. 616 ((ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE)) 617 .startCaptivePortalApp(mWifiManager.getCurrentNetwork()); 618 } 619 } 620 621 /** Get the PasspointConfiguration instance of the entry. */ getPasspointConfig()622 public PasspointConfiguration getPasspointConfig() { 623 return mPasspointConfig; 624 } 625 } 626