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