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.TETHERING_WIFI; 20 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; 21 import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING; 22 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; 23 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING; 24 import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; 25 26 import android.content.BroadcastReceiver; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.net.ConnectivityManager; 31 import android.net.wifi.WifiManager; 32 import android.os.Handler; 33 import android.os.Looper; 34 import android.util.Log; 35 import android.widget.Switch; 36 37 import androidx.annotation.VisibleForTesting; 38 39 import com.android.settings.datausage.DataSaverBackend; 40 import com.android.settings.widget.SettingsMainSwitchBar; 41 import com.android.settingslib.core.lifecycle.LifecycleObserver; 42 import com.android.settingslib.core.lifecycle.events.OnStart; 43 import com.android.settingslib.core.lifecycle.events.OnStop; 44 import com.android.settingslib.widget.OnMainSwitchChangeListener; 45 46 /** 47 * Controller for logic pertaining to switch Wi-Fi tethering. 48 */ 49 public class WifiTetherSwitchBarController implements 50 LifecycleObserver, OnStart, OnStop, DataSaverBackend.Listener, OnMainSwitchChangeListener { 51 52 private static final String TAG = "WifiTetherSBC"; 53 private static final IntentFilter WIFI_INTENT_FILTER; 54 55 private final Context mContext; 56 private final SettingsMainSwitchBar mSwitchBar; 57 private final Switch mSwitch; 58 private final ConnectivityManager mConnectivityManager; 59 private final WifiManager mWifiManager; 60 61 @VisibleForTesting 62 DataSaverBackend mDataSaverBackend; 63 @VisibleForTesting 64 final ConnectivityManager.OnStartTetheringCallback mOnStartTetheringCallback = 65 new ConnectivityManager.OnStartTetheringCallback() { 66 @Override 67 public void onTetheringFailed() { 68 super.onTetheringFailed(); 69 Log.e(TAG, "Failed to start Wi-Fi Tethering."); 70 handleWifiApStateChanged(mWifiManager.getWifiApState()); 71 } 72 }; 73 74 static { 75 WIFI_INTENT_FILTER = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); 76 } 77 WifiTetherSwitchBarController(Context context, SettingsMainSwitchBar switchBar)78 WifiTetherSwitchBarController(Context context, SettingsMainSwitchBar switchBar) { 79 mContext = context; 80 mSwitchBar = switchBar; 81 mSwitch = mSwitchBar.getSwitch(); 82 mDataSaverBackend = new DataSaverBackend(context); 83 mConnectivityManager = 84 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 85 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 86 mSwitchBar.setChecked(mWifiManager.getWifiApState() == WIFI_AP_STATE_ENABLED); 87 updateWifiSwitch(); 88 } 89 90 @Override onStart()91 public void onStart() { 92 mDataSaverBackend.addListener(this); 93 mSwitchBar.addOnSwitchChangeListener(this); 94 mContext.registerReceiver(mReceiver, WIFI_INTENT_FILTER, 95 Context.RECEIVER_EXPORTED_UNAUDITED); 96 handleWifiApStateChanged(mWifiManager.getWifiApState()); 97 } 98 99 @Override onStop()100 public void onStop() { 101 mDataSaverBackend.remListener(this); 102 mContext.unregisterReceiver(mReceiver); 103 } 104 105 @Override onSwitchChanged(Switch switchView, boolean isChecked)106 public void onSwitchChanged(Switch switchView, boolean isChecked) { 107 // Filter out unnecessary callbacks when switch is disabled. 108 if (!switchView.isEnabled()) return; 109 110 if (isChecked) { 111 startTether(); 112 } else { 113 stopTether(); 114 } 115 } 116 stopTether()117 void stopTether() { 118 if (!isWifiApActivated()) return; 119 120 mSwitchBar.setEnabled(false); 121 mConnectivityManager.stopTethering(TETHERING_WIFI); 122 } 123 startTether()124 void startTether() { 125 if (isWifiApActivated()) return; 126 127 mSwitchBar.setEnabled(false); 128 mConnectivityManager.startTethering(TETHERING_WIFI, true /* showProvisioningUi */, 129 mOnStartTetheringCallback, new Handler(Looper.getMainLooper())); 130 } 131 isWifiApActivated()132 private boolean isWifiApActivated() { 133 final int wifiApState = mWifiManager.getWifiApState(); 134 if (wifiApState == WIFI_AP_STATE_ENABLED || wifiApState == WIFI_AP_STATE_ENABLING) { 135 return true; 136 } 137 return false; 138 } 139 140 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 141 @Override 142 public void onReceive(Context context, Intent intent) { 143 String action = intent.getAction(); 144 if (WifiManager.WIFI_AP_STATE_CHANGED_ACTION.equals(action)) { 145 final int state = intent.getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_FAILED); 146 handleWifiApStateChanged(state); 147 } 148 } 149 }; 150 151 @VisibleForTesting handleWifiApStateChanged(int state)152 void handleWifiApStateChanged(int state) { 153 if (state == WIFI_AP_STATE_ENABLING || state == WIFI_AP_STATE_DISABLING) return; 154 155 final boolean shouldBeChecked = (state == WIFI_AP_STATE_ENABLED); 156 if (mSwitch.isChecked() != shouldBeChecked) { 157 mSwitch.setChecked(shouldBeChecked); 158 } 159 updateWifiSwitch(); 160 } 161 updateWifiSwitch()162 private void updateWifiSwitch() { 163 mSwitchBar.setEnabled(!mDataSaverBackend.isDataSaverEnabled()); 164 } 165 166 @Override onDataSaverChanged(boolean isDataSaving)167 public void onDataSaverChanged(boolean isDataSaving) { 168 updateWifiSwitch(); 169 } 170 171 @Override onAllowlistStatusChanged(int uid, boolean isAllowlisted)172 public void onAllowlistStatusChanged(int uid, boolean isAllowlisted) { 173 // we don't care, since we just want to read the value 174 } 175 176 @Override onDenylistStatusChanged(int uid, boolean isDenylisted)177 public void onDenylistStatusChanged(int uid, boolean isDenylisted) { 178 // we don't care, since we just want to read the value 179 } 180 } 181