1 /*
2  * Copyright (C) 2017 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.wifi.tether;
18 
19 import static android.net.TetheringManager.TETHERING_WIFI;
20 import static android.net.wifi.WifiManager.SAP_START_FAILURE_GENERAL;
21 
22 import static com.android.settings.wifi.WifiUtils.canShowWifiHotspot;
23 
24 import android.content.Context;
25 import android.net.wifi.SoftApConfiguration;
26 import android.net.wifi.WifiClient;
27 import android.net.wifi.WifiManager;
28 import android.text.BidiFormatter;
29 
30 import androidx.annotation.NonNull;
31 import androidx.annotation.VisibleForTesting;
32 import androidx.preference.PreferenceScreen;
33 
34 import com.android.settings.R;
35 import com.android.settings.Utils;
36 import com.android.settings.core.PreferenceControllerMixin;
37 import com.android.settings.network.tether.TetheringManagerModel;
38 import com.android.settings.widget.GenericSwitchController;
39 import com.android.settings.widget.SwitchWidgetController;
40 import com.android.settingslib.PrimarySwitchPreference;
41 import com.android.settingslib.core.AbstractPreferenceController;
42 import com.android.settingslib.core.lifecycle.Lifecycle;
43 import com.android.settingslib.core.lifecycle.LifecycleObserver;
44 import com.android.settingslib.core.lifecycle.events.OnStart;
45 import com.android.settingslib.core.lifecycle.events.OnStop;
46 import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
47 import com.android.settingslib.wifi.WifiUtils;
48 
49 import java.util.List;
50 
51 public class WifiTetherPreferenceController extends AbstractPreferenceController
52         implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop,
53         SwitchWidgetController.OnSwitchChangeListener {
54 
55     private static final String WIFI_TETHER_SETTINGS = "wifi_tether";
56 
57     private WifiManager mWifiManager;
58     private boolean mIsWifiTetheringAllow;
59     private int mSoftApState;
60     @VisibleForTesting
61     PrimarySwitchPreference mPreference;
62     @VisibleForTesting
63     WifiTetherSoftApManager mWifiTetherSoftApManager;
64     @VisibleForTesting
65     TetheringManagerModel mTetheringManagerModel;
66     @VisibleForTesting
67     boolean mIsDataSaverEnabled;
68     @VisibleForTesting
69     SwitchWidgetController mSwitch;
70 
WifiTetherPreferenceController(Context context, Lifecycle lifecycle, TetheringManagerModel tetheringManagerModel)71     public WifiTetherPreferenceController(Context context, Lifecycle lifecycle,
72             TetheringManagerModel tetheringManagerModel) {
73         // TODO(b/246537032):Use fragment context to WifiManager service will caused memory leak
74         this(context, lifecycle,
75                 context.getApplicationContext().getSystemService(WifiManager.class),
76                 true /* initSoftApManager */,
77                 WifiEnterpriseRestrictionUtils.isWifiTetheringAllowed(context),
78                 tetheringManagerModel);
79     }
80 
81     @VisibleForTesting
WifiTetherPreferenceController( Context context, Lifecycle lifecycle, WifiManager wifiManager, boolean initSoftApManager, boolean isWifiTetheringAllow, TetheringManagerModel tetheringManagerModel)82     WifiTetherPreferenceController(
83             Context context,
84             Lifecycle lifecycle,
85             WifiManager wifiManager,
86             boolean initSoftApManager,
87             boolean isWifiTetheringAllow,
88             TetheringManagerModel tetheringManagerModel) {
89         super(context);
90         mIsWifiTetheringAllow = isWifiTetheringAllow;
91         if (!isWifiTetheringAllow) return;
92 
93         mTetheringManagerModel = tetheringManagerModel;
94         mWifiManager = wifiManager;
95 
96         if (lifecycle != null) {
97             lifecycle.addObserver(this);
98         }
99         if (initSoftApManager) {
100             initWifiTetherSoftApManager();
101         }
102     }
103 
104     @Override
isAvailable()105     public boolean isAvailable() {
106         return canShowWifiHotspot(mContext) && !Utils.isMonkeyRunning();
107     }
108 
109     @Override
displayPreference(PreferenceScreen screen)110     public void displayPreference(PreferenceScreen screen) {
111         super.displayPreference(screen);
112         mPreference = screen.findPreference(WIFI_TETHER_SETTINGS);
113         if (mPreference == null) {
114             // unavailable
115             return;
116         }
117         if (mSwitch == null) {
118             mSwitch = new GenericSwitchController(mPreference);
119             mSwitch.setListener(this);
120             updateSwitch();
121         }
122         mPreference.setEnabled(canEnabled());
123         if (!mIsWifiTetheringAllow) {
124             mPreference.setSummary(R.string.not_allowed_by_ent);
125         }
126     }
127 
128     @Override
getPreferenceKey()129     public String getPreferenceKey() {
130         return WIFI_TETHER_SETTINGS;
131     }
132 
133     @Override
onStart()134     public void onStart() {
135         if (mPreference != null) {
136             if (mWifiTetherSoftApManager != null) {
137                 mWifiTetherSoftApManager.registerSoftApCallback();
138             }
139             if (mSwitch != null) {
140                 mSwitch.startListening();
141             }
142         }
143     }
144 
145     @Override
onStop()146     public void onStop() {
147         if (mPreference != null) {
148             if (mWifiTetherSoftApManager != null) {
149                 mWifiTetherSoftApManager.unRegisterSoftApCallback();
150             }
151             if (mSwitch != null) {
152                 mSwitch.stopListening();
153             }
154         }
155     }
156 
157     @VisibleForTesting
initWifiTetherSoftApManager()158     void initWifiTetherSoftApManager() {
159         // This manager only handles the number of connected devices, other parts are handled by
160         // normal BroadcastReceiver in this controller
161         mWifiTetherSoftApManager = new WifiTetherSoftApManager(mWifiManager,
162                 new WifiTetherSoftApManager.WifiTetherSoftApCallback() {
163                     @Override
164                     public void onStateChanged(int state, int failureReason) {
165                         mSoftApState = state;
166                         handleWifiApStateChanged(state, failureReason);
167                     }
168 
169                     @Override
170                     public void onConnectedClientsChanged(List<WifiClient> clients) {
171                         if (mPreference != null
172                                 && mSoftApState == WifiManager.WIFI_AP_STATE_ENABLED) {
173                             // Only show the number of clients when state is on
174                             mPreference.setSummary(
175                                     WifiUtils.getWifiTetherSummaryForConnectedDevices(mContext,
176                                             clients.size()));
177                         }
178                     }
179                 });
180     }
181 
182     @VisibleForTesting
handleWifiApStateChanged(int state, int reason)183     void handleWifiApStateChanged(int state, int reason) {
184         switch (state) {
185             case WifiManager.WIFI_AP_STATE_ENABLING:
186                 mPreference.setSummary(R.string.wifi_tether_starting);
187                 break;
188             case WifiManager.WIFI_AP_STATE_ENABLED:
189                 mSwitch.setChecked(true);
190                 final SoftApConfiguration softApConfig = mWifiManager.getSoftApConfiguration();
191                 updateConfigSummary(softApConfig);
192                 break;
193             case WifiManager.WIFI_AP_STATE_DISABLING:
194                 mPreference.setSummary(R.string.wifi_tether_stopping);
195                 break;
196             case WifiManager.WIFI_AP_STATE_DISABLED:
197                 mSwitch.setChecked(false);
198                 mPreference.setSummary(R.string.wifi_hotspot_off_subtext);
199                 break;
200             default:
201                 if (reason == WifiManager.SAP_START_FAILURE_NO_CHANNEL) {
202                     mPreference.setSummary(R.string.wifi_sap_no_channel_error);
203                 } else {
204                     mPreference.setSummary(R.string.wifi_error);
205                 }
206         }
207     }
208 
updateConfigSummary(@onNull SoftApConfiguration softApConfig)209     private void updateConfigSummary(@NonNull SoftApConfiguration softApConfig) {
210         if (softApConfig == null) {
211             // Should never happen.
212             return;
213         }
214         mPreference.setSummary(mContext.getString(R.string.wifi_tether_enabled_subtext,
215                 BidiFormatter.getInstance().unicodeWrap(softApConfig.getSsid())));
216     }
217 
218     /**
219      * Sets the Data Saver state for preference update.
220      */
setDataSaverEnabled(boolean enabled)221     public void setDataSaverEnabled(boolean enabled) {
222         mIsDataSaverEnabled = enabled;
223         if (mPreference != null) {
224             mPreference.setEnabled(canEnabled());
225         }
226         if (mSwitch != null) {
227             mSwitch.setEnabled(canEnabled());
228         }
229     }
230 
canEnabled()231     private boolean canEnabled() {
232         return mIsWifiTetheringAllow && !mIsDataSaverEnabled;
233     }
234 
235     @VisibleForTesting
updateSwitch()236     protected void updateSwitch() {
237         if (mWifiManager == null) return;
238         int wifiApState = mWifiManager.getWifiApState();
239         mSwitch.setEnabled(canEnabled());
240         mSwitch.setChecked(wifiApState == WifiManager.WIFI_AP_STATE_ENABLED);
241         handleWifiApStateChanged(wifiApState, SAP_START_FAILURE_GENERAL);
242     }
243 
244     @Override
onSwitchToggled(boolean isChecked)245     public boolean onSwitchToggled(boolean isChecked) {
246         if (isChecked) {
247             mTetheringManagerModel.startTethering(TETHERING_WIFI);
248         } else {
249             mTetheringManagerModel.stopTethering(TETHERING_WIFI);
250         }
251         return true;
252     }
253 }
254