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