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 Long.MAX_VALUE, /* needed so that scan results aren't rejected because 138 they are older than scan start */ 139 ie, anqpLines, generateIERawDatafromScanResultIE(ie)); 140 results[i] = detail; 141 } 142 return results; 143 } 144 145 /** 146 * Create scan results with no IE information. 147 */ generateNativeResults(int seed, int... freqs)148 public static ScanDetail[] generateNativeResults(int seed, int... freqs) { 149 return generateNativeResults(true, seed, freqs); 150 } 151 152 /** 153 * Create a ScanResults with randomly generated results seeded by the id. 154 * @see #generateNativeResults for more details on how results are generated 155 */ create(int id, int bandScanned, int... freqs)156 public static ScanResults create(int id, int bandScanned, int... freqs) { 157 return create(id, bandScanned, generateNativeResults(id, freqs)); 158 } 159 create(int id, int bandScanned, ScanDetail... nativeResults)160 public static ScanResults create(int id, int bandScanned, 161 ScanDetail... nativeResults) { 162 return new ScanResults(id, bandScanned, -1, nativeResults); 163 } 164 165 /** 166 * Create scan results that contain all results for the native results and 167 * full scan results, but limits the number of onResults results after sorting 168 * by RSSI 169 */ createOverflowing(int id, int bandScanned, int maxResults, ScanDetail... nativeResults)170 public static ScanResults createOverflowing(int id, int bandScanned, int maxResults, 171 ScanDetail... nativeResults) { 172 return new ScanResults(id, bandScanned, maxResults, nativeResults); 173 } 174 ScanResults(int id, int bandScanned, int maxResults, ScanDetail... nativeResults)175 private ScanResults(int id, int bandScanned, int maxResults, ScanDetail... nativeResults) { 176 mScanResults = new ScanResult[nativeResults.length]; 177 for (int i = 0; i < nativeResults.length; ++i) { 178 mScanDetails.add(nativeResults[i]); 179 mScanResults[i] = nativeResults[i].getScanResult(); 180 } 181 ScanResult[] sortedScanResults = Arrays.copyOf(mScanResults, mScanResults.length); 182 Arrays.sort(sortedScanResults, SCAN_RESULT_RSSI_COMPARATOR); 183 mRawScanData = new ScanData(id, 0, 0, bandScanned, sortedScanResults); 184 if (maxResults == -1) { 185 mScanData = mRawScanData; 186 } else { 187 ScanResult[] reducedScanResults = Arrays.copyOf(sortedScanResults, 188 Math.min(sortedScanResults.length, maxResults)); 189 mScanData = new ScanData(id, 0, 0, bandScanned, reducedScanResults); 190 } 191 } 192 getScanDetailArrayList()193 public ArrayList<ScanDetail> getScanDetailArrayList() { 194 return mScanDetails; 195 } 196 getScanData()197 public ScanData getScanData() { 198 return mScanData; 199 } 200 getRawScanResults()201 public ScanResult[] getRawScanResults() { 202 return mScanResults; 203 } 204 getRawScanData()205 public ScanData getRawScanData() { 206 return mRawScanData; 207 } 208 } 209