blob: 63b190332de18537e3f977bf23cf5d5e259af9ff [file] [log] [blame]
Jan Nordqvistee699a62016-02-05 15:30:26 -08001package com.android.hotspot2;
2
3import android.content.Context;
4import android.content.Intent;
5import android.net.CaptivePortal;
6import android.net.ConnectivityManager;
7import android.net.ICaptivePortal;
8import android.net.Network;
9import android.net.wifi.WifiConfiguration;
10import android.net.wifi.WifiEnterpriseConfig;
11import android.net.wifi.WifiInfo;
12import android.net.wifi.WifiManager;
13import android.util.Log;
14
15import com.android.configparse.ConfigBuilder;
16import com.android.hotspot2.omadm.MOManager;
17import com.android.hotspot2.omadm.MOTree;
18import com.android.hotspot2.omadm.OMAConstants;
19import com.android.hotspot2.omadm.OMAException;
20import com.android.hotspot2.omadm.OMAParser;
21import com.android.hotspot2.osu.OSUCertType;
22import com.android.hotspot2.osu.OSUInfo;
23import com.android.hotspot2.osu.OSUManager;
24import com.android.hotspot2.osu.commands.MOData;
25import com.android.hotspot2.pps.HomeSP;
26
27import org.xml.sax.SAXException;
28
29import java.io.IOException;
30import java.net.URL;
31import java.security.GeneralSecurityException;
32import java.security.PrivateKey;
33import java.security.cert.X509Certificate;
34import java.util.ArrayList;
35import java.util.Collection;
36import java.util.HashMap;
37import java.util.List;
38import java.util.Map;
39
40public 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
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
61 public WifiConfiguration getWifiConfiguration() {
62 return mWifiConfiguration;
63 }
64
65 public HomeSP getHomeSP() {
66 return mHomeSP;
67 }
68
69 public MOTree getmMOTree() {
70 return mMOTree;
71 }
72 }
73
74 public WifiNetworkAdapter(Context context, OSUManager osuManager) {
75 mOSUManager = osuManager;
76 mContext = context;
77 }
78
79 public void initialize() {
80 loadAllSps();
81 }
82
83 public void networkConfigChange(WifiConfiguration configuration) {
84 loadAllSps();
85 }
86
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
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
110 public MOTree getMOTree(HomeSP homeSP) {
111 PasspointConfig config = mPasspointConfigs.get(homeSP.getFQDN());
112 return config != null ? config.getmMOTree() : null;
113 }
114
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
134 public HomeSP addSP(MOTree instanceTree) throws IOException, SAXException {
135 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
136 String xml = instanceTree.toXml();
Peter Qiu28a3d4452016-11-14 15:18:09 -0800137 // TODO(b/32883320): use the new API for adding Passpoint configuration.
138 return null;
Jan Nordqvistee699a62016-02-05 15:30:26 -0800139 }
140
141 public void removeSP(String fqdn) throws IOException {
142 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
143 }
144
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
151 public Network getCurrentNetwork() {
152 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
153 return wifiManager.getCurrentNetwork();
154 }
155
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
170 public WifiInfo getConnectionInfo() {
171 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
172 return wifiManager.getConnectionInfo();
173 }
174
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
182 public WifiConfiguration getWifiConfig(HomeSP homeSP) {
183 PasspointConfig passpointConfig = mPasspointConfigs.get(homeSP.getFQDN());
184 return passpointConfig != null ? passpointConfig.getWifiConfiguration() : null;
185 }
186
187 public WifiConfiguration getActivePasspointNetwork() {
188 PasspointConfig passpointConfig = getActivePasspointConfig();
189 return passpointConfig != null ? passpointConfig.getWifiConfiguration() : null;
190 }
191
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
206 public HomeSP getCurrentSP() {
207 PasspointConfig passpointConfig = getActivePasspointConfig();
208 return passpointConfig != null ? passpointConfig.getHomeSP() : null;
209 }
210
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
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
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 */
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
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
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 */
385 public void setHoldoffTime(long holdoff, boolean ess) {
386
387 }
388}