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) {
}
}