• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 package com.android.settings.network;
17 
18 import static android.os.UserManager.DISALLOW_CONFIG_TETHERING;
19 
20 import static com.android.settingslib.RestrictedLockUtilsInternal.checkIfRestrictionEnforced;
21 
22 import android.bluetooth.BluetoothAdapter;
23 import android.bluetooth.BluetoothPan;
24 import android.bluetooth.BluetoothProfile;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.database.ContentObserver;
30 import android.net.ConnectivityManager;
31 import android.net.Uri;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.os.UserHandle;
35 import android.provider.Settings;
36 
37 import androidx.annotation.VisibleForTesting;
38 import androidx.preference.Preference;
39 import androidx.preference.PreferenceScreen;
40 
41 import com.android.settings.R;
42 import com.android.settings.TetherSettings;
43 import com.android.settings.core.PreferenceControllerMixin;
44 import com.android.settingslib.TetherUtil;
45 import com.android.settingslib.core.AbstractPreferenceController;
46 import com.android.settingslib.core.lifecycle.Lifecycle;
47 import com.android.settingslib.core.lifecycle.LifecycleObserver;
48 import com.android.settingslib.core.lifecycle.events.OnCreate;
49 import com.android.settingslib.core.lifecycle.events.OnDestroy;
50 import com.android.settingslib.core.lifecycle.events.OnPause;
51 import com.android.settingslib.core.lifecycle.events.OnResume;
52 
53 import java.util.concurrent.atomic.AtomicReference;
54 
55 public class TetherPreferenceController extends AbstractPreferenceController implements
56         PreferenceControllerMixin, LifecycleObserver, OnCreate, OnResume, OnPause, OnDestroy {
57 
58     private static final String KEY_TETHER_SETTINGS = "tether_settings";
59 
60     private final boolean mAdminDisallowedTetherConfig;
61     private final AtomicReference<BluetoothPan> mBluetoothPan;
62     private final ConnectivityManager mConnectivityManager;
63     private final BluetoothAdapter mBluetoothAdapter;
64     @VisibleForTesting
65     final BluetoothProfile.ServiceListener mBtProfileServiceListener =
66             new android.bluetooth.BluetoothProfile.ServiceListener() {
67                 public void onServiceConnected(int profile, BluetoothProfile proxy) {
68                     mBluetoothPan.set((BluetoothPan) proxy);
69                     updateSummary();
70                 }
71 
72                 public void onServiceDisconnected(int profile) {
73                     mBluetoothPan.set(null);
74                 }
75             };
76 
77     private SettingObserver mAirplaneModeObserver;
78     private Preference mPreference;
79     private TetherBroadcastReceiver mTetherReceiver;
80 
81     @VisibleForTesting(otherwise = VisibleForTesting.NONE)
TetherPreferenceController()82     TetherPreferenceController() {
83         super(null);
84         mAdminDisallowedTetherConfig = false;
85         mBluetoothPan = new AtomicReference<>();
86         mConnectivityManager = null;
87         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
88     }
89 
TetherPreferenceController(Context context, Lifecycle lifecycle)90     public TetherPreferenceController(Context context, Lifecycle lifecycle) {
91         super(context);
92         mBluetoothPan = new AtomicReference<>();
93         mAdminDisallowedTetherConfig = isTetherConfigDisallowed(context);
94         mConnectivityManager =
95                 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
96         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
97         if (lifecycle != null) {
98             lifecycle.addObserver(this);
99         }
100     }
101 
102     @Override
displayPreference(PreferenceScreen screen)103     public void displayPreference(PreferenceScreen screen) {
104         super.displayPreference(screen);
105         mPreference = screen.findPreference(KEY_TETHER_SETTINGS);
106         if (mPreference != null && !mAdminDisallowedTetherConfig) {
107             mPreference.setTitle(
108                     com.android.settingslib.Utils.getTetheringLabel(mConnectivityManager));
109 
110             // Grey out if provisioning is not available.
111             mPreference.setEnabled(!TetherSettings.isProvisioningNeededButUnavailable(mContext));
112         }
113     }
114 
115     @Override
isAvailable()116     public boolean isAvailable() {
117         return TetherUtil.isTetherAvailable(mContext);
118     }
119 
120     @Override
updateState(Preference preference)121     public void updateState(Preference preference) {
122         updateSummary();
123     }
124 
125     @Override
getPreferenceKey()126     public String getPreferenceKey() {
127         return KEY_TETHER_SETTINGS;
128     }
129 
130     @Override
onCreate(Bundle savedInstanceState)131     public void onCreate(Bundle savedInstanceState) {
132         if (mBluetoothAdapter != null &&
133             mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) {
134             mBluetoothAdapter.getProfileProxy(mContext, mBtProfileServiceListener,
135                     BluetoothProfile.PAN);
136         }
137     }
138 
139     @Override
onResume()140     public void onResume() {
141         if (mAirplaneModeObserver == null) {
142             mAirplaneModeObserver = new SettingObserver();
143         }
144         if (mTetherReceiver == null) {
145             mTetherReceiver = new TetherBroadcastReceiver();
146         }
147         mContext.registerReceiver(
148                 mTetherReceiver, new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
149         mContext.getContentResolver()
150                 .registerContentObserver(mAirplaneModeObserver.uri, false, mAirplaneModeObserver);
151     }
152 
153     @Override
onPause()154     public void onPause() {
155         if (mAirplaneModeObserver != null) {
156             mContext.getContentResolver().unregisterContentObserver(mAirplaneModeObserver);
157         }
158         if (mTetherReceiver != null) {
159             mContext.unregisterReceiver(mTetherReceiver);
160         }
161     }
162 
163     @Override
onDestroy()164     public void onDestroy() {
165         final BluetoothProfile profile = mBluetoothPan.getAndSet(null);
166         if (profile != null && mBluetoothAdapter != null) {
167             mBluetoothAdapter.closeProfileProxy(BluetoothProfile.PAN, profile);
168         }
169     }
170 
isTetherConfigDisallowed(Context context)171     public static boolean isTetherConfigDisallowed(Context context) {
172         return checkIfRestrictionEnforced(
173                 context, DISALLOW_CONFIG_TETHERING, UserHandle.myUserId()) != null;
174     }
175 
176     @VisibleForTesting
updateSummary()177     void updateSummary() {
178         if (mPreference == null) {
179             // Preference is not ready yet.
180             return;
181         }
182         String[] allTethered = mConnectivityManager.getTetheredIfaces();
183         String[] wifiTetherRegex = mConnectivityManager.getTetherableWifiRegexs();
184         String[] bluetoothRegex = mConnectivityManager.getTetherableBluetoothRegexs();
185 
186         boolean hotSpotOn = false;
187         boolean tetherOn = false;
188         if (allTethered != null) {
189             if (wifiTetherRegex != null) {
190                 for (String tethered : allTethered) {
191                     for (String regex : wifiTetherRegex) {
192                         if (tethered.matches(regex)) {
193                             hotSpotOn = true;
194                             break;
195                         }
196                     }
197                 }
198             }
199             if (allTethered.length > 1) {
200                 // We have more than 1 tethered connection
201                 tetherOn = true;
202             } else if (allTethered.length == 1) {
203                 // We have more than 1 tethered, it's either wifiTether (hotspot), or other type of
204                 // tether.
205                 tetherOn = !hotSpotOn;
206             } else {
207                 // No tethered connection.
208                 tetherOn = false;
209             }
210         }
211         if (!tetherOn
212                 && bluetoothRegex != null && bluetoothRegex.length > 0
213                 && mBluetoothAdapter != null
214                 && mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) {
215             // Check bluetooth state. It's not included in mConnectivityManager.getTetheredIfaces.
216             final BluetoothPan pan = mBluetoothPan.get();
217             tetherOn = pan != null && pan.isTetheringOn();
218         }
219         if (!hotSpotOn && !tetherOn) {
220             // Both off
221             mPreference.setSummary(R.string.switch_off_text);
222         } else if (hotSpotOn && tetherOn) {
223             // Both on
224             mPreference.setSummary(R.string.tether_settings_summary_hotspot_on_tether_on);
225         } else if (hotSpotOn) {
226             mPreference.setSummary(R.string.tether_settings_summary_hotspot_on_tether_off);
227         } else {
228             mPreference.setSummary(R.string.tether_settings_summary_hotspot_off_tether_on);
229         }
230     }
231 
updateSummaryToOff()232     private void updateSummaryToOff() {
233         if (mPreference == null) {
234             // Preference is not ready yet.
235             return;
236         }
237         mPreference.setSummary(R.string.switch_off_text);
238     }
239 
240     class SettingObserver extends ContentObserver {
241 
242         public final Uri uri;
243 
SettingObserver()244         public SettingObserver() {
245             super(new Handler());
246             uri = Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON);
247         }
248 
249         @Override
onChange(boolean selfChange, Uri uri)250         public void onChange(boolean selfChange, Uri uri) {
251             super.onChange(selfChange, uri);
252             if (this.uri.equals(uri)) {
253                 boolean isAirplaneMode = Settings.Global.getInt(mContext.getContentResolver(),
254                         Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
255                 if (isAirplaneMode) {
256                     // Airplane mode is on. Update summary to say tether is OFF directly. We cannot
257                     // go through updateSummary() because turning off tether takes time, and we
258                     // might still get "ON" status when rerun updateSummary(). So, just say it's off
259                     updateSummaryToOff();
260                 }
261             }
262         }
263     }
264 
265     @VisibleForTesting
266     class TetherBroadcastReceiver extends BroadcastReceiver {
267 
268         @Override
onReceive(Context context, Intent intent)269         public void onReceive(Context context, Intent intent) {
270             updateSummary();
271         }
272 
273     }
274 }
275