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.settings.network; 18 19 import static android.net.ConnectivityManager.TETHERING_BLUETOOTH; 20 import static android.net.ConnectivityManager.TETHERING_USB; 21 import static android.net.ConnectivityManager.TETHERING_WIFI; 22 import static android.net.TetheringManager.TETHERING_ETHERNET; 23 24 import static java.lang.annotation.RetentionPolicy.SOURCE; 25 26 import android.annotation.IntDef; 27 import android.bluetooth.BluetoothAdapter; 28 import android.bluetooth.BluetoothPan; 29 import android.content.BroadcastReceiver; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.net.ConnectivityManager; 34 import android.net.TetheringManager; 35 import android.net.wifi.WifiManager; 36 import android.os.Handler; 37 import android.os.HandlerExecutor; 38 import android.os.Looper; 39 import android.os.UserManager; 40 import android.text.TextUtils; 41 import android.util.Log; 42 43 import androidx.annotation.Nullable; 44 import androidx.lifecycle.Lifecycle; 45 import androidx.lifecycle.LifecycleObserver; 46 import androidx.lifecycle.OnLifecycleEvent; 47 48 import com.android.internal.annotations.VisibleForTesting; 49 import com.android.settings.datausage.DataSaverBackend; 50 import com.android.settings.widget.SwitchWidgetController; 51 52 import java.lang.annotation.Retention; 53 import java.lang.ref.WeakReference; 54 import java.util.ArrayList; 55 import java.util.List; 56 import java.util.concurrent.atomic.AtomicReference; 57 58 /** 59 * TetherEnabler is a helper to manage Tethering switch on/off state. It offers helper functions to 60 * turn on/off different types of tethering interfaces and ensures tethering state updated by data 61 * saver state. 62 * 63 * This class is not designed for extending. It's extendable solely for the test purpose. 64 */ 65 66 public class TetherEnabler implements SwitchWidgetController.OnSwitchChangeListener, 67 DataSaverBackend.Listener, LifecycleObserver { 68 69 /** 70 * Interface definition for a callback to be invoked when the tethering has been updated. 71 */ 72 public interface OnTetherStateUpdateListener { 73 /** 74 * Called when the tethering state has changed. 75 * 76 * @param state The new tethering state. 77 */ onTetherStateUpdated(@etheringState int state)78 void onTetherStateUpdated(@TetheringState int state); 79 } 80 81 private static final String TAG = "TetherEnabler"; 82 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 83 84 85 @Retention(SOURCE) 86 @IntDef( 87 flag = true, 88 value = { 89 TETHERING_OFF, 90 TETHERING_WIFI_ON, 91 TETHERING_USB_ON, 92 TETHERING_BLUETOOTH_ON, 93 TETHERING_ETHERNET_ON 94 } 95 ) 96 @interface TetheringState {} 97 public static final int TETHERING_OFF = 0; 98 public static final int TETHERING_WIFI_ON = 1 << TETHERING_WIFI; 99 public static final int TETHERING_USB_ON = 1 << TETHERING_USB; 100 public static final int TETHERING_BLUETOOTH_ON = 1 << TETHERING_BLUETOOTH; 101 public static final int TETHERING_ETHERNET_ON = 1 << TETHERING_ETHERNET; 102 103 @VisibleForTesting 104 final List<OnTetherStateUpdateListener> mListeners; 105 private final Handler mMainThreadHandler; 106 private final SwitchWidgetController mSwitchWidgetController; 107 private final WifiManager mWifiManager; 108 private final ConnectivityManager mConnectivityManager; 109 private final TetheringManager mTetheringManager; 110 private final UserManager mUserManager; 111 private final String mEthernetRegex; 112 private final DataSaverBackend mDataSaverBackend; 113 private boolean mDataSaverEnabled; 114 @VisibleForTesting 115 boolean mBluetoothTetheringStoppedByUser; 116 private final Context mContext; 117 @VisibleForTesting 118 TetheringManager.TetheringEventCallback mTetheringEventCallback; 119 @VisibleForTesting 120 ConnectivityManager.OnStartTetheringCallback mOnStartTetheringCallback; 121 private final AtomicReference<BluetoothPan> mBluetoothPan; 122 private boolean mBluetoothEnableForTether; 123 private final BluetoothAdapter mBluetoothAdapter; 124 TetherEnabler(Context context, SwitchWidgetController switchWidgetController, AtomicReference<BluetoothPan> bluetoothPan)125 public TetherEnabler(Context context, SwitchWidgetController switchWidgetController, 126 AtomicReference<BluetoothPan> bluetoothPan) { 127 mContext = context; 128 mSwitchWidgetController = switchWidgetController; 129 mDataSaverBackend = new DataSaverBackend(context); 130 mConnectivityManager = 131 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 132 mTetheringManager = (TetheringManager) context.getSystemService(Context.TETHERING_SERVICE); 133 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 134 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 135 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 136 mBluetoothPan = bluetoothPan; 137 mEthernetRegex = 138 context.getString(com.android.internal.R.string.config_ethernet_iface_regex); 139 mDataSaverEnabled = mDataSaverBackend.isDataSaverEnabled(); 140 mListeners = new ArrayList<>(); 141 mMainThreadHandler = new Handler(Looper.getMainLooper()); 142 } 143 144 @OnLifecycleEvent(Lifecycle.Event.ON_START) onStart()145 public void onStart() { 146 mDataSaverBackend.addListener(this); 147 mSwitchWidgetController.setListener(this); 148 mSwitchWidgetController.startListening(); 149 final IntentFilter filter = new IntentFilter( 150 TetheringManager.ACTION_TETHER_STATE_CHANGED); 151 filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); 152 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 153 mContext.registerReceiver(mTetherChangeReceiver, filter); 154 mTetheringEventCallback = 155 new TetheringManager.TetheringEventCallback() { 156 @Override 157 public void onTetheredInterfacesChanged(List<String> interfaces) { 158 updateState(interfaces.toArray(new String[interfaces.size()])); 159 } 160 }; 161 mTetheringManager.registerTetheringEventCallback(new HandlerExecutor(mMainThreadHandler), 162 mTetheringEventCallback); 163 164 mOnStartTetheringCallback = new OnStartTetheringCallback(this); 165 updateState(null/*tethered*/); 166 } 167 168 @OnLifecycleEvent(Lifecycle.Event.ON_STOP) onStop()169 public void onStop() { 170 mBluetoothTetheringStoppedByUser = false; 171 mDataSaverBackend.remListener(this); 172 mSwitchWidgetController.stopListening(); 173 mContext.unregisterReceiver(mTetherChangeReceiver); 174 mTetheringManager.unregisterTetheringEventCallback(mTetheringEventCallback); 175 mTetheringEventCallback = null; 176 } 177 addListener(OnTetherStateUpdateListener listener)178 public void addListener(OnTetherStateUpdateListener listener) { 179 if (listener != null && !mListeners.contains(listener)) { 180 listener.onTetherStateUpdated(getTetheringState(null /* tethered */)); 181 mListeners.add(listener); 182 } 183 } 184 removeListener(OnTetherStateUpdateListener listener)185 public void removeListener(OnTetherStateUpdateListener listener) { 186 if (listener != null) { 187 mListeners.remove(listener); 188 } 189 } 190 setSwitchEnabled(boolean enabled)191 private void setSwitchEnabled(boolean enabled) { 192 mSwitchWidgetController.setEnabled( 193 enabled && !mDataSaverEnabled && mUserManager.isAdminUser()); 194 } 195 196 @VisibleForTesting updateState(@ullable String[] tethered)197 void updateState(@Nullable String[] tethered) { 198 int state = getTetheringState(tethered); 199 if (DEBUG) { 200 Log.d(TAG, "updateState: " + state); 201 } 202 setSwitchCheckedInternal(state != TETHERING_OFF); 203 setSwitchEnabled(true); 204 for (int i = 0, size = mListeners.size(); i < size; ++i) { 205 mListeners.get(i).onTetherStateUpdated(state); 206 } 207 } 208 setSwitchCheckedInternal(boolean checked)209 private void setSwitchCheckedInternal(boolean checked) { 210 try { 211 mSwitchWidgetController.stopListening(); 212 } catch (IllegalStateException e) { 213 Log.e(TAG, "failed to stop switch widget listener when set check internally"); 214 return; 215 } 216 mSwitchWidgetController.setChecked(checked); 217 mSwitchWidgetController.startListening(); 218 } 219 220 @VisibleForTesting 221 @TetheringState getTetheringState(@ullable String[] tethered)222 int getTetheringState(@Nullable String[] tethered) { 223 int tetherState = TETHERING_OFF; 224 if (tethered == null) { 225 tethered = mTetheringManager.getTetheredIfaces(); 226 } 227 228 if (mWifiManager.isWifiApEnabled()) { 229 tetherState |= TETHERING_WIFI_ON; 230 } 231 232 // Only check bluetooth tethering state if not stopped by user already. 233 if (!mBluetoothTetheringStoppedByUser) { 234 final BluetoothPan pan = mBluetoothPan.get(); 235 if (mBluetoothAdapter != null && 236 mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON 237 && pan != null && pan.isTetheringOn()) { 238 tetherState |= TETHERING_BLUETOOTH_ON; 239 } 240 } 241 242 String[] usbRegexs = mTetheringManager.getTetherableUsbRegexs(); 243 for (String s : tethered) { 244 for (String regex : usbRegexs) { 245 if (s.matches(regex)) { 246 tetherState |= TETHERING_USB_ON; 247 } 248 } 249 if (s.matches(mEthernetRegex)) { 250 tetherState |= TETHERING_ETHERNET_ON; 251 } 252 } 253 254 return tetherState; 255 } 256 isTethering(@etheringState int state, int choice)257 public static boolean isTethering(@TetheringState int state, int choice) { 258 return (state & (1 << choice)) != TETHERING_OFF; 259 } 260 261 @Override onSwitchToggled(boolean isChecked)262 public boolean onSwitchToggled(boolean isChecked) { 263 if (isChecked) { 264 startTethering(TETHERING_WIFI); 265 } else { 266 stopTethering(TETHERING_USB); 267 stopTethering(TETHERING_WIFI); 268 stopTethering(TETHERING_BLUETOOTH); 269 stopTethering(TETHERING_ETHERNET); 270 } 271 return true; 272 } 273 stopTethering(int choice)274 public void stopTethering(int choice) { 275 int state = getTetheringState(null /* tethered */); 276 if (isTethering(state, choice)) { 277 setSwitchEnabled(false); 278 mConnectivityManager.stopTethering(choice); 279 if (choice == TETHERING_BLUETOOTH) { 280 // Stop bluetooth tether won't invoke tether state changed callback, so we need this 281 // boolean to remember the user action and update UI state immediately. 282 mBluetoothTetheringStoppedByUser = true; 283 updateState(null /* tethered */); 284 } 285 } 286 } 287 startTethering(int choice)288 public void startTethering(int choice) { 289 if (choice == TETHERING_BLUETOOTH) { 290 mBluetoothTetheringStoppedByUser = false; 291 } 292 int state = getTetheringState(null /* tethered */); 293 if (isTethering(state, choice)) { 294 return; 295 } 296 297 if (choice == TETHERING_BLUETOOTH && mBluetoothAdapter != null 298 && mBluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF) { 299 if (DEBUG) { 300 Log.d(TAG, "Turn on bluetooth first."); 301 } 302 mBluetoothEnableForTether = true; 303 mBluetoothAdapter.enable(); 304 return; 305 } 306 307 setSwitchEnabled(false); 308 mConnectivityManager.startTethering(choice, true /* showProvisioningUi */, 309 mOnStartTetheringCallback, mMainThreadHandler); 310 } 311 312 private final BroadcastReceiver mTetherChangeReceiver = new BroadcastReceiver() { 313 @Override 314 public void onReceive(Context context, Intent intent) { 315 final String action = intent.getAction(); 316 boolean shouldUpdateState = false; 317 if (TextUtils.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION, action)) { 318 shouldUpdateState = handleWifiApStateChanged(intent.getIntExtra( 319 WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED)); 320 } else if (TextUtils.equals(BluetoothAdapter.ACTION_STATE_CHANGED, action)) { 321 shouldUpdateState = handleBluetoothStateChanged(intent 322 .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)); 323 } 324 325 if (shouldUpdateState) { 326 updateState(null /* tethered */); 327 } 328 } 329 }; 330 handleBluetoothStateChanged(int state)331 private boolean handleBluetoothStateChanged(int state) { 332 switch (state) { 333 case BluetoothAdapter.STATE_ON: 334 if (mBluetoothEnableForTether) { 335 startTethering(TETHERING_BLUETOOTH); 336 } 337 // Fall through. 338 case BluetoothAdapter.STATE_OFF: 339 // Fall through. 340 case BluetoothAdapter.ERROR: 341 mBluetoothEnableForTether = false; 342 return true; 343 default: 344 // Return false for transition states. 345 return false; 346 } 347 } 348 handleWifiApStateChanged(int state)349 private boolean handleWifiApStateChanged(int state) { 350 switch (state) { 351 case WifiManager.WIFI_AP_STATE_FAILED: 352 Log.e(TAG, "Wifi AP is failed!"); 353 // fall through 354 case WifiManager.WIFI_AP_STATE_ENABLED: 355 // fall through 356 case WifiManager.WIFI_AP_STATE_DISABLED: 357 return true; 358 default: 359 // return false for transition state 360 return false; 361 } 362 } 363 364 @Override onDataSaverChanged(boolean isDataSaving)365 public void onDataSaverChanged(boolean isDataSaving) { 366 mDataSaverEnabled = isDataSaving; 367 setSwitchEnabled(true); 368 } 369 370 @Override onAllowlistStatusChanged(int uid, boolean isAllowlisted)371 public void onAllowlistStatusChanged(int uid, boolean isAllowlisted) { 372 // we don't care, since we just want to read the value 373 } 374 375 @Override onDenylistStatusChanged(int uid, boolean isDenylisted)376 public void onDenylistStatusChanged(int uid, boolean isDenylisted) { 377 // we don't care, since we just want to read the value 378 } 379 380 private static final class OnStartTetheringCallback extends 381 ConnectivityManager.OnStartTetheringCallback { 382 final WeakReference<TetherEnabler> mTetherEnabler; 383 OnStartTetheringCallback(TetherEnabler enabler)384 OnStartTetheringCallback(TetherEnabler enabler) { 385 mTetherEnabler = new WeakReference<>(enabler); 386 } 387 388 @Override onTetheringStarted()389 public void onTetheringStarted() { 390 update(); 391 } 392 393 @Override onTetheringFailed()394 public void onTetheringFailed() { 395 update(); 396 } 397 update()398 private void update() { 399 TetherEnabler enabler = mTetherEnabler.get(); 400 if (enabler != null) { 401 enabler.updateState(null/*tethered*/); 402 } 403 } 404 } 405 } 406