/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settingslib.wifi; import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED; import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED; import android.annotation.IntDef; import android.annotation.MainThread; import android.app.AppGlobals; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.net.ConnectivityManager; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkInfo.State; import android.net.NetworkKey; import android.net.NetworkScoreManager; import android.net.NetworkScorerAppData; import android.net.ScoredNetwork; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.KeyMgmt; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkScoreCache; import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.hotspot2.ProvisioningCallback; import android.os.Bundle; import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import android.util.Pair; import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.CollectionUtils; import com.android.settingslib.R; import com.android.settingslib.utils.ThreadUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; /** * Represents a selectable Wifi Network for use in various wifi selection menus backed by * {@link WifiTracker}. * *
An AccessPoint, which would be more fittingly named "WifiNetwork", is an aggregation of
* {@link ScanResult ScanResults} along with pertinent metadata (e.g. current connection info,
* network scores) required to successfully render the network to the user.
*
* @deprecated WifiTracker/AccessPoint is no longer supported, and will be removed in a future
* release. Clients that need a dynamic list of available wifi networks should migrate to one of the
* newer tracker classes,
* {@link com.android.wifitrackerlib.WifiPickerTracker},
* {@link com.android.wifitrackerlib.SavedNetworkTracker},
* {@link com.android.wifitrackerlib.NetworkDetailsTracker},
* in conjunction with {@link com.android.wifitrackerlib.WifiEntry} to represent each wifi network.
*/
@Deprecated
public class AccessPoint implements Comparable This lock should be held for all modifications to {@link #mScanResults} and
* {@link #mExtraScanResults}.
*/
private final Object mLock = new Object();
@IntDef({Speed.NONE, Speed.SLOW, Speed.MODERATE, Speed.FAST, Speed.VERY_FAST})
@Retention(RetentionPolicy.SOURCE)
public @interface Speed {
/**
* Constant value representing an unlabeled / unscored network.
*/
int NONE = 0;
/**
* Constant value representing a slow speed network connection.
*/
int SLOW = 5;
/**
* Constant value representing a medium speed network connection.
*/
int MODERATE = 10;
/**
* Constant value representing a fast speed network connection.
*/
int FAST = 20;
/**
* Constant value representing a very fast speed network connection.
*/
int VERY_FAST = 30;
}
@IntDef({PasspointConfigurationVersion.INVALID,
PasspointConfigurationVersion.NO_OSU_PROVISIONED,
PasspointConfigurationVersion.OSU_PROVISIONED})
@Retention(RetentionPolicy.SOURCE)
public @interface PasspointConfigurationVersion {
int INVALID = 0;
int NO_OSU_PROVISIONED = 1; // R1.
int OSU_PROVISIONED = 2; // R2 or R3.
}
/** The underlying set of scan results comprising this AccessPoint. */
@GuardedBy("mLock")
private final ArraySet This cache should not be evicted with scan results, as the values here are used to
* generate a fallback in the absence of scores for the visible APs.
*/
private final Map Any cached {@link TimestampedScoredNetwork} objects older than the given max age in millis
* will be removed when this method is invoked.
*
* Precondition: {@link #mRssi} is up to date before invoking this method.
*
* @param scoreCache The score cache to use to retrieve scores
* @param maxScoreCacheAgeMillis the maximum age in milliseconds of scores to consider when
* generating speed labels
*
* @return true if the set speed has changed
*/
private boolean updateScores(WifiNetworkScoreCache scoreCache, long maxScoreCacheAgeMillis) {
long nowMillis = SystemClock.elapsedRealtime();
synchronized (mLock) {
for (ScanResult result : mScanResults) {
ScoredNetwork score = scoreCache.getScoredNetwork(result);
if (score == null) {
continue;
}
TimestampedScoredNetwork timedScore = mScoredNetworkCache.get(result.BSSID);
if (timedScore == null) {
mScoredNetworkCache.put(
result.BSSID, new TimestampedScoredNetwork(score, nowMillis));
} else {
// Update data since the has been seen in the score cache
timedScore.update(score, nowMillis);
}
}
}
// Remove old cached networks
long evictionCutoff = nowMillis - maxScoreCacheAgeMillis;
Iterator Use {@link #isReachable()} to determine if an AccessPoint is in range, as this method will
* always return at least 0.
*/
public int getLevel() {
return getWifiManager().calculateSignalLevel(mRssi);
}
public int getRssi() {
return mRssi;
}
/**
* Returns the underlying scan result set.
*
* Callers should not modify this set.
*/
public Set If the given connection is active, the existing value of {@link #mRssi} will be returned.
* If the given AccessPoint is not active, a value will be calculated from previous scan
* results, returning the best RSSI for all matching AccessPoints averaged with the previous
* value. If the access point is not connected and there are no scan results, the rssi will be
* set to {@link #UNREACHABLE_RSSI}.
*/
private void updateBestRssiInfo() {
if (this.isActive()) {
return;
}
ScanResult bestResult = null;
int bestRssi = UNREACHABLE_RSSI;
synchronized (mLock) {
for (ScanResult result : mScanResults) {
if (result.level > bestRssi) {
bestRssi = result.level;
bestResult = result;
}
}
}
// Set the rssi to the average of the current rssi and the previous rssi.
if (bestRssi != UNREACHABLE_RSSI && mRssi != UNREACHABLE_RSSI) {
mRssi = (mRssi + bestRssi) / 2;
} else {
mRssi = bestRssi;
}
if (bestResult != null) {
ssid = bestResult.SSID;
bssid = bestResult.BSSID;
security = getSecurity(mContext, bestResult);
if (security == SECURITY_PSK || security == SECURITY_SAE) {
pskType = getPskType(bestResult);
}
if (security == SECURITY_EAP) {
mEapType = getEapType(bestResult);
}
mIsPskSaeTransitionMode = AccessPoint.isPskSaeTransitionMode(bestResult);
mIsOweTransitionMode = AccessPoint.isOweTransitionMode(bestResult);
}
// Update the config SSID of a Passpoint network to that of the best RSSI
if (isPasspoint()) {
mConfig.SSID = convertToQuotedString(ssid);
}
}
/**
* Returns if the network should be considered metered.
*/
public boolean isMetered() {
return mIsScoredNetworkMetered
|| WifiConfiguration.isMetered(mConfig, mInfo);
}
public NetworkInfo getNetworkInfo() {
return mNetworkInfo;
}
public int getSecurity() {
return security;
}
public String getSecurityString(boolean concise) {
Context context = mContext;
if (isPasspoint() || isPasspointConfig()) {
return concise ? context.getString(R.string.wifi_security_short_eap) :
context.getString(R.string.wifi_security_eap);
}
if (mIsPskSaeTransitionMode) {
return concise ? context.getString(R.string.wifi_security_short_psk_sae) :
context.getString(R.string.wifi_security_psk_sae);
}
if (mIsOweTransitionMode) {
return concise ? context.getString(R.string.wifi_security_short_none_owe) :
context.getString(R.string.wifi_security_none_owe);
}
switch(security) {
case SECURITY_EAP:
switch (mEapType) {
case EAP_WPA:
return concise ? context.getString(R.string.wifi_security_short_eap_wpa) :
context.getString(R.string.wifi_security_eap_wpa);
case EAP_WPA2_WPA3:
return concise
? context.getString(R.string.wifi_security_short_eap_wpa2_wpa3) :
context.getString(R.string.wifi_security_eap_wpa2_wpa3);
case EAP_UNKNOWN:
default:
return concise
? context.getString(R.string.wifi_security_short_eap) :
context.getString(R.string.wifi_security_eap);
}
case SECURITY_EAP_SUITE_B:
return concise ? context.getString(R.string.wifi_security_short_eap_suiteb) :
context.getString(R.string.wifi_security_eap_suiteb);
case SECURITY_PSK:
switch (pskType) {
case PSK_WPA:
return concise ? context.getString(R.string.wifi_security_short_wpa) :
context.getString(R.string.wifi_security_wpa);
case PSK_WPA2:
return concise ? context.getString(R.string.wifi_security_short_wpa2) :
context.getString(R.string.wifi_security_wpa2);
case PSK_WPA_WPA2:
return concise ? context.getString(R.string.wifi_security_short_wpa_wpa2) :
context.getString(R.string.wifi_security_wpa_wpa2);
case PSK_UNKNOWN:
default:
return concise ? context.getString(R.string.wifi_security_short_psk_generic)
: context.getString(R.string.wifi_security_psk_generic);
}
case SECURITY_WEP:
return concise ? context.getString(R.string.wifi_security_short_wep) :
context.getString(R.string.wifi_security_wep);
case SECURITY_SAE:
return concise ? context.getString(R.string.wifi_security_short_sae) :
context.getString(R.string.wifi_security_sae);
case SECURITY_OWE:
return concise ? context.getString(R.string.wifi_security_short_owe) :
context.getString(R.string.wifi_security_owe);
case SECURITY_NONE:
default:
return concise ? "" : context.getString(R.string.wifi_security_none);
}
}
public String getSsidStr() {
return ssid;
}
public String getBssid() {
return bssid;
}
public CharSequence getSsid() {
return ssid;
}
/**
* Returns the name associated with the stored config.
* @deprecated Please use {@link #getTitle()} instead to get the display name of an AccessPoint.
*/
@Deprecated
public String getConfigName() {
if (mConfig != null && mConfig.isPasspoint()) {
return mConfig.providerFriendlyName;
} else if (mPasspointUniqueId != null) {
return mProviderFriendlyName;
} else {
return ssid;
}
}
public DetailedState getDetailedState() {
if (mNetworkInfo != null) {
return mNetworkInfo.getDetailedState();
}
Log.w(TAG, "NetworkInfo is null, cannot return detailed state");
return null;
}
public String getSavedNetworkSummary() {
WifiConfiguration config = mConfig;
if (config != null) {
PackageManager pm = mContext.getPackageManager();
String systemName = pm.getNameForUid(android.os.Process.SYSTEM_UID);
int userId = UserHandle.getUserId(config.creatorUid);
ApplicationInfo appInfo = null;
if (config.creatorName != null && config.creatorName.equals(systemName)) {
appInfo = mContext.getApplicationInfo();
} else {
try {
IPackageManager ipm = AppGlobals.getPackageManager();
appInfo = ipm.getApplicationInfo(config.creatorName, 0 /* flags */, userId);
} catch (RemoteException rex) {
}
}
if (appInfo != null &&
!appInfo.packageName.equals(mContext.getString(R.string.settings_package)) &&
!appInfo.packageName.equals(
mContext.getString(R.string.certinstaller_package))) {
return mContext.getString(R.string.saved_network, appInfo.loadLabel(pm));
}
}
if (isPasspointConfigurationR1() && isExpired()) {
return mContext.getString(R.string.wifi_passpoint_expired);
}
return "";
}
/**
* Returns the display title for the AccessPoint, such as for an AccessPointPreference's title.
*/
public String getTitle() {
if (isPasspoint() && !TextUtils.isEmpty(mConfig.providerFriendlyName)) {
return mConfig.providerFriendlyName;
} else if (isPasspointConfig() && !TextUtils.isEmpty(mProviderFriendlyName)) {
return mProviderFriendlyName;
} else if (isOsuProvider() && !TextUtils.isEmpty(mOsuProvider.getFriendlyName())) {
return mOsuProvider.getFriendlyName();
} else if (!TextUtils.isEmpty(getSsidStr())) {
return getSsidStr();
} else {
return "";
}
}
public String getSummary() {
return getSettingsSummary();
}
public String getSettingsSummary() {
return getSettingsSummary(false /*convertSavedAsDisconnected*/);
}
/**
* Returns the summary for the AccessPoint.
*/
public String getSettingsSummary(boolean convertSavedAsDisconnected) {
if (isPasspointConfigurationR1() && isExpired()) {
return mContext.getString(R.string.wifi_passpoint_expired);
}
// Update to new summary
StringBuilder summary = new StringBuilder();
if (isOsuProvider()) {
if (mOsuProvisioningComplete) {
summary.append(mContext.getString(R.string.osu_sign_up_complete));
} else if (mOsuFailure != null) {
summary.append(mOsuFailure);
} else if (mOsuStatus != null) {
summary.append(mOsuStatus);
} else {
summary.append(mContext.getString(R.string.tap_to_sign_up));
}
} else if (isActive()) {
summary.append(getSummary(mContext, /* ssid */ null, getDetailedState(),
mInfo != null && mInfo.isEphemeral(),
mInfo != null ? mInfo.getRequestingPackageName() : null));
} else { // not active
if (mConfig != null && mConfig.hasNoInternetAccess()) {
int messageID =
mConfig.getNetworkSelectionStatus().getNetworkSelectionStatus()
== NETWORK_SELECTION_PERMANENTLY_DISABLED
? R.string.wifi_no_internet_no_reconnect
: R.string.wifi_no_internet;
summary.append(mContext.getString(messageID));
} else if (mConfig != null
&& (mConfig.getNetworkSelectionStatus().getNetworkSelectionStatus()
!= NETWORK_SELECTION_ENABLED)) {
WifiConfiguration.NetworkSelectionStatus networkStatus =
mConfig.getNetworkSelectionStatus();
switch (networkStatus.getNetworkSelectionDisableReason()) {
case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE:
summary.append(mContext.getString(R.string.wifi_disabled_password_failure));
break;
case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD:
summary.append(mContext.getString(R.string.wifi_check_password_try_again));
break;
case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE:
summary.append(mContext.getString(R.string.wifi_disabled_network_failure));
break;
case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION:
summary.append(mContext.getString(R.string.wifi_disabled_generic));
break;
}
} else if (!isReachable()) { // Wifi out of range
summary.append(mContext.getString(R.string.wifi_not_in_range));
} else { // In range, not disabled.
if (mConfig != null) { // Is saved network
// Last attempt to connect to this failed. Show reason why
switch (mConfig.getRecentFailureReason()) {
case WifiConfiguration.RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA:
summary.append(mContext.getString(
R.string.wifi_ap_unable_to_handle_new_sta));
break;
default:
if (convertSavedAsDisconnected) {
// Disconnected
summary.append(mContext.getString(R.string.wifi_disconnected));
} else {
// "Saved"
summary.append(mContext.getString(R.string.wifi_remembered));
}
break;
}
}
}
}
if (isVerboseLoggingEnabled()) {
summary.append(WifiUtils.buildLoggingSummary(this, mConfig));
}
if (mConfig != null && (WifiUtils.isMeteredOverridden(mConfig) || mConfig.meteredHint)) {
return mContext.getResources().getString(
R.string.preference_summary_default_combination,
WifiUtils.getMeteredLabel(mContext, mConfig),
summary.toString());
}
// If Speed label and summary are both present, use the preference combination to combine
// the two, else return the non-null one.
if (getSpeedLabel() != null && summary.length() != 0) {
return mContext.getResources().getString(
R.string.preference_summary_default_combination,
getSpeedLabel(),
summary.toString());
} else if (getSpeedLabel() != null) {
return getSpeedLabel();
} else {
return summary.toString();
}
}
/**
* Return whether this is the active connection.
* For ephemeral connections (networkId is invalid), this returns false if the network is
* disconnected.
*/
public boolean isActive() {
return mNetworkInfo != null &&
(networkId != WifiConfiguration.INVALID_NETWORK_ID ||
mNetworkInfo.getState() != State.DISCONNECTED);
}
public boolean isConnectable() {
return getLevel() != -1 && getDetailedState() == null;
}
public boolean isEphemeral() {
return mInfo != null && mInfo.isEphemeral() &&
mNetworkInfo != null && mNetworkInfo.getState() != State.DISCONNECTED;
}
/**
* Return true if this AccessPoint represents a Passpoint AP.
*/
public boolean isPasspoint() {
return mConfig != null && mConfig.isPasspoint();
}
/**
* Return true if this AccessPoint represents a Passpoint provider configuration.
*/
public boolean isPasspointConfig() {
return mPasspointUniqueId != null && mConfig == null;
}
/**
* Return true if this AccessPoint represents an OSU Provider.
*/
public boolean isOsuProvider() {
return mOsuProvider != null;
}
/**
* Return true if this AccessPoint is expired.
*/
public boolean isExpired() {
if (mSubscriptionExpirationTimeInMillis <= 0) {
// Expiration time not specified.
return false;
} else {
return System.currentTimeMillis() >= mSubscriptionExpirationTimeInMillis;
}
}
public boolean isPasspointConfigurationR1() {
return mPasspointConfigurationVersion == PasspointConfigurationVersion.NO_OSU_PROVISIONED;
}
/**
* Return true if {@link PasspointConfiguration#isOsuProvisioned} is true, this may refer to R2
* or R3.
*/
public boolean isPasspointConfigurationOsuProvisioned() {
return mPasspointConfigurationVersion == PasspointConfigurationVersion.OSU_PROVISIONED;
}
/**
* Starts the OSU Provisioning flow.
*/
public void startOsuProvisioning(@Nullable WifiManager.ActionListener connectListener) {
mConnectListener = connectListener;
getWifiManager().startSubscriptionProvisioning(
mOsuProvider,
mContext.getMainExecutor(),
new AccessPointProvisioningCallback()
);
}
/**
* Return whether the given {@link WifiInfo} is for this access point.
* If the current AP does not have a network Id then the config is used to
* match based on SSID and security.
*/
private boolean isInfoForThisAccessPoint(WifiConfiguration config, WifiInfo info) {
if (info.isOsuAp() || mOsuStatus != null) {
return (info.isOsuAp() && mOsuStatus != null);
} else if (info.isPasspointAp() || isPasspoint()) {
// TODO: Use TextUtils.equals(info.getPasspointUniqueId(), mConfig.getKey()) when API
// is available
return (info.isPasspointAp() && isPasspoint()
&& TextUtils.equals(info.getPasspointFqdn(), mConfig.FQDN)
&& TextUtils.equals(info.getPasspointProviderFriendlyName(),
mConfig.providerFriendlyName));
}
if (networkId != WifiConfiguration.INVALID_NETWORK_ID) {
return networkId == info.getNetworkId();
} else if (config != null) {
return matches(config, info);
} else {
// Might be an ephemeral connection with no WifiConfiguration. Try matching on SSID.
// (Note that we only do this if the WifiConfiguration explicitly equals INVALID).
// TODO: Handle hex string SSIDs.
return TextUtils.equals(removeDoubleQuotes(info.getSSID()), ssid);
}
}
public boolean isSaved() {
return mConfig != null;
}
public Object getTag() {
return mTag;
}
public void setTag(Object tag) {
mTag = tag;
}
/**
* Generate and save a default wifiConfiguration with common values.
* Can only be called for unsecured networks.
*/
public void generateOpenNetworkConfig() {
if (!isOpenNetwork()) {
throw new IllegalStateException();
}
if (mConfig != null)
return;
mConfig = new WifiConfiguration();
mConfig.SSID = AccessPoint.convertToQuotedString(ssid);
if (security == SECURITY_NONE) {
mConfig.allowedKeyManagement.set(KeyMgmt.NONE);
} else {
mConfig.allowedKeyManagement.set(KeyMgmt.OWE);
mConfig.requirePmf = true;
}
}
public void saveWifiState(Bundle savedState) {
if (ssid != null) savedState.putString(KEY_SSID, getSsidStr());
savedState.putInt(KEY_SECURITY, security);
savedState.putInt(KEY_SPEED, mSpeed);
savedState.putInt(KEY_PSKTYPE, pskType);
savedState.putInt(KEY_EAPTYPE, mEapType);
if (mConfig != null) savedState.putParcelable(KEY_CONFIG, mConfig);
savedState.putParcelable(KEY_WIFIINFO, mInfo);
synchronized (mLock) {
savedState.putParcelableArray(KEY_SCANRESULTS,
mScanResults.toArray(new Parcelable[mScanResults.size()
+ mExtraScanResults.size()]));
}
savedState.putParcelableArrayList(KEY_SCOREDNETWORKCACHE,
new ArrayList<>(mScoredNetworkCache.values()));
if (mNetworkInfo != null) {
savedState.putParcelable(KEY_NETWORKINFO, mNetworkInfo);
}
if (mPasspointUniqueId != null) {
savedState.putString(KEY_PASSPOINT_UNIQUE_ID, mPasspointUniqueId);
}
if (mFqdn != null) {
savedState.putString(KEY_FQDN, mFqdn);
}
if (mProviderFriendlyName != null) {
savedState.putString(KEY_PROVIDER_FRIENDLY_NAME, mProviderFriendlyName);
}
savedState.putLong(KEY_SUBSCRIPTION_EXPIRATION_TIME_IN_MILLIS,
mSubscriptionExpirationTimeInMillis);
savedState.putInt(KEY_PASSPOINT_CONFIGURATION_VERSION, mPasspointConfigurationVersion);
savedState.putBoolean(KEY_IS_PSK_SAE_TRANSITION_MODE, mIsPskSaeTransitionMode);
savedState.putBoolean(KEY_IS_OWE_TRANSITION_MODE, mIsOweTransitionMode);
}
public void setListener(AccessPointListener listener) {
mAccessPointListener = listener;
}
/**
* Sets {@link #mScanResults} to the given collection and updates info based on the best RSSI
* scan result.
*
* @param scanResults a collection of scan results to add to the internal set
*/
void setScanResults(Collection All methods are invoked on the Main Thread.
*/
public interface AccessPointListener {
/**
* Indicates a change to the externally visible state of the AccessPoint trigger by an
* update of ScanResults, saved configuration state, connection state, or score
* (labels/metered) state.
*
* Clients should refresh their view of the AccessPoint to match the updated state when
* this is invoked. Overall this method is extraneous if clients are listening to
* {@link WifiTracker.WifiListener#onAccessPointsChanged()} callbacks.
*
* Examples of changes include signal strength, connection state, speed label, and
* generally anything that would impact the summary string.
*
* @param accessPoint The accessPoint object the listener was registered on which has
* changed
*/
@MainThread void onAccessPointChanged(AccessPoint accessPoint);
/**
* Indicates the "wifi pie signal level" has changed, retrieved via calls to
* {@link AccessPoint#getLevel()}.
*
* This call is a subset of {@link #onAccessPointChanged(AccessPoint)} , hence is also
* extraneous if the client is already reacting to that or the
* {@link WifiTracker.WifiListener#onAccessPointsChanged()} callbacks.
*
* @param accessPoint The accessPoint object the listener was registered on whose level has
* changed
*/
@MainThread void onLevelChanged(AccessPoint accessPoint);
}
private static boolean isVerboseLoggingEnabled() {
return WifiTracker.sVerboseLogging || Log.isLoggable(TAG, Log.VERBOSE);
}
/**
* Callbacks relaying changes to the OSU provisioning status started in startOsuProvisioning().
*
* All methods are invoked on the Main Thread
*/
@VisibleForTesting
class AccessPointProvisioningCallback extends ProvisioningCallback {
@Override
@MainThread public void onProvisioningFailure(int status) {
if (TextUtils.equals(mOsuStatus, mContext.getString(R.string.osu_completing_sign_up))) {
mOsuFailure = mContext.getString(R.string.osu_sign_up_failed);
} else {
mOsuFailure = mContext.getString(R.string.osu_connect_failed);
}
mOsuStatus = null;
mOsuProvisioningComplete = false;
ThreadUtils.postOnMainThread(() -> {
if (mAccessPointListener != null) {
mAccessPointListener.onAccessPointChanged(AccessPoint.this);
}
});
}
@Override
@MainThread public void onProvisioningStatus(int status) {
String newStatus = null;
switch (status) {
case OSU_STATUS_AP_CONNECTING:
case OSU_STATUS_AP_CONNECTED:
case OSU_STATUS_SERVER_CONNECTING:
case OSU_STATUS_SERVER_VALIDATED:
case OSU_STATUS_SERVER_CONNECTED:
case OSU_STATUS_INIT_SOAP_EXCHANGE:
case OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE:
newStatus = String.format(mContext.getString(R.string.osu_opening_provider),
mOsuProvider.getFriendlyName());
break;
case OSU_STATUS_REDIRECT_RESPONSE_RECEIVED:
case OSU_STATUS_SECOND_SOAP_EXCHANGE:
case OSU_STATUS_THIRD_SOAP_EXCHANGE:
case OSU_STATUS_RETRIEVING_TRUST_ROOT_CERTS:
newStatus = mContext.getString(
R.string.osu_completing_sign_up);
break;
}
boolean updated = !TextUtils.equals(mOsuStatus, newStatus);
mOsuStatus = newStatus;
mOsuFailure = null;
mOsuProvisioningComplete = false;
if (updated) {
ThreadUtils.postOnMainThread(() -> {
if (mAccessPointListener != null) {
mAccessPointListener.onAccessPointChanged(AccessPoint.this);
}
});
}
}
@Override
@MainThread public void onProvisioningComplete() {
mOsuProvisioningComplete = true;
mOsuFailure = null;
mOsuStatus = null;
ThreadUtils.postOnMainThread(() -> {
if (mAccessPointListener != null) {
mAccessPointListener.onAccessPointChanged(AccessPoint.this);
}
});
// Connect to the freshly provisioned network.
WifiManager wifiManager = getWifiManager();
PasspointConfiguration passpointConfig = wifiManager
.getMatchingPasspointConfigsForOsuProviders(Collections.singleton(mOsuProvider))
.get(mOsuProvider);
if (passpointConfig == null) {
Log.e(TAG, "Missing PasspointConfiguration for newly provisioned network!");
if (mConnectListener != null) {
mConnectListener.onFailure(0);
}
return;
}
String uniqueId = passpointConfig.getUniqueId();
for (Pair