1 /* 2 * Copyright (C) 2019 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 package com.android.settings.wifi.details2; 17 18 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; 19 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; 20 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; 21 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 22 23 import android.app.Activity; 24 import android.app.AlertDialog; 25 import android.app.settings.SettingsEnums; 26 import android.content.AsyncQueryHandler; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.database.Cursor; 30 import android.graphics.Bitmap; 31 import android.graphics.drawable.BitmapDrawable; 32 import android.graphics.drawable.Drawable; 33 import android.graphics.drawable.VectorDrawable; 34 import android.net.CaptivePortalData; 35 import android.net.ConnectivityManager; 36 import android.net.ConnectivityManager.NetworkCallback; 37 import android.net.LinkAddress; 38 import android.net.LinkProperties; 39 import android.net.Network; 40 import android.net.NetworkCapabilities; 41 import android.net.NetworkInfo; 42 import android.net.NetworkRequest; 43 import android.net.RouteInfo; 44 import android.net.Uri; 45 import android.net.wifi.ScanResult; 46 import android.net.wifi.WifiConfiguration; 47 import android.net.wifi.WifiInfo; 48 import android.net.wifi.WifiManager; 49 import android.os.Handler; 50 import android.provider.Telephony.CarrierId; 51 import android.telephony.SubscriptionInfo; 52 import android.telephony.SubscriptionManager; 53 import android.telephony.TelephonyManager; 54 import android.text.TextUtils; 55 import android.util.FeatureFlagUtils; 56 import android.util.Log; 57 import android.widget.ImageView; 58 import android.widget.Toast; 59 60 import androidx.annotation.VisibleForTesting; 61 import androidx.core.text.BidiFormatter; 62 import androidx.preference.Preference; 63 import androidx.preference.PreferenceCategory; 64 import androidx.preference.PreferenceFragmentCompat; 65 import androidx.preference.PreferenceScreen; 66 import androidx.recyclerview.widget.RecyclerView; 67 68 import com.android.net.module.util.Inet4AddressUtils; 69 import com.android.settings.R; 70 import com.android.settings.Utils; 71 import com.android.settings.core.FeatureFlags; 72 import com.android.settings.core.PreferenceControllerMixin; 73 import com.android.settings.datausage.WifiDataUsageSummaryPreferenceController; 74 import com.android.settings.network.SubscriptionUtil; 75 import com.android.settings.widget.EntityHeaderController; 76 import com.android.settings.wifi.WifiDialog2; 77 import com.android.settings.wifi.WifiDialog2.WifiDialog2Listener; 78 import com.android.settings.wifi.WifiUtils; 79 import com.android.settings.wifi.details.WifiNetworkDetailsFragment; 80 import com.android.settings.wifi.dpp.WifiDppUtils; 81 import com.android.settingslib.core.AbstractPreferenceController; 82 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 83 import com.android.settingslib.core.lifecycle.Lifecycle; 84 import com.android.settingslib.core.lifecycle.LifecycleObserver; 85 import com.android.settingslib.core.lifecycle.events.OnPause; 86 import com.android.settingslib.core.lifecycle.events.OnResume; 87 import com.android.settingslib.utils.StringUtil; 88 import com.android.settingslib.widget.ActionButtonsPreference; 89 import com.android.settingslib.widget.LayoutPreference; 90 import com.android.wifitrackerlib.WifiEntry; 91 import com.android.wifitrackerlib.WifiEntry.ConnectCallback; 92 import com.android.wifitrackerlib.WifiEntry.ConnectedInfo; 93 import com.android.wifitrackerlib.WifiEntry.DisconnectCallback; 94 import com.android.wifitrackerlib.WifiEntry.ForgetCallback; 95 import com.android.wifitrackerlib.WifiEntry.SignInCallback; 96 import com.android.wifitrackerlib.WifiEntry.WifiEntryCallback; 97 98 import java.net.Inet4Address; 99 import java.net.Inet6Address; 100 import java.net.InetAddress; 101 import java.time.Duration; 102 import java.time.Instant; 103 import java.time.ZonedDateTime; 104 import java.time.format.DateTimeFormatter; 105 import java.time.format.FormatStyle; 106 import java.util.List; 107 import java.util.StringJoiner; 108 import java.util.stream.Collectors; 109 110 // TODO(b/151133650): Replace AbstractPreferenceController with BasePreferenceController. 111 /** 112 * Controller for logic pertaining to displaying Wifi information for the 113 * {@link WifiNetworkDetailsFragment}. 114 */ 115 public class WifiDetailPreferenceController2 extends AbstractPreferenceController 116 implements PreferenceControllerMixin, WifiDialog2Listener, LifecycleObserver, OnPause, 117 OnResume, WifiEntryCallback, ConnectCallback, DisconnectCallback, ForgetCallback, 118 SignInCallback { 119 120 private static final String TAG = "WifiDetailsPrefCtrl2"; 121 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 122 123 @VisibleForTesting 124 static final String KEY_HEADER = "connection_header"; 125 @VisibleForTesting 126 static final String KEY_DATA_USAGE_HEADER = "status_header"; 127 @VisibleForTesting 128 static final String KEY_BUTTONS_PREF = "buttons"; 129 @VisibleForTesting 130 static final String KEY_SIGNAL_STRENGTH_PREF = "signal_strength"; 131 @VisibleForTesting 132 static final String KEY_TX_LINK_SPEED = "tx_link_speed"; 133 @VisibleForTesting 134 static final String KEY_RX_LINK_SPEED = "rx_link_speed"; 135 @VisibleForTesting 136 static final String KEY_FREQUENCY_PREF = "frequency"; 137 @VisibleForTesting 138 static final String KEY_SECURITY_PREF = "security"; 139 @VisibleForTesting 140 static final String KEY_SSID_PREF = "ssid"; 141 @VisibleForTesting 142 static final String KEY_EAP_SIM_SUBSCRIPTION_PREF = "eap_sim_subscription"; 143 @VisibleForTesting 144 static final String KEY_MAC_ADDRESS_PREF = "mac_address"; 145 @VisibleForTesting 146 static final String KEY_IP_ADDRESS_PREF = "ip_address"; 147 @VisibleForTesting 148 static final String KEY_GATEWAY_PREF = "gateway"; 149 @VisibleForTesting 150 static final String KEY_SUBNET_MASK_PREF = "subnet_mask"; 151 @VisibleForTesting 152 static final String KEY_DNS_PREF = "dns"; 153 @VisibleForTesting 154 static final String KEY_IPV6_CATEGORY = "ipv6_category"; 155 @VisibleForTesting 156 static final String KEY_IPV6_ADDRESSES_PREF = "ipv6_addresses"; 157 @VisibleForTesting 158 static final String KEY_WIFI_TYPE_PREF = "type"; 159 160 private final WifiEntry mWifiEntry; 161 private final ConnectivityManager mConnectivityManager; 162 private final PreferenceFragmentCompat mFragment; 163 private final Handler mHandler; 164 private LinkProperties mLinkProperties; 165 private Network mNetwork; 166 private NetworkInfo mNetworkInfo; 167 private NetworkCapabilities mNetworkCapabilities; 168 private int mRssiSignalLevel = -1; 169 @VisibleForTesting boolean mShowX; // Shows the Wi-Fi signal icon of Pie+x when it's true. 170 private String[] mSignalStr; 171 private WifiInfo mWifiInfo; 172 private final WifiManager mWifiManager; 173 private final MetricsFeatureProvider mMetricsFeatureProvider; 174 175 // UI elements - in order of appearance 176 private ActionButtonsPreference mButtonsPref; 177 private EntityHeaderController mEntityHeaderController; 178 private Preference mSignalStrengthPref; 179 private Preference mTxLinkSpeedPref; 180 private Preference mRxLinkSpeedPref; 181 private Preference mFrequencyPref; 182 private Preference mSecurityPref; 183 private Preference mSsidPref; 184 private Preference mEapSimSubscriptionPref; 185 private Preference mMacAddressPref; 186 private Preference mIpAddressPref; 187 private Preference mGatewayPref; 188 private Preference mSubnetPref; 189 private Preference mDnsPref; 190 private Preference mTypePref; 191 private PreferenceCategory mIpv6Category; 192 private Preference mIpv6AddressPref; 193 private Lifecycle mLifecycle; 194 Preference mDataUsageSummaryPref; 195 WifiDataUsageSummaryPreferenceController mSummaryHeaderController; 196 197 private final IconInjector mIconInjector; 198 private final Clock mClock; 199 200 private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder() 201 .clearCapabilities().addTransportType(TRANSPORT_WIFI).build(); 202 203 private CarrierIdAsyncQueryHandler mCarrierIdAsyncQueryHandler; 204 private static final int TOKEN_QUERY_CARRIER_ID_AND_UPDATE_SIM_SUMMARY = 1; 205 private static final int COLUMN_CARRIER_NAME = 0; 206 207 private class CarrierIdAsyncQueryHandler extends AsyncQueryHandler { 208 CarrierIdAsyncQueryHandler(Context context)209 private CarrierIdAsyncQueryHandler(Context context) { 210 super(context.getContentResolver()); 211 } 212 213 @Override onQueryComplete(int token, Object cookie, Cursor cursor)214 protected void onQueryComplete(int token, Object cookie, Cursor cursor) { 215 if (token == TOKEN_QUERY_CARRIER_ID_AND_UPDATE_SIM_SUMMARY) { 216 if (mContext == null || cursor == null || !cursor.moveToFirst()) { 217 if (cursor != null) { 218 cursor.close(); 219 } 220 mEapSimSubscriptionPref.setSummary(R.string.wifi_require_sim_card_to_connect); 221 return; 222 } 223 mEapSimSubscriptionPref.setSummary(mContext.getString( 224 R.string.wifi_require_specific_sim_card_to_connect, 225 cursor.getString(COLUMN_CARRIER_NAME))); 226 cursor.close(); 227 return; 228 } 229 } 230 } 231 232 // Must be run on the UI thread since it directly manipulates UI state. 233 private final NetworkCallback mNetworkCallback = new NetworkCallback() { 234 @Override 235 public void onLinkPropertiesChanged(Network network, LinkProperties lp) { 236 if (network.equals(mNetwork) && !lp.equals(mLinkProperties)) { 237 mLinkProperties = lp; 238 refreshEntityHeader(); 239 refreshButtons(); 240 refreshIpLayerInfo(); 241 } 242 } 243 244 private boolean hasCapabilityChanged(NetworkCapabilities nc, int cap) { 245 // If this is the first time we get NetworkCapabilities, report that something changed. 246 if (mNetworkCapabilities == null) return true; 247 248 // nc can never be null, see ConnectivityService#callCallbackForRequest. 249 return mNetworkCapabilities.hasCapability(cap) != nc.hasCapability(cap); 250 } 251 252 private boolean hasPrivateDnsStatusChanged(NetworkCapabilities nc) { 253 // If this is the first time that WifiDetailPreferenceController2 gets 254 // NetworkCapabilities, report that something has changed and assign nc to 255 // mNetworkCapabilities in onCapabilitiesChanged. Note that the NetworkCapabilities 256 // from onCapabilitiesChanged() will never be null, so calling 257 // mNetworkCapabilities.isPrivateDnsBroken() would be safe next time. 258 if (mNetworkCapabilities == null) { 259 return true; 260 } 261 262 return mNetworkCapabilities.isPrivateDnsBroken() != nc.isPrivateDnsBroken(); 263 } 264 265 @Override 266 public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) { 267 // If the network just validated or lost Internet access or detected partial internet 268 // connectivity or private dns was broken, refresh network state. Don't do this on 269 // every NetworkCapabilities change because refreshEntityHeader sends IPCs to the 270 // system server from the UI thread, which can cause jank. 271 if (network.equals(mNetwork) && !nc.equals(mNetworkCapabilities)) { 272 if (hasPrivateDnsStatusChanged(nc) 273 || hasCapabilityChanged(nc, NET_CAPABILITY_VALIDATED) 274 || hasCapabilityChanged(nc, NET_CAPABILITY_CAPTIVE_PORTAL) 275 || hasCapabilityChanged(nc, NET_CAPABILITY_PARTIAL_CONNECTIVITY)) { 276 refreshEntityHeader(); 277 } 278 mNetworkCapabilities = nc; 279 refreshButtons(); 280 refreshIpLayerInfo(); 281 } 282 } 283 284 @Override 285 public void onLost(Network network) { 286 // Ephemeral network not a saved network, leave detail page once disconnected 287 if (!mWifiEntry.isSaved() && network.equals(mNetwork)) { 288 if (DEBUG) { 289 Log.d(TAG, "OnLost and exit WifiNetworkDetailsPage"); 290 } 291 mFragment.getActivity().finish(); 292 } 293 } 294 }; 295 296 /** 297 * To get an instance of {@link WifiDetailPreferenceController2} 298 */ newInstance( WifiEntry wifiEntry, ConnectivityManager connectivityManager, Context context, PreferenceFragmentCompat fragment, Handler handler, Lifecycle lifecycle, WifiManager wifiManager, MetricsFeatureProvider metricsFeatureProvider)299 public static WifiDetailPreferenceController2 newInstance( 300 WifiEntry wifiEntry, 301 ConnectivityManager connectivityManager, 302 Context context, 303 PreferenceFragmentCompat fragment, 304 Handler handler, 305 Lifecycle lifecycle, 306 WifiManager wifiManager, 307 MetricsFeatureProvider metricsFeatureProvider) { 308 return new WifiDetailPreferenceController2( 309 wifiEntry, connectivityManager, context, fragment, handler, lifecycle, 310 wifiManager, metricsFeatureProvider, new IconInjector(context), new Clock()); 311 } 312 313 @VisibleForTesting WifiDetailPreferenceController2( WifiEntry wifiEntry, ConnectivityManager connectivityManager, Context context, PreferenceFragmentCompat fragment, Handler handler, Lifecycle lifecycle, WifiManager wifiManager, MetricsFeatureProvider metricsFeatureProvider, IconInjector injector, Clock clock)314 /* package */ WifiDetailPreferenceController2( 315 WifiEntry wifiEntry, 316 ConnectivityManager connectivityManager, 317 Context context, 318 PreferenceFragmentCompat fragment, 319 Handler handler, 320 Lifecycle lifecycle, 321 WifiManager wifiManager, 322 MetricsFeatureProvider metricsFeatureProvider, 323 IconInjector injector, 324 Clock clock) { 325 super(context); 326 327 mWifiEntry = wifiEntry; 328 mWifiEntry.setListener(this); 329 mConnectivityManager = connectivityManager; 330 mFragment = fragment; 331 mHandler = handler; 332 mSignalStr = context.getResources().getStringArray(R.array.wifi_signal); 333 mWifiManager = wifiManager; 334 mMetricsFeatureProvider = metricsFeatureProvider; 335 mIconInjector = injector; 336 mClock = clock; 337 338 mLifecycle = lifecycle; 339 lifecycle.addObserver(this); 340 } 341 342 @Override isAvailable()343 public boolean isAvailable() { 344 return true; 345 } 346 347 @Override getPreferenceKey()348 public String getPreferenceKey() { 349 // Returns null since this controller contains more than one Preference 350 return null; 351 } 352 353 @Override displayPreference(PreferenceScreen screen)354 public void displayPreference(PreferenceScreen screen) { 355 super.displayPreference(screen); 356 357 setupEntityHeader(screen); 358 359 mButtonsPref = ((ActionButtonsPreference) screen.findPreference(KEY_BUTTONS_PREF)) 360 .setButton1Text(R.string.forget) 361 .setButton1Icon(R.drawable.ic_settings_delete) 362 .setButton1OnClickListener(view -> forgetNetwork()) 363 .setButton2Text(R.string.wifi_sign_in_button_text) 364 .setButton2Icon(R.drawable.ic_settings_sign_in) 365 .setButton2OnClickListener(view -> signIntoNetwork()) 366 .setButton3Text(getConnectDisconnectButtonTextResource()) 367 .setButton3Icon(getConnectDisconnectButtonIconResource()) 368 .setButton3OnClickListener(view -> connectDisconnectNetwork()) 369 .setButton4Text(R.string.share) 370 .setButton4Icon(R.drawable.ic_qrcode_24dp) 371 .setButton4OnClickListener(view -> shareNetwork()); 372 updateCaptivePortalButton(); 373 374 mSignalStrengthPref = screen.findPreference(KEY_SIGNAL_STRENGTH_PREF); 375 mTxLinkSpeedPref = screen.findPreference(KEY_TX_LINK_SPEED); 376 mRxLinkSpeedPref = screen.findPreference(KEY_RX_LINK_SPEED); 377 mFrequencyPref = screen.findPreference(KEY_FREQUENCY_PREF); 378 mSecurityPref = screen.findPreference(KEY_SECURITY_PREF); 379 380 mSsidPref = screen.findPreference(KEY_SSID_PREF); 381 mEapSimSubscriptionPref = screen.findPreference(KEY_EAP_SIM_SUBSCRIPTION_PREF); 382 mMacAddressPref = screen.findPreference(KEY_MAC_ADDRESS_PREF); 383 mIpAddressPref = screen.findPreference(KEY_IP_ADDRESS_PREF); 384 mGatewayPref = screen.findPreference(KEY_GATEWAY_PREF); 385 mSubnetPref = screen.findPreference(KEY_SUBNET_MASK_PREF); 386 mDnsPref = screen.findPreference(KEY_DNS_PREF); 387 mTypePref = screen.findPreference(KEY_WIFI_TYPE_PREF); 388 389 mIpv6Category = screen.findPreference(KEY_IPV6_CATEGORY); 390 mIpv6AddressPref = screen.findPreference(KEY_IPV6_ADDRESSES_PREF); 391 } 392 393 /** 394 * Update text, icon and listener of the captive portal button. 395 * @return True if the button should be shown. 396 */ updateCaptivePortalButton()397 private boolean updateCaptivePortalButton() { 398 final Uri venueInfoUrl = getCaptivePortalVenueInfoUrl(); 399 if (venueInfoUrl == null) { 400 mButtonsPref.setButton2Text(R.string.wifi_sign_in_button_text) 401 .setButton2Icon(R.drawable.ic_settings_sign_in) 402 .setButton2OnClickListener(view -> signIntoNetwork()); 403 return canSignIntoNetwork(); 404 } 405 406 mButtonsPref.setButton2Text(R.string.wifi_venue_website_button_text) 407 .setButton2Icon(R.drawable.ic_settings_sign_in) 408 .setButton2OnClickListener(view -> { 409 final Intent infoIntent = new Intent(Intent.ACTION_VIEW); 410 infoIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 411 infoIntent.setData(venueInfoUrl); 412 mContext.startActivity(infoIntent); 413 }); 414 // Only show the venue website when the network is connected. 415 return mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED; 416 } 417 getCaptivePortalVenueInfoUrl()418 private Uri getCaptivePortalVenueInfoUrl() { 419 final LinkProperties lp = mLinkProperties; 420 if (lp == null) { 421 return null; 422 } 423 final CaptivePortalData data = lp.getCaptivePortalData(); 424 if (data == null) { 425 return null; 426 } 427 return data.getVenueInfoUrl(); 428 } 429 setupEntityHeader(PreferenceScreen screen)430 private void setupEntityHeader(PreferenceScreen screen) { 431 LayoutPreference headerPref = screen.findPreference(KEY_HEADER); 432 433 if (usingDataUsageHeader(mContext)) { 434 headerPref.setVisible(false); 435 mDataUsageSummaryPref = screen.findPreference(KEY_DATA_USAGE_HEADER); 436 mDataUsageSummaryPref.setVisible(true); 437 mSummaryHeaderController = 438 new WifiDataUsageSummaryPreferenceController(mFragment.getActivity(), 439 mLifecycle, (PreferenceFragmentCompat) mFragment, 440 mWifiEntry.getTitle()); 441 return; 442 } 443 444 mEntityHeaderController = 445 EntityHeaderController.newInstance( 446 mFragment.getActivity(), mFragment, 447 headerPref.findViewById(R.id.entity_header)); 448 449 ImageView iconView = headerPref.findViewById(R.id.entity_header_icon); 450 451 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 452 453 mEntityHeaderController.setLabel(mWifiEntry.getTitle()); 454 } 455 getExpiryTimeSummary()456 private String getExpiryTimeSummary() { 457 if (mLinkProperties == null || mLinkProperties.getCaptivePortalData() == null) { 458 return null; 459 } 460 461 final long expiryTimeMillis = mLinkProperties.getCaptivePortalData().getExpiryTimeMillis(); 462 if (expiryTimeMillis <= 0) { 463 return null; 464 } 465 final ZonedDateTime now = mClock.now(); 466 final ZonedDateTime expiryTime = ZonedDateTime.ofInstant( 467 Instant.ofEpochMilli(expiryTimeMillis), 468 now.getZone()); 469 470 if (now.isAfter(expiryTime)) { 471 return null; 472 } 473 474 if (now.plusDays(2).isAfter(expiryTime)) { 475 // Expiration within 2 days: show a duration 476 return mContext.getString(R.string.wifi_time_remaining, StringUtil.formatElapsedTime( 477 mContext, 478 Duration.between(now, expiryTime).getSeconds() * 1000, 479 false /* withSeconds */, false /* collapseTimeUnit */)); 480 } 481 482 // For more than 2 days, show the expiry date 483 return mContext.getString(R.string.wifi_expiry_time, 484 DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT).format(expiryTime)); 485 } 486 refreshEntityHeader()487 private void refreshEntityHeader() { 488 if (usingDataUsageHeader(mContext)) { 489 mSummaryHeaderController.updateState(mDataUsageSummaryPref); 490 } else { 491 mEntityHeaderController 492 .setSummary(mWifiEntry.getSummary()) 493 .setSecondSummary(getExpiryTimeSummary()) 494 .setRecyclerView(mFragment.getListView(), mLifecycle) 495 .done(mFragment.getActivity(), true /* rebind */); 496 } 497 } 498 499 @VisibleForTesting updateNetworkInfo()500 void updateNetworkInfo() { 501 if (mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED) { 502 mNetwork = mWifiManager.getCurrentNetwork(); 503 mLinkProperties = mConnectivityManager.getLinkProperties(mNetwork); 504 mNetworkCapabilities = mConnectivityManager.getNetworkCapabilities(mNetwork); 505 mNetworkInfo = mConnectivityManager.getNetworkInfo(mNetwork); 506 mWifiInfo = mWifiManager.getConnectionInfo(); 507 } else { 508 mNetwork = null; 509 mLinkProperties = null; 510 mNetworkCapabilities = null; 511 mNetworkInfo = null; 512 mWifiInfo = null; 513 } 514 } 515 516 @Override onResume()517 public void onResume() { 518 // Disable the animation of the EntityHeaderController 519 final RecyclerView recyclerView = mFragment.getListView(); 520 if (recyclerView != null) { 521 recyclerView.setItemAnimator(null); 522 } 523 524 // Ensure mNetwork is set before any callbacks above are delivered, since our 525 // NetworkCallback only looks at changes to mNetwork. 526 updateNetworkInfo(); 527 refreshPage(); 528 mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback, 529 mHandler); 530 } 531 532 @Override onPause()533 public void onPause() { 534 mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); 535 } 536 refreshPage()537 private void refreshPage() { 538 Log.d(TAG, "Update UI!"); 539 540 // refresh header 541 refreshEntityHeader(); 542 543 // refresh Buttons 544 refreshButtons(); 545 546 // Update Connection Header icon and Signal Strength Preference 547 refreshRssiViews(); 548 // Frequency Pref 549 refreshFrequency(); 550 // Security Pref 551 refreshSecurity(); 552 // Transmit Link Speed Pref 553 refreshTxSpeed(); 554 // Receive Link Speed Pref 555 refreshRxSpeed(); 556 // IP related information 557 refreshIpLayerInfo(); 558 // SSID Pref 559 refreshSsid(); 560 // EAP SIM subscription 561 refreshEapSimSubscription(); 562 // MAC Address Pref 563 refreshMacAddress(); 564 // Wifi Type 565 refreshWifiType(); 566 } 567 refreshRssiViews()568 private void refreshRssiViews() { 569 final int signalLevel = mWifiEntry.getLevel(); 570 571 // Disappears signal view if not in range. e.g. for saved networks. 572 if (signalLevel == WifiEntry.WIFI_LEVEL_UNREACHABLE) { 573 mSignalStrengthPref.setVisible(false); 574 mRssiSignalLevel = -1; 575 return; 576 } 577 578 final boolean showX = mWifiEntry.shouldShowXLevelIcon(); 579 580 if (mRssiSignalLevel == signalLevel && mShowX == showX) { 581 return; 582 } 583 mRssiSignalLevel = signalLevel; 584 mShowX = showX; 585 Drawable wifiIcon = mIconInjector.getIcon(mShowX, mRssiSignalLevel); 586 587 if (mEntityHeaderController != null) { 588 mEntityHeaderController 589 .setIcon(redrawIconForHeader(wifiIcon)).done(mFragment.getActivity(), 590 true /* rebind */); 591 } 592 593 Drawable wifiIconDark = wifiIcon.getConstantState().newDrawable().mutate(); 594 wifiIconDark.setTintList(Utils.getColorAttr(mContext, android.R.attr.colorControlNormal)); 595 mSignalStrengthPref.setIcon(wifiIconDark); 596 597 mSignalStrengthPref.setSummary(mSignalStr[mRssiSignalLevel]); 598 mSignalStrengthPref.setVisible(true); 599 } 600 redrawIconForHeader(Drawable original)601 private Drawable redrawIconForHeader(Drawable original) { 602 final int iconSize = mContext.getResources().getDimensionPixelSize( 603 R.dimen.wifi_detail_page_header_image_size); 604 final int actualWidth = original.getMinimumWidth(); 605 final int actualHeight = original.getMinimumHeight(); 606 607 if ((actualWidth == iconSize && actualHeight == iconSize) 608 || !VectorDrawable.class.isInstance(original)) { 609 return original; 610 } 611 612 // clear tint list to make sure can set 87% black after enlarge 613 original.setTintList(null); 614 615 // enlarge icon size 616 final Bitmap bitmap = Utils.createBitmap(original, 617 iconSize /*width*/, 618 iconSize /*height*/); 619 Drawable newIcon = new BitmapDrawable(null /*resource*/, bitmap); 620 621 // config color for 87% black after enlarge 622 newIcon.setTintList(Utils.getColorAttr(mContext, android.R.attr.textColorPrimary)); 623 624 return newIcon; 625 } 626 refreshFrequency()627 private void refreshFrequency() { 628 final ConnectedInfo connectedInfo = mWifiEntry.getConnectedInfo(); 629 if (connectedInfo == null) { 630 mFrequencyPref.setVisible(false); 631 return; 632 } 633 634 // TODO(b/190390803): We should get the band string directly from WifiEntry.ConnectedInfo 635 // instead of doing the frequency -> band conversion here. 636 final int frequency = connectedInfo.frequencyMhz; 637 String band = null; 638 if (frequency >= WifiEntry.MIN_FREQ_24GHZ && frequency < WifiEntry.MAX_FREQ_24GHZ) { 639 band = mContext.getResources().getString(R.string.wifi_band_24ghz); 640 } else if (frequency >= WifiEntry.MIN_FREQ_5GHZ && frequency < WifiEntry.MAX_FREQ_5GHZ) { 641 band = mContext.getResources().getString(R.string.wifi_band_5ghz); 642 } else if (frequency >= WifiEntry.MIN_FREQ_6GHZ && frequency < WifiEntry.MAX_FREQ_6GHZ) { 643 band = mContext.getResources().getString(R.string.wifi_band_6ghz); 644 } else { 645 // Connecting state is unstable, make it disappeared if unexpected 646 if (mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTING) { 647 mFrequencyPref.setVisible(false); 648 } else { 649 Log.e(TAG, "Unexpected frequency " + frequency); 650 } 651 return; 652 } 653 mFrequencyPref.setSummary(band); 654 mFrequencyPref.setVisible(true); 655 } 656 refreshSecurity()657 private void refreshSecurity() { 658 mSecurityPref.setSummary(mWifiEntry.getSecurityString(false /* concise */)); 659 } 660 refreshTxSpeed()661 private void refreshTxSpeed() { 662 if (mWifiInfo == null 663 || mWifiEntry.getConnectedState() != WifiEntry.CONNECTED_STATE_CONNECTED) { 664 mTxLinkSpeedPref.setVisible(false); 665 return; 666 } 667 668 int txLinkSpeedMbps = mWifiInfo.getTxLinkSpeedMbps(); 669 mTxLinkSpeedPref.setVisible(txLinkSpeedMbps >= 0); 670 mTxLinkSpeedPref.setSummary(mContext.getString( 671 R.string.tx_link_speed, mWifiInfo.getTxLinkSpeedMbps())); 672 } 673 refreshRxSpeed()674 private void refreshRxSpeed() { 675 if (mWifiInfo == null 676 || mWifiEntry.getConnectedState() != WifiEntry.CONNECTED_STATE_CONNECTED) { 677 mRxLinkSpeedPref.setVisible(false); 678 return; 679 } 680 681 int rxLinkSpeedMbps = mWifiInfo.getRxLinkSpeedMbps(); 682 mRxLinkSpeedPref.setVisible(rxLinkSpeedMbps >= 0); 683 mRxLinkSpeedPref.setSummary(mContext.getString( 684 R.string.rx_link_speed, mWifiInfo.getRxLinkSpeedMbps())); 685 } 686 refreshSsid()687 private void refreshSsid() { 688 if (mWifiEntry.isSubscription() && mWifiEntry.getSsid() != null) { 689 mSsidPref.setVisible(true); 690 mSsidPref.setSummary(mWifiEntry.getSsid()); 691 } else { 692 mSsidPref.setVisible(false); 693 } 694 } 695 refreshEapSimSubscription()696 private void refreshEapSimSubscription() { 697 mEapSimSubscriptionPref.setVisible(false); 698 699 if (mWifiEntry.getSecurity() != WifiEntry.SECURITY_EAP) { 700 return; 701 } 702 final WifiConfiguration config = mWifiEntry.getWifiConfiguration(); 703 if (config == null || config.enterpriseConfig == null) { 704 return; 705 } 706 if (!config.enterpriseConfig.isAuthenticationSimBased()) { 707 return; 708 } 709 710 mEapSimSubscriptionPref.setVisible(true); 711 712 // Checks if the SIM subscription is active. 713 final List<SubscriptionInfo> activeSubscriptionInfos = mContext 714 .getSystemService(SubscriptionManager.class).getActiveSubscriptionInfoList(); 715 final int defaultDataSubscriptionId = SubscriptionManager.getDefaultDataSubscriptionId(); 716 if (activeSubscriptionInfos != null) { 717 for (SubscriptionInfo subscriptionInfo : activeSubscriptionInfos) { 718 final CharSequence displayName = SubscriptionUtil.getUniqueSubscriptionDisplayName( 719 subscriptionInfo, mContext); 720 if (config.carrierId == subscriptionInfo.getCarrierId()) { 721 mEapSimSubscriptionPref.setSummary(displayName); 722 return; 723 } 724 725 // When it's UNKNOWN_CARRIER_ID, devices connects it with the SIM subscription of 726 // defaultDataSubscriptionId. 727 if (config.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID 728 && defaultDataSubscriptionId == subscriptionInfo.getSubscriptionId()) { 729 mEapSimSubscriptionPref.setSummary(displayName); 730 return; 731 } 732 } 733 } 734 735 if (config.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) { 736 mEapSimSubscriptionPref.setSummary(R.string.wifi_no_related_sim_card); 737 return; 738 } 739 740 // The Wi-Fi network has specified carrier id, query carrier name from CarrierIdProvider. 741 if (mCarrierIdAsyncQueryHandler == null) { 742 mCarrierIdAsyncQueryHandler = new CarrierIdAsyncQueryHandler(mContext); 743 } 744 mCarrierIdAsyncQueryHandler.cancelOperation(TOKEN_QUERY_CARRIER_ID_AND_UPDATE_SIM_SUMMARY); 745 mCarrierIdAsyncQueryHandler.startQuery(TOKEN_QUERY_CARRIER_ID_AND_UPDATE_SIM_SUMMARY, 746 null /* cookie */, 747 CarrierId.All.CONTENT_URI, 748 new String[]{CarrierId.CARRIER_NAME}, 749 CarrierId.CARRIER_ID + "=?", 750 new String[] {Integer.toString(config.carrierId)}, 751 null /* orderBy */); 752 } 753 refreshMacAddress()754 private void refreshMacAddress() { 755 final String macAddress = mWifiEntry.getMacAddress(); 756 if (TextUtils.isEmpty(macAddress)) { 757 mMacAddressPref.setVisible(false); 758 return; 759 } 760 761 mMacAddressPref.setVisible(true); 762 mMacAddressPref.setTitle(getMacAddressTitle()); 763 764 if (macAddress.equals(WifiInfo.DEFAULT_MAC_ADDRESS)) { 765 mMacAddressPref.setSummary(R.string.device_info_not_available); 766 } else { 767 mMacAddressPref.setSummary(macAddress); 768 } 769 } 770 refreshWifiType()771 private void refreshWifiType() { 772 final ConnectedInfo connectedInfo = mWifiEntry.getConnectedInfo(); 773 if (connectedInfo == null) { 774 mTypePref.setVisible(false); 775 return; 776 } 777 778 final int typeString = getWifiStandardTypeString(connectedInfo.wifiStandard); 779 if (typeString != -1) { 780 mTypePref.setSummary(typeString); 781 mTypePref.setVisible(true); 782 } else { 783 mTypePref.setVisible(false); 784 } 785 } 786 getWifiStandardTypeString(int wifiStandardType)787 private int getWifiStandardTypeString(int wifiStandardType) { 788 Log.d(TAG, "Wifi Type " + wifiStandardType); 789 switch (wifiStandardType) { 790 case ScanResult.WIFI_STANDARD_11AX: 791 return R.string.wifi_type_11AX; 792 case ScanResult.WIFI_STANDARD_11AC: 793 return R.string.wifi_type_11AC; 794 case ScanResult.WIFI_STANDARD_11N: 795 return R.string.wifi_type_11N; 796 default: 797 return -1; 798 } 799 } 800 getMacAddressTitle()801 private int getMacAddressTitle() { 802 if (mWifiEntry.getPrivacy() == WifiEntry.PRIVACY_RANDOMIZED_MAC) { 803 return mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED 804 ? R.string.wifi_advanced_randomized_mac_address_title 805 : R.string.wifi_advanced_randomized_mac_address_disconnected_title; 806 } 807 return R.string.wifi_advanced_device_mac_address_title; 808 } 809 updatePreference(Preference pref, String detailText)810 private void updatePreference(Preference pref, String detailText) { 811 if (!TextUtils.isEmpty(detailText)) { 812 pref.setSummary(detailText); 813 pref.setVisible(true); 814 } else { 815 pref.setVisible(false); 816 } 817 } 818 refreshButtons()819 private void refreshButtons() { 820 final boolean canForgetNetwork = canForgetNetwork(); 821 final boolean showCaptivePortalButton = updateCaptivePortalButton(); 822 final boolean canConnectDisconnectNetwork = mWifiEntry.canConnect() 823 || mWifiEntry.canDisconnect(); 824 final boolean canShareNetwork = canShareNetwork(); 825 826 mButtonsPref.setButton1Visible(canForgetNetwork); 827 mButtonsPref.setButton2Visible(showCaptivePortalButton); 828 // Keep the connect/disconnected button visible if we can connect/disconnect, or if we are 829 // in the middle of connecting (greyed out). 830 mButtonsPref.setButton3Visible(canConnectDisconnectNetwork 831 || mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTING); 832 mButtonsPref.setButton3Enabled(canConnectDisconnectNetwork); 833 mButtonsPref.setButton3Text(getConnectDisconnectButtonTextResource()); 834 mButtonsPref.setButton3Icon(getConnectDisconnectButtonIconResource()); 835 mButtonsPref.setButton4Visible(canShareNetwork); 836 mButtonsPref.setVisible(canForgetNetwork 837 || showCaptivePortalButton 838 || canConnectDisconnectNetwork 839 || canShareNetwork); 840 } 841 getConnectDisconnectButtonTextResource()842 private int getConnectDisconnectButtonTextResource() { 843 switch (mWifiEntry.getConnectedState()) { 844 case WifiEntry.CONNECTED_STATE_DISCONNECTED: 845 return R.string.wifi_connect; 846 case WifiEntry.CONNECTED_STATE_CONNECTED: 847 return R.string.wifi_disconnect_button_text; 848 case WifiEntry.CONNECTED_STATE_CONNECTING: 849 return R.string.wifi_connecting; 850 default: 851 throw new IllegalStateException("Invalid WifiEntry connected state"); 852 } 853 } 854 getConnectDisconnectButtonIconResource()855 private int getConnectDisconnectButtonIconResource() { 856 switch (mWifiEntry.getConnectedState()) { 857 case WifiEntry.CONNECTED_STATE_DISCONNECTED: 858 case WifiEntry.CONNECTED_STATE_CONNECTING: 859 return R.drawable.ic_settings_wireless; 860 case WifiEntry.CONNECTED_STATE_CONNECTED: 861 return R.drawable.ic_settings_close; 862 default: 863 throw new IllegalStateException("Invalid WifiEntry connected state"); 864 } 865 } 866 refreshIpLayerInfo()867 private void refreshIpLayerInfo() { 868 // Hide IP layer info if not a connected network. 869 if (mWifiEntry.getConnectedState() != WifiEntry.CONNECTED_STATE_CONNECTED 870 || mNetwork == null || mLinkProperties == null) { 871 mIpAddressPref.setVisible(false); 872 mSubnetPref.setVisible(false); 873 mGatewayPref.setVisible(false); 874 mDnsPref.setVisible(false); 875 mIpv6Category.setVisible(false); 876 return; 877 } 878 879 // Find IPv4 and IPv6 addresses. 880 String ipv4Address = null; 881 String subnet = null; 882 StringJoiner ipv6Addresses = new StringJoiner("\n"); 883 884 for (LinkAddress addr : mLinkProperties.getLinkAddresses()) { 885 if (addr.getAddress() instanceof Inet4Address) { 886 ipv4Address = addr.getAddress().getHostAddress(); 887 subnet = ipv4PrefixLengthToSubnetMask(addr.getPrefixLength()); 888 } else if (addr.getAddress() instanceof Inet6Address) { 889 ipv6Addresses.add(addr.getAddress().getHostAddress()); 890 } 891 } 892 893 // Find IPv4 default gateway. 894 String gateway = null; 895 for (RouteInfo routeInfo : mLinkProperties.getRoutes()) { 896 if (routeInfo.hasGateway() && routeInfo.isDefaultRoute() 897 && routeInfo.getDestination().getAddress() instanceof Inet4Address) { 898 gateway = routeInfo.getGateway().getHostAddress(); 899 break; 900 } 901 } 902 903 // Find all (IPv4 and IPv6) DNS addresses. 904 String dnsServers = mLinkProperties.getDnsServers().stream() 905 .map(InetAddress::getHostAddress) 906 .collect(Collectors.joining("\n")); 907 908 // Update UI. 909 updatePreference(mIpAddressPref, ipv4Address); 910 updatePreference(mSubnetPref, subnet); 911 updatePreference(mGatewayPref, gateway); 912 updatePreference(mDnsPref, dnsServers); 913 914 if (ipv6Addresses.length() > 0) { 915 mIpv6AddressPref.setSummary( 916 BidiFormatter.getInstance().unicodeWrap(ipv6Addresses.toString())); 917 mIpv6Category.setVisible(true); 918 } else { 919 mIpv6Category.setVisible(false); 920 } 921 } 922 ipv4PrefixLengthToSubnetMask(int prefixLength)923 private static String ipv4PrefixLengthToSubnetMask(int prefixLength) { 924 try { 925 return Inet4AddressUtils.getPrefixMaskAsInet4Address(prefixLength).getHostAddress(); 926 } catch (IllegalArgumentException e) { 927 return null; 928 } 929 } 930 931 /** 932 * Returns whether the network represented by this preference can be modified. 933 */ canModifyNetwork()934 public boolean canModifyNetwork() { 935 return mWifiEntry.isSaved() 936 && !WifiUtils.isNetworkLockedDown(mContext, mWifiEntry.getWifiConfiguration()); 937 } 938 939 /** 940 * Returns whether the network represented by this preference can be forgotten. 941 */ canForgetNetwork()942 public boolean canForgetNetwork() { 943 return mWifiEntry.canForget() 944 && !WifiUtils.isNetworkLockedDown(mContext, mWifiEntry.getWifiConfiguration()); 945 } 946 947 /** 948 * Returns whether the user can sign into the network represented by this preference. 949 */ canSignIntoNetwork()950 private boolean canSignIntoNetwork() { 951 return mWifiEntry.canSignIn(); 952 } 953 954 /** 955 * Returns whether the user can share the network represented by this preference with QR code. 956 */ canShareNetwork()957 private boolean canShareNetwork() { 958 return mWifiEntry.canShare(); 959 } 960 961 /** 962 * Forgets the wifi network associated with this preference. 963 */ forgetNetwork()964 private void forgetNetwork() { 965 if (mWifiEntry.isSubscription()) { 966 // Post a dialog to confirm if user really want to forget the passpoint network. 967 showConfirmForgetDialog(); 968 return; 969 } else { 970 mWifiEntry.forget(this); 971 } 972 973 final Activity activity = mFragment.getActivity(); 974 if (activity != null) { 975 mMetricsFeatureProvider.action(activity, SettingsEnums.ACTION_WIFI_FORGET); 976 activity.finish(); 977 } 978 } 979 980 @VisibleForTesting showConfirmForgetDialog()981 protected void showConfirmForgetDialog() { 982 final AlertDialog dialog = new AlertDialog.Builder(mContext) 983 .setPositiveButton(R.string.forget, ((dialog1, which) -> { 984 try { 985 mWifiEntry.forget(this); 986 } catch (RuntimeException e) { 987 Log.e(TAG, "Failed to remove Passpoint configuration: " + e); 988 } 989 mMetricsFeatureProvider.action( 990 mFragment.getActivity(), SettingsEnums.ACTION_WIFI_FORGET); 991 mFragment.getActivity().finish(); 992 })) 993 .setNegativeButton(R.string.cancel, null /* listener */) 994 .setTitle(R.string.wifi_forget_dialog_title) 995 .setMessage(R.string.forget_passpoint_dialog_message) 996 .create(); 997 dialog.show(); 998 } 999 1000 /** 1001 * Show QR code to share the network represented by this preference. 1002 */ launchWifiDppConfiguratorActivity()1003 private void launchWifiDppConfiguratorActivity() { 1004 final Intent intent = WifiDppUtils.getConfiguratorQrCodeGeneratorIntentOrNull(mContext, 1005 mWifiManager, mWifiEntry); 1006 1007 if (intent == null) { 1008 Log.e(TAG, "Launch Wi-Fi DPP QR code generator with a wrong Wi-Fi network!"); 1009 } else { 1010 mMetricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN, 1011 SettingsEnums.ACTION_SETTINGS_SHARE_WIFI_QR_CODE, 1012 SettingsEnums.SETTINGS_WIFI_DPP_CONFIGURATOR, 1013 /* key */ null, 1014 /* value */ Integer.MIN_VALUE); 1015 1016 mContext.startActivity(intent); 1017 } 1018 } 1019 1020 /** 1021 * Share the wifi network with QR code. 1022 */ shareNetwork()1023 private void shareNetwork() { 1024 WifiDppUtils.showLockScreen(mContext, () -> launchWifiDppConfiguratorActivity()); 1025 } 1026 1027 /** 1028 * Sign in to the captive portal found on this wifi network associated with this preference. 1029 */ signIntoNetwork()1030 private void signIntoNetwork() { 1031 mMetricsFeatureProvider.action( 1032 mFragment.getActivity(), SettingsEnums.ACTION_WIFI_SIGNIN); 1033 mWifiEntry.signIn(this); 1034 } 1035 1036 @Override onSubmit(WifiDialog2 dialog)1037 public void onSubmit(WifiDialog2 dialog) { 1038 if (dialog.getController() != null) { 1039 mWifiManager.save(dialog.getController().getConfig(), new WifiManager.ActionListener() { 1040 @Override 1041 public void onSuccess() { 1042 } 1043 1044 @Override 1045 public void onFailure(int reason) { 1046 Activity activity = mFragment.getActivity(); 1047 if (activity != null) { 1048 Toast.makeText(activity, 1049 R.string.wifi_failed_save_message, 1050 Toast.LENGTH_SHORT).show(); 1051 } 1052 } 1053 }); 1054 } 1055 } 1056 1057 /** 1058 * Wrapper for testing compatibility. 1059 */ 1060 @VisibleForTesting 1061 static class IconInjector { 1062 private final Context mContext; 1063 IconInjector(Context context)1064 IconInjector(Context context) { 1065 mContext = context; 1066 } 1067 getIcon(boolean showX, int level)1068 public Drawable getIcon(boolean showX, int level) { 1069 return mContext.getDrawable(WifiUtils.getInternetIconResource(level, showX)).mutate(); 1070 } 1071 } 1072 1073 @VisibleForTesting 1074 static class Clock { now()1075 public ZonedDateTime now() { 1076 return ZonedDateTime.now(); 1077 } 1078 } 1079 usingDataUsageHeader(Context context)1080 private boolean usingDataUsageHeader(Context context) { 1081 return FeatureFlagUtils.isEnabled(context, FeatureFlags.WIFI_DETAILS_DATAUSAGE_HEADER); 1082 } 1083 1084 @VisibleForTesting connectDisconnectNetwork()1085 void connectDisconnectNetwork() { 1086 if (mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_DISCONNECTED) { 1087 mWifiEntry.connect(this); 1088 } else { 1089 mWifiEntry.disconnect(this); 1090 } 1091 } 1092 1093 /** 1094 * Indicates the state of the WifiEntry has changed and clients may retrieve updates through 1095 * the WifiEntry getter methods. 1096 */ 1097 @Override onUpdated()1098 public void onUpdated() { 1099 updateNetworkInfo(); 1100 refreshPage(); 1101 1102 // Refresh the Preferences in fragment. 1103 ((WifiNetworkDetailsFragment) mFragment).refreshPreferences(); 1104 } 1105 1106 /** 1107 * Result of the connect request indicated by the CONNECT_STATUS constants. 1108 */ 1109 @Override onConnectResult(@onnectStatus int status)1110 public void onConnectResult(@ConnectStatus int status) { 1111 if (status == ConnectCallback.CONNECT_STATUS_SUCCESS) { 1112 Toast.makeText(mContext, 1113 mContext.getString(R.string.wifi_connected_to_message, mWifiEntry.getTitle()), 1114 Toast.LENGTH_SHORT).show(); 1115 } else if (mWifiEntry.getLevel() == WifiEntry.WIFI_LEVEL_UNREACHABLE) { 1116 Toast.makeText(mContext, 1117 R.string.wifi_not_in_range_message, 1118 Toast.LENGTH_SHORT).show(); 1119 } else { 1120 Toast.makeText(mContext, 1121 R.string.wifi_failed_connect_message, 1122 Toast.LENGTH_SHORT).show(); 1123 } 1124 } 1125 1126 /** 1127 * Result of the disconnect request indicated by the DISCONNECT_STATUS constants. 1128 */ 1129 @Override onDisconnectResult(@isconnectStatus int status)1130 public void onDisconnectResult(@DisconnectStatus int status) { 1131 if (status == DisconnectCallback.DISCONNECT_STATUS_SUCCESS) { 1132 final Activity activity = mFragment.getActivity(); 1133 if (activity != null) { 1134 Toast.makeText(activity, 1135 activity.getString(R.string.wifi_disconnected_from, mWifiEntry.getTitle()), 1136 Toast.LENGTH_SHORT).show(); 1137 } 1138 } else { 1139 Log.e(TAG, "Disconnect Wi-Fi network failed"); 1140 } 1141 } 1142 1143 /** 1144 * Result of the forget request indicated by the FORGET_STATUS constants. 1145 */ 1146 @Override onForgetResult(@orgetStatus int status)1147 public void onForgetResult(@ForgetStatus int status) { 1148 if (status != ForgetCallback.FORGET_STATUS_SUCCESS) { 1149 Log.e(TAG, "Forget Wi-Fi network failed"); 1150 } 1151 1152 final Activity activity = mFragment.getActivity(); 1153 if (activity != null) { 1154 mMetricsFeatureProvider.action(activity, SettingsEnums.ACTION_WIFI_FORGET); 1155 activity.finish(); 1156 } 1157 } 1158 1159 /** 1160 * Result of the sign-in request indicated by the SIGNIN_STATUS constants. 1161 */ 1162 @Override onSignInResult(@ignInStatus int status)1163 public void onSignInResult(@SignInStatus int status) { 1164 refreshPage(); 1165 } 1166 } 1167