summaryrefslogtreecommitdiffstats
path: root/service/java/com/android/server/wifi/scanner/ScanScheduleUtil.java
blob: 9267aa0f500d3b5c82374bd32db7d96ed154fd97 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.wifi.scanner;

import android.annotation.Nullable;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiScanner.ScanData;
import android.net.wifi.WifiScanner.ScanSettings;

import com.android.server.wifi.WifiNative;

import java.util.ArrayList;
import java.util.List;

/**
 * A class with utilities for dealing with scan schedules.
 */
public class ScanScheduleUtil {

    /**
     * Compares two ChannelSettings for equality.
     */
    public static boolean channelEquals(@Nullable WifiNative.ChannelSettings channel1,
                                         @Nullable WifiNative.ChannelSettings channel2) {
        if (channel1 == null || channel2 == null) return false;
        if (channel1 == channel2) return true;

        if (channel1.frequency != channel2.frequency) return false;
        if (channel1.dwell_time_ms != channel2.dwell_time_ms) return false;
        return channel1.passive == channel2.passive;
    }

    /**
     * Compares two BucketSettings for equality.
     */
    public static boolean bucketEquals(@Nullable WifiNative.BucketSettings bucket1,
                                        @Nullable WifiNative.BucketSettings bucket2) {
        if (bucket1 == null || bucket2 == null) return false;
        if (bucket1 == bucket2) return true;

        if (bucket1.bucket != bucket2.bucket) return false;
        if (bucket1.band != bucket2.band) return false;
        if (bucket1.period_ms != bucket2.period_ms) return false;
        if (bucket1.report_events != bucket2.report_events) return false;
        if (bucket1.num_channels != bucket2.num_channels) return false;
        for (int c = 0; c < bucket1.num_channels; c++) {
            if (!channelEquals(bucket1.channels[c], bucket2.channels[c])) {
                return false;
            }
        }

        return true;
    }

    /**
     * Compares two ScanSettings for equality.
     */
    public static boolean scheduleEquals(@Nullable WifiNative.ScanSettings schedule1,
                                         @Nullable WifiNative.ScanSettings schedule2) {
        if (schedule1 == null || schedule2 == null) return false;
        if (schedule1 == schedule2) return true;

        if (schedule1.base_period_ms != schedule2.base_period_ms) return false;
        if (schedule1.max_ap_per_scan != schedule2.max_ap_per_scan) return false;
        if (schedule1.report_threshold_percent != schedule2.report_threshold_percent) return false;
        if (schedule1.report_threshold_num_scans != schedule2.report_threshold_num_scans) {
            return false;
        }
        if (schedule1.num_buckets != schedule2.num_buckets) return false;
        for (int b = 0; b < schedule1.num_buckets; b++) {
            if (!bucketEquals(schedule1.buckets[b], schedule2.buckets[b])) {
                return false;
            }
        }

        return true;
    }

    /**
     * Check if the specified bucket was scanned. If not all information is available then this
     * method will return true.
     *
     * @param scheduledBucket Index of the bucket to check for, zero indexed, or -1 if any scan
     *                        should be treated as scanning this bucket.
     * @param bucketsScannedBitSet The bitset of all buckets scanned, 0 if unavailable
     */
    private static boolean isBucketMaybeScanned(int scheduledBucket, int bucketsScannedBitSet) {
        if (bucketsScannedBitSet == 0 || scheduledBucket < 0) {
            return true;
        } else {
            return (bucketsScannedBitSet & (1 << scheduledBucket)) != 0;
        }
    }

    /**
     * Check if the specified bucket was scanned. If not all information is available then this
     * method will return false.
     *
     * @param scheduledBucket Index of the bucket to check for, zero indexed, or -1 if any scan
     *                        should be treated as scanning this bucket.
     * @param bucketsScannedBitSet The bitset of all buckets scanned, 0 if unavailable
     */
    private static boolean isBucketDefinitlyScanned(int scheduledBucket, int bucketsScannedBitSet) {
        if (scheduledBucket < 0) {
            return true;
        } else if (bucketsScannedBitSet == 0) {
            return false;
        } else {
            return (bucketsScannedBitSet & (1 << scheduledBucket)) != 0;
        }
    }

    /**
     * Returns true if the given scan result should be reported to a listener with the given
     * settings.
     */
    public static boolean shouldReportFullScanResultForSettings(ChannelHelper channelHelper,
            ScanResult result, int bucketsScanned, ScanSettings settings, int scheduledBucket) {
        if (isBucketMaybeScanned(scheduledBucket, bucketsScanned)) {
            return channelHelper.settingsContainChannel(settings, result.frequency);
        } else {
            return false;
        }
    }

    /**
     * Returns a filtered version of the scan results from the chip that represents only the data
     * requested in the settings. Will return null if the result should not be reported.
     *
     * If a ScanData indicates that the bucket the settings were placed in was scanned then it
     * will always be included (filtered to only include requested channels). If it indicates that
     * the bucket was definitely not scanned then the scan data will not be reported.
     * If it is not possible to determine if the settings bucket was scanned or not then a
     * ScanData will be included if the scan was empty or there was at least one scan result that
     * matches a requested channel (again the results will be filtered to only include requested
     * channels.
     */
    public static ScanData[] filterResultsForSettings(ChannelHelper channelHelper,
            ScanData[] scanDatas, ScanSettings settings, int scheduledBucket) {
        List<ScanData> filteredScanDatas = new ArrayList<>(scanDatas.length);
        List<ScanResult> filteredResults = new ArrayList<>();
        for (ScanData scanData : scanDatas) {
            // only report ScanData if the settings bucket could have been scanned
            if (isBucketMaybeScanned(scheduledBucket, scanData.getBucketsScanned())) {
                filteredResults.clear();
                for (ScanResult scanResult : scanData.getResults()) {
                    if (channelHelper.settingsContainChannel(settings, scanResult.frequency)) {
                        filteredResults.add(scanResult);
                    }
                    if (settings.numBssidsPerScan > 0
                            && filteredResults.size() >= settings.numBssidsPerScan) {
                        break;
                    }
                }
                // will include scan results if the scan was empty, there was at least one
                // one result that matched the scan request or we are sure that all the requested
                // channels were scanned.
                if (filteredResults.size() == scanData.getResults().length) {
                    filteredScanDatas.add(scanData);
                } else if (filteredResults.size() > 0 || isBucketDefinitlyScanned(scheduledBucket,
                                scanData.getBucketsScanned())) {
                    filteredScanDatas.add(new ScanData(scanData.getId(),
                                    scanData.getFlags(),
                                    filteredResults.toArray(
                                            new ScanResult[filteredResults.size()])));
                }
            }
        }
        if (filteredScanDatas.size() == 0) {
            return null;
        } else {
            return filteredScanDatas.toArray(new ScanData[filteredScanDatas.size()]);
        }
    }
}