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 Context.RECEIVER_EXPORTED/*UNAUDITED*/); 171 mTetheringEventCallback = 172 new TetheringManager.TetheringEventCallback() { 173 @Override 174 public void onTetheredInterfacesChanged(List<String> interfaces) { 175 updateState(interfaces.toArray(new String[interfaces.size()])); 176 } 177 }; 178 mTetheringManager.registerTetheringEventCallback(new HandlerExecutor(mMainThreadHandler), 179 mTetheringEventCallback); 180 181 mOnStartTetheringCallback = new OnStartTetheringCallback(this); 182 updateState(null/*tethered*/); 183 if (mEthernetManager != null) { 184 mEthernetManager.addInterfaceStateListener(r -> mMainThreadHandler.post(r), 185 mEthernetListener); 186 } 187 } 188 189 @OnLifecycleEvent(Lifecycle.Event.ON_STOP) onStop()190 public void onStop() { 191 mBluetoothTetheringStoppedByUser = false; 192 mDataSaverBackend.remListener(this); 193 mSwitchWidgetController.stopListening(); 194 mContext.unregisterReceiver(mTetherChangeReceiver); 195 mTetheringManager.unregisterTetheringEventCallback(mTetheringEventCallback); 196 mTetheringEventCallback = null; 197 if (mEthernetManager != null) { 198 mEthernetManager.removeInterfaceStateListener(mEthernetListener); 199 } 200 } 201 addListener(OnTetherStateUpdateListener listener)202 public void addListener(OnTetherStateUpdateListener listener) { 203 if (listener != null && !mListeners.contains(listener)) { 204 listener.onTetherStateUpdated(getTetheringState(null /* tethered */)); 205 mListeners.add(listener); 206 } 207 } 208 removeListener(OnTetherStateUpdateListener listener)209 public void removeListener(OnTetherStateUpdateListener listener) { 210 if (listener != null) { 211 mListeners.remove(listener); 212 } 213 } 214 setSwitchEnabled(boolean enabled)215 private void setSwitchEnabled(boolean enabled) { 216 mSwitchWidgetController.setEnabled( 217 enabled && !mDataSaverEnabled && mUserManager.isAdminUser()); 218 } 219 220 @VisibleForTesting updateState(@ullable String[] tethered)221 void updateState(@Nullable String[] tethered) { 222 int state = getTetheringState(tethered); 223 if (DEBUG) { 224 Log.d(TAG, "updateState: " + state); 225 } 226 setSwitchCheckedInternal(state != TETHERING_OFF); 227 setSwitchEnabled(true); 228 for (int i = 0, size = mListeners.size(); i < size; ++i) { 229 mListeners.get(i).onTetherStateUpdated(state); 230 } 231 } 232 setSwitchCheckedInternal(boolean checked)233 private void setSwitchCheckedInternal(boolean checked) { 234 try { 235 mSwitchWidgetController.stopListening(); 236 } catch (IllegalStateException e) { 237 Log.e(TAG, "failed to stop switch widget listener when set check internally"); 238 return; 239 } 240 mSwitchWidgetController.setChecked(checked); 241 mSwitchWidgetController.startListening(); 242 } 243 244 @VisibleForTesting 245 @TetheringState getTetheringState(@ullable String[] tethered)246 int getTetheringState(@Nullable String[] tethered) { 247 int tetherState = TETHERING_OFF; 248 if (tethered == null) { 249 tethered = mTetheringManager.getTetheredIfaces(); 250 } 251 252 if (mWifiManager.isWifiApEnabled()) { 253 tetherState |= TETHERING_WIFI_ON; 254 } 255 256 // Only check bluetooth tethering state if not stopped by user already. 257 if (!mBluetoothTetheringStoppedByUser) { 258 final BluetoothPan pan = mBluetoothPan.get(); 259 if (mBluetoothAdapter != null && 260 mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON 261 && pan != null && pan.isTetheringOn()) { 262 tetherState |= TETHERING_BLUETOOTH_ON; 263 } 264 } 265 266 String[] usbRegexs = mTetheringManager.getTetherableUsbRegexs(); 267 for (String s : tethered) { 268 for (String regex : usbRegexs) { 269 if (s.matches(regex)) { 270 tetherState |= TETHERING_USB_ON; 271 } 272 } 273 if (mAvailableInterfaces.containsKey(s)) { 274 tetherState |= TETHERING_ETHERNET_ON; 275 } 276 } 277 278 return tetherState; 279 } 280 isTethering(@etheringState int state, int choice)281 public static boolean isTethering(@TetheringState int state, int choice) { 282 return (state & (1 << choice)) != TETHERING_OFF; 283 } 284 285 @Override onSwitchToggled(boolean isChecked)286 public boolean onSwitchToggled(boolean isChecked) { 287 if (isChecked) { 288 startTethering(TETHERING_WIFI); 289 } else { 290 stopTethering(TETHERING_USB); 291 stopTethering(TETHERING_WIFI); 292 stopTethering(TETHERING_BLUETOOTH); 293 stopTethering(TETHERING_ETHERNET); 294 } 295 return true; 296 } 297 stopTethering(int choice)298 public void stopTethering(int choice) { 299 int state = getTetheringState(null /* tethered */); 300 if (isTethering(state, choice)) { 301 setSwitchEnabled(false); 302 mConnectivityManager.stopTethering(choice); 303 if (choice == TETHERING_BLUETOOTH) { 304 // Stop bluetooth tether won't invoke tether state changed callback, so we need this 305 // boolean to remember the user action and update UI state immediately. 306 mBluetoothTetheringStoppedByUser = true; 307 updateState(null /* tethered */); 308 } 309 } 310 } 311 startTethering(int choice)312 public void startTethering(int choice) { 313 if (choice == TETHERING_BLUETOOTH) { 314 mBluetoothTetheringStoppedByUser = false; 315 } 316 int state = getTetheringState(null /* tethered */); 317 if (isTethering(state, choice)) { 318 return; 319 } 320 321 if (choice == TETHERING_BLUETOOTH && mBluetoothAdapter != null 322 && mBluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF) { 323 if (DEBUG) { 324 Log.d(TAG, "Turn on bluetooth first."); 325 } 326 mBluetoothEnableForTether = true; 327 mBluetoothAdapter.enable(); 328 return; 329 } 330 331 setSwitchEnabled(false); 332 mConnectivityManager.startTethering(choice, true /* showProvisioningUi */, 333 mOnStartTetheringCallback, mMainThreadHandler); 334 } 335 336 private final BroadcastReceiver mTetherChangeReceiver = new BroadcastReceiver() { 337 @Override 338 public void onReceive(Context context, Intent intent) { 339 final String action = intent.getAction(); 340 boolean shouldUpdateState = false; 341 if (TextUtils.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION, action)) { 342 shouldUpdateState = handleWifiApStateChanged(intent.getIntExtra( 343 WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED)); 344 } else if (TextUtils.equals(BluetoothAdapter.ACTION_STATE_CHANGED, action)) { 345 shouldUpdateState = handleBluetoothStateChanged(intent 346 .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)); 347 } 348 349 if (shouldUpdateState) { 350 updateState(null /* tethered */); 351 } 352 } 353 }; 354 handleBluetoothStateChanged(int state)355 private boolean handleBluetoothStateChanged(int state) { 356 switch (state) { 357 case BluetoothAdapter.STATE_ON: 358 if (mBluetoothEnableForTether) { 359 startTethering(TETHERING_BLUETOOTH); 360 } 361 // Fall through. 362 case BluetoothAdapter.STATE_OFF: 363 // Fall through. 364 case BluetoothAdapter.ERROR: 365 mBluetoothEnableForTether = false; 366 return true; 367 default: 368 // Return false for transition states. 369 return false; 370 } 371 } 372 handleWifiApStateChanged(int state)373 private boolean handleWifiApStateChanged(int state) { 374 switch (state) { 375 case WifiManager.WIFI_AP_STATE_FAILED: 376 Log.e(TAG, "Wifi AP is failed!"); 377 // fall through 378 case WifiManager.WIFI_AP_STATE_ENABLED: 379 // fall through 380 case WifiManager.WIFI_AP_STATE_DISABLED: 381 return true; 382 default: 383 // return false for transition state 384 return false; 385 } 386 } 387 388 @Override onDataSaverChanged(boolean isDataSaving)389 public void onDataSaverChanged(boolean isDataSaving) { 390 mDataSaverEnabled = isDataSaving; 391 setSwitchEnabled(true); 392 } 393 394 @Override onAllowlistStatusChanged(int uid, boolean isAllowlisted)395 public void onAllowlistStatusChanged(int uid, boolean isAllowlisted) { 396 // we don't care, since we just want to read the value 397 } 398 399 @Override onDenylistStatusChanged(int uid, boolean isDenylisted)400 public void onDenylistStatusChanged(int uid, boolean isDenylisted) { 401 // we don't care, since we just want to read the value 402 } 403 404 private static final class OnStartTetheringCallback extends 405 ConnectivityManager.OnStartTetheringCallback { 406 final WeakReference<TetherEnabler> mTetherEnabler; 407 OnStartTetheringCallback(TetherEnabler enabler)408 OnStartTetheringCallback(TetherEnabler enabler) { 409 mTetherEnabler = new WeakReference<>(enabler); 410 } 411 412 @Override onTetheringStarted()413 public void onTetheringStarted() { 414 update(); 415 } 416 417 @Override onTetheringFailed()418 public void onTetheringFailed() { 419 update(); 420 } 421 update()422 private void update() { 423 TetherEnabler enabler = mTetherEnabler.get(); 424 if (enabler != null) { 425 enabler.updateState(null/*tethered*/); 426 } 427 } 428 } 429 } 430