1 /* 2 * Copyright (C) 2017 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.settings.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.SoftApConfiguration; 28 import android.net.wifi.WifiManager; 29 import android.os.Bundle; 30 import android.os.UserManager; 31 import android.util.FeatureFlagUtils; 32 import android.util.Log; 33 34 import androidx.annotation.VisibleForTesting; 35 import androidx.preference.PreferenceGroup; 36 37 import com.android.settings.R; 38 import com.android.settings.SettingsActivity; 39 import com.android.settings.core.FeatureFlags; 40 import com.android.settings.dashboard.RestrictedDashboardFragment; 41 import com.android.settings.search.BaseSearchIndexProvider; 42 import com.android.settings.widget.SwitchBar; 43 import com.android.settingslib.TetherUtil; 44 import com.android.settingslib.core.AbstractPreferenceController; 45 import com.android.settingslib.search.SearchIndexable; 46 47 import java.util.ArrayList; 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 private static final int EXPANDED_CHILD_COUNT_WITH_SECURITY_NON = 3; 58 private static final int EXPANDED_CHILD_COUNT_DEFAULT = 4; 59 60 @VisibleForTesting 61 static final String KEY_WIFI_TETHER_NETWORK_NAME = "wifi_tether_network_name"; 62 @VisibleForTesting 63 static final String KEY_WIFI_TETHER_NETWORK_PASSWORD = "wifi_tether_network_password"; 64 @VisibleForTesting 65 static final String KEY_WIFI_TETHER_AUTO_OFF = "wifi_tether_auto_turn_off"; 66 @VisibleForTesting 67 static final String KEY_WIFI_TETHER_NETWORK_AP_BAND = "wifi_tether_network_ap_band"; 68 69 private WifiTetherSwitchBarController mSwitchBarController; 70 private WifiTetherSSIDPreferenceController mSSIDPreferenceController; 71 private WifiTetherPasswordPreferenceController mPasswordPreferenceController; 72 private WifiTetherApBandPreferenceController mApBandPreferenceController; 73 private WifiTetherSecurityPreferenceController mSecurityPreferenceController; 74 75 private WifiManager mWifiManager; 76 private boolean mRestartWifiApAfterConfigChange; 77 private boolean mUnavailable; 78 79 @VisibleForTesting 80 TetherChangeReceiver mTetherChangeReceiver; 81 82 static { 83 TETHER_STATE_CHANGE_FILTER = new IntentFilter(ACTION_TETHER_STATE_CHANGED); 84 TETHER_STATE_CHANGE_FILTER.addAction(WIFI_AP_STATE_CHANGED_ACTION); 85 } 86 WifiTetherSettings()87 public WifiTetherSettings() { 88 super(UserManager.DISALLOW_CONFIG_TETHERING); 89 } 90 91 @Override getMetricsCategory()92 public int getMetricsCategory() { 93 return SettingsEnums.WIFI_TETHER_SETTINGS; 94 } 95 96 @Override getLogTag()97 protected String getLogTag() { 98 return "WifiTetherSettings"; 99 } 100 101 @Override onCreate(Bundle icicle)102 public void onCreate(Bundle icicle) { 103 super.onCreate(icicle); 104 setIfOnlyAvailableForAdmins(true); 105 if (isUiRestricted()) { 106 mUnavailable = true; 107 } 108 } 109 110 @Override onAttach(Context context)111 public void onAttach(Context context) { 112 super.onAttach(context); 113 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 114 mTetherChangeReceiver = new TetherChangeReceiver(); 115 116 mSSIDPreferenceController = use(WifiTetherSSIDPreferenceController.class); 117 mSecurityPreferenceController = use(WifiTetherSecurityPreferenceController.class); 118 mPasswordPreferenceController = use(WifiTetherPasswordPreferenceController.class); 119 mApBandPreferenceController = use(WifiTetherApBandPreferenceController.class); 120 } 121 122 @Override onActivityCreated(Bundle savedInstanceState)123 public void onActivityCreated(Bundle savedInstanceState) { 124 super.onActivityCreated(savedInstanceState); 125 if (mUnavailable) { 126 return; 127 } 128 // Assume we are in a SettingsActivity. This is only safe because we currently use 129 // SettingsActivity as base for all preference fragments. 130 final SettingsActivity activity = (SettingsActivity) getActivity(); 131 final SwitchBar switchBar = activity.getSwitchBar(); 132 mSwitchBarController = new WifiTetherSwitchBarController(activity, switchBar); 133 getSettingsLifecycle().addObserver(mSwitchBarController); 134 switchBar.show(); 135 } 136 137 @Override onStart()138 public void onStart() { 139 super.onStart(); 140 if (mUnavailable) { 141 if (!isUiRestrictedByOnlyAdmin()) { 142 getEmptyTextView().setText(R.string.tethering_settings_not_available); 143 } 144 getPreferenceScreen().removeAll(); 145 return; 146 } 147 final Context context = getContext(); 148 if (context != null) { 149 context.registerReceiver(mTetherChangeReceiver, TETHER_STATE_CHANGE_FILTER); 150 } 151 } 152 153 @Override onStop()154 public void onStop() { 155 super.onStop(); 156 if (mUnavailable) { 157 return; 158 } 159 final Context context = getContext(); 160 if (context != null) { 161 context.unregisterReceiver(mTetherChangeReceiver); 162 } 163 } 164 165 166 @Override getPreferenceScreenResId()167 protected int getPreferenceScreenResId() { 168 return R.xml.wifi_tether_settings; 169 } 170 171 @Override createPreferenceControllers(Context context)172 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 173 return buildPreferenceControllers(context, this::onTetherConfigUpdated); 174 } 175 buildPreferenceControllers(Context context, WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener)176 private static List<AbstractPreferenceController> buildPreferenceControllers(Context context, 177 WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener) { 178 final List<AbstractPreferenceController> controllers = new ArrayList<>(); 179 controllers.add(new WifiTetherSSIDPreferenceController(context, listener)); 180 controllers.add(new WifiTetherSecurityPreferenceController(context, listener)); 181 controllers.add(new WifiTetherPasswordPreferenceController(context, listener)); 182 controllers.add(new WifiTetherApBandPreferenceController(context, listener)); 183 controllers.add( 184 new WifiTetherAutoOffPreferenceController(context, KEY_WIFI_TETHER_AUTO_OFF)); 185 186 return controllers; 187 } 188 189 @Override onTetherConfigUpdated(AbstractPreferenceController context)190 public void onTetherConfigUpdated(AbstractPreferenceController context) { 191 final SoftApConfiguration config = buildNewConfig(); 192 mPasswordPreferenceController.updateVisibility(config.getSecurityType()); 193 194 /** 195 * if soft AP is stopped, bring up 196 * else restart with new config 197 * TODO: update config on a running access point when framework support is added 198 */ 199 if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) { 200 Log.d("TetheringSettings", 201 "Wifi AP config changed while enabled, stop and restart"); 202 mRestartWifiApAfterConfigChange = true; 203 mSwitchBarController.stopTether(); 204 } 205 mWifiManager.setSoftApConfiguration(config); 206 207 if (context instanceof WifiTetherSecurityPreferenceController) { 208 reConfigInitialExpandedChildCount(); 209 } 210 } 211 buildNewConfig()212 private SoftApConfiguration buildNewConfig() { 213 final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); 214 final int securityType = mSecurityPreferenceController.getSecurityType(); 215 configBuilder.setSsid(mSSIDPreferenceController.getSSID()); 216 if (securityType == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) { 217 configBuilder.setPassphrase( 218 mPasswordPreferenceController.getPasswordValidated(securityType), 219 SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); 220 } 221 configBuilder.setBand(mApBandPreferenceController.getBandIndex()); 222 return configBuilder.build(); 223 } 224 startTether()225 private void startTether() { 226 mRestartWifiApAfterConfigChange = false; 227 mSwitchBarController.startTether(); 228 } 229 updateDisplayWithNewConfig()230 private void updateDisplayWithNewConfig() { 231 use(WifiTetherSSIDPreferenceController.class) 232 .updateDisplay(); 233 use(WifiTetherSecurityPreferenceController.class) 234 .updateDisplay(); 235 use(WifiTetherPasswordPreferenceController.class) 236 .updateDisplay(); 237 use(WifiTetherApBandPreferenceController.class) 238 .updateDisplay(); 239 } 240 241 public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 242 new BaseSearchIndexProvider(R.xml.wifi_tether_settings) { 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 protected boolean isPageSearchEnabled(Context context) { 262 return !FeatureFlagUtils.isEnabled(context, FeatureFlags.TETHER_ALL_IN_ONE); 263 } 264 265 @Override 266 public List<AbstractPreferenceController> createPreferenceControllers( 267 Context context) { 268 return buildPreferenceControllers(context, null /* listener */); 269 } 270 }; 271 272 @VisibleForTesting 273 class TetherChangeReceiver extends BroadcastReceiver { 274 @Override onReceive(Context content, Intent intent)275 public void onReceive(Context content, Intent intent) { 276 String action = intent.getAction(); 277 Log.d(TAG, "updating display config due to receiving broadcast action " + action); 278 updateDisplayWithNewConfig(); 279 if (action.equals(ACTION_TETHER_STATE_CHANGED)) { 280 if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_DISABLED 281 && mRestartWifiApAfterConfigChange) { 282 startTether(); 283 } 284 } else if (action.equals(WIFI_AP_STATE_CHANGED_ACTION)) { 285 int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 0); 286 if (state == WifiManager.WIFI_AP_STATE_DISABLED 287 && mRestartWifiApAfterConfigChange) { 288 startTether(); 289 } 290 } 291 } 292 } 293 reConfigInitialExpandedChildCount()294 private void reConfigInitialExpandedChildCount() { 295 final PreferenceGroup screen = getPreferenceScreen(); 296 if (mSecurityPreferenceController.getSecurityType() 297 == SoftApConfiguration.SECURITY_TYPE_OPEN) { 298 screen.setInitialExpandedChildrenCount(EXPANDED_CHILD_COUNT_WITH_SECURITY_NON); 299 return; 300 } 301 screen.setInitialExpandedChildrenCount(EXPANDED_CHILD_COUNT_DEFAULT); 302 } 303 304 @Override getInitialExpandedChildCount()305 public int getInitialExpandedChildCount() { 306 if (mSecurityPreferenceController == null) { 307 return EXPANDED_CHILD_COUNT_DEFAULT; 308 } 309 310 return (mSecurityPreferenceController.getSecurityType() 311 == SoftApConfiguration.SECURITY_TYPE_OPEN) 312 ? EXPANDED_CHILD_COUNT_WITH_SECURITY_NON : EXPANDED_CHILD_COUNT_DEFAULT; 313 } 314 } 315