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 58 @VisibleForTesting 59 static final String KEY_WIFI_TETHER_NETWORK_NAME = "wifi_tether_network_name"; 60 @VisibleForTesting 61 static final String KEY_WIFI_TETHER_NETWORK_PASSWORD = "wifi_tether_network_password"; 62 @VisibleForTesting 63 static final String KEY_WIFI_TETHER_AUTO_OFF = "wifi_tether_auto_turn_off"; 64 @VisibleForTesting 65 static final String KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY = 66 WifiTetherMaximizeCompatibilityPreferenceController.PREF_KEY; 67 68 private WifiTetherSwitchBarController mSwitchBarController; 69 private WifiTetherSSIDPreferenceController mSSIDPreferenceController; 70 private WifiTetherPasswordPreferenceController mPasswordPreferenceController; 71 private WifiTetherSecurityPreferenceController mSecurityPreferenceController; 72 private WifiTetherMaximizeCompatibilityPreferenceController mMaxCompatibilityPrefController; 73 74 private WifiManager mWifiManager; 75 private boolean mRestartWifiApAfterConfigChange; 76 private boolean mUnavailable; 77 78 @VisibleForTesting 79 TetherChangeReceiver mTetherChangeReceiver; 80 81 static { 82 TETHER_STATE_CHANGE_FILTER = new IntentFilter(ACTION_TETHER_STATE_CHANGED); 83 TETHER_STATE_CHANGE_FILTER.addAction(WIFI_AP_STATE_CHANGED_ACTION); 84 } 85 WifiTetherSettings()86 public WifiTetherSettings() { 87 super(UserManager.DISALLOW_CONFIG_TETHERING); 88 } 89 90 @Override getMetricsCategory()91 public int getMetricsCategory() { 92 return SettingsEnums.WIFI_TETHER_SETTINGS; 93 } 94 95 @Override getLogTag()96 protected String getLogTag() { 97 return "WifiTetherSettings"; 98 } 99 100 @Override onCreate(Bundle icicle)101 public void onCreate(Bundle icicle) { 102 super.onCreate(icicle); 103 setIfOnlyAvailableForAdmins(true); 104 if (isUiRestricted()) { 105 mUnavailable = true; 106 } 107 } 108 109 @Override onAttach(Context context)110 public void onAttach(Context context) { 111 super.onAttach(context); 112 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 113 mTetherChangeReceiver = new TetherChangeReceiver(); 114 115 mSSIDPreferenceController = use(WifiTetherSSIDPreferenceController.class); 116 mSecurityPreferenceController = use(WifiTetherSecurityPreferenceController.class); 117 mPasswordPreferenceController = use(WifiTetherPasswordPreferenceController.class); 118 mMaxCompatibilityPrefController = 119 use(WifiTetherMaximizeCompatibilityPreferenceController.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 SettingsMainSwitchBar switchBar = activity.getSwitchBar(); 132 switchBar.setTitle(getContext().getString(R.string.use_wifi_hotsopt_main_switch_title)); 133 mSwitchBarController = new WifiTetherSwitchBarController(activity, switchBar); 134 getSettingsLifecycle().addObserver(mSwitchBarController); 135 switchBar.show(); 136 } 137 138 @Override onStart()139 public void onStart() { 140 super.onStart(); 141 if (mUnavailable) { 142 if (!isUiRestrictedByOnlyAdmin()) { 143 getEmptyTextView().setText(R.string.tethering_settings_not_available); 144 } 145 getPreferenceScreen().removeAll(); 146 return; 147 } 148 final Context context = getContext(); 149 if (context != null) { 150 context.registerReceiver(mTetherChangeReceiver, TETHER_STATE_CHANGE_FILTER); 151 } 152 } 153 154 @Override onStop()155 public void onStop() { 156 super.onStop(); 157 if (mUnavailable) { 158 return; 159 } 160 final Context context = getContext(); 161 if (context != null) { 162 context.unregisterReceiver(mTetherChangeReceiver); 163 } 164 } 165 166 167 @Override getPreferenceScreenResId()168 protected int getPreferenceScreenResId() { 169 return R.xml.wifi_tether_settings; 170 } 171 172 @Override createPreferenceControllers(Context context)173 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 174 return buildPreferenceControllers(context, this::onTetherConfigUpdated); 175 } 176 buildPreferenceControllers(Context context, WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener)177 private static List<AbstractPreferenceController> buildPreferenceControllers(Context context, 178 WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener) { 179 final List<AbstractPreferenceController> controllers = new ArrayList<>(); 180 controllers.add(new WifiTetherSSIDPreferenceController(context, listener)); 181 controllers.add(new WifiTetherSecurityPreferenceController(context, listener)); 182 controllers.add(new WifiTetherPasswordPreferenceController(context, listener)); 183 controllers.add( 184 new WifiTetherAutoOffPreferenceController(context, KEY_WIFI_TETHER_AUTO_OFF)); 185 controllers.add(new WifiTetherMaximizeCompatibilityPreferenceController(context, listener)); 186 return controllers; 187 } 188 189 @Override onTetherConfigUpdated(AbstractPreferenceController context)190 public void onTetherConfigUpdated(AbstractPreferenceController context) { 191 final SoftApConfiguration config = buildNewConfig(); 192 mPasswordPreferenceController.setSecurityType(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 buildNewConfig()208 private SoftApConfiguration buildNewConfig() { 209 final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); 210 final int securityType = mSecurityPreferenceController.getSecurityType(); 211 configBuilder.setSsid(mSSIDPreferenceController.getSSID()); 212 if (securityType != SoftApConfiguration.SECURITY_TYPE_OPEN) { 213 configBuilder.setPassphrase( 214 mPasswordPreferenceController.getPasswordValidated(securityType), 215 securityType); 216 } 217 mMaxCompatibilityPrefController.setupMaximizeCompatibility(configBuilder); 218 return configBuilder.build(); 219 } 220 startTether()221 private void startTether() { 222 mRestartWifiApAfterConfigChange = false; 223 mSwitchBarController.startTether(); 224 } 225 updateDisplayWithNewConfig()226 private void updateDisplayWithNewConfig() { 227 use(WifiTetherSSIDPreferenceController.class).updateDisplay(); 228 use(WifiTetherSecurityPreferenceController.class).updateDisplay(); 229 use(WifiTetherPasswordPreferenceController.class).updateDisplay(); 230 use(WifiTetherMaximizeCompatibilityPreferenceController.class).updateDisplay(); 231 } 232 233 public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 234 new BaseSearchIndexProvider(R.xml.wifi_tether_settings) { 235 236 @Override 237 public List<String> getNonIndexableKeys(Context context) { 238 final List<String> keys = super.getNonIndexableKeys(context); 239 240 if (!TetherUtil.isTetherAvailable(context)) { 241 keys.add(KEY_WIFI_TETHER_NETWORK_NAME); 242 keys.add(KEY_WIFI_TETHER_NETWORK_PASSWORD); 243 keys.add(KEY_WIFI_TETHER_AUTO_OFF); 244 keys.add(KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); 245 } 246 247 // Remove duplicate 248 keys.add(KEY_WIFI_TETHER_SCREEN); 249 return keys; 250 } 251 252 @Override 253 protected boolean isPageSearchEnabled(Context context) { 254 return !FeatureFlagUtils.isEnabled(context, FeatureFlags.TETHER_ALL_IN_ONE); 255 } 256 257 @Override 258 public List<AbstractPreferenceController> createPreferenceControllers( 259 Context context) { 260 return buildPreferenceControllers(context, null /* listener */); 261 } 262 }; 263 264 @VisibleForTesting 265 class TetherChangeReceiver extends BroadcastReceiver { 266 @Override onReceive(Context content, Intent intent)267 public void onReceive(Context content, Intent intent) { 268 String action = intent.getAction(); 269 Log.d(TAG, "updating display config due to receiving broadcast action " + action); 270 updateDisplayWithNewConfig(); 271 if (action.equals(ACTION_TETHER_STATE_CHANGED)) { 272 if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_DISABLED 273 && mRestartWifiApAfterConfigChange) { 274 startTether(); 275 } 276 } else if (action.equals(WIFI_AP_STATE_CHANGED_ACTION)) { 277 int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 0); 278 if (state == WifiManager.WIFI_AP_STATE_DISABLED 279 && mRestartWifiApAfterConfigChange) { 280 startTether(); 281 } 282 } 283 } 284 } 285 } 286