package io.particle.android.sdk.utils; import android.content.Context; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Build.VERSION_CODES; import java.util.Arrays; import java.util.Collections; import java.util.List; import javax.annotation.ParametersAreNonnullByDefault; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import io.particle.android.sdk.utils.Funcy.Predicate; import static io.particle.android.sdk.utils.Py.truthy; @ParametersAreNonnullByDefault public class WifiFacade { private static final TLog log = TLog.get(WifiFacade.class); @NonNull public static Predicate<ScanResult> is24Ghz = scanResult -> { // this approach lifted from the ScanResult source return scanResult.frequency > 2300 && scanResult.frequency < 2500; }; // "truthy": see Py.truthy() javadoc // tl;dr: not null or "" (empty string) public static Predicate<ScanResult> isWifiNameTruthy = scanResult -> truthy(scanResult.SSID); public static WifiFacade get(Context ctx) { Context appCtx = ctx.getApplicationContext(); return new WifiFacade( (WifiManager) appCtx.getSystemService(Context.WIFI_SERVICE), (ConnectivityManager) appCtx.getSystemService(Context.CONNECTIVITY_SERVICE) ); } private final WifiManager wifiManager; private final ConnectivityManager connectivityManager; private WifiFacade(WifiManager wifiManager, ConnectivityManager connectivityManager) { this.wifiManager = wifiManager; this.connectivityManager = connectivityManager; } public int getIdForConfiguredNetwork(SSID ssid) { WifiConfiguration configuredNetwork = Funcy.findFirstMatch( getConfiguredNetworks(), wifiConfiguration -> { if (wifiConfiguration.SSID == null) { return false; } else { return SSID.from(wifiConfiguration).equals(ssid); } }); if (configuredNetwork == null) { log.d("No network found (returning -1) for SSID: " + ssid); return -1; } else { return configuredNetwork.networkId; } } public boolean reenableNetwork(SSID ssid) { int networkId = getIdForConfiguredNetwork(ssid); if (networkId == -1) { log.w("reenableNetwork(): no network found for SSID?? " + ssid); return false; } else { log.d("Reenabling network configuration for:" + ssid); return wifiManager.enableNetwork(networkId, false); } } public boolean removeNetwork(SSID ssid) { int networkId = getIdForConfiguredNetwork(ssid); if (networkId == -1) { log.w("No network found for SSID " + ssid); return false; } else { log.d("Removing network configuration for:" + ssid); return removeNetwork(networkId); } } @Nullable public SSID getCurrentlyConnectedSSID() { WifiInfo connectionInfo = getConnectionInfo(); if (connectionInfo == null) { log.w("getCurrentlyConnectedSSID(): WifiManager.getConnectionInfo() returned null"); return null; } else { SSID ssid = SSID.from(connectionInfo); log.d("Currently connected to: " + ssid + ", supplicant state: " + connectionInfo.getSupplicantState()); return ssid; } } @Nullable @RequiresApi(api = VERSION_CODES.LOLLIPOP) public Network getNetworkObjectForCurrentWifiConnection() { // Android doesn't have any means of directly asking // "I want the Network obj for the Wi-Fi network with SSID <foo>". // Instead, you have to infer it based on the fact that you can only // have one connected Wi-Fi connection at a time. // (Update: one *regular* Wi-Fi connection, anyway. See below.) return Funcy.findFirstMatch( Arrays.asList(connectivityManager.getAllNetworks()), network -> { NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network); if (capabilities == null) { return false; } // Don't try using the P2P Wi-Fi interfaces on recent Samsung devices if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P)) { return false; } return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI); } ); } public int addNetwork(WifiConfiguration config) { log.d("addNetwork with SSID " + config.SSID + ": " + config); return wifiManager.addNetwork(config); } public boolean disconnect() { log.d("disconnect()"); return wifiManager.disconnect(); } public boolean enableNetwork(int networkId, boolean disableOthers) { log.d("enableNetwork for networkID " + networkId); return wifiManager.enableNetwork(networkId, disableOthers); } public WifiConfiguration getWifiConfiguration(SSID ssid) { List<WifiConfiguration> wifiConfigurations = getConfiguredNetworks(); for (WifiConfiguration configuration : wifiConfigurations) { log.d("Found configured wifi: " + configuration.SSID); if (configuration.SSID.equals(ssid.inQuotes())) { return configuration; } } return null; } @Nullable public WifiInfo getConnectionInfo() { return wifiManager.getConnectionInfo(); } public List<ScanResult> getScanResults() { // per the WifiManager docs, this seems like it should never return null, but I've // gotten null back before, possibly on an older API level. List<ScanResult> results = wifiManager.getScanResults(); return (results == null) ? Collections.emptyList() : results; } public boolean isWifiEnabled() { return wifiManager.isWifiEnabled(); } public void reassociate() { log.d("reassociate"); wifiManager.reassociate(); } public boolean reconnect() { log.d("reconnect"); return wifiManager.reconnect(); } public boolean removeNetwork(int networkId) { log.d("Removing network configuration for networkId: " + networkId); return wifiManager.removeNetwork(networkId); } public boolean setWifiEnabled(boolean enabled) { log.d("setWifiEnabled: " + enabled); return wifiManager.setWifiEnabled(enabled); } public boolean startScan() { log.d("startScan()"); return wifiManager.startScan(); } private List<WifiConfiguration> getConfiguredNetworks() { List<WifiConfiguration> configuredNetworks = wifiManager.getConfiguredNetworks(); return (configuredNetworks == null) ? Collections.emptyList() : configuredNetworks; } }