Home | History | Annotate | Download | only in osu
      1 package com.android.hotspot2.osu;
      2 
      3 import android.net.wifi.AnqpInformationElement;
      4 import android.net.wifi.ScanResult;
      5 import android.util.Log;
      6 
      7 import com.android.anqp.Constants;
      8 import com.android.anqp.HSOsuProvidersElement;
      9 import com.android.anqp.OSUProvider;
     10 
     11 import java.net.ProtocolException;
     12 import java.nio.ByteBuffer;
     13 import java.nio.ByteOrder;
     14 import java.util.Collection;
     15 import java.util.HashMap;
     16 import java.util.Map;
     17 import java.util.Set;
     18 
     19 /**
     20  * This class holds a stable set of OSU information as well as scan results based on a trail of
     21  * scan results.
     22  * The purpose of this class is to provide a stable set of information over a a limited span of
     23  * time (SCAN_BATCH_HISTORY_SIZE scan batches) so that OSU entries in the selection list does not
     24  * come and go with temporarily lost scan results.
     25  * The stable set of scan results are used by the remediation flow to retrieve ANQP information
     26  * for the current network to determine whether the currently associated network is a roaming
     27  * network for the Home SP whose timer has currently fired.
     28  */
     29 public class OSUCache {
     30     private static final int SCAN_BATCH_HISTORY_SIZE = 8;
     31 
     32     private int mInstant;
     33     private final Map<OSUProvider, ScanResult> mBatchedOSUs = new HashMap<>();
     34     private final Map<OSUProvider, ScanInstance> mCache = new HashMap<>();
     35 
     36     private static class ScanInstance {
     37         private final ScanResult mScanResult;
     38         private int mInstant;
     39 
     40         private ScanInstance(ScanResult scanResult, int instant) {
     41             mScanResult = scanResult;
     42             mInstant = instant;
     43         }
     44 
     45         public ScanResult getScanResult() {
     46             return mScanResult;
     47         }
     48 
     49         public int getInstant() {
     50             return mInstant;
     51         }
     52 
     53         private boolean bssidEqual(ScanResult scanResult) {
     54             return mScanResult.BSSID.equals(scanResult.BSSID);
     55         }
     56 
     57         private void updateInstant(int newInstant) {
     58             mInstant = newInstant;
     59         }
     60 
     61         @Override
     62         public String toString() {
     63             return mScanResult.SSID + " @ " + mInstant;
     64         }
     65     }
     66 
     67     public OSUCache() {
     68         mInstant = 0;
     69     }
     70 
     71     private void clear() {
     72         mBatchedOSUs.clear();
     73     }
     74 
     75     public void clearAll() {
     76         clear();
     77         mCache.clear();
     78     }
     79 
     80     public Map<OSUProvider, ScanResult> pushScanResults(Collection<ScanResult> scanResults) {
     81         for (ScanResult scanResult : scanResults) {
     82             AnqpInformationElement[] osuInfo = scanResult.anqpElements;
     83             if (osuInfo != null && osuInfo.length > 0) {
     84                 Log.d(OSUManager.TAG, scanResult.SSID +
     85                         " has " + osuInfo.length + " ANQP elements");
     86                 putResult(scanResult, osuInfo);
     87             }
     88         }
     89         return scanEnd();
     90     }
     91 
     92     private void putResult(ScanResult scanResult, AnqpInformationElement[] elements) {
     93         for (AnqpInformationElement ie : elements) {
     94             Log.d(OSUManager.TAG, String.format("ANQP IE %d vid %x size %d", ie.getElementId(),
     95                     ie.getVendorId(), ie.getPayload().length));
     96             if (ie.getElementId() == AnqpInformationElement.HS_OSU_PROVIDERS
     97                     && ie.getVendorId() == AnqpInformationElement.HOTSPOT20_VENDOR_ID) {
     98                 try {
     99                     HSOsuProvidersElement providers = new HSOsuProvidersElement(
    100                             Constants.ANQPElementType.HSOSUProviders,
    101                             ByteBuffer.wrap(ie.getPayload()).order(ByteOrder.LITTLE_ENDIAN));
    102 
    103                     putProviders(scanResult, providers);
    104                 } catch (ProtocolException pe) {
    105                     Log.w(OSUManager.TAG,
    106                             "Failed to parse OSU element: " + pe);
    107                 }
    108             }
    109         }
    110     }
    111 
    112     private void putProviders(ScanResult scanResult, HSOsuProvidersElement osuProviders) {
    113         Log.d(OSUManager.TAG, osuProviders.getProviders().size() + " OSU providers in element");
    114         for (OSUProvider provider : osuProviders.getProviders()) {
    115             // Make a predictive put
    116             ScanResult existing = mBatchedOSUs.put(provider, scanResult);
    117             if (existing != null && existing.level > scanResult.level) {
    118                 // But undo it if the entry already held a better RSSI
    119                 mBatchedOSUs.put(provider, existing);
    120             }
    121         }
    122     }
    123 
    124     private Map<OSUProvider, ScanResult> scanEnd() {
    125         // Update the trail of OSU Providers:
    126         int changes = 0;
    127         Map<OSUProvider, ScanInstance> aged = new HashMap<>(mCache);
    128         for (Map.Entry<OSUProvider, ScanResult> entry : mBatchedOSUs.entrySet()) {
    129             ScanInstance current = aged.remove(entry.getKey());
    130             if (current == null || !current.bssidEqual(entry.getValue())) {
    131                 mCache.put(entry.getKey(), new ScanInstance(entry.getValue(), mInstant));
    132                 changes++;
    133                 if (current == null) {
    134                     Log.d(OSUManager.TAG,
    135                             "Add OSU " + entry.getKey() + " from " + entry.getValue().SSID);
    136                 } else {
    137                     Log.d(OSUManager.TAG, "Update OSU " + entry.getKey() + " with " +
    138                             entry.getValue().SSID + " to " + current);
    139                 }
    140             } else {
    141                 Log.d(OSUManager.TAG, "Existing OSU " + entry.getKey() + ", "
    142                         + current.getInstant() + " -> " + mInstant);
    143                 current.updateInstant(mInstant);
    144             }
    145         }
    146 
    147         for (Map.Entry<OSUProvider, ScanInstance> entry : aged.entrySet()) {
    148             if (mInstant - entry.getValue().getInstant() > SCAN_BATCH_HISTORY_SIZE) {
    149                 Log.d(OSUManager.TAG, "Remove OSU " + entry.getKey() + ", "
    150                         + entry.getValue().getInstant() + " @ " + mInstant);
    151                 mCache.remove(entry.getKey());
    152                 changes++;
    153             }
    154         }
    155 
    156         mInstant++;
    157         clear();
    158 
    159         // Return the latest results if there were any changes from last batch
    160         if (changes > 0) {
    161             Map<OSUProvider, ScanResult> results = new HashMap<>(mCache.size());
    162             for (Map.Entry<OSUProvider, ScanInstance> entry : mCache.entrySet()) {
    163                 results.put(entry.getKey(), entry.getValue().getScanResult());
    164             }
    165             return results;
    166         } else {
    167             return null;
    168         }
    169     }
    170 
    171     private static String toBSSIDStrings(Set<Long> bssids) {
    172         StringBuilder sb = new StringBuilder();
    173         for (Long bssid : bssids) {
    174             sb.append(String.format(" %012x", bssid));
    175         }
    176         return sb.toString();
    177     }
    178 }
    179