package com.android.hotspot2; import android.content.Context; import android.content.Intent; import android.net.CaptivePortal; import android.net.ConnectivityManager; import android.net.ICaptivePortal; import android.net.Network; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.util.Log; import com.android.configparse.ConfigBuilder; import com.android.hotspot2.omadm.MOManager; import com.android.hotspot2.omadm.MOTree; import com.android.hotspot2.omadm.OMAConstants; import com.android.hotspot2.omadm.OMAException; import com.android.hotspot2.omadm.OMAParser; import com.android.hotspot2.osu.OSUCertType; import com.android.hotspot2.osu.OSUInfo; import com.android.hotspot2.osu.OSUManager; import com.android.hotspot2.osu.commands.MOData; import com.android.hotspot2.pps.HomeSP; import org.xml.sax.SAXException; import java.io.IOException; import java.net.URL; import java.security.GeneralSecurityException; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; public class WifiNetworkAdapter { private final Context mContext; private final OSUManager mOSUManager; private final Map<String, PasspointConfig> mPasspointConfigs = new HashMap<>(); private static class PasspointConfig { private final WifiConfiguration mWifiConfiguration; private final MOTree mMOTree; private final HomeSP mHomeSP; private PasspointConfig(WifiConfiguration config) throws IOException, SAXException { mWifiConfiguration = config; OMAParser omaParser = new OMAParser(); mMOTree = omaParser.parse(config.getMoTree(), OMAConstants.PPS_URN); List<HomeSP> spList = MOManager.buildSPs(mMOTree); if (spList.size() != 1) { throw new OMAException("Expected exactly one HomeSP, got " + spList.size()); } mHomeSP = spList.iterator().next(); } public WifiConfiguration getWifiConfiguration() { return mWifiConfiguration; } public HomeSP getHomeSP() { return mHomeSP; } public MOTree getmMOTree() { return mMOTree; } } public WifiNetworkAdapter(Context context, OSUManager osuManager) { mOSUManager = osuManager; mContext = context; } public void initialize() { loadAllSps(); } public void networkConfigChange(WifiConfiguration configuration) { loadAllSps(); } private void loadAllSps() { Log.d(OSUManager.TAG, "Loading all SPs"); WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); for (WifiConfiguration config : wifiManager.getPrivilegedConfiguredNetworks()) { String moTree = config.getMoTree(); if (moTree != null) { try { mPasspointConfigs.put(config.FQDN, new PasspointConfig(config)); } catch (IOException | SAXException e) { Log.w(OSUManager.TAG, "Failed to parse MO: " + e); } } } } public Collection<HomeSP> getLoadedSPs() { List<HomeSP> homeSPs = new ArrayList<>(); for (PasspointConfig config : mPasspointConfigs.values()) { homeSPs.add(config.getHomeSP()); } return homeSPs; } public MOTree getMOTree(HomeSP homeSP) { PasspointConfig config = mPasspointConfigs.get(homeSP.getFQDN()); return config != null ? config.getmMOTree() : null; } public void launchBrowser(URL target, Network network, URL endRedirect) { Log.d(OSUManager.TAG, "Browser to " + target + ", land at " + endRedirect); final Intent intent = new Intent( ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN); intent.putExtra(ConnectivityManager.EXTRA_NETWORK, network); intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL, new CaptivePortal(new ICaptivePortal.Stub() { @Override public void appResponse(int response) { } })); //intent.setData(Uri.parse(target.toString())); !!! Doesn't work! intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL, target.toString()); intent.setFlags( Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); } public HomeSP addSP(MOTree instanceTree) throws IOException, SAXException { WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); String xml = instanceTree.toXml(); // TODO(b/32883320): use the new API for adding Passpoint configuration. return null; } public void removeSP(String fqdn) throws IOException { WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); } public HomeSP modifySP(HomeSP homeSP, Collection<MOData> mods) throws IOException { WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); return null; } public Network getCurrentNetwork() { WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); return wifiManager.getCurrentNetwork(); } public WifiConfiguration getActiveWifiConfig() { WifiInfo wifiInfo = getConnectionInfo(); if (wifiInfo == null) { return null; } WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); for (WifiConfiguration config : wifiManager.getConfiguredNetworks()) { if (config.networkId == wifiInfo.getNetworkId()) { return config; } } return null; } public WifiInfo getConnectionInfo() { WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); return wifiManager.getConnectionInfo(); } public PasspointMatch matchProviderWithCurrentNetwork(String fqdn) { WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); int ordinal = wifiManager.matchProviderWithCurrentNetwork(fqdn); return ordinal >= 0 && ordinal < PasspointMatch.values().length ? PasspointMatch.values()[ordinal] : null; } public WifiConfiguration getWifiConfig(HomeSP homeSP) { PasspointConfig passpointConfig = mPasspointConfigs.get(homeSP.getFQDN()); return passpointConfig != null ? passpointConfig.getWifiConfiguration() : null; } public WifiConfiguration getActivePasspointNetwork() { PasspointConfig passpointConfig = getActivePasspointConfig(); return passpointConfig != null ? passpointConfig.getWifiConfiguration() : null; } private PasspointConfig getActivePasspointConfig() { WifiInfo wifiInfo = getConnectionInfo(); if (wifiInfo == null) { return null; } for (PasspointConfig passpointConfig : mPasspointConfigs.values()) { if (passpointConfig.getWifiConfiguration().networkId == wifiInfo.getNetworkId()) { return passpointConfig; } } return null; } public HomeSP getCurrentSP() { PasspointConfig passpointConfig = getActivePasspointConfig(); return passpointConfig != null ? passpointConfig.getHomeSP() : null; } public void doIconQuery(long bssid, String fileName) { WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); Log.d("ZXZ", String.format("Icon query for %012x '%s'", bssid, fileName)); wifiManager.queryPasspointIcon(bssid, fileName); } public Integer addNetwork(HomeSP homeSP, Map<OSUCertType, List<X509Certificate>> certs, PrivateKey privateKey, Network osuNetwork) throws IOException, GeneralSecurityException { List<X509Certificate> aaaTrust = certs.get(OSUCertType.AAA); if (aaaTrust.isEmpty()) { aaaTrust = certs.get(OSUCertType.CA); // Get the CAs from the EST flow. } WifiConfiguration config = ConfigBuilder.buildConfig(homeSP, aaaTrust.iterator().next(), certs.get(OSUCertType.Client), privateKey); WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); int nwkId = wifiManager.addNetwork(config); boolean saved = false; if (nwkId >= 0) { saved = wifiManager.saveConfiguration(); } Log.d(OSUManager.TAG, "Wifi configuration " + nwkId + " " + (saved ? "saved" : "not saved")); if (saved) { reconnect(osuNetwork, nwkId); return nwkId; } else { return null; } } public void updateNetwork(HomeSP homeSP, X509Certificate caCert, List<X509Certificate> clientCerts, PrivateKey privateKey) throws IOException, GeneralSecurityException { WifiConfiguration config = getWifiConfig(homeSP); if (config == null) { throw new IOException("Failed to find matching network config"); } Log.d(OSUManager.TAG, "Found matching config " + config.networkId + ", updating"); WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; WifiConfiguration newConfig = ConfigBuilder.buildConfig(homeSP, caCert != null ? caCert : enterpriseConfig.getCaCertificate(), clientCerts, privateKey); newConfig.networkId = config.networkId; WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); wifiManager.save(newConfig, null); wifiManager.saveConfiguration(); } /** * Connect to an OSU provisioning network. The connection should not bring down other existing * connection and the network should not be made the default network since the connection * is solely for sign up and is neither intended for nor likely provides access to any * generic resources. * * @param osuInfo The OSU info object that defines the parameters for the network. An OSU * network is either an open network, or, if the OSU NAI is set, an "OSEN" * network, which is an anonymous EAP-TLS network with special keys. * @param info An opaque string that is passed on to any user notification. The string is used * for the name of the service provider. * @return an Integer holding the network-id of the just added network configuration, or null * if the network existed prior to this call (was not added by the OSU infrastructure). * The value will be used at the end of the OSU flow to delete the network as applicable. * @throws IOException Issues: * 1. The network id is not returned. addNetwork cannot be called from here since the method * runs in the context of the app and doesn't have the appropriate permission. * 2. The connection is not immediately usable if the network was not previously selected * manually. */ public Integer connect(OSUInfo osuInfo, final String info) throws IOException { WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); WifiConfiguration config = new WifiConfiguration(); config.SSID = '"' + osuInfo.getSSID() + '"'; if (osuInfo.getOSUBssid() != 0) { config.BSSID = Utils.macToString(osuInfo.getOSUBssid()); Log.d(OSUManager.TAG, String.format("Setting BSSID of '%s' to %012x", osuInfo.getSSID(), osuInfo.getOSUBssid())); } if (osuInfo.getOSUProvider().getOsuNai() == null) { config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); } else { config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OSEN); config.allowedProtocols.set(WifiConfiguration.Protocol.OSEN); config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GTK_NOT_USED); config.enterpriseConfig = new WifiEnterpriseConfig(); config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.UNAUTH_TLS); config.enterpriseConfig.setIdentity(osuInfo.getOSUProvider().getOsuNai()); // !!! OSEN CA Cert??? } int networkId = wifiManager.addNetwork(config); if (wifiManager.enableNetwork(networkId, true)) { return networkId; } else { return null; } /* sequence of addNetwork(), enableNetwork(), saveConfiguration() and reconnect() wifiManager.connect(config, new WifiManager.ActionListener() { @Override public void onSuccess() { // Connection event comes from network change intent registered in initialize } @Override public void onFailure(int reason) { mOSUManager.notifyUser(OSUOperationStatus.ProvisioningFailure, "Cannot connect to OSU network: " + reason, info); } }); return null; /* try { int nwkID = wifiManager.addOrUpdateOSUNetwork(config); if (nwkID == WifiConfiguration.INVALID_NETWORK_ID) { throw new IOException("Failed to add OSU network"); } wifiManager.enableNetwork(nwkID, false); wifiManager.reconnect(); return nwkID; } catch (SecurityException se) { Log.d("ZXZ", "Blah: " + se, se); wifiManager.connect(config, new WifiManager.ActionListener() { @Override public void onSuccess() { // Connection event comes from network change intent registered in initialize } @Override public void onFailure(int reason) { mOSUManager.notifyUser(OSUOperationStatus.ProvisioningFailure, "Cannot connect to OSU network: " + reason, info); } }); return null; } */ } private void reconnect(Network osuNetwork, int newNwkId) { WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); if (osuNetwork != null) { wifiManager.disableNetwork(osuNetwork.netId); } if (newNwkId != WifiConfiguration.INVALID_NETWORK_ID) { wifiManager.enableNetwork(newNwkId, true); } } public void deleteNetwork(int id) { WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); wifiManager.disableNetwork(id); wifiManager.forget(id, null); } /** * Set the re-authentication hold off time for the current network * * @param holdoff hold off time in milliseconds * @param ess set if the hold off pertains to an ESS rather than a BSS */ public void setHoldoffTime(long holdoff, boolean ess) { } }