Home | History | Annotate | Download | only in hotspot2
      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 
     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();
    137         wifiManager.addPasspointManagementObject(xml);
    138         return MOManager.buildSP(xml);
    139     }
    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 }
    389