Home | History | Annotate | Download | only in osu
      1 package com.android.hotspot2.osu;
      2 
      3 import android.content.ComponentName;
      4 import android.content.Context;
      5 import android.content.Intent;
      6 import android.content.ServiceConnection;
      7 import android.net.wifi.ScanResult;
      8 import android.net.wifi.WifiConfiguration;
      9 import android.net.wifi.WifiInfo;
     10 import android.net.wifi.WifiManager;
     11 import android.os.IBinder;
     12 import android.os.RemoteException;
     13 import android.util.Log;
     14 
     15 import com.android.anqp.HSIconFileElement;
     16 import com.android.anqp.OSUProvider;
     17 import com.android.hotspot2.AppBridge;
     18 import com.android.hotspot2.PasspointMatch;
     19 import com.android.hotspot2.Utils;
     20 import com.android.hotspot2.app.OSUData;
     21 import com.android.hotspot2.flow.FlowService;
     22 import com.android.hotspot2.flow.OSUInfo;
     23 import com.android.hotspot2.osu.service.RemediationHandler;
     24 import com.android.hotspot2.flow.IFlowService;
     25 
     26 import java.io.File;
     27 import java.net.MalformedURLException;
     28 import java.util.ArrayList;
     29 import java.util.Collection;
     30 import java.util.HashMap;
     31 import java.util.HashSet;
     32 import java.util.List;
     33 import java.util.Locale;
     34 import java.util.Map;
     35 import java.util.Set;
     36 import java.util.concurrent.atomic.AtomicInteger;
     37 
     38 public class OSUManager {
     39     public static final String TAG = "OSUMGR";
     40     public static final boolean R2_MOCK = true;
     41     private static final String REMEDIATION_FILE = "remediation.state";
     42 
     43     // Preferred icon parameters
     44     public static final Locale LOCALE = java.util.Locale.getDefault();
     45 
     46     private final AppBridge mAppBridge;
     47     private final Context mContext;
     48     private final IconCache mIconCache;
     49     private final RemediationHandler mRemediationHandler;
     50     private final Set<String> mOSUSSIDs = new HashSet<>();
     51     private final Map<OSUProvider, OSUInfo> mOSUMap = new HashMap<>();
     52     private final AtomicInteger mOSUSequence = new AtomicInteger();
     53 
     54     private final OSUCache mOSUCache;
     55 
     56     public OSUManager(Context context) {
     57         mContext = context;
     58         mAppBridge = new AppBridge(context);
     59         mIconCache = new IconCache(this);
     60         File appFolder = context.getFilesDir();
     61         mRemediationHandler =
     62                 new RemediationHandler(context, new File(appFolder, REMEDIATION_FILE));
     63         mOSUCache = new OSUCache();
     64     }
     65 
     66     public Context getContext() {
     67         return mContext;
     68     }
     69 
     70     public List<OSUData> getAvailableOSUs() {
     71         synchronized (mOSUMap) {
     72             List<OSUData> completeOSUs = new ArrayList<>();
     73             for (OSUInfo osuInfo : mOSUMap.values()) {
     74                 if (osuInfo.getIconStatus() == OSUInfo.IconStatus.Available) {
     75                     completeOSUs.add(new OSUData(osuInfo));
     76                 }
     77             }
     78             return completeOSUs;
     79         }
     80     }
     81 
     82     public void setOSUSelection(int osuID) {
     83         OSUInfo selection = null;
     84         for (OSUInfo osuInfo : mOSUMap.values()) {
     85             if (osuInfo.getOsuID() == osuID &&
     86                     osuInfo.getIconStatus() == OSUInfo.IconStatus.Available) {
     87                 selection = osuInfo;
     88                 break;
     89             }
     90         }
     91 
     92         Log.d(TAG, "Selected OSU ID " + osuID + ": " + selection);
     93 
     94         if (selection == null) {
     95             return;
     96         }
     97 
     98         final OSUInfo osu = selection;
     99 
    100         mContext.bindService(new Intent(mContext, FlowService.class), new ServiceConnection() {
    101             @Override
    102             public void onServiceConnected(ComponentName name, IBinder service) {
    103                 try {
    104                     IFlowService fs = IFlowService.Stub.asInterface(service);
    105                     fs.provision(osu);
    106                 } catch (RemoteException re) {
    107                     Log.e(OSUManager.TAG, "Caught re: " + re);
    108                 }
    109             }
    110 
    111             @Override
    112             public void onServiceDisconnected(ComponentName name) {
    113                 Log.d(OSUManager.TAG, "Service disconnect: " + name);
    114             }
    115         }, Context.BIND_AUTO_CREATE);
    116     }
    117 
    118     public void networkDeleted(final WifiConfiguration configuration) {
    119         if (configuration.FQDN == null) {
    120             return;
    121         }
    122 
    123         mRemediationHandler.networkConfigChange();
    124         mContext.bindService(new Intent(mContext, FlowService.class), new ServiceConnection() {
    125             @Override
    126             public void onServiceConnected(ComponentName name, IBinder service) {
    127                 try {
    128                     IFlowService fs = IFlowService.Stub.asInterface(service);
    129                     fs.spDeleted(configuration.FQDN);
    130                 } catch (RemoteException re) {
    131                     Log.e(OSUManager.TAG, "Caught re: " + re);
    132                 }
    133             }
    134 
    135             @Override
    136             public void onServiceDisconnected(ComponentName name) {
    137 
    138             }
    139         }, Context.BIND_AUTO_CREATE);
    140     }
    141 
    142     public void networkConnectChange(WifiInfo newNetwork) {
    143         mRemediationHandler.newConnection(newNetwork);
    144     }
    145 
    146     public void networkConfigChanged() {
    147         mRemediationHandler.networkConfigChange();
    148     }
    149 
    150     public void wifiStateChange(boolean on) {
    151         if (on) {
    152             return;
    153         }
    154 
    155         // Notify the remediation handler that there are no WiFi networks available.
    156         // Do NOT turn it off though as remediation, per at least this implementation, can take
    157         // place over cellular. The subject of remediation over cellular (when restriction is
    158         // "unrestricted") is not addresses by the WFA spec and direct ask to authors gives no
    159         // distinct answer one way or the other.
    160         mRemediationHandler.newConnection(null);
    161         int current = mOSUMap.size();
    162         mOSUMap.clear();
    163         mOSUCache.clearAll();
    164         mIconCache.tick(true);
    165         if (current > 0) {
    166             notifyOSUCount();
    167         }
    168     }
    169 
    170     public boolean isOSU(String ssid) {
    171         synchronized (mOSUMap) {
    172             return mOSUSSIDs.contains(ssid);
    173         }
    174     }
    175 
    176     public void pushScanResults(Collection<ScanResult> scanResults) {
    177         Map<OSUProvider, ScanResult> results = mOSUCache.pushScanResults(scanResults);
    178         if (results != null) {
    179             updateOSUInfoCache(results);
    180         }
    181         mIconCache.tick(false);
    182     }
    183 
    184     private void updateOSUInfoCache(Map<OSUProvider, ScanResult> results) {
    185         Map<OSUProvider, OSUInfo> osus = new HashMap<>();
    186         for (Map.Entry<OSUProvider, ScanResult> entry : results.entrySet()) {
    187             OSUInfo existing = mOSUMap.get(entry.getKey());
    188             long bssid = Utils.parseMac(entry.getValue().BSSID);
    189 
    190             if (existing == null) {
    191                 osus.put(entry.getKey(), new OSUInfo(entry.getValue(), entry.getKey(),
    192                         mOSUSequence.getAndIncrement()));
    193             } else if (existing.getBSSID() != bssid) {
    194                 HSIconFileElement icon = mIconCache.getIcon(existing);
    195                 if (icon != null && icon.equals(existing.getIconFileElement())) {
    196                     OSUInfo osuInfo = new OSUInfo(entry.getValue(), entry.getKey(),
    197                             existing.getOsuID());
    198                     osuInfo.setIconFileElement(icon, existing.getIconFileName());
    199                     osus.put(entry.getKey(), osuInfo);
    200                 } else {
    201                     osus.put(entry.getKey(), new OSUInfo(entry.getValue(),
    202                             entry.getKey(), mOSUSequence.getAndIncrement()));
    203                 }
    204             } else {
    205                 // Maintain existing entries.
    206                 osus.put(entry.getKey(), existing);
    207             }
    208         }
    209 
    210         mOSUMap.clear();
    211         mOSUMap.putAll(osus);
    212 
    213         mOSUSSIDs.clear();
    214         for (OSUInfo osuInfo : mOSUMap.values()) {
    215             mOSUSSIDs.add(osuInfo.getOsuSsid());
    216         }
    217 
    218         int mods = mIconCache.resolveIcons(mOSUMap.values());
    219 
    220         if (mOSUMap.isEmpty() || mods > 0) {
    221             notifyOSUCount();
    222         }
    223     }
    224 
    225     public void notifyIconReceived(long bssid, String fileName, byte[] data) {
    226         if (mIconCache.notifyIconReceived(bssid, fileName, data) > 0) {
    227             notifyOSUCount();
    228         }
    229     }
    230 
    231     public void doIconQuery(long bssid, String fileName) {
    232         WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
    233         wifiManager.queryPasspointIcon(bssid, fileName);
    234     }
    235 
    236     private void notifyOSUCount() {
    237         int count = 0;
    238         for (OSUInfo existing : mOSUMap.values()) {
    239             if (existing.getIconStatus() == OSUInfo.IconStatus.Available) {
    240                 count++;
    241             }
    242         }
    243         Log.d(TAG, "Latest OSU info: " + count + " with icons, map " + mOSUMap);
    244         mAppBridge.showOsuCount(count);
    245     }
    246 
    247     public void deauth(long bssid, boolean ess, int delay, String url)
    248             throws MalformedURLException {
    249         Log.d(TAG, String.format("De-auth imminent on %s, delay %ss to '%s'",
    250                 ess ? "ess" : "bss", delay, url));
    251         // TODO: Missing framework functionality:
    252         // mWifiNetworkAdapter.setHoldoffTime(delay * Constants.MILLIS_IN_A_SEC, ess);
    253         String spName = mRemediationHandler.getCurrentSpName();
    254         mAppBridge.showDeauth(spName, ess, delay, url);
    255     }
    256 
    257     public void wnmRemediate(final long bssid, final String url, PasspointMatch match) {
    258         mRemediationHandler.wnmReceived(bssid, url);
    259     }
    260 
    261     public void remediationDone(String fqdn, boolean policy) {
    262         mRemediationHandler.remediationDone(fqdn, policy);
    263     }
    264 }
    265