/*
* Copyright (C) 2017 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.
*/
#include "chre/apps/wifi_offload/channel_histogram.h"
#include "chre/apps/wifi_offload/utility.h"
namespace wifi_offload {
namespace {
/* Strictly increasing sequence of supported channel numbers in
* 2.4GHz (802.11b/g/n) and 5GHz (802.11a/h/j/n/ac) */
constexpr uint8_t kAllChannels[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
16, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58,
60, 62, 64, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120,
122, 124, 126, 128, 132, 134, 136, 138, 140, 142, 144, 149, 151, 153,
155, 157, 159, 161, 165, 183, 184, 185, 187, 188, 189, 192, 196,
};
static_assert(sizeof(kAllChannels) / sizeof(kAllChannels[0]) ==
ChannelHistogram::kNumChannels,
"some elements unspecified");
/**
* Returns the channel number of a given frequency based on 802.11.
*
* @param frequency Frequncy of the channel in MHz
*
* @return Channel number of the given frequency. Zero if unsupported
* frequency or channel number. Returned value will be in the range of
* [0, 255]
*/
uint8_t GetChannelNumber(uint32_t frequency) {
int channel_number =
utility::Ieee80211FrequencyToChannel(static_cast<int>(frequency));
if (channel_number <= 0 || channel_number > 255) {
LOGE("Unknown channel frequency %" PRIu32 " MHz.", frequency);
channel_number = 0;
}
return static_cast<uint8_t>(channel_number);
}
/**
* Returns the index of a given channel number in kAllChannels.
*
* @param channel_number Channel number we want to map to an index
*
* @return Index of the given channel number in kAllChannels. kNumChannels if
* not found. Returned value will be in the range of [0, kNumChannels]
*/
size_t GetChannelIndex(uint8_t channel_number) {
for (size_t i = 0; i < ChannelHistogram::kNumChannels; i++) {
if (channel_number == kAllChannels[i]) {
return i;
}
}
LOGE("Unsupported channel number: %" PRIu8, channel_number);
return ChannelHistogram::kNumChannels;
}
} // namespace
ChannelHistogram::ChannelHistogram() {
std::memset(scan_count_internal_high_res_, 0,
sizeof(scan_count_internal_high_res_));
}
bool ChannelHistogram::IsSupportedFrequency(uint32_t frequency) {
return GetChannelNumber(frequency) != 0;
}
uint8_t ChannelHistogram::GetChannelScanCount(uint8_t channel_number) const {
size_t index = GetChannelIndex(channel_number);
if (index == kNumChannels) {
return 0;
}
if (scan_count_internal_high_res_[index] == 0) {
return 0;
}
uint32_t max_count = 0;
// since there is at least one non-zero value, max_count won't be 0
for (const auto count : scan_count_internal_high_res_) {
if (max_count < count) {
max_count = count;
}
}
// linearly map from [1,max_count] to [1,255]
uint64_t scaled_value = scan_count_internal_high_res_[index];
scaled_value = scaled_value * 254 / max_count + 1;
return static_cast<uint8_t>(scaled_value);
}
bool ChannelHistogram::IncrementScanCountForFrequency(uint32_t frequency) {
size_t index = GetChannelIndex(GetChannelNumber(frequency));
if (index == kNumChannels) {
return false;
}
scan_count_internal_high_res_[index]++;
return true;
}
bool ChannelHistogram::IncrementScanCountForFrequencyForTest(
uint32_t frequency, uint32_t increase_count) {
return IncrementScanCountForChannelForTest(GetChannelNumber(frequency),
increase_count);
}
bool ChannelHistogram::IncrementScanCountForChannelForTest(
uint8_t channel, uint32_t increase_count) {
size_t index = GetChannelIndex(channel);
if (index == kNumChannels) {
return false;
}
scan_count_internal_high_res_[index] += increase_count;
return true;
}
bool ChannelHistogram::operator==(const ChannelHistogram &other) const {
if (this == &other) {
return true;
}
for (const auto channel : kAllChannels) {
// Compare scaled values, rather than raw values
if (GetChannelScanCount(channel) != other.GetChannelScanCount(channel)) {
return false;
}
}
return true;
}
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> ChannelHistogram::Serialize(
flatbuffers::FlatBufferBuilder *builder) const {
uint8_t lowResScanCount[kNumChannels];
for (size_t i = 0; i < kNumChannels; i++) {
lowResScanCount[i] = GetChannelScanCount(kAllChannels[i]);
}
return builder->CreateVector(lowResScanCount, kNumChannels);
}
bool ChannelHistogram::Deserialize(
const flatbuffers::Vector<uint8_t> &fbs_scan_count) {
if (fbs_scan_count.size() != kNumChannels) {
LOGE("Failed to deserialize ChannelHistogram. Null or incomplete members.");
return false;
}
for (uint8_t i = 0; i < kNumChannels; i++) {
scan_count_internal_high_res_[i] = fbs_scan_count.Get(i);
}
return true;
}
} // namespace wifi_offload