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 import com.android.server.wifi.util.NativeUtil; 25 26 import java.math.BigInteger; 27 import java.nio.charset.Charset; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.Collections; 31 import java.util.Comparator; 32 import java.util.List; 33 import java.util.Random; 34 35 /** 36 * Utility for creating scan results from a scan 37 */ 38 public class ScanResults { 39 private final ArrayList<ScanDetail> mScanDetails = new ArrayList<>(); 40 private final ScanData mScanData; 41 private final ScanData mRawScanData; 42 private final ScanResult[] mScanResults; 43 ScanResults(ArrayList<ScanDetail> scanDetails, ScanData scanData, ScanResult[] scanResults)44 private ScanResults(ArrayList<ScanDetail> scanDetails, ScanData scanData, 45 ScanResult[] scanResults) { 46 mScanDetails.addAll(scanDetails); 47 mScanData = scanData; 48 mRawScanData = scanData; 49 mScanResults = scanResults; 50 } 51 52 /** 53 * Merge the results contained in a number of ScanResults into a single ScanResults 54 */ merge(int bandScanned, ScanResults... others)55 public static ScanResults merge(int bandScanned, ScanResults... others) { 56 ArrayList<ScanDetail> scanDetails = new ArrayList<>(); 57 ArrayList<ScanResult> scanDataResults = new ArrayList<>(); 58 ArrayList<ScanResult> rawScanResults = new ArrayList<>(); 59 for (ScanResults other : others) { 60 scanDetails.addAll(other.getScanDetailArrayList()); 61 scanDataResults.addAll(Arrays.asList(other.getScanData().getResults())); 62 rawScanResults.addAll(Arrays.asList(other.getRawScanResults())); 63 } 64 Collections.sort(scanDataResults, SCAN_RESULT_RSSI_COMPARATOR); 65 int id = others[0].getScanData().getId(); 66 return new ScanResults(scanDetails, new ScanData(id, 0, 0, bandScanned, scanDataResults 67 .toArray(new ScanResult[scanDataResults.size()])), 68 rawScanResults.toArray(new ScanResult[rawScanResults.size()])); 69 } 70 generateBssid(Random r)71 private static String generateBssid(Random r) { 72 return String.format("%02X:%02X:%02X:%02X:%02X:%02X", 73 r.nextInt(256), r.nextInt(256), r.nextInt(256), 74 r.nextInt(256), r.nextInt(256), r.nextInt(256)); 75 } 76 77 public static final Comparator<ScanResult> SCAN_RESULT_RSSI_COMPARATOR = 78 new Comparator<ScanResult>() { 79 public int compare(ScanResult r1, ScanResult r2) { 80 return r2.level - r1.level; 81 } 82 }; 83 generateSsidIe(String ssid)84 public static ScanResult.InformationElement generateSsidIe(String ssid) { 85 ScanResult.InformationElement ie = new ScanResult.InformationElement(); 86 ie.id = ScanResult.InformationElement.EID_SSID; 87 ie.bytes = ssid.getBytes(Charset.forName("UTF-8")); 88 return ie; 89 } 90 generateIERawDatafromScanResultIE(ScanResult.InformationElement[] ies)91 public static byte[] generateIERawDatafromScanResultIE(ScanResult.InformationElement[] ies) { 92 ArrayList<Byte> ieRawData = new ArrayList<>(); 93 for (int i = 0; i < ies.length; i++) { 94 if (ies[i].id > 255 || ies[i].bytes.length > 255) { 95 break; 96 } 97 ieRawData.add(BigInteger.valueOf(ies[i].id).toByteArray()[0]); 98 ieRawData.add(BigInteger.valueOf(ies[i].bytes.length).toByteArray()[0]); 99 for (int j = 0; j < ies[i].bytes.length; j++) { 100 ieRawData.add(ies[i].bytes[j]); 101 } 102 } 103 return NativeUtil.byteArrayFromArrayList(ieRawData); 104 } 105 106 /** 107 * Generates an array of random ScanDetails with the given frequencies, seeded by the provided 108 * seed value and test method name and class (annotated with @Test). This method will be 109 * consistent between calls in the same test across runs. 110 * 111 * @param seed combined with a hash of the test method this seeds the random number generator 112 * @param freqs list of frequencies for the generated scan results, these will map 1 to 1 to 113 * to the returned scan details. Duplicates can be specified to create multiple 114 * ScanDetails with the same frequency. 115 */ generateNativeResults(boolean needIE, int seed, int... freqs)116 private static ScanDetail[] generateNativeResults(boolean needIE, int seed, int... freqs) { 117 ScanDetail[] results = new ScanDetail[freqs.length]; 118 // Seed the results based on the provided seed as well as the test method name 119 // This provides more varied scan results between individual tests that are very similar. 120 Random r = new Random(seed + WifiTestUtil.getTestMethod().hashCode()); 121 for (int i = 0; i < freqs.length; ++i) { 122 int freq = freqs[i]; 123 String ssid = new BigInteger(128, r).toString(36); 124 String bssid = generateBssid(r); 125 int rssi = r.nextInt(40) - 99; // -99 to -60 126 ScanResult.InformationElement[] ie; 127 if (needIE) { 128 ie = new ScanResult.InformationElement[1]; 129 ie[0] = generateSsidIe(ssid); 130 } else { 131 ie = new ScanResult.InformationElement[0]; 132 } 133 List<String> anqpLines = new ArrayList<>(); 134 NetworkDetail nd = new NetworkDetail(bssid, ie, anqpLines, freq); 135 ScanDetail detail = new ScanDetail(nd, WifiSsid.createFromAsciiEncoded(ssid), 136 bssid, "", rssi, freq, 137 // needed so that scan results aren't rejected because they are older than scan 138 // start. 139 Long.MAX_VALUE, 140 ie, anqpLines, generateIERawDatafromScanResultIE(ie)); 141 results[i] = detail; 142 } 143 return results; 144 } 145 146 /** 147 * Create scan results with no IE information. 148 */ generateNativeResults(int seed, int... freqs)149 public static ScanDetail[] generateNativeResults(int seed, int... freqs) { 150 return generateNativeResults(true, seed, freqs); 151 } 152 153 /** 154 * Create a ScanResults with randomly generated results seeded by the id. 155 * @see #generateNativeResults for more details on how results are generated 156 */ create(int id, int bandScanned, int... freqs)157 public static ScanResults create(int id, int bandScanned, int... freqs) { 158 return create(id, bandScanned, generateNativeResults(id, freqs)); 159 } 160 create(int id, int bandScanned, ScanDetail... nativeResults)161 public static ScanResults create(int id, int bandScanned, 162 ScanDetail... nativeResults) { 163 return new ScanResults(id, bandScanned, -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 */ createOverflowing(int id, int bandScanned, int maxResults, ScanDetail... nativeResults)171 public static ScanResults createOverflowing(int id, int bandScanned, int maxResults, 172 ScanDetail... nativeResults) { 173 return new ScanResults(id, bandScanned, maxResults, nativeResults); 174 } 175 ScanResults(int id, int bandScanned, int maxResults, ScanDetail... nativeResults)176 private ScanResults(int id, int bandScanned, int maxResults, ScanDetail... nativeResults) { 177 mScanResults = new ScanResult[nativeResults.length]; 178 for (int i = 0; i < nativeResults.length; ++i) { 179 mScanDetails.add(nativeResults[i]); 180 mScanResults[i] = nativeResults[i].getScanResult(); 181 } 182 ScanResult[] sortedScanResults = Arrays.copyOf(mScanResults, mScanResults.length); 183 Arrays.sort(sortedScanResults, SCAN_RESULT_RSSI_COMPARATOR); 184 mRawScanData = new ScanData(id, 0, 0, bandScanned, sortedScanResults); 185 if (maxResults == -1) { 186 mScanData = mRawScanData; 187 } else { 188 ScanResult[] reducedScanResults = Arrays.copyOf(sortedScanResults, 189 Math.min(sortedScanResults.length, maxResults)); 190 mScanData = new ScanData(id, 0, 0, bandScanned, reducedScanResults); 191 } 192 } 193 getScanDetailArrayList()194 public ArrayList<ScanDetail> getScanDetailArrayList() { 195 return mScanDetails; 196 } 197 getScanData()198 public ScanData getScanData() { 199 return mScanData; 200 } 201 getRawScanResults()202 public ScanResult[] getRawScanResults() { 203 return mScanResults; 204 } 205 getRawScanData()206 public ScanData getRawScanData() { 207 return mRawScanData; 208 } 209 } 210