Home | History | Annotate | Download | only in wifi
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.wifi;
     18 
     19 import android.net.wifi.ScanResult;
     20 import android.net.wifi.WifiScanner.ScanData;
     21 import android.net.wifi.WifiSsid;
     22 
     23 import com.android.server.wifi.hotspot2.NetworkDetail;
     24 
     25 import java.math.BigInteger;
     26 import java.nio.charset.Charset;
     27 import java.util.ArrayList;
     28 import java.util.Arrays;
     29 import java.util.Collections;
     30 import java.util.Comparator;
     31 import java.util.List;
     32 import java.util.Random;
     33 
     34 /**
     35  * Utility for creating scan results from a scan
     36  */
     37 public class ScanResults {
     38     private final ArrayList<ScanDetail> mScanDetails = new ArrayList<>();
     39     private final ScanData mScanData;
     40     private final ScanData mRawScanData;
     41     private final ScanResult[] mScanResults;
     42 
     43     private ScanResults(ArrayList<ScanDetail> scanDetails, ScanData scanData,
     44             ScanResult[] scanResults) {
     45         mScanDetails.addAll(scanDetails);
     46         mScanData = scanData;
     47         mRawScanData = scanData;
     48         mScanResults = scanResults;
     49     }
     50 
     51     /**
     52      * Merge the results contained in a number of ScanResults into a single ScanResults
     53      */
     54     public static ScanResults merge(ScanResults... others) {
     55         ArrayList<ScanDetail> scanDetails = new ArrayList<>();
     56         ArrayList<ScanResult> scanDataResults = new ArrayList<>();
     57         ArrayList<ScanResult> rawScanResults = new ArrayList<>();
     58         for (ScanResults other : others) {
     59             scanDetails.addAll(other.getScanDetailArrayList());
     60             scanDataResults.addAll(Arrays.asList(other.getScanData().getResults()));
     61             rawScanResults.addAll(Arrays.asList(other.getRawScanResults()));
     62         }
     63         Collections.sort(scanDataResults, SCAN_RESULT_RSSI_COMPARATOR);
     64         int id = others[0].getScanData().getId();
     65         return new ScanResults(scanDetails, new ScanData(id, 0, scanDataResults
     66                         .toArray(new ScanResult[scanDataResults.size()])),
     67                 rawScanResults.toArray(new ScanResult[rawScanResults.size()]));
     68     }
     69 
     70     private static String generateBssid(Random r) {
     71         return String.format("%02X:%02X:%02X:%02X:%02X:%02X",
     72                 r.nextInt(256), r.nextInt(256), r.nextInt(256),
     73                 r.nextInt(256), r.nextInt(256), r.nextInt(256));
     74     }
     75 
     76     public static final Comparator<ScanResult> SCAN_RESULT_RSSI_COMPARATOR =
     77             new Comparator<ScanResult>() {
     78         public int compare(ScanResult r1, ScanResult r2) {
     79             return r2.level - r1.level;
     80         }
     81     };
     82 
     83     public static ScanResult.InformationElement generateSsidIe(String ssid) {
     84         ScanResult.InformationElement ie = new ScanResult.InformationElement();
     85         ie.id = ScanResult.InformationElement.EID_SSID;
     86         ie.bytes = ssid.getBytes(Charset.forName("UTF-8"));
     87         return ie;
     88     }
     89 
     90     /**
     91      * Generates an array of random ScanDetails with the given frequencies, seeded by the provided
     92      * seed value and test method name and class (annotated with @Test). This method will be
     93      * consistent between calls in the same test across runs.
     94      *
     95      * @param seed combined with a hash of the test method this seeds the random number generator
     96      * @param freqs list of frequencies for the generated scan results, these will map 1 to 1 to
     97      *              to the returned scan details. Duplicates can be specified to create multiple
     98      *              ScanDetails with the same frequency.
     99      */
    100     private static ScanDetail[] generateNativeResults(boolean needIE, int seed, int... freqs) {
    101         ScanDetail[] results = new ScanDetail[freqs.length];
    102         // Seed the results based on the provided seed as well as the test method name
    103         // This provides more varied scan results between individual tests that are very similar.
    104         Random r = new Random(seed + WifiTestUtil.getTestMethod().hashCode());
    105         for (int i = 0; i < freqs.length; ++i) {
    106             int freq = freqs[i];
    107             String ssid = new BigInteger(128, r).toString(36);
    108             String bssid = generateBssid(r);
    109             int rssi = r.nextInt(40) - 99; // -99 to -60
    110             ScanResult.InformationElement[] ie;
    111             if (needIE) {
    112                 ie = new ScanResult.InformationElement[1];
    113                 ie[0] = generateSsidIe(ssid);
    114             } else {
    115                 ie = new ScanResult.InformationElement[0];
    116             }
    117             List<String> anqpLines = new ArrayList<>();
    118             NetworkDetail nd = new NetworkDetail(bssid, ie, anqpLines, freq);
    119             ScanDetail detail = new ScanDetail(nd, WifiSsid.createFromAsciiEncoded(ssid),
    120                     bssid, "", rssi, freq,
    121                     Long.MAX_VALUE, /* needed so that scan results aren't rejected because
    122                                         they are older than scan start */
    123                     ie, anqpLines);
    124             results[i] = detail;
    125         }
    126         return results;
    127     }
    128 
    129     /**
    130      * Create scan results with no IE information.
    131      */
    132     public static ScanDetail[] generateNativeResults(int seed, int... freqs) {
    133         return generateNativeResults(true, seed, freqs);
    134     }
    135 
    136     /**
    137      * Create a ScanResults with randomly generated results seeded by the id.
    138      * @see #generateNativeResults for more details on how results are generated
    139      */
    140     public static ScanResults create(int id, int... freqs) {
    141         return create(id, generateNativeResults(id, freqs));
    142     }
    143     public static ScanResults create(int id, boolean allChannelsScanned, int... freqs) {
    144         return create(id, allChannelsScanned, generateNativeResults(id, freqs));
    145     }
    146 
    147     /**
    148      * Create a ScanResults with no IE information.
    149      */
    150     public static ScanResults createWithNoIE(int id, int... freqs) {
    151         return create(id, generateNativeResults(false, id, freqs));
    152     }
    153 
    154     /**
    155      * Create a ScanResults with the given ScanDetails
    156      */
    157     public static ScanResults create(int id, ScanDetail... nativeResults) {
    158         return new ScanResults(id, false, -1, nativeResults);
    159     }
    160 
    161     public static ScanResults create(int id, boolean allChannelsScanned,
    162             ScanDetail... nativeResults) {
    163         return new ScanResults(id, allChannelsScanned, -1, nativeResults);
    164     }
    165 
    166     /**
    167      * Create scan results that contain all results for the native results and
    168      * full scan results, but limits the number of onResults results after sorting
    169      * by RSSI
    170      */
    171     public static ScanResults createOverflowing(int id, int maxResults,
    172             ScanDetail... nativeResults) {
    173         return new ScanResults(id, false, maxResults, nativeResults);
    174     }
    175 
    176     private ScanResults(int id, boolean allChannelsScanned, int maxResults,
    177             ScanDetail... nativeResults) {
    178         mScanResults = new ScanResult[nativeResults.length];
    179         for (int i = 0; i < nativeResults.length; ++i) {
    180             mScanDetails.add(nativeResults[i]);
    181             mScanResults[i] = nativeResults[i].getScanResult();
    182         }
    183         ScanResult[] sortedScanResults = Arrays.copyOf(mScanResults, mScanResults.length);
    184         Arrays.sort(sortedScanResults, SCAN_RESULT_RSSI_COMPARATOR);
    185         mRawScanData = new ScanData(id, 0, 0, allChannelsScanned, sortedScanResults);
    186         if (maxResults == -1) {
    187             mScanData = mRawScanData;
    188         } else {
    189             ScanResult[] reducedScanResults = Arrays.copyOf(sortedScanResults,
    190                     Math.min(sortedScanResults.length, maxResults));
    191             mScanData = new ScanData(id, 0, 0, allChannelsScanned, reducedScanResults);
    192         }
    193     }
    194 
    195     public ArrayList<ScanDetail> getScanDetailArrayList() {
    196         return mScanDetails;
    197     }
    198 
    199     public ScanData getScanData() {
    200         return mScanData;
    201     }
    202 
    203     public ScanResult[] getRawScanResults() {
    204         return mScanResults;
    205     }
    206 
    207     public ScanData getRawScanData() {
    208         return mRawScanData;
    209     }
    210 }
    211