blob: 41f64fd59f18cd47b66ce45d04c940a75b9b7034 [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.car.settings.wifi;
import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED;
import android.annotation.DrawableRes;
import android.annotation.Nullable;
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.NetworkInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.widget.Toast;
import androidx.annotation.StringRes;
import com.android.car.settings.R;
import com.android.car.settings.common.Logger;
import com.android.settingslib.wifi.AccessPoint;
import java.util.regex.Pattern;
/**
* A collections of util functions for WIFI.
*/
public class WifiUtil {
private static final Logger LOG = new Logger(WifiUtil.class);
/** Value that is returned when we fail to connect wifi. */
public static final int INVALID_NET_ID = -1;
private static final Pattern HEX_PATTERN = Pattern.compile("^[0-9A-F]+$");
@DrawableRes
public static int getIconRes(int state) {
switch (state) {
case WifiManager.WIFI_STATE_ENABLING:
case WifiManager.WIFI_STATE_DISABLED:
return R.drawable.ic_settings_wifi_disabled;
default:
return R.drawable.ic_settings_wifi;
}
}
public static boolean isWifiOn(int state) {
switch (state) {
case WifiManager.WIFI_STATE_ENABLING:
case WifiManager.WIFI_STATE_DISABLED:
return false;
default:
return true;
}
}
/**
* @return 0 if no proper description can be found.
*/
@StringRes
public static Integer getStateDesc(int state) {
switch (state) {
case WifiManager.WIFI_STATE_ENABLING:
return R.string.wifi_starting;
case WifiManager.WIFI_STATE_DISABLING:
return R.string.wifi_stopping;
case WifiManager.WIFI_STATE_DISABLED:
return R.string.wifi_disabled;
default:
return 0;
}
}
/**
* Returns {@Code true} if wifi is available on this device.
*/
public static boolean isWifiAvailable(Context context) {
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
}
/**
* Gets a unique key for a {@link AccessPoint}.
*/
public static String getKey(AccessPoint accessPoint) {
return String.valueOf(accessPoint.hashCode());
}
/**
* This method is a stripped and negated version of WifiConfigStore.canModifyNetwork.
*
* @param context Context of caller
* @param config The WiFi config.
* @return {@code 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();
// 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
}
}
}
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 {@code true} if the network security type doesn't require authentication.
*/
public static boolean isOpenNetwork(int security) {
return security == AccessPoint.SECURITY_NONE || security == AccessPoint.SECURITY_OWE;
}
/**
* Returns {@code 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));
}
/**
* Attempts to connect to a specified access point
* @param listener for callbacks on success or failure of connection attempt (can be null)
*/
public static void connectToAccessPoint(Context context, String ssid, int security,
String password, boolean hidden, @Nullable WifiManager.ActionListener listener) {
WifiManager wifiManager = context.getSystemService(WifiManager.class);
WifiConfiguration wifiConfig = getWifiConfig(ssid, security, password, hidden);
wifiManager.connect(wifiConfig, listener);
}
private static WifiConfiguration getWifiConfig(String ssid, int security,
String password, boolean hidden) {
WifiConfiguration wifiConfig = new WifiConfiguration();
wifiConfig.SSID = String.format("\"%s\"", ssid);
wifiConfig.hiddenSSID = hidden;
switch (security) {
case AccessPoint.SECURITY_NONE:
wifiConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
break;
case AccessPoint.SECURITY_WEP:
wifiConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP);
if (!TextUtils.isEmpty(password)) {
int length = password.length();
// WEP-40, WEP-104, and 256-bit WEP (WEP-232?)
if ((length == 10 || length == 26 || length == 58)
&& password.matches("[0-9A-Fa-f]*")) {
wifiConfig.wepKeys[0] = password;
} else {
wifiConfig.wepKeys[0] = '"' + password + '"';
}
}
break;
case AccessPoint.SECURITY_PSK:
wifiConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
if (!TextUtils.isEmpty(password)) {
if (password.matches("[0-9A-Fa-f]{64}")) {
wifiConfig.preSharedKey = password;
} else {
wifiConfig.preSharedKey = '"' + password + '"';
}
}
break;
case AccessPoint.SECURITY_EAP:
case AccessPoint.SECURITY_EAP_SUITE_B:
if (security == AccessPoint.SECURITY_EAP_SUITE_B) {
// allowedSuiteBCiphers will be set according to certificate type
wifiConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
} else {
wifiConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
}
if (!TextUtils.isEmpty(password)) {
wifiConfig.enterpriseConfig.setPassword(password);
}
break;
case AccessPoint.SECURITY_SAE:
wifiConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
if (!TextUtils.isEmpty(password)) {
wifiConfig.preSharedKey = '"' + password + '"';
}
break;
case AccessPoint.SECURITY_OWE:
wifiConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
break;
default:
throw new IllegalArgumentException("unknown security type " + security);
}
return wifiConfig;
}
/** Forget the network specified by {@code accessPoint}. */
public static void forget(Context context, AccessPoint accessPoint) {
WifiManager wifiManager = context.getSystemService(WifiManager.class);
if (!accessPoint.isSaved()) {
if (accessPoint.getNetworkInfo() != null
&& accessPoint.getNetworkInfo().getState() != NetworkInfo.State.DISCONNECTED) {
// Network is active but has no network ID - must be ephemeral.
wifiManager.disableEphemeralNetwork(
AccessPoint.convertToQuotedString(accessPoint.getSsidStr()));
} else {
// Should not happen, but a monkey seems to trigger it
LOG.e("Failed to forget invalid network " + accessPoint.getConfig());
return;
}
} else {
wifiManager.forget(accessPoint.getConfig().networkId, new WifiManager.ActionListener() {
@Override
public void onSuccess() {
LOG.d("Network successfully forgotten");
}
@Override
public void onFailure(int reason) {
LOG.d("Could not forget network. Failure code: " + reason);
Toast.makeText(context, R.string.wifi_failed_forget_message,
Toast.LENGTH_SHORT).show();
}
});
}
}
/** Returns {@code true} if the access point was disabled due to the wrong password. */
public static boolean isAccessPointDisabledByWrongPassword(AccessPoint accessPoint) {
WifiConfiguration config = accessPoint.getConfig();
if (config == null) {
return false;
}
WifiConfiguration.NetworkSelectionStatus networkStatus =
config.getNetworkSelectionStatus();
if (networkStatus == null
|| networkStatus.getNetworkSelectionStatus() == NETWORK_SELECTION_ENABLED) {
return false;
}
return networkStatus.getNetworkSelectionDisableReason()
== WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD;
}
private static boolean isHexString(String password) {
return HEX_PATTERN.matcher(password).matches();
}
}