AccessPointControllerImpl.java revision 17f3c3f4fb3845cf933fe82495b914112e819fb4
1/* 2 * Copyright (C) 2014 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.systemui.statusbar.policy; 18 19import android.app.ActivityManager; 20import android.content.BroadcastReceiver; 21import android.content.Context; 22import android.content.Intent; 23import android.content.IntentFilter; 24import android.net.wifi.ScanResult; 25import android.net.wifi.WifiConfiguration; 26import android.net.wifi.WifiConfiguration.KeyMgmt; 27import android.net.wifi.WifiInfo; 28import android.net.wifi.WifiManager; 29import android.net.wifi.WifiManager.ActionListener; 30import android.os.UserHandle; 31import android.os.UserManager; 32import android.provider.Settings; 33import android.text.TextUtils; 34import android.util.ArrayMap; 35import android.util.ArraySet; 36import android.util.Log; 37 38import com.android.systemui.R; 39 40import java.util.ArrayList; 41import java.util.Collections; 42import java.util.Comparator; 43import java.util.List; 44 45 46// TODO: Unify this logic with platform settings (see WifiSettings and AccessPoint). There is a 47// fair amount of complexity here in statuses and logic beyond just connected/disconnected. 48public class AccessPointControllerImpl implements NetworkController.AccessPointController { 49 private static final String TAG = "AccessPointController"; 50 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 51 52 // This string extra specifies a network to open the connect dialog on, so the user can enter 53 // network credentials. This is used by quick settings for secured networks. 54 private static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid"; 55 56 private static final int[] ICONS = { 57 R.drawable.ic_qs_wifi_0, 58 R.drawable.ic_qs_wifi_full_1, 59 R.drawable.ic_qs_wifi_full_2, 60 R.drawable.ic_qs_wifi_full_3, 61 R.drawable.ic_qs_wifi_full_4, 62 }; 63 64 private final Context mContext; 65 private final ArrayList<AccessPointCallback> mCallbacks = new ArrayList<AccessPointCallback>(); 66 private final WifiManager mWifiManager; 67 private final UserManager mUserManager; 68 private final Receiver mReceiver = new Receiver(); 69 70 private NetworkControllerImpl mNetworkController; 71 private boolean mScanning; 72 private int mCurrentUser; 73 74 public AccessPointControllerImpl(Context context) { 75 mContext = context; 76 mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 77 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 78 mCurrentUser = ActivityManager.getCurrentUser(); 79 } 80 81 void setNetworkController(NetworkControllerImpl networkController) { 82 mNetworkController = networkController; 83 } 84 85 public boolean canConfigWifi() { 86 return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, 87 new UserHandle(mCurrentUser)); 88 } 89 90 public void onUserSwitched(int newUserId) { 91 mCurrentUser = newUserId; 92 } 93 94 @Override 95 public void addAccessPointCallback(AccessPointCallback callback) { 96 if (callback == null || mCallbacks.contains(callback)) return; 97 if (DEBUG) Log.d(TAG, "addCallback " + callback); 98 mCallbacks.add(callback); 99 mReceiver.setListening(!mCallbacks.isEmpty()); 100 } 101 102 @Override 103 public void removeAccessPointCallback(AccessPointCallback callback) { 104 if (callback == null) return; 105 if (DEBUG) Log.d(TAG, "removeCallback " + callback); 106 mCallbacks.remove(callback); 107 mReceiver.setListening(!mCallbacks.isEmpty()); 108 } 109 110 @Override 111 public void scanForAccessPoints() { 112 if (mScanning) return; 113 if (DEBUG) Log.d(TAG, "scan!"); 114 mScanning = mWifiManager.startScan(); 115 // Grab current networks immediately while we wait for scan. 116 updateAccessPoints(); 117 } 118 119 public boolean connect(AccessPoint ap) { 120 if (ap == null) return false; 121 if (DEBUG) Log.d(TAG, "connect networkId=" + ap.networkId); 122 if (ap.networkId < 0) { 123 // Unknown network, need to add it. 124 if (ap.hasSecurity) { 125 Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS); 126 intent.putExtra(EXTRA_START_CONNECT_SSID, ap.ssid); 127 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 128 fireSettingsIntentCallback(intent); 129 return true; 130 } else { 131 WifiConfiguration config = new WifiConfiguration(); 132 config.SSID = "\"" + ap.ssid + "\""; 133 config.allowedKeyManagement.set(KeyMgmt.NONE); 134 mWifiManager.connect(config, mConnectListener); 135 } 136 } else { 137 mWifiManager.connect(ap.networkId, mConnectListener); 138 } 139 return false; 140 } 141 142 private void fireSettingsIntentCallback(Intent intent) { 143 for (AccessPointCallback callback : mCallbacks) { 144 callback.onSettingsActivityTriggered(intent); 145 } 146 } 147 148 private void fireAcccessPointsCallback(AccessPoint[] aps) { 149 for (AccessPointCallback callback : mCallbacks) { 150 callback.onAccessPointsChanged(aps); 151 } 152 } 153 154 private static String trimDoubleQuotes(String v) { 155 return v != null && v.length() >= 2 && v.charAt(0) == '\"' 156 && v.charAt(v.length() - 1) == '\"' ? v.substring(1, v.length() - 1) : v; 157 } 158 159 private int getConnectedNetworkId(WifiInfo wifiInfo) { 160 return wifiInfo != null ? wifiInfo.getNetworkId() : AccessPoint.NO_NETWORK; 161 } 162 163 private ArrayMap<String, WifiConfiguration> getConfiguredNetworksBySsid() { 164 final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); 165 if (configs == null || configs.size() == 0) return ArrayMap.EMPTY; 166 final ArrayMap<String, WifiConfiguration> rt = new ArrayMap<String, WifiConfiguration>(); 167 for (WifiConfiguration config : configs) { 168 rt.put(trimDoubleQuotes(config.SSID), config); 169 } 170 return rt; 171 } 172 173 private void updateAccessPoints() { 174 final WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); 175 final int connectedNetworkId = getConnectedNetworkId(wifiInfo); 176 if (DEBUG) Log.d(TAG, "connectedNetworkId: " + connectedNetworkId); 177 final List<ScanResult> scanResults = mWifiManager.getScanResults(); 178 final ArrayMap<String, WifiConfiguration> configured = getConfiguredNetworksBySsid(); 179 if (DEBUG) Log.d(TAG, "scanResults: " + scanResults); 180 final List<AccessPoint> aps = new ArrayList<AccessPoint>(scanResults.size()); 181 final ArraySet<String> ssids = new ArraySet<String>(); 182 for (ScanResult scanResult : scanResults) { 183 if (scanResult == null) { 184 continue; 185 } 186 final String ssid = scanResult.SSID; 187 if (TextUtils.isEmpty(ssid) || ssids.contains(ssid)) continue; 188 ssids.add(ssid); 189 final WifiConfiguration config = configured.get(ssid); 190 final int level = WifiManager.calculateSignalLevel(scanResult.level, ICONS.length); 191 final AccessPoint ap = new AccessPoint(); 192 ap.isConfigured = config != null; 193 ap.networkId = config != null ? config.networkId : AccessPoint.NO_NETWORK; 194 ap.ssid = ssid; 195 // Connected if either: 196 // -The network ID in the active WifiInfo matches this network's ID. 197 // -The network is ephemeral (no configuration) but the SSID matches. 198 ap.isConnected = (ap.networkId != AccessPoint.NO_NETWORK 199 && ap.networkId == connectedNetworkId) || 200 (ap.networkId == WifiConfiguration.INVALID_NETWORK_ID && wifiInfo != null && 201 ap.ssid.equals(trimDoubleQuotes(wifiInfo.getSSID()))); 202 if (ap.isConnected && mNetworkController != null) { 203 // Ensure we have the connected network's RSSI. 204 ap.level = mNetworkController.getConnectedWifiLevel(); 205 } else { 206 ap.level = level; 207 } 208 ap.iconId = ICONS[ap.level]; 209 // Based on Settings AccessPoint#getSecurity, keep up to date 210 // with better methods of determining no security or not. 211 ap.hasSecurity = scanResult.capabilities.contains("WEP") 212 || scanResult.capabilities.contains("PSK") 213 || scanResult.capabilities.contains("EAP"); 214 aps.add(ap); 215 } 216 Collections.sort(aps, mByStrength); 217 fireAcccessPointsCallback(aps.toArray(new AccessPoint[aps.size()])); 218 } 219 220 private final ActionListener mConnectListener = new ActionListener() { 221 @Override 222 public void onSuccess() { 223 if (DEBUG) Log.d(TAG, "connect success"); 224 } 225 226 @Override 227 public void onFailure(int reason) { 228 if (DEBUG) Log.d(TAG, "connect failure reason=" + reason); 229 } 230 }; 231 232 private final Comparator<AccessPoint> mByStrength = new Comparator<AccessPoint> () { 233 @Override 234 public int compare(AccessPoint lhs, AccessPoint rhs) { 235 return -Integer.compare(score(lhs), score(rhs)); 236 } 237 238 private int score(AccessPoint ap) { 239 return ap.level + (ap.isConnected ? 20 : 0) + (ap.isConfigured ? 10 : 0); 240 } 241 }; 242 243 private final class Receiver extends BroadcastReceiver { 244 private boolean mRegistered; 245 246 public void setListening(boolean listening) { 247 if (listening && !mRegistered) { 248 if (DEBUG) Log.d(TAG, "Registering receiver"); 249 final IntentFilter filter = new IntentFilter(); 250 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 251 filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 252 filter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION); 253 filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); 254 filter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); 255 filter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION); 256 filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 257 filter.addAction(WifiManager.RSSI_CHANGED_ACTION); 258 mContext.registerReceiver(this, filter); 259 mRegistered = true; 260 } else if (!listening && mRegistered) { 261 if (DEBUG) Log.d(TAG, "Unregistering receiver"); 262 mContext.unregisterReceiver(this); 263 mRegistered = false; 264 } 265 } 266 267 @Override 268 public void onReceive(Context context, Intent intent) { 269 if (DEBUG) Log.d(TAG, "onReceive " + intent.getAction()); 270 if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(intent.getAction())) { 271 updateAccessPoints(); 272 mScanning = false; 273 } 274 } 275 } 276} 277