package com.android.server.wifi; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.Intent; import android.net.DhcpInfo; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.MacAddress; import android.net.KeepalivePacketData; import android.net.Network; import android.net.NetworkAgent; import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; import android.net.wifi.ScanResult; import android.net.wifi.hotspot2.IProvisioningCallback; import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiSsid; import android.net.Uri; import android.os.Binder; import android.os.Process; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import com.android.modules.utils.ParceledListSlice; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.util.ArrayList; import java.util.List; import java.util.Arrays; import java.net.UnknownHostException; import java.time.Duration; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; public final class WifiHelper { private static final String TAG = "WifiHelper"; private static final String DEFAULT_SSID = "mywifi"; private static final String DEFAULT_BSSID = "02:00:00:00:00:00"; private static final String DEFAULT_MAC = "02:00:00:00:00:00"; //WifiInfo.DEFAULT_MAC_ADDRESS; private static final String DEFAULT_IP = "192.168.100.15"; private static final String DEFAULT_IPV6 = "fe80::cd7f:1cb2:3d5c:65ed"; private static final String DEFAULT_GATEWAY = "192.168.100.1"; private static final String DEFAULT_DNS1 = "8.8.8.8"; private static final String DEFAULT_DNS2 = "8.8.4.4"; private static final int DEFAULT_HESSID = 0; private static final int DEFAULT_ANQP_DOMAIN_ID = 0; private static final String DEFAULT_CAPABILITIES = ""; //[WAP2-PSK-CCMP][RSN-PSK-CCMP] private static final int DEFAULT_LEVEL = 0; private static final int DEFAULT_FREQUENCY = 2413; private static final int DEFAULT_DISTANCE = -1; private static final int DEFAULT_DISTANCESD = -1; private static final int DEFAULT_CHANNEL_WIDTH = 0; private static final int DEFAULT_CENTER_FREQ0 = -1; private static final int DEFAULT_CENTER_FREQ1 = -1; private static final boolean DEFAULT_IS_80211_MCRTTRESPONDER = true; private static final int DEFAULT_CARRIER_AP_EAP_TYPE = -1; private static final int DEFAULT_RSSI = -50; private static final int DEFAULT_LINKSPEED = 400; private static final int DEFAULT_TXLINKSPEED = 400; private static final int DEFAULT_RXLINKSPEED = 400; private static final boolean DEFAULT_HINT = false; private static final boolean DEFAULT_EPHEMERAL = false; private static final boolean DEFAULT_TRUSTED = false; private static final boolean DEFAULT_OSUAP = false; private static final String DEFAULT_FQDN = "null"; private static final int DEFAULT_TX_BAD = 0; private static final int DEFAULT_TX_RETRIES = 0; private static final int DEFAULT_TX_SUCCESS = 0; private static final int DEFAULT_RX_SUCCESS = 0; private static final int DEFAULT_TX_BADRATE = 0; private static final int DEFAULT_TX_RETRIES_RATE = 0; private static final int DEFAULT_TX_SUCCESS_RATE = 0; private static final int DEFAULT_RX_SUCCESS_RATE = 0; private static final int DEFAULT_SCORE = 90; private static final int DEFAULT_NET_ID = 0; private static final String DEFAULT_SCANRESULTS = "[{" + "\"SSID\":\"" + DEFAULT_SSID + "\"" + ",\"BSSID\":\"" + DEFAULT_BSSID + "\"" + ",\"IP\":\"" + DEFAULT_IP + "\"" + ",\"hessid\": " + DEFAULT_HESSID + ",\"anqpDomainId\": " + DEFAULT_ANQP_DOMAIN_ID + ",\"capabilities\":\" " + DEFAULT_CAPABILITIES + "\"" + ",\"level\":" + DEFAULT_LEVEL + ",\"linkSpeed\":" + DEFAULT_LINKSPEED + ",\"txLinkSpeed\":" + DEFAULT_TXLINKSPEED + ",\"rxLinkSpeed\":" + DEFAULT_RXLINKSPEED + ",\"frequency\":" + DEFAULT_FREQUENCY + ",\"distance\":" + DEFAULT_DISTANCE + ",\"distanceSd\":" + DEFAULT_DISTANCESD + ",\"channelWidth\":" + DEFAULT_CHANNEL_WIDTH + ",\"centerFreq0\":" + DEFAULT_CENTER_FREQ0 + ",\"centerFreq1\":" + DEFAULT_CENTER_FREQ1 + ",\"is80211McRTTResponder\":" + DEFAULT_IS_80211_MCRTTRESPONDER + "}]"; private Context mContext; private String mLastMAC; private String mLastSSID; private String mLastBSSID; private String mIpAddress; private String mDNS1; private String mDNS2; private String mGateway; private int mLastNetworkId = DEFAULT_NET_ID; private boolean dbg = (SystemProperties.getInt("persist.sys.cloud.debug.wifi", 0) == 1); private final WifiConfigManager mWifiConfigManager; public WifiHelper(Context context, WifiConfigManager wifiConfigManager) { mContext = context; mWifiConfigManager = wifiConfigManager; mWifiConfigManager.enableVerboseLogging(dbg); } private int ipToInt(String ipAddress) { try { InetAddress inetAddress = InetAddress.getByName(ipAddress); byte[] addressBytes = inetAddress.getAddress(); return (addressBytes[0] & 0xFF) << 24 | (addressBytes[1] & 0xFF) << 16 | (addressBytes[2] & 0xFF) << 8 | (addressBytes[3] & 0xFF); } catch (UnknownHostException e) { e.printStackTrace(); return 0; } } private Inet4Address convertToInet4Address(String ipAddress) { try { int result = ipToInt(ipAddress); byte[] ipAddressBytes = new byte[]{ (byte) ((result >> 24) & 0xFF), (byte) ((result >> 16) & 0xFF), (byte) ((result >> 8) & 0xFF), (byte) (result & 0xFF) }; InetAddress inetAddress = InetAddress.getByAddress(ipAddressBytes); if (inetAddress instanceof Inet4Address) { return (Inet4Address) inetAddress; } else { System.out.println("Not an IPv4 address"); return null; } } catch (UnknownHostException e) { e.printStackTrace(); return null; } } public WifiInfo getConnectionInfoFromVCloud(String callingPackage) { String jsonString = android.provider.VCloudSettings.Global.getString(mContext.getContentResolver(), "wifi.scanresults"); if (TextUtils.isEmpty(jsonString)) { jsonString = DEFAULT_SCANRESULTS; } try { WifiInfo wifiInfo = new WifiInfo(); wifiInfo.setRequestingPackageName(callingPackage); JSONArray jsonArray = new JSONArray(jsonString); JSONObject jsonObject = jsonArray.getJSONObject(0); mLastSSID = jsonObject.optString("SSID", DEFAULT_SSID); String mock_ssid = SystemProperties.get("persist.sys.cloud.wifi.ssid"); if (null != mock_ssid && !mock_ssid.isEmpty()) { mLastSSID = mock_ssid; } wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(mLastSSID)); mLastBSSID = jsonObject.optString("BSSID", DEFAULT_BSSID); wifiInfo.setBSSID(mLastBSSID); mLastMAC = jsonObject.optString("MAC", DEFAULT_MAC); wifiInfo.setMacAddress(mLastMAC); mIpAddress = jsonObject.optString("IP", DEFAULT_IP); String mock_ipv4 = SystemProperties.get("persist.sys.cloud.wifi.ip"); if (null != mock_ipv4 && !mock_ipv4.isEmpty()) { mIpAddress = mock_ipv4; } try { wifiInfo.setInetAddress(InetAddress.getByName(mIpAddress)); } catch (UnknownHostException e) { } mDNS1 = jsonObject.optString("DNS1", DEFAULT_DNS1); mDNS2 = jsonObject.optString("DNS2", DEFAULT_DNS2); String mockDns1 = SystemProperties.get("persist.sys.cloud.wifi.dns1"); if (null != mockDns1 && !mockDns1.isEmpty()) { mDNS1 = mockDns1; mDNS2 = SystemProperties.get("persist.sys.cloud.wifi.dns2"); } mGateway = jsonObject.optString("gateway", DEFAULT_GATEWAY); String mock_gateway = SystemProperties.get("persist.sys.cloud.wifi.gateway"); if (null != mock_gateway && !mock_gateway.isEmpty()) { mGateway = mock_gateway; } wifiInfo.setRssi(jsonObject.optInt("rssi", DEFAULT_RSSI)); wifiInfo.setLinkSpeed(jsonObject.optInt("linkSpeed", DEFAULT_LINKSPEED)); wifiInfo.setTxLinkSpeedMbps(jsonObject.optInt("txLinkSpeed", DEFAULT_TXLINKSPEED)); wifiInfo.setRxLinkSpeedMbps(jsonObject.optInt("rxLinkSpeed", DEFAULT_RXLINKSPEED)); wifiInfo.setMaxSupportedTxLinkSpeedMbps(jsonObject.optInt("txLinkSpeedMax", DEFAULT_TXLINKSPEED)); wifiInfo.setMaxSupportedRxLinkSpeedMbps(jsonObject.optInt("rxLinkSpeedMax", DEFAULT_RXLINKSPEED)); wifiInfo.setFrequency(jsonObject.optInt("frequency", DEFAULT_FREQUENCY)); wifiInfo.setMeteredHint(jsonObject.optBoolean("hint", DEFAULT_HINT)); wifiInfo.setEphemeral(jsonObject.optBoolean("ephemeral", DEFAULT_EPHEMERAL)); wifiInfo.setTrusted(jsonObject.optBoolean("trusted", DEFAULT_TRUSTED)); wifiInfo.setOsuAp(jsonObject.optBoolean("osuAp", DEFAULT_OSUAP)); wifiInfo.setFQDN(jsonObject.optString("fqdn", DEFAULT_FQDN)); wifiInfo.setCurrentSecurityType(WifiConfiguration.SECURITY_TYPE_OPEN); wifiInfo.setNetworkId(mLastNetworkId); return wifiInfo; } catch (JSONException e){ Log.e(TAG, "update wifiInfo failed:" + e); return null; } } public List ScanResultsFromVCloud() { String jsonString = android.provider.VCloudSettings.Global.getString(mContext.getContentResolver(), "wifi.scanresults"); if (TextUtils.isEmpty(jsonString)) { jsonString = DEFAULT_SCANRESULTS; } List scanResults = new ArrayList<>(); try { JSONArray jsonArray = new JSONArray(jsonString); for (int i = 0; i < jsonArray.length(); i++) { JSONObject jsonObject = jsonArray.getJSONObject(i); ScanResult scanResult = new ScanResult(); String ssid = jsonObject.optString("SSID", DEFAULT_SSID); String mock_ssid = SystemProperties.get("persist.sys.cloud.wifi.ssid"); if (null != mock_ssid && !mock_ssid.isEmpty()) { ssid = mock_ssid; } scanResult.wifiSsid = WifiSsid.createFromAsciiEncoded(ssid); scanResult.SSID = ssid; scanResult.BSSID = jsonObject.optString("BSSID", DEFAULT_BSSID); scanResult.hessid = jsonObject.optInt("hessid", DEFAULT_HESSID); scanResult.anqpDomainId = jsonObject.optInt("anqpDomainId", DEFAULT_ANQP_DOMAIN_ID); byte[] osuProviders = null; scanResult.capabilities = jsonObject.optString("capabilities", DEFAULT_CAPABILITIES); scanResult.level = jsonObject.optInt("level", DEFAULT_LEVEL); scanResult.frequency = jsonObject.optInt("frequency", DEFAULT_FREQUENCY); scanResult.timestamp = SystemClock.elapsedRealtimeNanos()/1000; scanResult.distanceCm = jsonObject.optInt("distance", DEFAULT_DISTANCE); scanResult.distanceSdCm = jsonObject.optInt("distanceSd", DEFAULT_DISTANCESD); scanResult.channelWidth = jsonObject.optInt("channelWidth", DEFAULT_CHANNEL_WIDTH); scanResult.centerFreq0 = jsonObject.optInt("centerFreq0", DEFAULT_CENTER_FREQ0); scanResult.centerFreq1 = jsonObject.optInt("centerFreq1", DEFAULT_CENTER_FREQ1); scanResult.is80211McRTTResponder = jsonObject.optBoolean("is80211McRTTResponder", DEFAULT_IS_80211_MCRTTRESPONDER); //scanResult.carrierApEapType = jsonObject.optInt("carrierApEapType", DEFAULT_CARRIER_AP_EAP_TYPE); if (scanResult.is80211McRTTResponder) { scanResult.setFlag(ScanResult.FLAG_80211mc_RESPONDER); } else { scanResult.clearFlag(ScanResult.FLAG_80211mc_RESPONDER); } boolean passpoint = jsonObject.optBoolean("passpoint", false); if (passpoint) { scanResult.setFlag(ScanResult.FLAG_PASSPOINT_NETWORK); } else { scanResult.clearFlag(ScanResult.FLAG_PASSPOINT_NETWORK); } scanResults.add(scanResult); break; } } catch (JSONException e){ Log.e(TAG, "update WifiInfo failed:" + e); } updateWifiConfigs(scanResults); return scanResults; } public boolean startScan(String packageName, String featureId) { if (dbg) Log.d(TAG, "startScan(" + packageName + "," + featureId + ")"); long callingIdentity = Binder.clearCallingIdentity(); Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, true); mContext.sendBroadcastAsUser(intent, UserHandle.ALL); return true; } public List getScanResults(String callingPackage, String callingFeatureId) { if (dbg) Log.d(TAG, "getScanResults(" + callingPackage + "," + callingFeatureId + "), stack:" + Arrays.toString(Thread.currentThread().getStackTrace())); List results = ScanResultsFromVCloud(); if (dbg) Log.d(TAG, "ScanResults:" + results); return results; } public WifiInfo getConnectionInfo(@NonNull String callingPackage, @Nullable String callingFeatureId) { if (dbg) Log.d(TAG, "getConnectionInfo(" + callingPackage + "," + callingFeatureId + "), stack:" + Arrays.toString(Thread.currentThread().getStackTrace())); WifiInfo wifiInfo = getConnectionInfoFromVCloud(callingPackage); if (dbg) Log.d(TAG, "ConnectionInfo:" + wifiInfo); return wifiInfo; } public ParceledListSlice getConfiguredNetworks(String packageName, String featureId) { long ident = Binder.clearCallingIdentity(); try { if (dbg) Log.d(TAG, "getConfiguredNetworks(" + packageName + "," + featureId + "), stack:" + Arrays.toString(Thread.currentThread().getStackTrace())); List scanResults = ScanResultsFromVCloud(); List configs = mWifiConfigManager.getSavedNetworks(Process.WIFI_UID); if (configs.size() > 0) { configs.get(0).networkId = mLastNetworkId; } if (dbg) Log.d(TAG, "configuredNetworks:" + configs); return new ParceledListSlice<>(WifiConfigurationUtil.convertMultiTypeConfigsToLegacyConfigs(configs)); } finally { Binder.restoreCallingIdentity(ident); } } public List getPasspointConfigurations(String packageName) { if (dbg) Log.d(TAG, "getPasspointConfigurations(" + packageName + ")"); List configs = new ArrayList<>(); return configs; } private static int ipToInteger(String ipAddress) { if (null == ipAddress || ipAddress.isEmpty()) { return 0; } String[] ipAddressArray = ipAddress.split("\\."); if (ipAddressArray.length != 4) { throw new IllegalArgumentException("Invalid IP address format"); } int rs = 0; for (int i = 0; i < 4; i++) { int octet = Integer.parseInt(ipAddressArray[i]) << 8 * i; rs = rs | octet; // ipInteger = (ipInteger << 8) | octet; } return rs; } public DhcpInfo getDhcpInfo(@NonNull String packageName) { if (dbg) Log.d(TAG, "getDhcpInfo:" + packageName); DhcpInfo dhcpInfo = new DhcpInfo(); dhcpInfo.serverAddress = ipToInteger(mGateway); dhcpInfo.ipAddress = ipToInteger(mIpAddress); dhcpInfo.gateway = ipToInteger(mGateway); dhcpInfo.dns1 = ipToInteger(mDNS1); dhcpInfo.dns2 = ipToInteger(mDNS2); if (dbg) Log.d(TAG, "dhcpInfo:" + dhcpInfo); return dhcpInfo; } public Network getCurrentNetwork() { if (dbg) Log.d(TAG, "getCurrentNetwork()"); Network network = new Network(mLastNetworkId, false); if (dbg) Log.d(TAG, "network:" + network); return network; } private void updateWifiConfigs(List scanResults) { List configs = mWifiConfigManager.getSavedNetworks(Process.WIFI_UID); if (dbg) { for(WifiConfiguration config: configs) { Log.e(TAG, "updateWifiConfigs WifiConfiguration config.SSID:" + config.SSID + ", config.BSSID:" + config.BSSID); } for (ScanResult scanResult: scanResults) { Log.e(TAG, "updateWifiConfigs ScanResult scanResult.SSID:" + scanResult.SSID + ", scanResult.BSSID:" + scanResult.BSSID); } } if (configs.size() > 1) { for(WifiConfiguration config: configs) { mWifiConfigManager.removeNetwork(config.networkId, Process.WIFI_UID, "Android"); } } else { for(WifiConfiguration config: configs) { boolean found = false; for (ScanResult scanResult: scanResults) { if (null != config.BSSID && config.BSSID.equals(scanResult.BSSID)){ found = true; break; } } if (found) { continue; } mWifiConfigManager.removeNetwork(config.networkId, Process.WIFI_UID, "Android"); } } boolean updateFailed = false; for (ScanResult scanResult: scanResults) { boolean found = false; for(WifiConfiguration config: configs) { if (config.BSSID.equals(scanResult.BSSID)){ found = true; break; } } if (found) { continue; } WifiConfiguration config = generateWifiConfig(mLastNetworkId, Process.WIFI_UID, "\"" + scanResult.SSID + "\"", true, true, null, null); config.BSSID = scanResult.BSSID; config.numAssociation = 2; NetworkUpdateResult result = mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID); if (!result.isSuccess()) { Log.e(TAG, "CONNECT_NETWORK adding/updating failed"); updateFailed = true; continue; } int netId = result.getNetworkId(); mWifiConfigManager.updateNetworkSelectionStatus(netId, NetworkSelectionStatus.NETWORK_SELECTION_ENABLED); } } private static WifiConfiguration generateWifiConfig(int networkId, int uid, String ssid, boolean shared, boolean enabled, String fqdn, String providerFriendlyName) { final WifiConfiguration config = new WifiConfiguration(); config.SSID = ssid; config.networkId = networkId; config.creatorUid = uid; config.shared = shared; config.status = enabled ? WifiConfiguration.Status.ENABLED : WifiConfiguration.Status.DISABLED; config.FQDN = fqdn; config.providerFriendlyName = providerFriendlyName; if (config.FQDN != null) { int uniqueId = config.FQDN.hashCode() + config.networkId + config.creatorUid; // Generate a Passpoint unique id locally for the test config.setPasspointUniqueId(config.FQDN + "_" + uniqueId); } config.addSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN); return config; } }