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.developeroptions.wifi.tether; 18 19 import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; 20 import static android.net.wifi.WifiManager.WIFI_AP_STATE_CHANGED_ACTION; 21 22 import android.app.settings.SettingsEnums; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.net.wifi.WifiConfiguration; 28 import android.net.wifi.WifiManager; 29 import android.os.Bundle; 30 import android.os.UserManager; 31 import android.provider.SearchIndexableResource; 32 import android.util.Log; 33 34 import androidx.annotation.VisibleForTesting; 35 36 import com.android.car.developeroptions.R; 37 import com.android.car.developeroptions.SettingsActivity; 38 import com.android.car.developeroptions.dashboard.RestrictedDashboardFragment; 39 import com.android.car.developeroptions.search.BaseSearchIndexProvider; 40 import com.android.car.developeroptions.widget.SwitchBar; 41 import com.android.car.developeroptions.widget.SwitchBarController; 42 import com.android.settingslib.TetherUtil; 43 import com.android.settingslib.core.AbstractPreferenceController; 44 import com.android.settingslib.search.SearchIndexable; 45 46 import java.util.ArrayList; 47 import java.util.Arrays; 48 import java.util.List; 49 50 @SearchIndexable 51 public class WifiTetherSettings extends RestrictedDashboardFragment 52 implements WifiTetherBasePreferenceController.OnTetherConfigUpdateListener { 53 54 private static final String TAG = "WifiTetherSettings"; 55 private static final IntentFilter TETHER_STATE_CHANGE_FILTER; 56 private static final String KEY_WIFI_TETHER_SCREEN = "wifi_tether_settings_screen"; 57 @VisibleForTesting 58 static final String KEY_WIFI_TETHER_NETWORK_NAME = "wifi_tether_network_name"; 59 @VisibleForTesting 60 static final String KEY_WIFI_TETHER_NETWORK_PASSWORD = "wifi_tether_network_password"; 61 @VisibleForTesting 62 static final String KEY_WIFI_TETHER_AUTO_OFF = "wifi_tether_auto_turn_off"; 63 @VisibleForTesting 64 static final String KEY_WIFI_TETHER_NETWORK_AP_BAND = "wifi_tether_network_ap_band"; 65 66 private WifiTetherSwitchBarController mSwitchBarController; 67 private WifiTetherSSIDPreferenceController mSSIDPreferenceController; 68 private WifiTetherPasswordPreferenceController mPasswordPreferenceController; 69 private WifiTetherApBandPreferenceController mApBandPreferenceController; 70 private WifiTetherSecurityPreferenceController mSecurityPreferenceController; 71 72 private WifiManager mWifiManager; 73 private boolean mRestartWifiApAfterConfigChange; 74 private boolean mUnavailable; 75 76 @VisibleForTesting 77 TetherChangeReceiver mTetherChangeReceiver; 78 79 static { 80 TETHER_STATE_CHANGE_FILTER = new IntentFilter(ACTION_TETHER_STATE_CHANGED); 81 TETHER_STATE_CHANGE_FILTER.addAction(WIFI_AP_STATE_CHANGED_ACTION); 82 } 83 WifiTetherSettings()84 public WifiTetherSettings() { 85 super(UserManager.DISALLOW_CONFIG_TETHERING); 86 } 87 88 @Override getMetricsCategory()89 public int getMetricsCategory() { 90 return SettingsEnums.WIFI_TETHER_SETTINGS; 91 } 92 93 @Override getLogTag()94 protected String getLogTag() { 95 return "WifiTetherSettings"; 96 } 97 98 @Override onCreate(Bundle icicle)99 public void onCreate(Bundle icicle) { 100 super.onCreate(icicle); 101 setIfOnlyAvailableForAdmins(true); 102 if (isUiRestricted()) { 103 mUnavailable = true; 104 } 105 } 106 107 @Override onAttach(Context context)108 public void onAttach(Context context) { 109 super.onAttach(context); 110 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 111 mTetherChangeReceiver = new TetherChangeReceiver(); 112 113 mSSIDPreferenceController = use(WifiTetherSSIDPreferenceController.class); 114 mSecurityPreferenceController = use(WifiTetherSecurityPreferenceController.class); 115 mPasswordPreferenceController = use(WifiTetherPasswordPreferenceController.class); 116 mApBandPreferenceController = use(WifiTetherApBandPreferenceController.class); 117 } 118 119 @Override onActivityCreated(Bundle savedInstanceState)120 public void onActivityCreated(Bundle savedInstanceState) { 121 super.onActivityCreated(savedInstanceState); 122 if (mUnavailable) { 123 return; 124 } 125 // Assume we are in a SettingsActivity. This is only safe because we currently use 126 // SettingsActivity as base for all preference fragments. 127 final SettingsActivity activity = (SettingsActivity) getActivity(); 128 final SwitchBar switchBar = activity.getSwitchBar(); 129 mSwitchBarController = new WifiTetherSwitchBarController(activity, 130 new SwitchBarController(switchBar)); 131 getSettingsLifecycle().addObserver(mSwitchBarController); 132 switchBar.show(); 133 } 134 135 @Override onStart()136 public void onStart() { 137 super.onStart(); 138 if (mUnavailable) { 139 if (!isUiRestrictedByOnlyAdmin()) { 140 getEmptyTextView().setText(R.string.tethering_settings_not_available); 141 } 142 getPreferenceScreen().removeAll(); 143 return; 144 } 145 final Context context = getContext(); 146 if (context != null) { 147 context.registerReceiver(mTetherChangeReceiver, TETHER_STATE_CHANGE_FILTER); 148 } 149 } 150 151 @Override onStop()152 public void onStop() { 153 super.onStop(); 154 if (mUnavailable) { 155 return; 156 } 157 final Context context = getContext(); 158 if (context != null) { 159 context.unregisterReceiver(mTetherChangeReceiver); 160 } 161 } 162 163 164 @Override getPreferenceScreenResId()165 protected int getPreferenceScreenResId() { 166 return R.xml.wifi_tether_settings; 167 } 168 169 @Override createPreferenceControllers(Context context)170 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 171 return buildPreferenceControllers(context, this::onTetherConfigUpdated); 172 } 173 buildPreferenceControllers(Context context, WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener)174 private static List<AbstractPreferenceController> buildPreferenceControllers(Context context, 175 WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener) { 176 final List<AbstractPreferenceController> controllers = new ArrayList<>(); 177 controllers.add(new WifiTetherSSIDPreferenceController(context, listener)); 178 controllers.add(new WifiTetherSecurityPreferenceController(context, listener)); 179 controllers.add(new WifiTetherPasswordPreferenceController(context, listener)); 180 controllers.add(new WifiTetherApBandPreferenceController(context, listener)); 181 controllers.add( 182 new WifiTetherAutoOffPreferenceController(context, KEY_WIFI_TETHER_AUTO_OFF)); 183 184 return controllers; 185 } 186 187 @Override onTetherConfigUpdated()188 public void onTetherConfigUpdated() { 189 final WifiConfiguration config = buildNewConfig(); 190 mPasswordPreferenceController.updateVisibility(config.getAuthType()); 191 192 /** 193 * if soft AP is stopped, bring up 194 * else restart with new config 195 * TODO: update config on a running access point when framework support is added 196 */ 197 if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) { 198 Log.d("TetheringSettings", 199 "Wifi AP config changed while enabled, stop and restart"); 200 mRestartWifiApAfterConfigChange = true; 201 mSwitchBarController.stopTether(); 202 } 203 mWifiManager.setWifiApConfiguration(config); 204 } 205 buildNewConfig()206 private WifiConfiguration buildNewConfig() { 207 final WifiConfiguration config = new WifiConfiguration(); 208 final int securityType = mSecurityPreferenceController.getSecurityType(); 209 210 config.SSID = mSSIDPreferenceController.getSSID(); 211 config.allowedKeyManagement.set(securityType); 212 config.preSharedKey = mPasswordPreferenceController.getPasswordValidated(securityType); 213 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); 214 config.apBand = mApBandPreferenceController.getBandIndex(); 215 return config; 216 } 217 startTether()218 private void startTether() { 219 mRestartWifiApAfterConfigChange = false; 220 mSwitchBarController.startTether(); 221 } 222 updateDisplayWithNewConfig()223 private void updateDisplayWithNewConfig() { 224 use(WifiTetherSSIDPreferenceController.class) 225 .updateDisplay(); 226 use(WifiTetherSecurityPreferenceController.class) 227 .updateDisplay(); 228 use(WifiTetherPasswordPreferenceController.class) 229 .updateDisplay(); 230 use(WifiTetherApBandPreferenceController.class) 231 .updateDisplay(); 232 } 233 234 public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 235 new BaseSearchIndexProvider() { 236 @Override 237 public List<SearchIndexableResource> getXmlResourcesToIndex( 238 Context context, boolean enabled) { 239 final SearchIndexableResource sir = new SearchIndexableResource(context); 240 sir.xmlResId = R.xml.wifi_tether_settings; 241 return Arrays.asList(sir); 242 } 243 244 @Override 245 public List<String> getNonIndexableKeys(Context context) { 246 final List<String> keys = super.getNonIndexableKeys(context); 247 248 if (!TetherUtil.isTetherAvailable(context)) { 249 keys.add(KEY_WIFI_TETHER_NETWORK_NAME); 250 keys.add(KEY_WIFI_TETHER_NETWORK_PASSWORD); 251 keys.add(KEY_WIFI_TETHER_AUTO_OFF); 252 keys.add(KEY_WIFI_TETHER_NETWORK_AP_BAND); 253 } 254 255 // Remove duplicate 256 keys.add(KEY_WIFI_TETHER_SCREEN); 257 return keys; 258 } 259 260 @Override 261 public List<AbstractPreferenceController> createPreferenceControllers( 262 Context context) { 263 return buildPreferenceControllers(context, null /* listener */); 264 } 265 }; 266 267 @VisibleForTesting 268 class TetherChangeReceiver extends BroadcastReceiver { 269 @Override onReceive(Context content, Intent intent)270 public void onReceive(Context content, Intent intent) { 271 String action = intent.getAction(); 272 Log.d(TAG, "updating display config due to receiving broadcast action " + action); 273 updateDisplayWithNewConfig(); 274 if (action.equals(ACTION_TETHER_STATE_CHANGED)) { 275 if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_DISABLED 276 && mRestartWifiApAfterConfigChange) { 277 startTether(); 278 } 279 } else if (action.equals(WIFI_AP_STATE_CHANGED_ACTION)) { 280 int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 0); 281 if (state == WifiManager.WIFI_AP_STATE_DISABLED 282 && mRestartWifiApAfterConfigChange) { 283 startTether(); 284 } 285 } 286 } 287 } 288 } 289