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.car.settings.wifi; 18 19 import android.annotation.FlaggedApi; 20 import android.content.Context; 21 import android.net.wifi.SoftApConfiguration; 22 import android.net.wifi.WifiManager; 23 import android.os.Handler; 24 import android.os.HandlerThread; 25 import android.os.Looper; 26 27 import androidx.annotation.MainThread; 28 import androidx.annotation.Nullable; 29 import androidx.lifecycle.Lifecycle; 30 import androidx.lifecycle.LifecycleObserver; 31 import androidx.lifecycle.OnLifecycleEvent; 32 33 import com.android.car.settings.Flags; 34 import com.android.wifitrackerlib.WifiEntry; 35 import com.android.wifitrackerlib.WifiPickerTracker; 36 37 import java.util.ArrayList; 38 import java.util.List; 39 import java.util.concurrent.Executor; 40 41 /** 42 * Manages Wifi configuration: e.g. monitors wifi states, change wifi setting etc. 43 */ 44 public class CarWifiManager implements WifiPickerTracker.WifiPickerTrackerCallback, 45 LifecycleObserver { 46 private static final String TAG = "CarWifiManager"; 47 48 private final Context mContext; 49 private final Lifecycle mLifecycle; 50 private final List<Listener> mListeners = new ArrayList<>(); 51 52 private HandlerThread mWorkerThread; 53 @Nullable private WifiPickerTracker mWifiTracker; 54 @Nullable private WifiManager mWifiManager; 55 56 public interface Listener { 57 /** 58 * Something about wifi setting changed. 59 */ onWifiEntriesChanged()60 void onWifiEntriesChanged(); 61 62 /** 63 * Called when the state of Wifi has changed, the state will be one of 64 * the following. 65 * 66 * <li>{@link WifiManager#WIFI_STATE_DISABLED}</li> 67 * <li>{@link WifiManager#WIFI_STATE_ENABLED}</li> 68 * <li>{@link WifiManager#WIFI_STATE_DISABLING}</li> 69 * <li>{@link WifiManager#WIFI_STATE_ENABLING}</li> 70 * <li>{@link WifiManager#WIFI_STATE_UNKNOWN}</li> 71 * <p> 72 * 73 * @param state The new state of wifi. 74 */ onWifiStateChanged(int state)75 void onWifiStateChanged(int state); 76 } 77 CarWifiManager(Context context, Lifecycle lifecycle)78 public CarWifiManager(Context context, Lifecycle lifecycle) { 79 mContext = context; 80 mLifecycle = lifecycle; 81 mLifecycle.addObserver(this); 82 mWifiManager = mContext.getSystemService(WifiManager.class); 83 mWorkerThread = new HandlerThread(TAG 84 + "{" + Integer.toHexString(System.identityHashCode(this)) + "}", 85 android.os.Process.THREAD_PRIORITY_BACKGROUND); 86 mWorkerThread.start(); 87 if (mWifiManager != null) { 88 mWifiTracker = WifiUtil.createWifiPickerTracker(lifecycle, context, 89 new Handler(Looper.getMainLooper()), mWorkerThread.getThreadHandler(), 90 /* listener= */ this); 91 } 92 } 93 94 /** 95 * Lifecycle method to clean up worker thread on destroy. 96 */ 97 @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) 98 @MainThread onDestroy()99 public void onDestroy() { 100 if (mWorkerThread != null) { 101 mWorkerThread.quit(); 102 } 103 mLifecycle.removeObserver(this); 104 } 105 106 /** 107 * Adds {@link Listener}. 108 */ addListener(Listener listener)109 public boolean addListener(Listener listener) { 110 return mListeners.add(listener); 111 } 112 113 /** 114 * Removes {@link Listener}. 115 */ removeListener(Listener listener)116 public boolean removeListener(Listener listener) { 117 return mListeners.remove(listener); 118 } 119 120 /** 121 * Returns the currently connected Wi-Fi entries or an empty list if there is no Wi-Fi 122 * network connected. 123 */ getConnectedWifiEntries()124 public List<WifiEntry> getConnectedWifiEntries() { 125 if (mWifiManager != null && mWifiManager.isWifiEnabled() && mWifiTracker != null) { 126 return mWifiTracker.getActiveWifiEntries(); 127 } 128 return new ArrayList<>(); 129 } 130 131 /** 132 * Returns a list of all reachable Wi-Fi entries, not including the connected Wi-Fi entry. 133 */ getAllWifiEntries()134 public List<WifiEntry> getAllWifiEntries() { 135 return getWifiEntries(false); 136 } 137 138 /** 139 * Returns a list of saved Wi-Fi entries, not including the connected Wi-Fi entry. 140 */ getSavedWifiEntries()141 public List<WifiEntry> getSavedWifiEntries() { 142 return getWifiEntries(true); 143 } 144 getWifiEntries(boolean onlySaved)145 private List<WifiEntry> getWifiEntries(boolean onlySaved) { 146 List<WifiEntry> wifiEntries = new ArrayList<WifiEntry>(); 147 if (mWifiManager != null && mWifiManager.isWifiEnabled() && mWifiTracker != null) { 148 for (WifiEntry wifiEntry : mWifiTracker.getWifiEntries()) { 149 // ignore out of reach Wi-Fi entries. 150 if (shouldIncludeWifiEntry(wifiEntry, onlySaved)) { 151 wifiEntries.add(wifiEntry); 152 } 153 } 154 } 155 return wifiEntries; 156 } 157 shouldIncludeWifiEntry(WifiEntry wifiEntry, boolean onlySaved)158 private boolean shouldIncludeWifiEntry(WifiEntry wifiEntry, boolean onlySaved) { 159 boolean reachable = wifiEntry.getLevel() != WifiEntry.WIFI_LEVEL_UNREACHABLE; 160 return onlySaved 161 ? reachable && wifiEntry.isSaved() 162 : reachable; 163 } 164 165 /** 166 * Returns {@code true} if Wifi is enabled 167 */ isWifiEnabled()168 public boolean isWifiEnabled() { 169 if (mWifiManager != null) { 170 return mWifiManager.isWifiEnabled(); 171 } 172 return false; 173 } 174 175 /** 176 * Returns {@code true} if Wifi tethering is enabled 177 */ isWifiApEnabled()178 public boolean isWifiApEnabled() { 179 if (mWifiManager != null) { 180 return mWifiManager.isWifiApEnabled(); 181 } 182 return false; 183 } 184 185 /** 186 * Gets {@link SoftApConfiguration} for tethering 187 */ 188 @Nullable getSoftApConfig()189 public SoftApConfiguration getSoftApConfig() { 190 if (mWifiManager != null) { 191 return mWifiManager.getSoftApConfiguration(); 192 } 193 return null; 194 } 195 196 /** 197 * Sets {@link SoftApConfiguration} for tethering 198 */ setSoftApConfig(SoftApConfiguration config)199 public void setSoftApConfig(SoftApConfiguration config) { 200 if (mWifiManager != null) { 201 mWifiManager.setSoftApConfiguration(config); 202 } 203 } 204 205 /** 206 * Gets the country code in ISO 3166 format. 207 */ 208 @Nullable getCountryCode()209 public String getCountryCode() { 210 if (mWifiManager != null) { 211 return mWifiManager.getCountryCode(); 212 } 213 return null; 214 } 215 216 /** 217 * Checks if the chipset supports 5GHz frequency band. 218 */ is5GhzBandSupported()219 public boolean is5GhzBandSupported() { 220 if (mWifiManager != null) { 221 return mWifiManager.is5GHzBandSupported(); 222 } 223 return false; 224 } 225 226 /** Gets the wifi state from {@link WifiManager}. */ getWifiState()227 public int getWifiState() { 228 if (mWifiManager != null) { 229 return mWifiManager.getWifiState(); 230 } 231 return WifiManager.WIFI_STATE_UNKNOWN; 232 } 233 234 /** Sets whether wifi is enabled. */ setWifiEnabled(boolean enabled)235 public boolean setWifiEnabled(boolean enabled) { 236 if (mWifiManager != null) { 237 return mWifiManager.setWifiEnabled(enabled); 238 } 239 return false; 240 } 241 242 /** Adds callback for Soft AP */ registerSoftApCallback(Executor executor, WifiManager.SoftApCallback callback)243 public void registerSoftApCallback(Executor executor, WifiManager.SoftApCallback callback) { 244 if (mWifiManager != null) { 245 mWifiManager.registerSoftApCallback(executor, callback); 246 } 247 } 248 249 /** Removes callback for Soft AP */ unregisterSoftApCallback(WifiManager.SoftApCallback callback)250 public void unregisterSoftApCallback(WifiManager.SoftApCallback callback) { 251 if (mWifiManager != null) { 252 mWifiManager.unregisterSoftApCallback(callback); 253 } 254 } 255 256 /** 257 * Returns whether Wi-Fi Dual Band is supported or not. 258 */ 259 @FlaggedApi(Flags.FLAG_HOTSPOT_UI_SPEED_UPDATE) isDualBandSupported()260 public boolean isDualBandSupported() { 261 if (mWifiManager != null) { 262 return mWifiManager.isBridgedApConcurrencySupported(); 263 } 264 return false; 265 } 266 267 @Override onWifiEntriesChanged()268 public void onWifiEntriesChanged() { 269 for (Listener listener : mListeners) { 270 listener.onWifiEntriesChanged(); 271 } 272 } 273 274 @Override onNumSavedNetworksChanged()275 public void onNumSavedNetworksChanged() { 276 } 277 278 @Override onNumSavedSubscriptionsChanged()279 public void onNumSavedSubscriptionsChanged() { 280 } 281 282 @Override onWifiStateChanged()283 public void onWifiStateChanged() { 284 if (mWifiTracker != null) { 285 int state = mWifiTracker.getWifiState(); 286 for (Listener listener : mListeners) { 287 listener.onWifiStateChanged(state); 288 } 289 } 290 } 291 292 /** 293 * @return true if this device supports Wi-Fi Easy-connect (Device Provisioning Protocol) 294 */ isEasyConnectSupported()295 public boolean isEasyConnectSupported() { 296 if (mWifiManager != null && mWifiManager.isEasyConnectSupported()) { 297 return true; 298 } 299 return false; 300 } 301 302 /** 303 * Returns the {@link WifiManager} 304 */ getWifiManager()305 public WifiManager getWifiManager() { 306 return mWifiManager; 307 } 308 } 309