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.TetheringManager.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.SettingsMainSwitchBar; 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_DEFAULT = 3; 58 59 @VisibleForTesting 60 static final String KEY_WIFI_TETHER_NETWORK_NAME = "wifi_tether_network_name"; 61 @VisibleForTesting 62 static final String KEY_WIFI_TETHER_NETWORK_PASSWORD = "wifi_tether_network_password"; 63 @VisibleForTesting 64 static final String KEY_WIFI_TETHER_AUTO_OFF = "wifi_tether_auto_turn_off"; 65 @VisibleForTesting 66 static final String KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY = 67 WifiTetherMaximizeCompatibilityPreferenceController.PREF_KEY; 68 69 private WifiTetherSwitchBarController mSwitchBarController; 70 private WifiTetherSSIDPreferenceController mSSIDPreferenceController; 71 private WifiTetherPasswordPreferenceController mPasswordPreferenceController; 72 private WifiTetherSecurityPreferenceController mSecurityPreferenceController; 73 private WifiTetherMaximizeCompatibilityPreferenceController mMaxCompatibilityPrefController; 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 mMaxCompatibilityPrefController = 120 use(WifiTetherMaximizeCompatibilityPreferenceController.class); 121 } 122 123 @Override onActivityCreated(Bundle savedInstanceState)124 public void onActivityCreated(Bundle savedInstanceState) { 125 super.onActivityCreated(savedInstanceState); 126 if (mUnavailable) { 127 return; 128 } 129 // Assume we are in a SettingsActivity. This is only safe because we currently use 130 // SettingsActivity as base for all preference fragments. 131 final SettingsActivity activity = (SettingsActivity) getActivity(); 132 final SettingsMainSwitchBar switchBar = activity.getSwitchBar(); 133 switchBar.setTitle(getContext().getString(R.string.use_wifi_hotsopt_main_switch_title)); 134 mSwitchBarController = new WifiTetherSwitchBarController(activity, switchBar); 135 getSettingsLifecycle().addObserver(mSwitchBarController); 136 switchBar.show(); 137 } 138 139 @Override onStart()140 public void onStart() { 141 super.onStart(); 142 if (mUnavailable) { 143 if (!isUiRestrictedByOnlyAdmin()) { 144 getEmptyTextView().setText(R.string.tethering_settings_not_available); 145 } 146 getPreferenceScreen().removeAll(); 147 return; 148 } 149 final Context context = getContext(); 150 if (context != null) { 151 context.registerReceiver(mTetherChangeReceiver, TETHER_STATE_CHANGE_FILTER); 152 } 153 } 154 155 @Override onStop()156 public void onStop() { 157 super.onStop(); 158 if (mUnavailable) { 159 return; 160 } 161 final Context context = getContext(); 162 if (context != null) { 163 context.unregisterReceiver(mTetherChangeReceiver); 164 } 165 } 166 167 168 @Override getPreferenceScreenResId()169 protected int getPreferenceScreenResId() { 170 return R.xml.wifi_tether_settings; 171 } 172 173 @Override createPreferenceControllers(Context context)174 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 175 return buildPreferenceControllers(context, this::onTetherConfigUpdated); 176 } 177 buildPreferenceControllers(Context context, WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener)178 private static List<AbstractPreferenceController> buildPreferenceControllers(Context context, 179 WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener) { 180 final List<AbstractPreferenceController> controllers = new ArrayList<>(); 181 controllers.add(new WifiTetherSSIDPreferenceController(context, listener)); 182 controllers.add(new WifiTetherSecurityPreferenceController(context, listener)); 183 controllers.add(new WifiTetherPasswordPreferenceController(context, listener)); 184 controllers.add( 185 new WifiTetherAutoOffPreferenceController(context, KEY_WIFI_TETHER_AUTO_OFF)); 186 controllers.add(new WifiTetherMaximizeCompatibilityPreferenceController(context, listener)); 187 return controllers; 188 } 189 190 @Override onTetherConfigUpdated(AbstractPreferenceController context)191 public void onTetherConfigUpdated(AbstractPreferenceController context) { 192 final SoftApConfiguration config = buildNewConfig(); 193 mPasswordPreferenceController.setSecurityType(config.getSecurityType()); 194 195 /** 196 * if soft AP is stopped, bring up 197 * else restart with new config 198 * TODO: update config on a running access point when framework support is added 199 */ 200 if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) { 201 Log.d("TetheringSettings", 202 "Wifi AP config changed while enabled, stop and restart"); 203 mRestartWifiApAfterConfigChange = true; 204 mSwitchBarController.stopTether(); 205 } 206 mWifiManager.setSoftApConfiguration(config); 207 208 if (context instanceof WifiTetherSecurityPreferenceController) { 209 reConfigInitialExpandedChildCount(); 210 } 211 } 212 buildNewConfig()213 private SoftApConfiguration buildNewConfig() { 214 final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); 215 final int securityType = mSecurityPreferenceController.getSecurityType(); 216 configBuilder.setSsid(mSSIDPreferenceController.getSSID()); 217 if (securityType != SoftApConfiguration.SECURITY_TYPE_OPEN) { 218 configBuilder.setPassphrase( 219 mPasswordPreferenceController.getPasswordValidated(securityType), 220 securityType); 221 } 222 mMaxCompatibilityPrefController.setupMaximizeCompatibility(configBuilder); 223 return configBuilder.build(); 224 } 225 startTether()226 private void startTether() { 227 mRestartWifiApAfterConfigChange = false; 228 mSwitchBarController.startTether(); 229 } 230 updateDisplayWithNewConfig()231 private void updateDisplayWithNewConfig() { 232 use(WifiTetherSSIDPreferenceController.class).updateDisplay(); 233 use(WifiTetherSecurityPreferenceController.class).updateDisplay(); 234 use(WifiTetherPasswordPreferenceController.class).updateDisplay(); 235 use(WifiTetherMaximizeCompatibilityPreferenceController.class).updateDisplay(); 236 } 237 238 public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 239 new BaseSearchIndexProvider(R.xml.wifi_tether_settings) { 240 241 @Override 242 public List<String> getNonIndexableKeys(Context context) { 243 final List<String> keys = super.getNonIndexableKeys(context); 244 245 if (!TetherUtil.isTetherAvailable(context)) { 246 keys.add(KEY_WIFI_TETHER_NETWORK_NAME); 247 keys.add(KEY_WIFI_TETHER_NETWORK_PASSWORD); 248 keys.add(KEY_WIFI_TETHER_AUTO_OFF); 249 keys.add(KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); 250 } 251 252 // Remove duplicate 253 keys.add(KEY_WIFI_TETHER_SCREEN); 254 return keys; 255 } 256 257 @Override 258 protected boolean isPageSearchEnabled(Context context) { 259 return !FeatureFlagUtils.isEnabled(context, FeatureFlags.TETHER_ALL_IN_ONE); 260 } 261 262 @Override 263 public List<AbstractPreferenceController> createPreferenceControllers( 264 Context context) { 265 return buildPreferenceControllers(context, null /* listener */); 266 } 267 }; 268 269 @VisibleForTesting 270 class TetherChangeReceiver extends BroadcastReceiver { 271 @Override onReceive(Context content, Intent intent)272 public void onReceive(Context content, Intent intent) { 273 String action = intent.getAction(); 274 Log.d(TAG, "updating display config due to receiving broadcast action " + action); 275 updateDisplayWithNewConfig(); 276 if (action.equals(ACTION_TETHER_STATE_CHANGED)) { 277 if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_DISABLED 278 && mRestartWifiApAfterConfigChange) { 279 startTether(); 280 } 281 } else if (action.equals(WIFI_AP_STATE_CHANGED_ACTION)) { 282 int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 0); 283 if (state == WifiManager.WIFI_AP_STATE_DISABLED 284 && mRestartWifiApAfterConfigChange) { 285 startTether(); 286 } 287 } 288 } 289 } 290 reConfigInitialExpandedChildCount()291 private void reConfigInitialExpandedChildCount() { 292 final PreferenceGroup screen = getPreferenceScreen(); 293 if (screen != null) { 294 screen.setInitialExpandedChildrenCount(getInitialExpandedChildCount()); 295 } 296 } 297 } 298