Home | History | Annotate | Download | only in scanner
      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.scanner;
     18 
     19 import android.annotation.Nullable;
     20 import android.net.wifi.ScanResult;
     21 import android.net.wifi.WifiScanner.ScanData;
     22 import android.net.wifi.WifiScanner.ScanSettings;
     23 
     24 import com.android.server.wifi.WifiNative;
     25 
     26 import java.util.ArrayList;
     27 import java.util.List;
     28 
     29 /**
     30  * A class with utilities for dealing with scan schedules.
     31  */
     32 public class ScanScheduleUtil {
     33 
     34     /**
     35      * Compares two ChannelSettings for equality.
     36      */
     37     public static boolean channelEquals(@Nullable WifiNative.ChannelSettings channel1,
     38                                          @Nullable WifiNative.ChannelSettings channel2) {
     39         if (channel1 == null || channel2 == null) return false;
     40         if (channel1 == channel2) return true;
     41 
     42         if (channel1.frequency != channel2.frequency) return false;
     43         if (channel1.dwell_time_ms != channel2.dwell_time_ms) return false;
     44         return channel1.passive == channel2.passive;
     45     }
     46 
     47     /**
     48      * Compares two BucketSettings for equality.
     49      */
     50     public static boolean bucketEquals(@Nullable WifiNative.BucketSettings bucket1,
     51                                         @Nullable WifiNative.BucketSettings bucket2) {
     52         if (bucket1 == null || bucket2 == null) return false;
     53         if (bucket1 == bucket2) return true;
     54 
     55         if (bucket1.bucket != bucket2.bucket) return false;
     56         if (bucket1.band != bucket2.band) return false;
     57         if (bucket1.period_ms != bucket2.period_ms) return false;
     58         if (bucket1.report_events != bucket2.report_events) return false;
     59         if (bucket1.num_channels != bucket2.num_channels) return false;
     60         for (int c = 0; c < bucket1.num_channels; c++) {
     61             if (!channelEquals(bucket1.channels[c], bucket2.channels[c])) {
     62                 return false;
     63             }
     64         }
     65 
     66         return true;
     67     }
     68 
     69     /**
     70      * Compares two ScanSettings for equality.
     71      */
     72     public static boolean scheduleEquals(@Nullable WifiNative.ScanSettings schedule1,
     73                                          @Nullable WifiNative.ScanSettings schedule2) {
     74         if (schedule1 == null || schedule2 == null) return false;
     75         if (schedule1 == schedule2) return true;
     76 
     77         if (schedule1.base_period_ms != schedule2.base_period_ms) return false;
     78         if (schedule1.max_ap_per_scan != schedule2.max_ap_per_scan) return false;
     79         if (schedule1.report_threshold_percent != schedule2.report_threshold_percent) return false;
     80         if (schedule1.report_threshold_num_scans != schedule2.report_threshold_num_scans) {
     81             return false;
     82         }
     83         if (schedule1.num_buckets != schedule2.num_buckets) return false;
     84         for (int b = 0; b < schedule1.num_buckets; b++) {
     85             if (!bucketEquals(schedule1.buckets[b], schedule2.buckets[b])) {
     86                 return false;
     87             }
     88         }
     89 
     90         return true;
     91     }
     92 
     93     /**
     94      * Check if the specified bucket was scanned. If not all information is available then this
     95      * method will return true.
     96      *
     97      * @param scheduledBucket Index of the bucket to check for, zero indexed, or -1 if any scan
     98      *                        should be treated as scanning this bucket.
     99      * @param bucketsScannedBitSet The bitset of all buckets scanned, 0 if unavailable
    100      */
    101     private static boolean isBucketMaybeScanned(int scheduledBucket, int bucketsScannedBitSet) {
    102         if (bucketsScannedBitSet == 0 || scheduledBucket < 0) {
    103             return true;
    104         } else {
    105             return (bucketsScannedBitSet & (1 << scheduledBucket)) != 0;
    106         }
    107     }
    108 
    109     /**
    110      * Check if the specified bucket was scanned. If not all information is available then this
    111      * method will return false.
    112      *
    113      * @param scheduledBucket Index of the bucket to check for, zero indexed, or -1 if any scan
    114      *                        should be treated as scanning this bucket.
    115      * @param bucketsScannedBitSet The bitset of all buckets scanned, 0 if unavailable
    116      */
    117     private static boolean isBucketDefinitlyScanned(int scheduledBucket, int bucketsScannedBitSet) {
    118         if (scheduledBucket < 0) {
    119             return true;
    120         } else if (bucketsScannedBitSet == 0) {
    121             return false;
    122         } else {
    123             return (bucketsScannedBitSet & (1 << scheduledBucket)) != 0;
    124         }
    125     }
    126 
    127     /**
    128      * Returns true if the given scan result should be reported to a listener with the given
    129      * settings.
    130      */
    131     public static boolean shouldReportFullScanResultForSettings(ChannelHelper channelHelper,
    132             ScanResult result, int bucketsScanned, ScanSettings settings, int scheduledBucket) {
    133         if (isBucketMaybeScanned(scheduledBucket, bucketsScanned)) {
    134             return channelHelper.settingsContainChannel(settings, result.frequency);
    135         } else {
    136             return false;
    137         }
    138     }
    139 
    140     /**
    141      * Returns a filtered version of the scan results from the chip that represents only the data
    142      * requested in the settings. Will return null if the result should not be reported.
    143      *
    144      * If a ScanData indicates that the bucket the settings were placed in was scanned then it
    145      * will always be included (filtered to only include requested channels). If it indicates that
    146      * the bucket was definitely not scanned then the scan data will not be reported.
    147      * If it is not possible to determine if the settings bucket was scanned or not then a
    148      * ScanData will be included if the scan was empty or there was at least one scan result that
    149      * matches a requested channel (again the results will be filtered to only include requested
    150      * channels.
    151      */
    152     public static ScanData[] filterResultsForSettings(ChannelHelper channelHelper,
    153             ScanData[] scanDatas, ScanSettings settings, int scheduledBucket) {
    154         List<ScanData> filteredScanDatas = new ArrayList<>(scanDatas.length);
    155         List<ScanResult> filteredResults = new ArrayList<>();
    156         for (ScanData scanData : scanDatas) {
    157             // only report ScanData if the settings bucket could have been scanned
    158             if (isBucketMaybeScanned(scheduledBucket, scanData.getBucketsScanned())) {
    159                 filteredResults.clear();
    160                 for (ScanResult scanResult : scanData.getResults()) {
    161                     if (channelHelper.settingsContainChannel(settings, scanResult.frequency)) {
    162                         filteredResults.add(scanResult);
    163                     }
    164                     if (settings.numBssidsPerScan > 0
    165                             && filteredResults.size() >= settings.numBssidsPerScan) {
    166                         break;
    167                     }
    168                 }
    169                 // will include scan results if the scan was empty, there was at least one
    170                 // one result that matched the scan request or we are sure that all the requested
    171                 // channels were scanned.
    172                 if (filteredResults.size() == scanData.getResults().length) {
    173                     filteredScanDatas.add(scanData);
    174                 } else if (filteredResults.size() > 0 || isBucketDefinitlyScanned(scheduledBucket,
    175                                 scanData.getBucketsScanned())) {
    176                     filteredScanDatas.add(new ScanData(scanData.getId(),
    177                                     scanData.getFlags(),
    178                                     filteredResults.toArray(
    179                                             new ScanResult[filteredResults.size()])));
    180                 }
    181             }
    182         }
    183         if (filteredScanDatas.size() == 0) {
    184             return null;
    185         } else {
    186             return filteredScanDatas.toArray(new ScanData[filteredScanDatas.size()]);
    187         }
    188     }
    189 }
    190