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