ScanResults.java revision 63539f1283899fbbf83ab90757961b4be51d5034
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
17package com.android.server.wifi;
18
19import android.net.wifi.ScanResult;
20import android.net.wifi.WifiScanner.ScanData;
21import android.net.wifi.WifiSsid;
22
23import com.android.server.wifi.hotspot2.NetworkDetail;
24
25import java.math.BigInteger;
26import java.nio.charset.Charset;
27import java.util.ArrayList;
28import java.util.Arrays;
29import java.util.Comparator;
30import java.util.List;
31import java.util.Random;
32
33/**
34 * Utility for creating scan results from a scan
35 */
36public class ScanResults {
37    private final ArrayList<ScanDetail> mScanDetails = new ArrayList<>();
38    private final ScanData mScanData;
39    private final ScanResult[] mScanResults;
40
41    private static String generateBssid(Random r) {
42        return String.format("%02X:%02X:%02X:%02X:%02X:%02X",
43                r.nextInt(256), r.nextInt(256), r.nextInt(256),
44                r.nextInt(256), r.nextInt(256), r.nextInt(256));
45    }
46
47    private static final Comparator<ScanResult> SCAN_RESULT_RSSI_COMPARATOR =
48            new Comparator<ScanResult>() {
49        public int compare(ScanResult r1, ScanResult r2) {
50            return r2.level - r1.level;
51        }
52    };
53
54    public static ScanResult.InformationElement generateSsidIe(String ssid) {
55        ScanResult.InformationElement ie = new ScanResult.InformationElement();
56        ie.id = ScanResult.InformationElement.EID_SSID;
57        ie.bytes = ssid.getBytes(Charset.forName("UTF-8"));
58        return ie;
59    }
60
61    /**
62     * Generates an array of random ScanDetails with the given frequencies, seeded by the provided
63     * seed value and test method name and class (annotated with @Test). This method will be
64     * consistent between calls in the same test across runs.
65     *
66     * @param seed combined with a hash of the test method this seeds the random number generator
67     * @param freqs list of frequencies for the generated scan results, these will map 1 to 1 to
68     *              to the returned scan details. Duplicates can be specified to create multiple
69     *              ScanDetails with the same frequency.
70     */
71    private static ScanDetail[] generateNativeResults(int seed, int... freqs) {
72        ScanDetail[] results = new ScanDetail[freqs.length];
73        // Seed the results based on the provided seed as well as the test method name
74        // This provides more varied scan results between individual tests that are very similar.
75        Random r = new Random(seed + WifiTestUtil.getTestMethod().hashCode());
76        for (int i = 0; i < freqs.length; ++i) {
77            int freq = freqs[i];
78            String ssid = new BigInteger(128, r).toString(36);
79            String bssid = generateBssid(r);
80            int rssi = r.nextInt(40) - 99; // -99 to -60
81            ScanResult.InformationElement ie[] = new ScanResult.InformationElement[1];
82            ie[0] = generateSsidIe(ssid);
83            List<String> anqpLines = new ArrayList<>();
84            NetworkDetail nd = new NetworkDetail(bssid, ie, anqpLines, freq);
85            ScanDetail detail = new ScanDetail(nd, WifiSsid.createFromAsciiEncoded(ssid),
86                    bssid, "", rssi, freq,
87                    Long.MAX_VALUE, /* needed so that scan results aren't rejected because
88                                        they are older than scan start */
89                    ie, anqpLines);
90            results[i] = detail;
91        }
92        return results;
93    }
94
95    /**
96     * Create a ScanResults with randomly generated results seeded by the id.
97     * @see #generateNativeResults for more details on how results are generated
98     */
99    public static ScanResults create(int id, int... freqs) {
100        return new ScanResults(id, -1, generateNativeResults(id, freqs));
101    }
102
103    /**
104     * Create a ScanResults with the given ScanDetails
105     */
106    public static ScanResults create(int id, ScanDetail... nativeResults) {
107        return new ScanResults(id, -1, nativeResults);
108    }
109
110    /**
111     * Create scan results that contain all results for the native results and
112     * full scan results, but limits the number of onResults results after sorting
113     * by RSSI
114     */
115    public static ScanResults createOverflowing(int id, int maxResults,
116            ScanDetail... nativeResults) {
117        return new ScanResults(id, maxResults, nativeResults);
118    }
119
120    private ScanResults(int id, int maxResults, ScanDetail... nativeResults) {
121        mScanResults = new ScanResult[nativeResults.length];
122        for (int i = 0; i < nativeResults.length; ++i) {
123            mScanDetails.add(nativeResults[i]);
124            mScanResults[i] = nativeResults[i].getScanResult();
125        }
126        ScanResult[] sortedScanResults = Arrays.copyOf(mScanResults, mScanResults.length);
127        Arrays.sort(sortedScanResults, SCAN_RESULT_RSSI_COMPARATOR);
128        if (maxResults == -1) {
129            mScanData = new ScanData(id, 0, sortedScanResults);
130        } else {
131            ScanResult[] reducedScanResults = Arrays.copyOf(sortedScanResults,
132                    Math.min(sortedScanResults.length, maxResults));
133            mScanData = new ScanData(id, 0, reducedScanResults);
134        }
135    }
136
137    public ArrayList<ScanDetail> getScanDetailArrayList() {
138        return mScanDetails;
139    }
140
141    public ScanData getScanData() {
142        return mScanData;
143    }
144
145    public ScanResult[] getRawScanResults() {
146        return mScanResults;
147    }
148}
149