blob: a9010acc68ca88784a2d04cf37757c17c44b8edb [file] [log] [blame]
/*
* Copyright (C) 2017 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.settings.wifi;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.NetworkCapabilities;
import android.net.TetheringManager;
import android.net.wifi.ScanResult;
import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.wifitrackerlib.WifiEntry;
import java.nio.charset.StandardCharsets;
/** A utility class for Wi-Fi functions. */
public class WifiUtils extends com.android.settingslib.wifi.WifiUtils {
static final String TAG = "WifiUtils";
private static final int SSID_ASCII_MIN_LENGTH = 1;
private static final int SSID_ASCII_MAX_LENGTH = 32;
private static final int PSK_PASSPHRASE_ASCII_MIN_LENGTH = 8;
private static final int PSK_PASSPHRASE_ASCII_MAX_LENGTH = 63;
private static Boolean sCanShowWifiHotspotCached;
public static boolean isSSIDTooLong(String ssid) {
if (TextUtils.isEmpty(ssid)) {
return false;
}
return ssid.getBytes(StandardCharsets.UTF_8).length > SSID_ASCII_MAX_LENGTH;
}
public static boolean isSSIDTooShort(String ssid) {
if (TextUtils.isEmpty(ssid)) {
return true;
}
return ssid.length() < SSID_ASCII_MIN_LENGTH;
}
/**
* Check if the hotspot password is valid.
*/
public static boolean isHotspotPasswordValid(String password, int securityType) {
final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
try {
if (securityType == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK
|| securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) {
if (password.length() < PSK_PASSPHRASE_ASCII_MIN_LENGTH
|| password.length() > PSK_PASSPHRASE_ASCII_MAX_LENGTH) {
return false;
}
}
configBuilder.setPassphrase(password, securityType);
} catch (Exception e) {
return false;
}
return true;
}
/**
* This method is a stripped and negated version of WifiConfigStore.canModifyNetwork.
*
* @param context Context of caller
* @param config The WiFi config.
* @return true if Settings cannot modify the config due to lockDown.
*/
public static boolean isNetworkLockedDown(Context context, WifiConfiguration config) {
if (config == null) {
return false;
}
final DevicePolicyManager dpm =
(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
final PackageManager pm = context.getPackageManager();
final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
// Check if device has DPM capability. If it has and dpm is still null, then we
// treat this case with suspicion and bail out.
if (pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) && dpm == null) {
return true;
}
boolean isConfigEligibleForLockdown = false;
if (dpm != null) {
final ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser();
if (deviceOwner != null) {
final int deviceOwnerUserId = dpm.getDeviceOwnerUserId();
try {
final int deviceOwnerUid = pm.getPackageUidAsUser(deviceOwner.getPackageName(),
deviceOwnerUserId);
isConfigEligibleForLockdown = deviceOwnerUid == config.creatorUid;
} catch (PackageManager.NameNotFoundException e) {
// don't care
}
} else if (dpm.isOrganizationOwnedDeviceWithManagedProfile()) {
int profileOwnerUserId = Utils.getManagedProfileId(um, UserHandle.myUserId());
final ComponentName profileOwner = dpm.getProfileOwnerAsUser(profileOwnerUserId);
if (profileOwner != null) {
try {
final int profileOwnerUid = pm.getPackageUidAsUser(
profileOwner.getPackageName(), profileOwnerUserId);
isConfigEligibleForLockdown = profileOwnerUid == config.creatorUid;
} catch (PackageManager.NameNotFoundException e) {
// don't care
}
}
}
}
if (!isConfigEligibleForLockdown) {
return false;
}
final ContentResolver resolver = context.getContentResolver();
final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
return isLockdownFeatureEnabled;
}
/** Returns true if the provided NetworkCapabilities indicate a captive portal network. */
public static boolean canSignIntoNetwork(NetworkCapabilities capabilities) {
return (capabilities != null
&& capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL));
}
/**
* Provides a simple way to generate a new {@link WifiConfiguration} obj from
* {@link ScanResult} or {@link WifiEntry}. Either {@code wifiEntry} or {@code scanResult
* } input should be not null for retrieving information, otherwise will throw
* IllegalArgumentException.
* This method prefers to take {@link WifiEntry} input in priority. Therefore this method
* will take {@link WifiEntry} input as preferred data extraction source when you input
* both {@link WifiEntry} and {@link ScanResult}, and ignore {@link ScanResult} input.
*
* Duplicated and simplified method from {@link WifiConfigController#getConfig()}.
* TODO(b/120827021): Should be removed if the there is have a common one in shared place (e.g.
* SettingsLib).
*
* @param wifiEntry Input data for retrieving WifiConfiguration.
* @param scanResult Input data for retrieving WifiConfiguration.
* @return WifiConfiguration obj based on input.
*/
public static WifiConfiguration getWifiConfig(WifiEntry wifiEntry, ScanResult scanResult) {
if (wifiEntry == null && scanResult == null) {
throw new IllegalArgumentException(
"At least one of WifiEntry and ScanResult input is required.");
}
final WifiConfiguration config = new WifiConfiguration();
final int security;
if (wifiEntry == null) {
config.SSID = "\"" + scanResult.SSID + "\"";
security = getWifiEntrySecurity(scanResult);
} else {
if (wifiEntry.getWifiConfiguration() == null) {
config.SSID = "\"" + wifiEntry.getSsid() + "\"";
} else {
config.networkId = wifiEntry.getWifiConfiguration().networkId;
config.hiddenSSID = wifiEntry.getWifiConfiguration().hiddenSSID;
}
security = wifiEntry.getSecurity();
}
switch (security) {
case WifiEntry.SECURITY_NONE:
config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
break;
case WifiEntry.SECURITY_WEP:
config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP);
break;
case WifiEntry.SECURITY_PSK:
config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
break;
case WifiEntry.SECURITY_EAP_SUITE_B:
config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
break;
case WifiEntry.SECURITY_EAP:
config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
break;
case WifiEntry.SECURITY_SAE:
config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
break;
case WifiEntry.SECURITY_OWE:
config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
break;
default:
break;
}
return config;
}
/**
* Gets security value from ScanResult.
*
* @param result ScanResult
* @return Related security value based on {@link WifiEntry}.
*/
public static int getWifiEntrySecurity(ScanResult result) {
if (result.capabilities.contains("WEP")) {
return WifiEntry.SECURITY_WEP;
} else if (result.capabilities.contains("SAE")) {
return WifiEntry.SECURITY_SAE;
} else if (result.capabilities.contains("PSK")) {
return WifiEntry.SECURITY_PSK;
} else if (result.capabilities.contains("EAP_SUITE_B_192")) {
return WifiEntry.SECURITY_EAP_SUITE_B;
} else if (result.capabilities.contains("EAP")) {
return WifiEntry.SECURITY_EAP;
} else if (result.capabilities.contains("OWE")) {
return WifiEntry.SECURITY_OWE;
}
return WifiEntry.SECURITY_NONE;
}
/**
* Check if Wi-Fi hotspot settings can be displayed.
*
* @param context Context of caller
* @return true if Wi-Fi hotspot settings can be displayed
*/
public static boolean checkShowWifiHotspot(Context context) {
if (context == null) return false;
boolean showWifiHotspotSettings =
context.getResources().getBoolean(R.bool.config_show_wifi_hotspot_settings);
if (!showWifiHotspotSettings) {
Log.w(TAG, "config_show_wifi_hotspot_settings:false");
return false;
}
WifiManager wifiManager = context.getSystemService(WifiManager.class);
if (wifiManager == null) {
Log.e(TAG, "WifiManager is null");
return false;
}
TetheringManager tetheringManager = context.getSystemService(TetheringManager.class);
if (tetheringManager == null) {
Log.e(TAG, "TetheringManager is null");
return false;
}
String[] wifiRegexs = tetheringManager.getTetherableWifiRegexs();
if (wifiRegexs == null || wifiRegexs.length == 0) {
Log.w(TAG, "TetherableWifiRegexs is empty");
return false;
}
return true;
}
/**
* Return the cached result to see if Wi-Fi hotspot settings can be displayed.
*
* @param context Context of caller
* @return true if Wi-Fi hotspot settings can be displayed
*/
public static boolean canShowWifiHotspot(Context context) {
if (sCanShowWifiHotspotCached == null) {
sCanShowWifiHotspotCached = checkShowWifiHotspot(context);
}
return sCanShowWifiHotspotCached;
}
/**
* Sets the sCanShowWifiHotspotCached for testing purposes.
*
* @param cached Cached value for #canShowWifiHotspot()
*/
@VisibleForTesting
public static void setCanShowWifiHotspotCached(Boolean cached) {
sCanShowWifiHotspotCached = cached;
}
}