1 /*
2  * Copyright (C) 2016 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 package com.android.server.wifi.util;
18 
19 import android.net.wifi.WifiConfiguration;
20 import android.net.wifi.WifiScanner;
21 import android.util.Log;
22 
23 import com.android.server.wifi.WifiNative;
24 
25 import java.util.ArrayList;
26 import java.util.Random;
27 
28 /**
29  * Provide utility functions for updating soft AP related configuration.
30  */
31 public class ApConfigUtil {
32     private static final String TAG = "ApConfigUtil";
33 
34     public static final int DEFAULT_AP_BAND = WifiConfiguration.AP_BAND_2GHZ;
35     public static final int DEFAULT_AP_CHANNEL = 6;
36 
37     /* Return code for updateConfiguration. */
38     public static final int SUCCESS = 0;
39     public static final int ERROR_NO_CHANNEL = 1;
40     public static final int ERROR_GENERIC = 2;
41 
42     /* Random number generator used for AP channel selection. */
43     private static final Random sRandom = new Random();
44 
45     /**
46      * Convert frequency to channel.
47      * @param frequency frequency to convert
48      * @return channel number associated with given frequency, -1 if no match
49      */
convertFrequencyToChannel(int frequency)50     public static int convertFrequencyToChannel(int frequency) {
51         if (frequency >= 2412 && frequency <= 2472) {
52             return (frequency - 2412) / 5 + 1;
53         } else if (frequency == 2484) {
54             return 14;
55         } else if (frequency >= 5170  &&  frequency <= 5825) {
56             /* DFS is included. */
57             return (frequency - 5170) / 5 + 34;
58         }
59 
60         return -1;
61     }
62 
63     /**
64      * Return a channel number for AP setup based on the frequency band.
65      * @param apBand 0 for 2GHz, 1 for 5GHz
66      * @param allowed2GChannels list of allowed 2GHz channels
67      * @param allowed5GFreqList list of allowed 5GHz frequencies
68      * @return a valid channel number on success, -1 on failure.
69      */
chooseApChannel(int apBand, ArrayList<Integer> allowed2GChannels, int[] allowed5GFreqList)70     public static int chooseApChannel(int apBand,
71                                       ArrayList<Integer> allowed2GChannels,
72                                       int[] allowed5GFreqList) {
73         if (apBand != WifiConfiguration.AP_BAND_2GHZ
74                 && apBand != WifiConfiguration.AP_BAND_5GHZ) {
75             Log.e(TAG, "Invalid band: " + apBand);
76             return -1;
77         }
78 
79         if (apBand == WifiConfiguration.AP_BAND_2GHZ)  {
80             /* Select a channel from 2GHz band. */
81             if (allowed2GChannels == null || allowed2GChannels.size() == 0) {
82                 Log.d(TAG, "2GHz allowed channel list not specified");
83                 /* Use default channel. */
84                 return DEFAULT_AP_CHANNEL;
85             }
86 
87             /* Pick a random channel. */
88             int index = sRandom.nextInt(allowed2GChannels.size());
89             return allowed2GChannels.get(index).intValue();
90         }
91 
92         /* 5G without DFS. */
93         if (allowed5GFreqList != null && allowed5GFreqList.length > 0) {
94             /* Pick a random channel from the list of supported channels. */
95             return convertFrequencyToChannel(
96                     allowed5GFreqList[sRandom.nextInt(allowed5GFreqList.length)]);
97         }
98 
99         Log.e(TAG, "No available channels on 5GHz band");
100         return -1;
101     }
102 
103     /**
104      * Update AP band and channel based on the provided country code and band.
105      * This will also set
106      * @param wifiNative reference to WifiNative
107      * @param countryCode country code
108      * @param allowed2GChannels list of allowed 2GHz channels
109      * @param config configuration to update
110      * @return an integer result code
111      */
updateApChannelConfig(WifiNative wifiNative, String countryCode, ArrayList<Integer> allowed2GChannels, WifiConfiguration config)112     public static int updateApChannelConfig(WifiNative wifiNative,
113                                             String countryCode,
114                                             ArrayList<Integer> allowed2GChannels,
115                                             WifiConfiguration config) {
116         /* Use default band and channel for device without HAL. */
117         if (!wifiNative.isHalStarted()) {
118             config.apBand = DEFAULT_AP_BAND;
119             config.apChannel = DEFAULT_AP_CHANNEL;
120             return SUCCESS;
121         }
122 
123         /* Country code is mandatory for 5GHz band. */
124         if (config.apBand == WifiConfiguration.AP_BAND_5GHZ
125                 && countryCode == null) {
126             Log.e(TAG, "5GHz band is not allowed without country code");
127             return ERROR_GENERIC;
128         }
129 
130         /* Select a channel if it is not specified. */
131         if (config.apChannel == 0) {
132             config.apChannel = chooseApChannel(
133                     config.apBand, allowed2GChannels,
134                     wifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ));
135             if (config.apChannel == -1) {
136                 if (wifiNative.isGetChannelsForBandSupported()) {
137                     /* We're not able to get channel when it is supported by HAL. */
138                     Log.e(TAG, "Failed to get available channel.");
139                     return ERROR_NO_CHANNEL;
140                 }
141 
142                 /* Use the default for HAL without get channel support. */
143                 config.apBand = DEFAULT_AP_BAND;
144                 config.apChannel = DEFAULT_AP_CHANNEL;
145             }
146         }
147 
148         return SUCCESS;
149     }
150 }
151