1 /*
2  * Copyright (C) 2019 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.car.settings.wifi;
18 
19 import static android.net.wifi.SoftApConfiguration.BAND_2GHZ;
20 import static android.net.wifi.SoftApConfiguration.BAND_5GHZ;
21 
22 import android.car.drivingstate.CarUxRestrictions;
23 import android.content.Context;
24 import android.net.wifi.SoftApConfiguration;
25 import android.util.SparseIntArray;
26 
27 import androidx.preference.ListPreference;
28 
29 import com.android.car.settings.Flags;
30 import com.android.car.settings.R;
31 import com.android.car.settings.common.FragmentController;
32 import com.android.internal.annotations.VisibleForTesting;
33 
34 import java.util.LinkedHashMap;
35 import java.util.Map;
36 
37 /**
38  * Controls WiFi Hotspot AP Band configuration.
39  */
40 public class WifiTetherApBandPreferenceController extends
41         WifiTetherBasePreferenceController<ListPreference> {
42     private static final String TAG = "CarWifiTetherApBandPref";
43 
44     /** Wi-Fi hotspot band 2.4GHz and 5GHz. */
45     @VisibleForTesting
46     static final int BAND_2GHZ_5GHZ = BAND_2GHZ | BAND_5GHZ;
47 
48     /** Wi-Fi hotspot dual band for 2.4GHz and 5GHz. */
49     @VisibleForTesting
50     static final int[] DUAL_BANDS = new int[] {
51             BAND_2GHZ,
52             BAND_2GHZ_5GHZ };
53 
54     private final Map<Integer, String> mHotspotBandMap = new LinkedHashMap<>();
55 
56     private int mBand;
57 
WifiTetherApBandPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)58     public WifiTetherApBandPreferenceController(Context context, String preferenceKey,
59             FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
60         this(context, preferenceKey, fragmentController, uxRestrictions,
61                 new CarWifiManager(context, fragmentController.getSettingsLifecycle()));
62     }
63 
64     @VisibleForTesting
WifiTetherApBandPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions, CarWifiManager carWifiManager)65     WifiTetherApBandPreferenceController(Context context, String preferenceKey,
66             FragmentController fragmentController, CarUxRestrictions uxRestrictions,
67             CarWifiManager carWifiManager) {
68         super(context, preferenceKey, fragmentController, uxRestrictions, carWifiManager);
69 
70         String[] bandNames = getContext().getResources().getStringArray(
71                 R.array.wifi_ap_band_summary);
72         String[] bandValues = getContext().getResources().getStringArray(
73                 R.array.wifi_ap_band);
74         for (int i = 0; i < bandNames.length; i++) {
75             mHotspotBandMap.put(Integer.parseInt(bandValues[i]), bandNames[i]);
76         }
77     }
78 
79     @Override
getPreferenceType()80     protected Class<ListPreference> getPreferenceType() {
81         return ListPreference.class;
82     }
83 
84     @Override
onCreateInternal()85     protected void onCreateInternal() {
86         super.onCreateInternal();
87         updatePreferenceEntries();
88     }
89 
90     @Override
updateState(ListPreference preference)91     public void updateState(ListPreference preference) {
92         super.updateState(preference);
93 
94         SoftApConfiguration config = getCarSoftApConfig();
95         int configBand = getBandFromConfig();
96         if (config == null) {
97             mBand = BAND_2GHZ;
98         } else if (!is5GhzBandSupported() && configBand > BAND_2GHZ) {
99             SoftApConfiguration newConfig = new SoftApConfiguration.Builder(config)
100                     .setBand(BAND_2GHZ)
101                     .build();
102             setCarSoftApConfig(newConfig);
103             mBand = BAND_2GHZ;
104         } else {
105             mBand = validateSelection(configBand);
106         }
107 
108         if (!is5GhzBandSupported()) {
109             preference.setEnabled(false);
110             preference.setSummary(R.string.wifi_ap_choose_2G);
111         } else {
112             preference.setValue(Integer.toString(mBand));
113             preference.setSummary(getSummary());
114         }
115     }
116 
117     @Override
getSummary()118     protected String getSummary() {
119         return mHotspotBandMap.getOrDefault(mBand,
120                 getContext().getString(R.string.wifi_ap_prefer_5G));
121     }
122 
123     @Override
getDefaultSummary()124     protected String getDefaultSummary() {
125         return null;
126     }
127 
128     @Override
handlePreferenceChanged(ListPreference preference, Object newValue)129     public boolean handlePreferenceChanged(ListPreference preference, Object newValue) {
130         mBand = validateSelection(Integer.parseInt((String) newValue));
131         updateApBand(); // updating AP band because mBandIndex may have been assigned a new value.
132         refreshUi();
133         return true;
134     }
135 
updatePreferenceEntries()136     private void updatePreferenceEntries() {
137         // If 5 GHz is not supported, default to 2 GHz
138         if (!is5GhzBandSupported()) {
139             mHotspotBandMap.keySet().removeIf(key -> key > BAND_2GHZ);
140         }
141 
142         if (!isDualBandSupported()) {
143             mHotspotBandMap.keySet().removeIf(key -> key == BAND_2GHZ_5GHZ);
144         }
145 
146         // If dual band is supported then there is no need to allow users to select
147         // between 2.4 GHz and 5 GHz since both bands will be available to connect to.
148         if (isDualBandSupported()) {
149             mHotspotBandMap.keySet().removeIf(key -> key < BAND_2GHZ_5GHZ);
150         }
151 
152         getPreference().setEntries(mHotspotBandMap.values().toArray(CharSequence[]::new));
153         getPreference().setEntryValues(
154                 mHotspotBandMap.keySet().stream().map(Object::toString).toArray(
155                         CharSequence[]::new));
156 
157         mBand = validateSelection(getBandFromConfig());
158         getPreference().setValue(Integer.toString(mBand));
159     }
160 
getBandFromConfig()161     private int getBandFromConfig() {
162         int band = 0;
163 
164         SparseIntArray channels = getCarSoftApConfig().getChannels();
165         for (int i = 0; i < channels.size(); i++) {
166             band |= channels.keyAt(i);
167         }
168 
169         if (band == BAND_2GHZ_5GHZ && channels.size() == 1) {
170             // band should be set to BAND_5GHZ to differentiate between dual band which would also
171             // be BAND_2GHZ_5GHZ.
172             band = BAND_5GHZ;
173         }
174 
175         return band;
176     }
177 
validateSelection(int band)178     private int validateSelection(int band) {
179         return mHotspotBandMap.containsKey(band) ? band : BAND_2GHZ;
180     }
181 
updateApBand()182     private void updateApBand() {
183         SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(
184                 getCarSoftApConfig());
185 
186         if (mBand == BAND_5GHZ) {
187             // Only BAND_5GHZ is not supported, must include BAND_2GHZ since some of countries
188             // don't support 5G
189             configBuilder.setBand(BAND_2GHZ_5GHZ);
190         } else if (Flags.hotspotUiSpeedUpdate() && mBand == BAND_2GHZ_5GHZ) {
191             configBuilder.setBands(DUAL_BANDS);
192         } else {
193             configBuilder.setBand(BAND_2GHZ);
194         }
195 
196         setCarSoftApConfig(configBuilder.build());
197         getPreference().setValue(Integer.toString(mBand));
198     }
199 
is5GhzBandSupported()200     private boolean is5GhzBandSupported() {
201         String countryCode = getCarWifiManager().getCountryCode();
202         return getCarWifiManager().is5GhzBandSupported() && countryCode != null;
203     }
204 
isDualBandSupported()205     private boolean isDualBandSupported() {
206         return Flags.hotspotUiSpeedUpdate() && getCarWifiManager().isDualBandSupported();
207     }
208 }
209