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.SoftApConfiguration; 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 SoftApConfiguration config = buildNewConfig(); 190 mPasswordPreferenceController.updateVisibility(config.getSecurityType()); 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.setSoftApConfiguration(config); 204 } 205 buildNewConfig()206 private SoftApConfiguration buildNewConfig() { 207 final int securityType = mSecurityPreferenceController.getSecurityType(); 208 209 return new SoftApConfiguration.Builder() 210 .setSsid(mSSIDPreferenceController.getSSID()) 211 .setPassphrase(mPasswordPreferenceController.getPasswordValidated(securityType), 212 securityType) 213 .setBand(mApBandPreferenceController.getBand()) 214 .build(); 215 } 216 startTether()217 private void startTether() { 218 mRestartWifiApAfterConfigChange = false; 219 mSwitchBarController.startTether(); 220 } 221 updateDisplayWithNewConfig()222 private void updateDisplayWithNewConfig() { 223 use(WifiTetherSSIDPreferenceController.class) 224 .updateDisplay(); 225 use(WifiTetherSecurityPreferenceController.class) 226 .updateDisplay(); 227 use(WifiTetherPasswordPreferenceController.class) 228 .updateDisplay(); 229 use(WifiTetherApBandPreferenceController.class) 230 .updateDisplay(); 231 } 232 233 public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 234 new BaseSearchIndexProvider() { 235 @Override 236 public List<SearchIndexableResource> getXmlResourcesToIndex( 237 Context context, boolean enabled) { 238 final SearchIndexableResource sir = new SearchIndexableResource(context); 239 sir.xmlResId = R.xml.wifi_tether_settings; 240 return Arrays.asList(sir); 241 } 242 243 @Override 244 public List<String> getNonIndexableKeys(Context context) { 245 final List<String> keys = super.getNonIndexableKeys(context); 246 247 if (!TetherUtil.isTetherAvailable(context)) { 248 keys.add(KEY_WIFI_TETHER_NETWORK_NAME); 249 keys.add(KEY_WIFI_TETHER_NETWORK_PASSWORD); 250 keys.add(KEY_WIFI_TETHER_AUTO_OFF); 251 keys.add(KEY_WIFI_TETHER_NETWORK_AP_BAND); 252 } 253 254 // Remove duplicate 255 keys.add(KEY_WIFI_TETHER_SCREEN); 256 return keys; 257 } 258 259 @Override 260 public List<AbstractPreferenceController> createPreferenceControllers( 261 Context context) { 262 return buildPreferenceControllers(context, null /* listener */); 263 } 264 }; 265 266 @VisibleForTesting 267 class TetherChangeReceiver extends BroadcastReceiver { 268 @Override onReceive(Context content, Intent intent)269 public void onReceive(Context content, Intent intent) { 270 String action = intent.getAction(); 271 Log.d(TAG, "updating display config due to receiving broadcast action " + action); 272 updateDisplayWithNewConfig(); 273 if (action.equals(ACTION_TETHER_STATE_CHANGED)) { 274 if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_DISABLED 275 && mRestartWifiApAfterConfigChange) { 276 startTether(); 277 } 278 } else if (action.equals(WIFI_AP_STATE_CHANGED_ACTION)) { 279 int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 0); 280 if (state == WifiManager.WIFI_AP_STATE_DISABLED 281 && mRestartWifiApAfterConfigChange) { 282 startTether(); 283 } 284 } 285 } 286 } 287 } 288