blob: 936b914a0093d4cccdefefcdceb1d6025d6950f3 [file] [log] [blame]
/*
* Copyright (C) 2016 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 static android.net.wifi.WifiScanner.WIFI_BAND_24_GHZ;
import static android.net.wifi.WifiScanner.WIFI_BAND_5_GHZ;
import static android.net.wifi.WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY;
import static android.net.wifi.WifiScanner.WIFI_BAND_60_GHZ;
import static android.net.wifi.WifiScanner.WIFI_BAND_6_GHZ;
import static android.net.wifi.WifiScanner.WIFI_BAND_ALL;
import static android.net.wifi.WifiScanner.WIFI_BAND_COUNT;
import static android.net.wifi.WifiScanner.WIFI_BAND_INDEX_24_GHZ;
import static android.net.wifi.WifiScanner.WIFI_BAND_INDEX_5_GHZ;
import static android.net.wifi.WifiScanner.WIFI_BAND_INDEX_5_GHZ_DFS_ONLY;
import static android.net.wifi.WifiScanner.WIFI_BAND_INDEX_60_GHZ;
import static android.net.wifi.WifiScanner.WIFI_BAND_INDEX_6_GHZ;
import static android.net.wifi.WifiScanner.WIFI_BAND_UNSPECIFIED;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiAnnotations.WifiBandBasic;
import android.net.wifi.WifiScanner;
import android.net.wifi.WifiScanner.WifiBandIndex;
import android.util.ArraySet;
import com.android.server.wifi.WifiNative;
import com.android.server.wifi.proto.WifiStatsLog;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* ChannelHelper that offers channel manipulation utilities when the channels in a band are known.
* This allows more fine operations on channels than if band channels are not known.
*/
public class KnownBandsChannelHelper extends ChannelHelper {
// 5G low includes U-NII-1 and Japan 4.9G band
public static final int BAND_5_GHZ_LOW_END_FREQ = 5240;
// 5G middle includes U-NII-2A and U-NII-2C
public static final int BAND_5_GHZ_MID_END_FREQ = 5710;
// 5G high includes U-NII-3
public static final int BAND_5_GHZ_HIGH_END_FREQ = ScanResult.BAND_5_GHZ_END_FREQ_MHZ;
// 6G low includes UNII-5
public static final int BAND_6_GHZ_LOW_END_FREQ = 6425;
// 6G middle includes UNII-6 and UNII-7
public static final int BAND_6_GHZ_MID_END_FREQ = 6875;
// 6G high includes UNII-8
public static final int BAND_6_GHZ_HIGH_END_FREQ = ScanResult.BAND_6_GHZ_END_FREQ_MHZ;
private WifiScanner.ChannelSpec[][] mBandsToChannels;
protected void setBandChannels(int[] channels2G, int[] channels5G, int[] channelsDfs,
int[] channels6G, int[] channels60G) {
mBandsToChannels = new WifiScanner.ChannelSpec[WIFI_BAND_COUNT][];
for (int i = 0; i < WIFI_BAND_COUNT; i++) {
mBandsToChannels[i] = NO_CHANNELS;
}
if (channels2G.length != 0) {
mBandsToChannels[WIFI_BAND_INDEX_24_GHZ] =
new WifiScanner.ChannelSpec[channels2G.length];
copyChannels(mBandsToChannels[WIFI_BAND_INDEX_24_GHZ], channels2G);
} else {
mBandsToChannels[WIFI_BAND_INDEX_24_GHZ] = NO_CHANNELS;
}
if (channels5G.length != 0) {
mBandsToChannels[WIFI_BAND_INDEX_5_GHZ] =
new WifiScanner.ChannelSpec[channels5G.length];
copyChannels(mBandsToChannels[WIFI_BAND_INDEX_5_GHZ], channels5G);
} else {
mBandsToChannels[WIFI_BAND_INDEX_5_GHZ] = NO_CHANNELS;
}
if (channelsDfs.length != 0) {
mBandsToChannels[WIFI_BAND_INDEX_5_GHZ_DFS_ONLY] =
new WifiScanner.ChannelSpec[channelsDfs.length];
copyChannels(mBandsToChannels[WIFI_BAND_INDEX_5_GHZ_DFS_ONLY], channelsDfs);
} else {
mBandsToChannels[WIFI_BAND_INDEX_5_GHZ_DFS_ONLY] = NO_CHANNELS;
}
if (channels6G.length != 0) {
mBandsToChannels[WIFI_BAND_INDEX_6_GHZ] =
new WifiScanner.ChannelSpec[channels6G.length];
copyChannels(mBandsToChannels[WIFI_BAND_INDEX_6_GHZ], channels6G);
} else {
mBandsToChannels[WIFI_BAND_INDEX_6_GHZ] = NO_CHANNELS;
}
if (channels60G.length != 0) {
mBandsToChannels[WIFI_BAND_INDEX_60_GHZ] =
new WifiScanner.ChannelSpec[channels60G.length];
copyChannels(mBandsToChannels[WIFI_BAND_INDEX_60_GHZ], channels60G);
} else {
mBandsToChannels[WIFI_BAND_INDEX_60_GHZ] = NO_CHANNELS;
}
}
private static void copyChannels(
WifiScanner.ChannelSpec[] channelSpec, int[] channels) {
for (int i = 0; i < channels.length; i++) {
channelSpec[i] = new WifiScanner.ChannelSpec(channels[i]);
}
}
@Override
public WifiScanner.ChannelSpec[][] getAvailableScanChannels(int band) {
if (band <= WIFI_BAND_UNSPECIFIED || band > WIFI_BAND_ALL) {
// Invalid value for band.
return null;
}
List<WifiScanner.ChannelSpec[]> channelList = new ArrayList<>();
for (@WifiBandIndex int index = 0; index < WIFI_BAND_COUNT; index++) {
if ((band & (1 << index)) != 0 && mBandsToChannels[index].length > 0) {
channelList.add(mBandsToChannels[index]);
}
}
return channelList.toArray(new WifiScanner.ChannelSpec[0][0]);
}
@Override
public boolean satisfies(ChannelHelper otherChannelHelper) {
if (!(otherChannelHelper instanceof KnownBandsChannelHelper)) return false;
KnownBandsChannelHelper otherKnownBandsChannelHelper =
(KnownBandsChannelHelper) otherChannelHelper;
// Compare all the channels in every band
for (@WifiBandIndex int i = 0; i < WIFI_BAND_COUNT; i++) {
Set<Integer> thisFrequencies = Arrays.stream(mBandsToChannels[i])
.map(spec -> spec.frequency)
.collect(Collectors.toSet());
Set<Integer> otherFrequencies = Arrays.stream(
otherKnownBandsChannelHelper.mBandsToChannels[i])
.map(spec -> spec.frequency)
.collect(Collectors.toSet());
if (!thisFrequencies.containsAll(otherFrequencies)) {
return false;
}
}
return true;
}
@Override
public int estimateScanDuration(WifiScanner.ScanSettings settings) {
if (settings.band == WIFI_BAND_UNSPECIFIED) {
return settings.channels.length * SCAN_PERIOD_PER_CHANNEL_MS;
} else {
WifiScanner.ChannelSpec[][] channels = getAvailableScanChannels(settings.band);
int len = 0;
for (int i = 0; i < channels.length; ++i) {
len += channels[i].length;
}
return len * SCAN_PERIOD_PER_CHANNEL_MS;
}
}
private boolean isDfsChannel(int frequency) {
for (WifiScanner.ChannelSpec dfsChannel :
mBandsToChannels[WIFI_BAND_INDEX_5_GHZ_DFS_ONLY]) {
if (frequency == dfsChannel.frequency) {
return true;
}
}
return false;
}
// TODO this should be rewritten to be based on the input data instead of hardcoded ranges
private int getBandFromChannel(int frequency) {
if (ScanResult.is24GHz(frequency)) {
return WIFI_BAND_24_GHZ;
} else if (ScanResult.is5GHz(frequency)) {
if (isDfsChannel(frequency)) {
return WIFI_BAND_5_GHZ_DFS_ONLY;
} else {
return WIFI_BAND_5_GHZ;
}
} else if (ScanResult.is6GHz(frequency)) {
return WIFI_BAND_6_GHZ;
} else if (ScanResult.is60GHz(frequency)) {
return WIFI_BAND_60_GHZ;
} else {
return WIFI_BAND_UNSPECIFIED;
}
}
private @WifiBandIndex int getIndexForBand(@WifiBandBasic int band) {
switch (band) {
case WIFI_BAND_24_GHZ:
return WIFI_BAND_INDEX_24_GHZ;
case WIFI_BAND_5_GHZ:
return WIFI_BAND_INDEX_5_GHZ;
case WIFI_BAND_5_GHZ_DFS_ONLY:
return WIFI_BAND_INDEX_5_GHZ_DFS_ONLY;
case WIFI_BAND_6_GHZ:
return WIFI_BAND_INDEX_6_GHZ;
case WIFI_BAND_60_GHZ:
return WIFI_BAND_INDEX_60_GHZ;
default:
return -1;
}
}
@Override
public boolean settingsContainChannel(WifiScanner.ScanSettings settings, int channel) {
WifiScanner.ChannelSpec[] settingsChannels;
@WifiBandBasic int band;
// If band is not specified in settings, limit check on channels in settings
if (settings.band == WIFI_BAND_UNSPECIFIED) {
settingsChannels = settings.channels;
} else {
// Get the proper band for this channel
band = getBandFromChannel(channel);
// Check if this band is included in band specified in settings
if ((settings.band & band) == WIFI_BAND_UNSPECIFIED) {
return false;
}
settingsChannels = mBandsToChannels[getIndexForBand(band)];
}
// Now search for the channel
for (int i = 0; i < settingsChannels.length; ++i) {
if (settingsChannels[i].frequency == channel) {
return true;
}
}
return false;
}
/**
* Convert Wifi channel frequency to a bucketed band value.
*
* @param frequency Frequency (e.g. 2417)
* @return WifiBandBucket enum value (e.g. BAND_2G)
*/
public static int getBand(int frequency) {
int band = WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__UNKNOWN;
if (ScanResult.is24GHz(frequency)) {
band = WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_2G;
} else if (ScanResult.is5GHz(frequency)) {
if (frequency <= BAND_5_GHZ_LOW_END_FREQ) {
band = WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_5G_LOW;
} else if (frequency <= BAND_5_GHZ_MID_END_FREQ) {
band = WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_5G_MIDDLE;
} else {
band = WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_5G_HIGH;
}
} else if (ScanResult.is6GHz(frequency)) {
if (frequency <= BAND_6_GHZ_LOW_END_FREQ) {
band = WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_6G_LOW;
} else if (frequency <= BAND_6_GHZ_MID_END_FREQ) {
band = WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_6G_MIDDLE;
} else {
band = WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_6G_HIGH;
}
}
return band;
}
/**
* ChannelCollection that merges channels so that the optimal schedule will be generated.
* When the max channels value is satisfied this implementation will always create a channel
* list that includes no more than the added channels.
*/
public class KnownBandsChannelCollection extends ChannelCollection {
/**
* Stores all channels, including those that belong to added bands.
*/
private final ArraySet<Integer> mChannels = new ArraySet<Integer>();
/**
* Contains only the bands that were explicitly added as bands.
*/
private int mExactBands = 0;
/**
* Contains all bands, including those that were added because an added channel was in that
* band.
*/
private int mAllBands = 0;
@Override
public void addChannel(int frequency) {
mChannels.add(frequency);
mAllBands |= getBandFromChannel(frequency);
}
@Override
public void addBand(int band) {
mExactBands |= band;
mAllBands |= band;
WifiScanner.ChannelSpec[][] bandChannels = getAvailableScanChannels(band);
for (int i = 0; i < bandChannels.length; ++i) {
for (int j = 0; j < bandChannels[i].length; ++j) {
mChannels.add(bandChannels[i][j].frequency);
}
}
}
@Override
public boolean containsChannel(int channel) {
return mChannels.contains(channel);
}
@Override
public boolean containsBand(int band) {
WifiScanner.ChannelSpec[][] bandChannels = getAvailableScanChannels(band);
for (int i = 0; i < bandChannels.length; ++i) {
for (int j = 0; j < bandChannels[i].length; ++j) {
if (!mChannels.contains(bandChannels[i][j].frequency)) {
return false;
}
}
}
return true;
}
@Override
public boolean partiallyContainsBand(int band) {
WifiScanner.ChannelSpec[][] bandChannels = getAvailableScanChannels(band);
for (int i = 0; i < bandChannels.length; ++i) {
for (int j = 0; j < bandChannels[i].length; ++j) {
if (mChannels.contains(bandChannels[i][j].frequency)) {
return true;
}
}
}
return false;
}
@Override
public boolean isEmpty() {
return mChannels.isEmpty();
}
@Override
public boolean isAllChannels() {
return containsBand(WIFI_BAND_ALL);
}
@Override
public void clear() {
mAllBands = 0;
mExactBands = 0;
mChannels.clear();
}
@Override
public Set<Integer> getMissingChannelsFromBand(int band) {
ArraySet<Integer> missingChannels = new ArraySet<>();
WifiScanner.ChannelSpec[][] bandChannels = getAvailableScanChannels(band);
for (int i = 0; i < bandChannels.length; ++i) {
for (int j = 0; j < bandChannels[i].length; ++j) {
if (!mChannels.contains(bandChannels[i][j].frequency)) {
missingChannels.add(bandChannels[i][j].frequency);
}
}
}
return missingChannels;
}
@Override
public Set<Integer> getContainingChannelsFromBand(int band) {
ArraySet<Integer> containingChannels = new ArraySet<>();
WifiScanner.ChannelSpec[][] bandChannels = getAvailableScanChannels(band);
for (int i = 0; i < bandChannels.length; ++i) {
for (int j = 0; j < bandChannels[i].length; ++j) {
if (mChannels.contains(bandChannels[i][j].frequency)) {
containingChannels.add(bandChannels[i][j].frequency);
}
}
}
return containingChannels;
}
@Override
public Set<Integer> getChannelSet() {
if (!isEmpty() && mAllBands != mExactBands) {
return mChannels;
} else {
return new ArraySet<>();
}
}
@Override
public void add6GhzPscChannels() {
Set<Integer> missingChannels = getMissingChannelsFromBand(WIFI_BAND_6_GHZ);
if (missingChannels.isEmpty()) {
return;
}
for (int freq : missingChannels) {
if (ScanResult.is6GHzPsc(freq)) {
mChannels.add(freq);
mAllBands |= WIFI_BAND_6_GHZ;
}
}
}
@Override
public void fillBucketSettings(WifiNative.BucketSettings bucketSettings, int maxChannels) {
if ((mChannels.size() > maxChannels || mAllBands == mExactBands) && mAllBands != 0) {
bucketSettings.band = mAllBands;
bucketSettings.num_channels = 0;
bucketSettings.channels = null;
} else {
bucketSettings.band = WIFI_BAND_UNSPECIFIED;
bucketSettings.num_channels = mChannels.size();
bucketSettings.channels = new WifiNative.ChannelSettings[mChannels.size()];
for (int i = 0; i < mChannels.size(); ++i) {
WifiNative.ChannelSettings channelSettings = new WifiNative.ChannelSettings();
channelSettings.frequency = mChannels.valueAt(i);
bucketSettings.channels[i] = channelSettings;
}
}
}
@Override
public Set<Integer> getScanFreqs() {
if (mExactBands == WIFI_BAND_ALL) {
return null;
} else {
return new ArraySet<Integer>(mChannels);
}
}
public Set<Integer> getAllChannels() {
return new ArraySet<Integer>(mChannels);
}
}
@Override
public KnownBandsChannelCollection createChannelCollection() {
return new KnownBandsChannelCollection();
}
}