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