blob: 79db041ac7af0eda1f737929bdcde3117fbeef97 [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 android.annotation.DrawableRes;
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.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 provided NetworkCapabilities indicate a captive portal network.
*/
public static boolean canSignIntoNetwork(NetworkCapabilities capabilities) {
return (capabilities != null
&& capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL));
}
/**
* Returns netId. -1 if connection fails.
*/
public static int connectToAccessPoint(Context context, String ssid, int security,
String password, boolean hidden) {
WifiManager wifiManager = context.getSystemService(WifiManager.class);
WifiConfiguration wifiConfig = new WifiConfiguration();
wifiConfig.SSID = String.format("\"%s\"", ssid);
wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
wifiConfig.hiddenSSID = hidden;
switch (security) {
case AccessPoint.SECURITY_NONE:
wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
wifiConfig.allowedAuthAlgorithms.clear();
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
break;
case AccessPoint.SECURITY_WEP:
wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
wifiConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
wifiConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
wifiConfig.wepKeys[0] = isHexString(password) ? password
: "\"" + password + "\"";
wifiConfig.wepTxKeyIndex = 0;
break;
case AccessPoint.SECURITY_PSK:
case AccessPoint.SECURITY_EAP:
wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
wifiConfig.preSharedKey = String.format("\"%s\"", password);
break;
default:
throw new IllegalArgumentException("invalid security type");
}
int netId = wifiManager.addNetwork(wifiConfig);
// This only means wifiManager failed writing the new wifiConfig to the db. It doesn't mean
// the network is invalid.
if (netId == INVALID_NET_ID) {
Toast.makeText(context, R.string.wifi_failed_connect_message,
Toast.LENGTH_SHORT).show();
} else {
wifiManager.enableNetwork(netId, true);
}
return netId;
}
/** 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 ActionFailedListener(context, R.string.wifi_failed_forget_message));
}
}
/** 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.isNetworkEnabled()) {
return false;
}
return networkStatus.getNetworkSelectionDisableReason()
== WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD;
}
private static boolean isHexString(String password) {
return HEX_PATTERN.matcher(password).matches();
}
/**
* A shared implementation of {@link WifiManager.ActionListener} which shows a failure message
* in a toast.
*/
public static class ActionFailedListener implements WifiManager.ActionListener {
private final Context mContext;
@StringRes
private final int mFailureMessage;
public ActionFailedListener(Context context, @StringRes int failureMessage) {
mContext = context;
mFailureMessage = failureMessage;
}
@Override
public void onSuccess() {
}
@Override
public void onFailure(int reason) {
Toast.makeText(mContext, mFailureMessage, Toast.LENGTH_SHORT).show();
}
}
}