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 17package com.android.settings.wifi.tether; 18 19import android.content.BroadcastReceiver; 20import android.content.Context; 21import android.content.Intent; 22import android.content.IntentFilter; 23import android.net.ConnectivityManager; 24import android.net.wifi.WifiConfiguration; 25import android.net.wifi.WifiManager; 26import android.provider.Settings; 27import android.support.annotation.VisibleForTesting; 28import android.support.v7.preference.Preference; 29import android.support.v7.preference.PreferenceScreen; 30import android.text.BidiFormatter; 31 32import com.android.settings.R; 33import com.android.settings.Utils; 34import com.android.settings.core.PreferenceControllerMixin; 35import com.android.settingslib.core.AbstractPreferenceController; 36import com.android.settingslib.core.lifecycle.Lifecycle; 37import com.android.settingslib.core.lifecycle.LifecycleObserver; 38import com.android.settingslib.core.lifecycle.events.OnStart; 39import com.android.settingslib.core.lifecycle.events.OnStop; 40 41public class WifiTetherPreferenceController extends AbstractPreferenceController 42 implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop { 43 44 private static final String WIFI_TETHER_SETTINGS = "wifi_tether"; 45 private static final IntentFilter AIRPLANE_INTENT_FILTER = new IntentFilter( 46 Intent.ACTION_AIRPLANE_MODE_CHANGED); 47 private static final int ID_NULL = -1; 48 49 private final ConnectivityManager mConnectivityManager; 50 private final String[] mWifiRegexs; 51 private final WifiManager mWifiManager; 52 private final Lifecycle mLifecycle; 53 private int mSoftApState; 54 @VisibleForTesting 55 Preference mPreference; 56 @VisibleForTesting 57 WifiTetherSoftApManager mWifiTetherSoftApManager; 58 59 public WifiTetherPreferenceController(Context context, Lifecycle lifecycle) { 60 this(context, lifecycle, true /* initSoftApManager */); 61 } 62 63 @VisibleForTesting 64 WifiTetherPreferenceController(Context context, Lifecycle lifecycle, 65 boolean initSoftApManager) { 66 super(context); 67 mConnectivityManager = 68 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 69 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 70 mWifiRegexs = mConnectivityManager.getTetherableWifiRegexs(); 71 mLifecycle = lifecycle; 72 if (lifecycle != null) { 73 lifecycle.addObserver(this); 74 } 75 if (initSoftApManager) { 76 initWifiTetherSoftApManager(); 77 } 78 } 79 80 @Override 81 public boolean isAvailable() { 82 return mWifiRegexs != null 83 && mWifiRegexs.length != 0 84 && !Utils.isMonkeyRunning(); 85 } 86 87 @Override 88 public void displayPreference(PreferenceScreen screen) { 89 super.displayPreference(screen); 90 mPreference = screen.findPreference(WIFI_TETHER_SETTINGS); 91 if (mPreference == null) { 92 // unavailable 93 return; 94 } 95 } 96 97 @Override 98 public String getPreferenceKey() { 99 return WIFI_TETHER_SETTINGS; 100 } 101 102 @Override 103 public void onStart() { 104 if (mPreference != null) { 105 mContext.registerReceiver(mReceiver, AIRPLANE_INTENT_FILTER); 106 clearSummaryForAirplaneMode(); 107 if (mWifiTetherSoftApManager != null) { 108 mWifiTetherSoftApManager.registerSoftApCallback(); 109 } 110 } 111 } 112 113 @Override 114 public void onStop() { 115 if (mPreference != null) { 116 mContext.unregisterReceiver(mReceiver); 117 if (mWifiTetherSoftApManager != null) { 118 mWifiTetherSoftApManager.unRegisterSoftApCallback(); 119 } 120 } 121 } 122 123 @VisibleForTesting 124 void initWifiTetherSoftApManager() { 125 // This manager only handles the number of connected devices, other parts are handled by 126 // normal BroadcastReceiver in this controller 127 mWifiTetherSoftApManager = new WifiTetherSoftApManager(mWifiManager, 128 new WifiTetherSoftApManager.WifiTetherSoftApCallback() { 129 @Override 130 public void onStateChanged(int state, int failureReason) { 131 mSoftApState = state; 132 handleWifiApStateChanged(state, failureReason); 133 } 134 135 @Override 136 public void onNumClientsChanged(int numClients) { 137 if (mPreference != null 138 && mSoftApState == WifiManager.WIFI_AP_STATE_ENABLED) { 139 // Only show the number of clients when state is on 140 mPreference.setSummary(mContext.getResources().getQuantityString( 141 R.plurals.wifi_tether_connected_summary, numClients, 142 numClients)); 143 } 144 } 145 }); 146 } 147 148 // 149 // Everything below is copied from WifiApEnabler 150 // 151 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 152 @Override 153 public void onReceive(Context context, Intent intent) { 154 String action = intent.getAction(); 155 if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) { 156 clearSummaryForAirplaneMode(R.string.wifi_hotspot_off_subtext); 157 } 158 } 159 }; 160 161 @VisibleForTesting 162 void handleWifiApStateChanged(int state, int reason) { 163 switch (state) { 164 case WifiManager.WIFI_AP_STATE_ENABLING: 165 mPreference.setSummary(R.string.wifi_tether_starting); 166 break; 167 case WifiManager.WIFI_AP_STATE_ENABLED: 168 WifiConfiguration wifiConfig = mWifiManager.getWifiApConfiguration(); 169 updateConfigSummary(wifiConfig); 170 break; 171 case WifiManager.WIFI_AP_STATE_DISABLING: 172 mPreference.setSummary(R.string.wifi_tether_stopping); 173 break; 174 case WifiManager.WIFI_AP_STATE_DISABLED: 175 mPreference.setSummary(R.string.wifi_hotspot_off_subtext); 176 clearSummaryForAirplaneMode(); 177 break; 178 default: 179 if (reason == WifiManager.SAP_START_FAILURE_NO_CHANNEL) { 180 mPreference.setSummary(R.string.wifi_sap_no_channel_error); 181 } else { 182 mPreference.setSummary(R.string.wifi_error); 183 } 184 clearSummaryForAirplaneMode(); 185 } 186 } 187 188 private void updateConfigSummary(WifiConfiguration wifiConfig) { 189 final String s = mContext.getString( 190 com.android.internal.R.string.wifi_tether_configure_ssid_default); 191 192 mPreference.setSummary(mContext.getString(R.string.wifi_tether_enabled_subtext, 193 BidiFormatter.getInstance().unicodeWrap( 194 (wifiConfig == null) ? s : wifiConfig.SSID))); 195 } 196 197 private void clearSummaryForAirplaneMode() { 198 clearSummaryForAirplaneMode(ID_NULL); 199 } 200 201 private void clearSummaryForAirplaneMode(int defaultId) { 202 boolean isAirplaneMode = Settings.Global.getInt(mContext.getContentResolver(), 203 Settings.Global.AIRPLANE_MODE_ON, 0) != 0; 204 if (isAirplaneMode) { 205 mPreference.setSummary(R.string.wifi_tether_disabled_by_airplane); 206 } else if (defaultId != ID_NULL){ 207 mPreference.setSummary(defaultId); 208 } 209 } 210 // 211 // Everything above is copied from WifiApEnabler 212 // 213} 214