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