content: /*
   * 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 android.net.wifi.WifiScanner;
  import android.util.ArraySet;
  
  import com.android.server.wifi.WifiNative;
  
  import java.util.Set;
  
  /**
   * ChannelHelper offers an abstraction for channel manipulation utilities allowing operation to be
   * adjusted based on the amount of information known about the available channels.
   */
  public abstract class ChannelHelper {
  
      // TODO: Currently this is simply an estimate and is used for both active and passive channels
      //       scans. Eventually it should be split between passive and active and perhaps retrieved
      //       from the driver.
      /**
       * The estimated period spent scanning each channel. This is used for estimating scan duration.
       */
      public static final int SCAN_PERIOD_PER_CHANNEL_MS = 200;
  
      protected static final WifiScanner.ChannelSpec[] NO_CHANNELS = new WifiScanner.ChannelSpec[0];
  
      /**
       * Create a new collection that can be used to store channels
       */
      public abstract ChannelCollection createChannelCollection();
  
      /**
       * Return true if the specified channel is expected for a scan with the given settings
       */
      public abstract boolean settingsContainChannel(WifiScanner.ScanSettings settings, int channel);
  
      /**
       * Get the channels that are available for scanning on the supplied band.
       * This method may return empty if the information is not available.
       */
      public abstract WifiScanner.ChannelSpec[] getAvailableScanChannels(int band);
  
      /**
       * Estimates the duration that the chip will spend scanning with the given settings
       */
      public abstract int estimateScanDuration(WifiScanner.ScanSettings settings);
  
      /**
       * Update the channel information that this object has. The source of the update is
       * implementation dependent and may result in no change. Warning the behavior of a
       * ChannelCollection created using {@link #createChannelCollection createChannelCollection} is
       * undefined after calling this method until the {@link ChannelColleciton#clear() clear} method
       * is called on it.
       */
      public void updateChannels() {
          // default implementation does nothing
      }
  
      /**
       * Object that supports accumulation of channels and bands
       */
      public abstract class ChannelCollection {
          /**
           * Add a channel to the collection
           */
          public abstract void addChannel(int channel);
          /**
           * Add all channels in the band to the collection
           */
          public abstract void addBand(int band);
          /**
           * @return true if the collection contains the supplied channel
           */
          public abstract boolean containsChannel(int channel);
          /**
           * @return true if the collection contains all the channels of the supplied band
           */
          public abstract boolean containsBand(int band);
          /**
           * @return true if the collection contains some of the channels of the supplied band
           */
          public abstract boolean partiallyContainsBand(int band);
          /**
           * @return true if the collection contains no channels
           */
          public abstract boolean isEmpty();
          /**
           * @return true if the collection contains all available channels
           */
          public abstract boolean isAllChannels();
          /**
           * Remove all channels from the collection
           */
          public abstract void clear();
          /**
           * Retrieves a list of channels from the band which are missing in the channel collection.
           */
          public abstract Set<Integer> getMissingChannelsFromBand(int band);
          /**
           * Retrieves a list of channels from the band which are contained in the channel collection.
           */
          public abstract Set<Integer> getContainingChannelsFromBand(int band);
          /**
           * Gets a list of channels specified in the current channel collection. This will return
           * an empty set if an entire Band if specified or if the list is empty.
           */
          public abstract Set<Integer> getChannelSet();
  
          /**
           * Add all channels in the ScanSetting to the collection
           */
          public void addChannels(WifiScanner.ScanSettings scanSettings) {
              if (scanSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
                  for (int j = 0; j < scanSettings.channels.length; ++j) {
                      addChannel(scanSettings.channels[j].frequency);
                  }
              } else {
                  addBand(scanSettings.band);
              }
          }
  
          /**
           * Add all channels in the BucketSettings to the collection
           */
          public void addChannels(WifiNative.BucketSettings bucketSettings) {
              if (bucketSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
                  for (int j = 0; j < bucketSettings.channels.length; ++j) {
                      addChannel(bucketSettings.channels[j].frequency);
                  }
              } else {
                  addBand(bucketSettings.band);
              }
          }
  
          /**
           * Checks if all channels in ScanSetting is in the collection
           */
          public boolean containsSettings(WifiScanner.ScanSettings scanSettings) {
              if (scanSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
                  for (int j = 0; j < scanSettings.channels.length; ++j) {
                      if (!containsChannel(scanSettings.channels[j].frequency)) {
                          return false;
                      }
                  }
                  return true;
              } else {
                  return containsBand(scanSettings.band);
              }
          }
  
          /**
           * Checks if at least some of the channels in ScanSetting is in the collection
           */
          public boolean partiallyContainsSettings(WifiScanner.ScanSettings scanSettings) {
              if (scanSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
                  for (int j = 0; j < scanSettings.channels.length; ++j) {
                      if (containsChannel(scanSettings.channels[j].frequency)) {
                          return true;
                      }
                  }
                  return false;
              } else {
                  return partiallyContainsBand(scanSettings.band);
              }
          }
  
          /**
           * Retrieves a list of missing channels in the collection from the provided settings.
           */
          public Set<Integer> getMissingChannelsFromSettings(WifiScanner.ScanSettings scanSettings) {
              if (scanSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
                  ArraySet<Integer> missingChannels = new ArraySet<>();
                  for (int j = 0; j < scanSettings.channels.length; ++j) {
                      if (!containsChannel(scanSettings.channels[j].frequency)) {
                          missingChannels.add(scanSettings.channels[j].frequency);
                      }
                  }
                  return missingChannels;
              } else {
                  return getMissingChannelsFromBand(scanSettings.band);
              }
          }
  
          /**
           * Retrieves a list of containing channels in the collection from the provided settings.
           */
          public Set<Integer> getContainingChannelsFromSettings(
                  WifiScanner.ScanSettings scanSettings) {
              if (scanSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
                  ArraySet<Integer> containingChannels = new ArraySet<>();
                  for (int j = 0; j < scanSettings.channels.length; ++j) {
                      if (containsChannel(scanSettings.channels[j].frequency)) {
                          containingChannels.add(scanSettings.channels[j].frequency);
                      }
                  }
                  return containingChannels;
              } else {
                  return getContainingChannelsFromBand(scanSettings.band);
              }
          }
  
          /**
           * Store the channels in this collection in the supplied BucketSettings. If maxChannels is
           * exceeded or a band better describes the channels then a band is specified instead of a
           * channel list.
           */
          public abstract void fillBucketSettings(WifiNative.BucketSettings bucket, int maxChannels);
  
          /**
           * Gets the list of channels scan. Will either be a collection of all channels or null
           * if all channels should be scanned.
           */
          public abstract Set<Integer> getScanFreqs();
      }
  
  
      /*
       * Utility methods for converting band/channels to strings
       */
  
      /**
       * Create a string representation of the channels in the ScanSettings.
       * If it contains a list of channels then the channels are returned, otherwise a string name of
       * the band is returned.
       */
      public static String toString(WifiScanner.ScanSettings scanSettings) {
          if (scanSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
              return toString(scanSettings.channels);
          } else {
              return bandToString(scanSettings.band);
          }
      }
  
      /**
       * Create a string representation of the channels in the BucketSettings.
       * If it contains a list of channels then the channels are returned, otherwise a string name of
       * the band is returned.
       */
      public static String toString(WifiNative.BucketSettings bucketSettings) {
          if (bucketSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
              return toString(bucketSettings.channels, bucketSettings.num_channels);
          } else {
              return bandToString(bucketSettings.band);
          }
      }
  
      private static String toString(WifiScanner.ChannelSpec[] channels) {
          if (channels == null) {
              return "null";
          }
  
          StringBuilder sb = new StringBuilder();
          sb.append("[");
          for (int c = 0; c < channels.length; c++) {
              sb.append(channels[c].frequency);
              if (c != channels.length - 1) {
                  sb.append(",");
              }
          }
          sb.append("]");
          return sb.toString();
      }
  
      private static String toString(WifiNative.ChannelSettings[] channels, int numChannels) {
          if (channels == null) {
              return "null";
          }
  
          StringBuilder sb = new StringBuilder();
          sb.append("[");
          for (int c = 0; c < numChannels; c++) {
              sb.append(channels[c].frequency);
              if (c != numChannels - 1) {
                  sb.append(",");
              }
          }
          sb.append("]");
          return sb.toString();
      }
  
      /**
       * Converts a WifiScanner.WIFI_BAND_* constant to a meaningful String
       */
      public static String bandToString(int band) {
          switch (band) {
              case WifiScanner.WIFI_BAND_UNSPECIFIED:
                  return "unspecified";
              case WifiScanner.WIFI_BAND_24_GHZ:
                  return "24Ghz";
              case WifiScanner.WIFI_BAND_5_GHZ:
                  return "5Ghz (no DFS)";
              case WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY:
                  return "5Ghz (DFS only)";
              case WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS:
                  return "5Ghz (DFS incl)";
              case WifiScanner.WIFI_BAND_BOTH:
                  return "24Ghz & 5Ghz (no DFS)";
              case WifiScanner.WIFI_BAND_BOTH_WITH_DFS:
                  return "24Ghz & 5Ghz (DFS incl)";
          }
          return "invalid band";
      }
  }
  