1/* 2 * Copyright (C) 2015 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 */ 16package com.android.systemui.statusbar.policy; 17 18import android.content.Context; 19import android.content.Intent; 20import android.database.ContentObserver; 21import android.net.NetworkBadging; 22import android.net.NetworkCapabilities; 23import android.net.NetworkKey; 24import android.net.NetworkScoreManager; 25import android.net.ScoredNetwork; 26import android.net.wifi.WifiManager; 27import android.net.wifi.WifiNetworkScoreCache; 28import android.net.wifi.WifiNetworkScoreCache.CacheListener; 29import android.os.Handler; 30import android.os.Looper; 31import android.os.Message; 32import android.os.Messenger; 33import android.provider.Settings; 34import android.util.Log; 35 36import com.android.internal.annotations.VisibleForTesting; 37import com.android.internal.util.AsyncChannel; 38import com.android.settingslib.Utils; 39import com.android.settingslib.wifi.WifiStatusTracker; 40import com.android.systemui.statusbar.policy.NetworkController.IconState; 41import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; 42 43import com.android.systemui.R; 44 45import java.util.Objects; 46import java.util.List; 47 48 49public class WifiSignalController extends 50 SignalController<WifiSignalController.WifiState, SignalController.IconGroup> { 51 52 private final WifiManager mWifiManager; 53 private final AsyncChannel mWifiChannel; 54 private final boolean mHasMobileData; 55 private final NetworkScoreManager mNetworkScoreManager; 56 private final WifiNetworkScoreCache mScoreCache; 57 private final WifiStatusTracker mWifiTracker; 58 59 private boolean mScoringUiEnabled = false; 60 61 public WifiSignalController(Context context, boolean hasMobileData, 62 CallbackHandler callbackHandler, NetworkControllerImpl networkController, 63 NetworkScoreManager networkScoreManager) { 64 super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI, 65 callbackHandler, networkController); 66 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 67 mWifiTracker = new WifiStatusTracker(mWifiManager); 68 mHasMobileData = hasMobileData; 69 Handler handler = new WifiHandler(Looper.getMainLooper()); 70 mWifiChannel = new AsyncChannel(); 71 Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger(); 72 if (wifiMessenger != null) { 73 mWifiChannel.connect(context, handler, wifiMessenger); 74 } 75 // WiFi only has one state. 76 mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup( 77 "Wi-Fi Icons", 78 WifiIcons.WIFI_SIGNAL_STRENGTH, 79 WifiIcons.QS_WIFI_SIGNAL_STRENGTH, 80 AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH, 81 WifiIcons.WIFI_NO_NETWORK, 82 WifiIcons.QS_WIFI_NO_NETWORK, 83 WifiIcons.WIFI_NO_NETWORK, 84 WifiIcons.QS_WIFI_NO_NETWORK, 85 AccessibilityContentDescriptions.WIFI_NO_CONNECTION 86 ); 87 88 mScoreCache = new WifiNetworkScoreCache(context, new CacheListener(handler) { 89 @Override 90 public void networkCacheUpdated(List<ScoredNetwork> networks) { 91 mCurrentState.badgeEnum = getWifiBadgeEnum(); 92 notifyListenersIfNecessary(); 93 } 94 }); 95 96 // Setup scoring 97 mNetworkScoreManager = networkScoreManager; 98 configureScoringGating(); 99 registerScoreCache(); 100 } 101 102 private void configureScoringGating() { 103 ContentObserver observer = new ContentObserver(new Handler(Looper.getMainLooper())) { 104 @Override 105 public void onChange(boolean selfChange) { 106 mScoringUiEnabled = 107 Settings.Global.getInt( 108 mContext.getContentResolver(), 109 Settings.Global.NETWORK_SCORING_UI_ENABLED, 0) == 1; 110 } 111 }; 112 mContext.getContentResolver().registerContentObserver( 113 Settings.Global.getUriFor(Settings.Global.NETWORK_SCORING_UI_ENABLED), 114 false /* notifyForDescendants */, 115 observer); 116 117 observer.onChange(false /* selfChange */); // Set the initial values 118 } 119 120 private void registerScoreCache() { 121 Log.d(mTag, "Registered score cache"); 122 mNetworkScoreManager.registerNetworkScoreCache( 123 NetworkKey.TYPE_WIFI, 124 mScoreCache, 125 NetworkScoreManager.CACHE_FILTER_CURRENT_NETWORK); 126 } 127 128 @Override 129 protected WifiState cleanState() { 130 return new WifiState(); 131 } 132 133 @Override 134 public void notifyListeners(SignalCallback callback) { 135 // only show wifi in the cluster if connected or if wifi-only 136 boolean wifiVisible = mCurrentState.enabled 137 && (mCurrentState.connected || !mHasMobileData); 138 String wifiDesc = wifiVisible ? mCurrentState.ssid : null; 139 boolean ssidPresent = wifiVisible && mCurrentState.ssid != null; 140 String contentDescription = getStringIfExists(getContentDescription()); 141 if (mCurrentState.inetCondition == 0) { 142 contentDescription += 143 ("," + mContext.getString(R.string.accessibility_quick_settings_no_internet)); 144 } 145 146 IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), 147 Utils.getWifiBadgeResource(mCurrentState.badgeEnum), contentDescription); 148 IconState qsIcon = new IconState( 149 mCurrentState.connected, getQsCurrentIconId(), 150 Utils.getWifiBadgeResource(mCurrentState.badgeEnum), contentDescription); 151 callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon, 152 ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut, 153 wifiDesc, mCurrentState.isTransient); 154 } 155 156 @Override 157 public int getCurrentIconId() { 158 if (mCurrentState.badgeEnum != NetworkBadging.BADGING_NONE) { 159 return Utils.WIFI_PIE_FOR_BADGING[mCurrentState.level]; 160 } 161 return super.getCurrentIconId(); 162 } 163 164 /** 165 * Extract wifi state directly from broadcasts about changes in wifi state. 166 */ 167 public void handleBroadcast(Intent intent) { 168 // Update the WifiStatusTracker with the new information and update the score cache. 169 NetworkKey previousNetworkKey = mWifiTracker.networkKey; 170 mWifiTracker.handleBroadcast(intent); 171 updateScoreCacheIfNecessary(previousNetworkKey); 172 173 mCurrentState.isTransient = mWifiTracker.state == WifiManager.WIFI_STATE_ENABLING 174 || mWifiTracker.state == WifiManager.WIFI_AP_STATE_DISABLING 175 || mWifiTracker.connecting; 176 mCurrentState.enabled = mWifiTracker.enabled; 177 mCurrentState.connected = mWifiTracker.connected; 178 mCurrentState.ssid = mWifiTracker.ssid; 179 mCurrentState.rssi = mWifiTracker.rssi; 180 mCurrentState.level = mWifiTracker.level; 181 mCurrentState.badgeEnum = getWifiBadgeEnum(); 182 notifyListenersIfNecessary(); 183 } 184 185 /** 186 * Clears old scores out of the cache and requests new scores if the network key has changed. 187 * 188 * <p>New scores are requested asynchronously. 189 */ 190 private void updateScoreCacheIfNecessary(NetworkKey previousNetworkKey) { 191 if (mWifiTracker.networkKey == null) { 192 return; 193 } 194 if ((previousNetworkKey == null) || !mWifiTracker.networkKey.equals(previousNetworkKey)) { 195 mScoreCache.clearScores(); 196 mNetworkScoreManager.requestScores(new NetworkKey[]{mWifiTracker.networkKey}); 197 } 198 } 199 200 /** 201 * Returns the wifi badge enum for the current {@link #mWifiTracker} state. 202 * 203 * <p>{@link #updateScoreCacheIfNecessary} should be called prior to this method. 204 */ 205 private int getWifiBadgeEnum() { 206 if (!mScoringUiEnabled || mWifiTracker.networkKey == null) { 207 return NetworkBadging.BADGING_NONE; 208 } 209 ScoredNetwork score = mScoreCache.getScoredNetwork(mWifiTracker.networkKey); 210 211 if (score != null) { 212 return score.calculateBadge(mWifiTracker.rssi); 213 } 214 return NetworkBadging.BADGING_NONE; 215 } 216 217 @VisibleForTesting 218 void setActivity(int wifiActivity) { 219 mCurrentState.activityIn = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT 220 || wifiActivity == WifiManager.DATA_ACTIVITY_IN; 221 mCurrentState.activityOut = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT 222 || wifiActivity == WifiManager.DATA_ACTIVITY_OUT; 223 notifyListenersIfNecessary(); 224 } 225 226 /** 227 * Handler to receive the data activity on wifi. 228 */ 229 private class WifiHandler extends Handler { 230 WifiHandler(Looper looper) { 231 super(looper); 232 } 233 234 @Override 235 public void handleMessage(Message msg) { 236 switch (msg.what) { 237 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: 238 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { 239 mWifiChannel.sendMessage(Message.obtain(this, 240 AsyncChannel.CMD_CHANNEL_FULL_CONNECTION)); 241 } else { 242 Log.e(mTag, "Failed to connect to wifi"); 243 } 244 break; 245 case WifiManager.DATA_ACTIVITY_NOTIFICATION: 246 setActivity(msg.arg1); 247 break; 248 default: 249 // Ignore 250 break; 251 } 252 } 253 } 254 255 static class WifiState extends SignalController.State { 256 String ssid; 257 int badgeEnum; 258 boolean isTransient; 259 260 @Override 261 public void copyFrom(State s) { 262 super.copyFrom(s); 263 WifiState state = (WifiState) s; 264 ssid = state.ssid; 265 badgeEnum = state.badgeEnum; 266 isTransient = state.isTransient; 267 } 268 269 @Override 270 protected void toString(StringBuilder builder) { 271 super.toString(builder); 272 builder.append(',').append("ssid=").append(ssid); 273 builder.append(',').append("badgeEnum=").append(badgeEnum); 274 builder.append(',').append("isTransient=").append(isTransient); 275 } 276 277 @Override 278 public boolean equals(Object o) { 279 return super.equals(o) 280 && Objects.equals(((WifiState) o).ssid, ssid) 281 && (((WifiState) o).badgeEnum == badgeEnum) 282 && (((WifiState) o).isTransient == isTransient); 283 } 284 } 285} 286