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.util;
  
  import android.net.wifi.WifiConfiguration;
  import android.net.wifi.WifiScanner;
  import android.util.Log;
  
  import com.android.server.wifi.WifiNative;
  
  import java.util.ArrayList;
  import java.util.Random;
  
  /**
   * Provide utility functions for updating soft AP related configuration.
   */
  public class ApConfigUtil {
      private static final String TAG = "ApConfigUtil";
  
      public static final int DEFAULT_AP_BAND = WifiConfiguration.AP_BAND_2GHZ;
      public static final int DEFAULT_AP_CHANNEL = 6;
      public static final int HIGHEST_2G_AP_CHANNEL = 14;
      /* Return code for updateConfiguration. */
      public static final int SUCCESS = 0;
      public static final int ERROR_NO_CHANNEL = 1;
      public static final int ERROR_GENERIC = 2;
  
      /* Random number generator used for AP channel selection. */
      private static final Random sRandom = new Random();
  
      /**
       * Convert frequency to channel.
       * @param frequency frequency to convert
       * @return channel number associated with given frequency, -1 if no match
       */
      public static int convertFrequencyToChannel(int frequency) {
          if (frequency >= 2412 && frequency <= 2472) {
              return (frequency - 2412) / 5 + 1;
          } else if (frequency == 2484) {
              return 14;
          } else if (frequency >= 5170  &&  frequency <= 5825) {
              /* DFS is included. */
              return (frequency - 5170) / 5 + 34;
          }
  
          return -1;
      }
  
      /**
       * Return a channel number for AP setup based on the frequency band.
       * @param apBand one of the value of WifiConfiguration.AP_BAND_*.
       * @param allowed2GChannels list of allowed 2GHz channels
       * @param allowed5GFreqList list of allowed 5GHz frequencies
       * @return a valid channel number on success, -1 on failure.
       */
      public static int chooseApChannel(int apBand,
                                        ArrayList<Integer> allowed2GChannels,
                                        int[] allowed5GFreqList) {
          if (apBand != WifiConfiguration.AP_BAND_2GHZ
                  && apBand != WifiConfiguration.AP_BAND_5GHZ
                          && apBand != WifiConfiguration.AP_BAND_ANY) {
              Log.e(TAG, "Invalid band: " + apBand);
              return -1;
          }
  
          // TODO(b/72120668): Create channel selection logic for AP_BAND_ANY.
          if (apBand == WifiConfiguration.AP_BAND_2GHZ
                  || apBand == WifiConfiguration.AP_BAND_ANY)  {
              /* Select a channel from 2GHz band. */
              if (allowed2GChannels == null || allowed2GChannels.size() == 0) {
                  Log.d(TAG, "2GHz allowed channel list not specified");
                  /* Use default channel. */
                  return DEFAULT_AP_CHANNEL;
              }
  
              /* Pick a random channel. */
              int index = sRandom.nextInt(allowed2GChannels.size());
              return allowed2GChannels.get(index).intValue();
          }
  
          /* 5G without DFS. */
          if (allowed5GFreqList != null && allowed5GFreqList.length > 0) {
              /* Pick a random channel from the list of supported channels. */
              return convertFrequencyToChannel(
                      allowed5GFreqList[sRandom.nextInt(allowed5GFreqList.length)]);
          }
  
          Log.e(TAG, "No available channels on 5GHz band");
          return -1;
      }
  
      /**
       * Update AP band and channel based on the provided country code and band.
       * This will also set
       * @param wifiNative reference to WifiNative
       * @param countryCode country code
       * @param allowed2GChannels list of allowed 2GHz channels
       * @param config configuration to update
       * @return an integer result code
       */
      public static int updateApChannelConfig(WifiNative wifiNative,
                                              String countryCode,
                                              ArrayList<Integer> allowed2GChannels,
                                              WifiConfiguration config) {
          /* Use default band and channel for device without HAL. */
          if (!wifiNative.isHalStarted()) {
              config.apBand = DEFAULT_AP_BAND;
              config.apChannel = DEFAULT_AP_CHANNEL;
              return SUCCESS;
          }
  
          /* Country code is mandatory for 5GHz band. */
          if (config.apBand == WifiConfiguration.AP_BAND_5GHZ
                  && countryCode == null) {
              Log.e(TAG, "5GHz band is not allowed without country code");
              return ERROR_GENERIC;
          }
  
          /* Select a channel if it is not specified. */
          if (config.apChannel == 0) {
              config.apChannel = chooseApChannel(
                      config.apBand, allowed2GChannels,
                      wifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ));
              if (config.apChannel == -1) {
                  /* We're not able to get channel from wificond. */
                  Log.e(TAG, "Failed to get available channel.");
                  return ERROR_NO_CHANNEL;
              }
          }
  
          return SUCCESS;
      }
  }
  