• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ScanResults(ArrayList<ScanDetail> scanDetails, ScanData scanData, ScanResult[] scanResults)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      */
merge(ScanResults... others)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 
generateBssid(Random r)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 
generateSsidIe(String ssid)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      */
generateNativeResults(boolean needIE, int seed, int... freqs)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      */
generateNativeResults(int seed, int... freqs)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      */
create(int id, int... freqs)140     public static ScanResults create(int id, int... freqs) {
141         return new ScanResults(id, -1, generateNativeResults(id, freqs));
142     }
143 
144     /**
145      * Create a ScanResults with no IE information.
146      */
createWithNoIE(int id, int... freqs)147     public static ScanResults createWithNoIE(int id, int... freqs) {
148         return new ScanResults(id, -1, generateNativeResults(false, id, freqs));
149     }
150 
151     /**
152      * Create a ScanResults with the given ScanDetails
153      */
create(int id, ScanDetail... nativeResults)154     public static ScanResults create(int id, ScanDetail... nativeResults) {
155         return new ScanResults(id, -1, nativeResults);
156     }
157 
158     /**
159      * Create scan results that contain all results for the native results and
160      * full scan results, but limits the number of onResults results after sorting
161      * by RSSI
162      */
createOverflowing(int id, int maxResults, ScanDetail... nativeResults)163     public static ScanResults createOverflowing(int id, int maxResults,
164             ScanDetail... nativeResults) {
165         return new ScanResults(id, maxResults, nativeResults);
166     }
167 
ScanResults(int id, int maxResults, ScanDetail... nativeResults)168     private ScanResults(int id, int maxResults, ScanDetail... nativeResults) {
169         mScanResults = new ScanResult[nativeResults.length];
170         for (int i = 0; i < nativeResults.length; ++i) {
171             mScanDetails.add(nativeResults[i]);
172             mScanResults[i] = nativeResults[i].getScanResult();
173         }
174         ScanResult[] sortedScanResults = Arrays.copyOf(mScanResults, mScanResults.length);
175         Arrays.sort(sortedScanResults, SCAN_RESULT_RSSI_COMPARATOR);
176         mRawScanData = new ScanData(id, 0, sortedScanResults);
177         if (maxResults == -1) {
178             mScanData = mRawScanData;
179         } else {
180             ScanResult[] reducedScanResults = Arrays.copyOf(sortedScanResults,
181                     Math.min(sortedScanResults.length, maxResults));
182             mScanData = new ScanData(id, 0, reducedScanResults);
183         }
184     }
185 
getScanDetailArrayList()186     public ArrayList<ScanDetail> getScanDetailArrayList() {
187         return mScanDetails;
188     }
189 
getScanData()190     public ScanData getScanData() {
191         return mScanData;
192     }
193 
getRawScanResults()194     public ScanResult[] getRawScanResults() {
195         return mScanResults;
196     }
197 
getRawScanData()198     public ScanData getRawScanData() {
199         return mRawScanData;
200     }
201 }
202