1/*
2 * Copyright (C) 2017 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#include "chre/apps/wifi_offload/channel_histogram.h"
18#include "chre/apps/wifi_offload/utility.h"
19
20namespace wifi_offload {
21namespace {
22
23/* Strictly increasing sequence of supported channel numbers in
24 * 2.4GHz (802.11b/g/n) and 5GHz (802.11a/h/j/n/ac) */
25constexpr uint8_t kAllChannels[] = {
26    1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,
27    16,  34,  36,  38,  40,  42,  44,  46,  48,  50,  52,  54,  56,  58,
28    60,  62,  64,  100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120,
29    122, 124, 126, 128, 132, 134, 136, 138, 140, 142, 144, 149, 151, 153,
30    155, 157, 159, 161, 165, 183, 184, 185, 187, 188, 189, 192, 196,
31};
32static_assert(sizeof(kAllChannels) / sizeof(kAllChannels[0]) ==
33                  ChannelHistogram::kNumChannels,
34              "some elements unspecified");
35
36/**
37 * Returns the channel number of a given frequency based on 802.11.
38 *
39 * @param frequency Frequncy of the channel in MHz
40 *
41 * @return Channel number of the given frequency. Zero if unsupported
42 *         frequency or channel number. Returned value will be in the range of
43 *         [0, 255]
44 */
45uint8_t GetChannelNumber(uint32_t frequency) {
46  int channel_number =
47      utility::Ieee80211FrequencyToChannel(static_cast<int>(frequency));
48  if (channel_number <= 0 || channel_number > 255) {
49    LOGE("Unknown channel frequency %" PRIu32 " MHz.", frequency);
50    channel_number = 0;
51  }
52  return static_cast<uint8_t>(channel_number);
53}
54
55/**
56 * Returns the index of a given channel number in kAllChannels.
57 *
58 * @param channel_number Channel number we want to map to an index
59 *
60 * @return Index of the given channel number in kAllChannels. kNumChannels if
61 *         not found. Returned value will be in the range of [0, kNumChannels]
62 */
63size_t GetChannelIndex(uint8_t channel_number) {
64  for (size_t i = 0; i < ChannelHistogram::kNumChannels; i++) {
65    if (channel_number == kAllChannels[i]) {
66      return i;
67    }
68  }
69
70  LOGE("Unsupported channel number: %" PRIu8, channel_number);
71  return ChannelHistogram::kNumChannels;
72}
73
74}  // namespace
75
76ChannelHistogram::ChannelHistogram() {
77  std::memset(scan_count_internal_high_res_, 0,
78              sizeof(scan_count_internal_high_res_));
79}
80
81bool ChannelHistogram::IsSupportedFrequency(uint32_t frequency) {
82  return GetChannelNumber(frequency) != 0;
83}
84
85uint8_t ChannelHistogram::GetChannelScanCount(uint8_t channel_number) const {
86  size_t index = GetChannelIndex(channel_number);
87  if (index == kNumChannels) {
88    return 0;
89  }
90
91  if (scan_count_internal_high_res_[index] == 0) {
92    return 0;
93  }
94
95  uint32_t max_count = 0;
96  // since there is at least one non-zero value, max_count won't be 0
97  for (const auto count : scan_count_internal_high_res_) {
98    if (max_count < count) {
99      max_count = count;
100    }
101  }
102
103  // linearly map from [1,max_count] to [1,255]
104  uint64_t scaled_value = scan_count_internal_high_res_[index];
105  scaled_value = scaled_value * 254 / max_count + 1;
106  return static_cast<uint8_t>(scaled_value);
107}
108
109bool ChannelHistogram::IncrementScanCountForFrequency(uint32_t frequency) {
110  size_t index = GetChannelIndex(GetChannelNumber(frequency));
111  if (index == kNumChannels) {
112    return false;
113  }
114
115  scan_count_internal_high_res_[index]++;
116  return true;
117}
118
119bool ChannelHistogram::IncrementScanCountForFrequencyForTest(
120    uint32_t frequency, uint32_t increase_count) {
121  return IncrementScanCountForChannelForTest(GetChannelNumber(frequency),
122                                             increase_count);
123}
124
125bool ChannelHistogram::IncrementScanCountForChannelForTest(
126    uint8_t channel, uint32_t increase_count) {
127  size_t index = GetChannelIndex(channel);
128  if (index == kNumChannels) {
129    return false;
130  }
131
132  scan_count_internal_high_res_[index] += increase_count;
133  return true;
134}
135
136bool ChannelHistogram::operator==(const ChannelHistogram &other) const {
137  if (this == &other) {
138    return true;
139  }
140
141  for (const auto channel : kAllChannels) {
142    // Compare scaled values, rather than raw values
143    if (GetChannelScanCount(channel) != other.GetChannelScanCount(channel)) {
144      return false;
145    }
146  }
147  return true;
148}
149
150flatbuffers::Offset<flatbuffers::Vector<uint8_t>> ChannelHistogram::Serialize(
151    flatbuffers::FlatBufferBuilder *builder) const {
152  uint8_t lowResScanCount[kNumChannels];
153  for (size_t i = 0; i < kNumChannels; i++) {
154    lowResScanCount[i] = GetChannelScanCount(kAllChannels[i]);
155  }
156  return builder->CreateVector(lowResScanCount, kNumChannels);
157}
158
159bool ChannelHistogram::Deserialize(
160    const flatbuffers::Vector<uint8_t> &fbs_scan_count) {
161  if (fbs_scan_count.size() != kNumChannels) {
162    LOGE("Failed to deserialize ChannelHistogram. Null or incomplete members.");
163    return false;
164  }
165
166  for (uint8_t i = 0; i < kNumChannels; i++) {
167    scan_count_internal_high_res_[i] = fbs_scan_count.Get(i);
168  }
169  return true;
170}
171
172}  // namespace wifi_offload
173