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
17package com.android.server.wifi.util;
18
19import android.net.wifi.WifiConfiguration;
20import android.net.wifi.WifiScanner;
21import android.util.Log;
22
23import com.android.server.wifi.WifiNative;
24
25import java.util.ArrayList;
26import java.util.Random;
27
28/**
29 * Provide utility functions for updating soft AP related configuration.
30 */
31public 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     */
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     */
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     */
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