1 package com.android.hotspot2; 2 3 import android.content.Context; 4 import android.content.Intent; 5 import android.net.CaptivePortal; 6 import android.net.ConnectivityManager; 7 import android.net.ICaptivePortal; 8 import android.net.Network; 9 import android.net.wifi.WifiConfiguration; 10 import android.net.wifi.WifiEnterpriseConfig; 11 import android.net.wifi.WifiInfo; 12 import android.net.wifi.WifiManager; 13 import android.util.Log; 14 15 import com.android.configparse.ConfigBuilder; 16 import com.android.hotspot2.omadm.MOManager; 17 import com.android.hotspot2.omadm.MOTree; 18 import com.android.hotspot2.omadm.OMAConstants; 19 import com.android.hotspot2.omadm.OMAException; 20 import com.android.hotspot2.omadm.OMAParser; 21 import com.android.hotspot2.osu.OSUCertType; 22 import com.android.hotspot2.osu.OSUInfo; 23 import com.android.hotspot2.osu.OSUManager; 24 import com.android.hotspot2.osu.commands.MOData; 25 import com.android.hotspot2.pps.HomeSP; 26 27 import org.xml.sax.SAXException; 28 29 import java.io.IOException; 30 import java.net.URL; 31 import java.security.GeneralSecurityException; 32 import java.security.PrivateKey; 33 import java.security.cert.X509Certificate; 34 import java.util.ArrayList; 35 import java.util.Collection; 36 import java.util.HashMap; 37 import java.util.List; 38 import java.util.Map; 39 40 public class WifiNetworkAdapter { 41 private final Context mContext; 42 private final OSUManager mOSUManager; 43 private final Map<String, PasspointConfig> mPasspointConfigs = new HashMap<>(); 44 45 private static class PasspointConfig { 46 private final WifiConfiguration mWifiConfiguration; 47 private final MOTree mMOTree; 48 private final HomeSP mHomeSP; 49 PasspointConfig(WifiConfiguration config)50 private PasspointConfig(WifiConfiguration config) throws IOException, SAXException { 51 mWifiConfiguration = config; 52 OMAParser omaParser = new OMAParser(); 53 mMOTree = omaParser.parse(config.getMoTree(), OMAConstants.PPS_URN); 54 List<HomeSP> spList = MOManager.buildSPs(mMOTree); 55 if (spList.size() != 1) { 56 throw new OMAException("Expected exactly one HomeSP, got " + spList.size()); 57 } 58 mHomeSP = spList.iterator().next(); 59 } 60 getWifiConfiguration()61 public WifiConfiguration getWifiConfiguration() { 62 return mWifiConfiguration; 63 } 64 getHomeSP()65 public HomeSP getHomeSP() { 66 return mHomeSP; 67 } 68 getmMOTree()69 public MOTree getmMOTree() { 70 return mMOTree; 71 } 72 } 73 WifiNetworkAdapter(Context context, OSUManager osuManager)74 public WifiNetworkAdapter(Context context, OSUManager osuManager) { 75 mOSUManager = osuManager; 76 mContext = context; 77 } 78 initialize()79 public void initialize() { 80 loadAllSps(); 81 } 82 networkConfigChange(WifiConfiguration configuration)83 public void networkConfigChange(WifiConfiguration configuration) { 84 loadAllSps(); 85 } 86 loadAllSps()87 private void loadAllSps() { 88 Log.d(OSUManager.TAG, "Loading all SPs"); 89 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 90 for (WifiConfiguration config : wifiManager.getPrivilegedConfiguredNetworks()) { 91 String moTree = config.getMoTree(); 92 if (moTree != null) { 93 try { 94 mPasspointConfigs.put(config.FQDN, new PasspointConfig(config)); 95 } catch (IOException | SAXException e) { 96 Log.w(OSUManager.TAG, "Failed to parse MO: " + e); 97 } 98 } 99 } 100 } 101 getLoadedSPs()102 public Collection<HomeSP> getLoadedSPs() { 103 List<HomeSP> homeSPs = new ArrayList<>(); 104 for (PasspointConfig config : mPasspointConfigs.values()) { 105 homeSPs.add(config.getHomeSP()); 106 } 107 return homeSPs; 108 } 109 getMOTree(HomeSP homeSP)110 public MOTree getMOTree(HomeSP homeSP) { 111 PasspointConfig config = mPasspointConfigs.get(homeSP.getFQDN()); 112 return config != null ? config.getmMOTree() : null; 113 } 114 launchBrowser(URL target, Network network, URL endRedirect)115 public void launchBrowser(URL target, Network network, URL endRedirect) { 116 Log.d(OSUManager.TAG, "Browser to " + target + ", land at " + endRedirect); 117 118 final Intent intent = new Intent( 119 ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN); 120 intent.putExtra(ConnectivityManager.EXTRA_NETWORK, network); 121 intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL, 122 new CaptivePortal(new ICaptivePortal.Stub() { 123 @Override 124 public void appResponse(int response) { 125 } 126 })); 127 //intent.setData(Uri.parse(target.toString())); !!! Doesn't work! 128 intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL, target.toString()); 129 intent.setFlags( 130 Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); 131 mContext.startActivity(intent); 132 } 133 addSP(MOTree instanceTree)134 public HomeSP addSP(MOTree instanceTree) throws IOException, SAXException { 135 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 136 String xml = instanceTree.toXml(); 137 wifiManager.addPasspointManagementObject(xml); 138 return MOManager.buildSP(xml); 139 } 140 removeSP(String fqdn)141 public void removeSP(String fqdn) throws IOException { 142 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 143 } 144 modifySP(HomeSP homeSP, Collection<MOData> mods)145 public HomeSP modifySP(HomeSP homeSP, Collection<MOData> mods) 146 throws IOException { 147 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 148 return null; 149 } 150 getCurrentNetwork()151 public Network getCurrentNetwork() { 152 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 153 return wifiManager.getCurrentNetwork(); 154 } 155 getActiveWifiConfig()156 public WifiConfiguration getActiveWifiConfig() { 157 WifiInfo wifiInfo = getConnectionInfo(); 158 if (wifiInfo == null) { 159 return null; 160 } 161 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 162 for (WifiConfiguration config : wifiManager.getConfiguredNetworks()) { 163 if (config.networkId == wifiInfo.getNetworkId()) { 164 return config; 165 } 166 } 167 return null; 168 } 169 getConnectionInfo()170 public WifiInfo getConnectionInfo() { 171 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 172 return wifiManager.getConnectionInfo(); 173 } 174 matchProviderWithCurrentNetwork(String fqdn)175 public PasspointMatch matchProviderWithCurrentNetwork(String fqdn) { 176 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 177 int ordinal = wifiManager.matchProviderWithCurrentNetwork(fqdn); 178 return ordinal >= 0 && ordinal < PasspointMatch.values().length ? 179 PasspointMatch.values()[ordinal] : null; 180 } 181 getWifiConfig(HomeSP homeSP)182 public WifiConfiguration getWifiConfig(HomeSP homeSP) { 183 PasspointConfig passpointConfig = mPasspointConfigs.get(homeSP.getFQDN()); 184 return passpointConfig != null ? passpointConfig.getWifiConfiguration() : null; 185 } 186 getActivePasspointNetwork()187 public WifiConfiguration getActivePasspointNetwork() { 188 PasspointConfig passpointConfig = getActivePasspointConfig(); 189 return passpointConfig != null ? passpointConfig.getWifiConfiguration() : null; 190 } 191 getActivePasspointConfig()192 private PasspointConfig getActivePasspointConfig() { 193 WifiInfo wifiInfo = getConnectionInfo(); 194 if (wifiInfo == null) { 195 return null; 196 } 197 198 for (PasspointConfig passpointConfig : mPasspointConfigs.values()) { 199 if (passpointConfig.getWifiConfiguration().networkId == wifiInfo.getNetworkId()) { 200 return passpointConfig; 201 } 202 } 203 return null; 204 } 205 getCurrentSP()206 public HomeSP getCurrentSP() { 207 PasspointConfig passpointConfig = getActivePasspointConfig(); 208 return passpointConfig != null ? passpointConfig.getHomeSP() : null; 209 } 210 doIconQuery(long bssid, String fileName)211 public void doIconQuery(long bssid, String fileName) { 212 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 213 Log.d("ZXZ", String.format("Icon query for %012x '%s'", bssid, fileName)); 214 wifiManager.queryPasspointIcon(bssid, fileName); 215 } 216 addNetwork(HomeSP homeSP, Map<OSUCertType, List<X509Certificate>> certs, PrivateKey privateKey, Network osuNetwork)217 public Integer addNetwork(HomeSP homeSP, Map<OSUCertType, List<X509Certificate>> certs, 218 PrivateKey privateKey, Network osuNetwork) 219 throws IOException, GeneralSecurityException { 220 221 List<X509Certificate> aaaTrust = certs.get(OSUCertType.AAA); 222 if (aaaTrust.isEmpty()) { 223 aaaTrust = certs.get(OSUCertType.CA); // Get the CAs from the EST flow. 224 } 225 226 WifiConfiguration config = ConfigBuilder.buildConfig(homeSP, 227 aaaTrust.iterator().next(), 228 certs.get(OSUCertType.Client), privateKey); 229 230 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 231 int nwkId = wifiManager.addNetwork(config); 232 boolean saved = false; 233 if (nwkId >= 0) { 234 saved = wifiManager.saveConfiguration(); 235 } 236 Log.d(OSUManager.TAG, "Wifi configuration " + nwkId + 237 " " + (saved ? "saved" : "not saved")); 238 239 if (saved) { 240 reconnect(osuNetwork, nwkId); 241 return nwkId; 242 } else { 243 return null; 244 } 245 } 246 updateNetwork(HomeSP homeSP, X509Certificate caCert, List<X509Certificate> clientCerts, PrivateKey privateKey)247 public void updateNetwork(HomeSP homeSP, X509Certificate caCert, 248 List<X509Certificate> clientCerts, PrivateKey privateKey) 249 throws IOException, GeneralSecurityException { 250 251 WifiConfiguration config = getWifiConfig(homeSP); 252 if (config == null) { 253 throw new IOException("Failed to find matching network config"); 254 } 255 Log.d(OSUManager.TAG, "Found matching config " + config.networkId + ", updating"); 256 257 WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; 258 WifiConfiguration newConfig = ConfigBuilder.buildConfig(homeSP, 259 caCert != null ? caCert : enterpriseConfig.getCaCertificate(), 260 clientCerts, privateKey); 261 newConfig.networkId = config.networkId; 262 263 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 264 wifiManager.save(newConfig, null); 265 wifiManager.saveConfiguration(); 266 } 267 268 /** 269 * Connect to an OSU provisioning network. The connection should not bring down other existing 270 * connection and the network should not be made the default network since the connection 271 * is solely for sign up and is neither intended for nor likely provides access to any 272 * generic resources. 273 * 274 * @param osuInfo The OSU info object that defines the parameters for the network. An OSU 275 * network is either an open network, or, if the OSU NAI is set, an "OSEN" 276 * network, which is an anonymous EAP-TLS network with special keys. 277 * @param info An opaque string that is passed on to any user notification. The string is used 278 * for the name of the service provider. 279 * @return an Integer holding the network-id of the just added network configuration, or null 280 * if the network existed prior to this call (was not added by the OSU infrastructure). 281 * The value will be used at the end of the OSU flow to delete the network as applicable. 282 * @throws IOException Issues: 283 * 1. The network id is not returned. addNetwork cannot be called from here since the method 284 * runs in the context of the app and doesn't have the appropriate permission. 285 * 2. The connection is not immediately usable if the network was not previously selected 286 * manually. 287 */ connect(OSUInfo osuInfo, final String info)288 public Integer connect(OSUInfo osuInfo, final String info) throws IOException { 289 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 290 291 WifiConfiguration config = new WifiConfiguration(); 292 config.SSID = '"' + osuInfo.getSSID() + '"'; 293 if (osuInfo.getOSUBssid() != 0) { 294 config.BSSID = Utils.macToString(osuInfo.getOSUBssid()); 295 Log.d(OSUManager.TAG, String.format("Setting BSSID of '%s' to %012x", 296 osuInfo.getSSID(), osuInfo.getOSUBssid())); 297 } 298 299 if (osuInfo.getOSUProvider().getOsuNai() == null) { 300 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); 301 } else { 302 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OSEN); 303 config.allowedProtocols.set(WifiConfiguration.Protocol.OSEN); 304 config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); 305 config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GTK_NOT_USED); 306 config.enterpriseConfig = new WifiEnterpriseConfig(); 307 config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.UNAUTH_TLS); 308 config.enterpriseConfig.setIdentity(osuInfo.getOSUProvider().getOsuNai()); 309 // !!! OSEN CA Cert??? 310 } 311 312 int networkId = wifiManager.addNetwork(config); 313 if (wifiManager.enableNetwork(networkId, true)) { 314 return networkId; 315 } else { 316 return null; 317 } 318 319 /* sequence of addNetwork(), enableNetwork(), saveConfiguration() and reconnect() 320 wifiManager.connect(config, new WifiManager.ActionListener() { 321 @Override 322 public void onSuccess() { 323 // Connection event comes from network change intent registered in initialize 324 } 325 326 @Override 327 public void onFailure(int reason) { 328 mOSUManager.notifyUser(OSUOperationStatus.ProvisioningFailure, 329 "Cannot connect to OSU network: " + reason, info); 330 } 331 }); 332 return null; 333 334 /* 335 try { 336 int nwkID = wifiManager.addOrUpdateOSUNetwork(config); 337 if (nwkID == WifiConfiguration.INVALID_NETWORK_ID) { 338 throw new IOException("Failed to add OSU network"); 339 } 340 wifiManager.enableNetwork(nwkID, false); 341 wifiManager.reconnect(); 342 return nwkID; 343 } 344 catch (SecurityException se) { 345 Log.d("ZXZ", "Blah: " + se, se); 346 wifiManager.connect(config, new WifiManager.ActionListener() { 347 @Override 348 public void onSuccess() { 349 // Connection event comes from network change intent registered in initialize 350 } 351 352 @Override 353 public void onFailure(int reason) { 354 mOSUManager.notifyUser(OSUOperationStatus.ProvisioningFailure, 355 "Cannot connect to OSU network: " + reason, info); 356 } 357 }); 358 return null; 359 } 360 */ 361 } 362 reconnect(Network osuNetwork, int newNwkId)363 private void reconnect(Network osuNetwork, int newNwkId) { 364 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 365 if (osuNetwork != null) { 366 wifiManager.disableNetwork(osuNetwork.netId); 367 } 368 if (newNwkId != WifiConfiguration.INVALID_NETWORK_ID) { 369 wifiManager.enableNetwork(newNwkId, true); 370 } 371 } 372 deleteNetwork(int id)373 public void deleteNetwork(int id) { 374 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 375 wifiManager.disableNetwork(id); 376 wifiManager.forget(id, null); 377 } 378 379 /** 380 * Set the re-authentication hold off time for the current network 381 * 382 * @param holdoff hold off time in milliseconds 383 * @param ess set if the hold off pertains to an ESS rather than a BSS 384 */ setHoldoffTime(long holdoff, boolean ess)385 public void setHoldoffTime(long holdoff, boolean ess) { 386 387 } 388 } 389