1 /* 2 * Copyright (C) 2010 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; 18 19 import static com.android.settings.network.SatelliteWarningDialogActivity.EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG; 20 import static com.android.settings.network.SatelliteWarningDialogActivity.TYPE_IS_WIFI; 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.ConnectivityManager; 28 import android.net.NetworkInfo; 29 import android.net.wifi.SupplicantState; 30 import android.net.wifi.WifiInfo; 31 import android.net.wifi.WifiManager; 32 import android.provider.Settings; 33 import android.util.Log; 34 import android.widget.Toast; 35 36 import androidx.annotation.VisibleForTesting; 37 38 import com.android.settings.R; 39 import com.android.settings.network.SatelliteRepository; 40 import com.android.settings.network.SatelliteWarningDialogActivity; 41 import com.android.settings.widget.SwitchWidgetController; 42 import com.android.settingslib.WirelessUtils; 43 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 44 45 import java.util.concurrent.ExecutionException; 46 import java.util.concurrent.Executors; 47 import java.util.concurrent.TimeUnit; 48 import java.util.concurrent.TimeoutException; 49 import java.util.concurrent.atomic.AtomicBoolean; 50 51 public class WifiEnabler implements SwitchWidgetController.OnSwitchChangeListener { 52 private static final String TAG = WifiEnabler.class.getSimpleName(); 53 private final SwitchWidgetController mSwitchWidget; 54 private final WifiManager mWifiManager; 55 private final ConnectivityManager mConnectivityManager; 56 private final MetricsFeatureProvider mMetricsFeatureProvider; 57 58 private Context mContext; 59 private boolean mListeningToOnSwitchChange = false; 60 private AtomicBoolean mConnected = new AtomicBoolean(false); 61 private SatelliteRepository mSatelliteRepository; 62 @VisibleForTesting 63 AtomicBoolean mIsSatelliteOn = new AtomicBoolean(false); 64 65 private boolean mStateMachineEvent; 66 private final IntentFilter mIntentFilter; 67 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 68 @Override 69 public void onReceive(Context context, Intent intent) { 70 String action = intent.getAction(); 71 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { 72 handleWifiStateChanged(mWifiManager.getWifiState()); 73 } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) { 74 if (!mConnected.get()) { 75 handleStateChanged(WifiInfo.getDetailedStateOf((SupplicantState) 76 intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE))); 77 } 78 } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { 79 NetworkInfo info = (NetworkInfo) intent.getParcelableExtra( 80 WifiManager.EXTRA_NETWORK_INFO); 81 mConnected.set(info.isConnected()); 82 handleStateChanged(info.getDetailedState()); 83 } 84 } 85 }; 86 WifiEnabler(Context context, SwitchWidgetController switchWidget, MetricsFeatureProvider metricsFeatureProvider)87 public WifiEnabler(Context context, SwitchWidgetController switchWidget, 88 MetricsFeatureProvider metricsFeatureProvider) { 89 this(context, switchWidget, metricsFeatureProvider, 90 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)); 91 } 92 93 @VisibleForTesting WifiEnabler(Context context, SwitchWidgetController switchWidget, MetricsFeatureProvider metricsFeatureProvider, ConnectivityManager connectivityManager)94 WifiEnabler(Context context, SwitchWidgetController switchWidget, 95 MetricsFeatureProvider metricsFeatureProvider, 96 ConnectivityManager connectivityManager) { 97 mContext = context; 98 mSwitchWidget = switchWidget; 99 mSwitchWidget.setListener(this); 100 mMetricsFeatureProvider = metricsFeatureProvider; 101 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 102 mConnectivityManager = connectivityManager; 103 104 mIntentFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION); 105 // The order matters! We really should not depend on this. :( 106 mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); 107 mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 108 mSatelliteRepository = new SatelliteRepository(context); 109 setupSwitchController(); 110 } 111 setupSwitchController()112 public void setupSwitchController() { 113 final int state = mWifiManager.getWifiState(); 114 handleWifiStateChanged(state); 115 if (!mListeningToOnSwitchChange) { 116 mSwitchWidget.startListening(); 117 mListeningToOnSwitchChange = true; 118 } 119 mSwitchWidget.setupView(); 120 } 121 teardownSwitchController()122 public void teardownSwitchController() { 123 if (mListeningToOnSwitchChange) { 124 mSwitchWidget.stopListening(); 125 mListeningToOnSwitchChange = false; 126 } 127 mSwitchWidget.teardownView(); 128 } 129 resume(Context context)130 public void resume(Context context) { 131 mContext = context; 132 // Wi-Fi state is sticky, so just let the receiver update UI 133 mContext.registerReceiver(mReceiver, mIntentFilter, 134 Context.RECEIVER_EXPORTED_UNAUDITED); 135 if (!mListeningToOnSwitchChange) { 136 mSwitchWidget.startListening(); 137 mListeningToOnSwitchChange = true; 138 } 139 // Refresh satellite mode status. 140 try { 141 mIsSatelliteOn.set( 142 mSatelliteRepository.requestIsEnabled(Executors.newSingleThreadExecutor()) 143 .get(2000, TimeUnit.MILLISECONDS)); 144 } catch (ExecutionException | TimeoutException | InterruptedException e) { 145 Log.e(TAG, "Error to get satellite status : " + e); 146 } 147 } 148 pause()149 public void pause() { 150 mContext.unregisterReceiver(mReceiver); 151 if (mListeningToOnSwitchChange) { 152 mSwitchWidget.stopListening(); 153 mListeningToOnSwitchChange = false; 154 } 155 } 156 handleWifiStateChanged(int state)157 private void handleWifiStateChanged(int state) { 158 // Clear any previous state 159 mSwitchWidget.setDisabledByAdmin(null); 160 161 switch (state) { 162 case WifiManager.WIFI_STATE_ENABLING: 163 break; 164 case WifiManager.WIFI_STATE_ENABLED: 165 setSwitchBarChecked(true); 166 mSwitchWidget.setEnabled(true); 167 break; 168 case WifiManager.WIFI_STATE_DISABLING: 169 break; 170 case WifiManager.WIFI_STATE_DISABLED: 171 setSwitchBarChecked(false); 172 mSwitchWidget.setEnabled(true); 173 break; 174 default: 175 setSwitchBarChecked(false); 176 mSwitchWidget.setEnabled(true); 177 } 178 } 179 setSwitchBarChecked(boolean checked)180 private void setSwitchBarChecked(boolean checked) { 181 mStateMachineEvent = true; 182 mSwitchWidget.setChecked(checked); 183 mStateMachineEvent = false; 184 } 185 handleStateChanged(@uppressWarnings"unused") NetworkInfo.DetailedState state)186 private void handleStateChanged(@SuppressWarnings("unused") NetworkInfo.DetailedState state) { 187 // After the refactoring from a CheckBoxPreference to a Switch, this method is useless since 188 // there is nowhere to display a summary. 189 // This code is kept in case a future change re-introduces an associated text. 190 /* 191 // WifiInfo is valid if and only if Wi-Fi is enabled. 192 // Here we use the state of the switch as an optimization. 193 if (state != null && mSwitch.isChecked()) { 194 WifiInfo info = mWifiManager.getConnectionInfo(); 195 if (info != null) { 196 //setSummary(Summary.get(mContext, info.getSSID(), state)); 197 } 198 } 199 */ 200 } 201 202 @Override onSwitchToggled(boolean isChecked)203 public boolean onSwitchToggled(boolean isChecked) { 204 //Do nothing if called as a result of a state machine event 205 if (mStateMachineEvent) { 206 return true; 207 } 208 209 // Show dialog and do nothing under satellite mode. 210 if (mIsSatelliteOn.get()) { 211 mContext.startActivity( 212 new Intent(mContext, SatelliteWarningDialogActivity.class) 213 .putExtra( 214 EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG, 215 TYPE_IS_WIFI) 216 ); 217 return false; 218 } 219 220 // Show toast message if Wi-Fi is not allowed in airplane mode 221 if (isChecked && !WirelessUtils.isRadioAllowed(mContext, Settings.Global.RADIO_WIFI)) { 222 Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show(); 223 // Reset switch to off. No infinite check/listener loop. 224 mSwitchWidget.setChecked(false); 225 return false; 226 } 227 228 if (isChecked) { 229 mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_WIFI_ON); 230 } else { 231 // Log if user was connected at the time of switching off. 232 mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_WIFI_OFF, 233 mConnected.get()); 234 } 235 if (!mWifiManager.setWifiEnabled(isChecked)) { 236 // Error 237 mSwitchWidget.setEnabled(true); 238 Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show(); 239 } 240 return true; 241 } 242 } 243