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