blob: ad4c2110d24ec78ef0a31298818c014572354d82 [file] [log] [blame]
John Spurlock7f8f22a2014-07-02 18:54:17 -04001/*
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
Jason Monk5d325072014-10-27 11:38:47 -040019import android.app.ActivityManager;
John Spurlock7f8f22a2014-07-02 18:54:17 -040020import 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;
Jason Monk5d325072014-10-27 11:38:47 -040026import android.net.wifi.WifiConfiguration.KeyMgmt;
John Spurlock7f8f22a2014-07-02 18:54:17 -040027import android.net.wifi.WifiInfo;
28import android.net.wifi.WifiManager;
29import android.net.wifi.WifiManager.ActionListener;
Jason Monk5d325072014-10-27 11:38:47 -040030import android.os.UserHandle;
31import android.os.UserManager;
32import android.provider.Settings;
John Spurlock7f8f22a2014-07-02 18:54:17 -040033import android.text.TextUtils;
34import android.util.ArrayMap;
35import android.util.ArraySet;
36import android.util.Log;
37
38import com.android.systemui.R;
John Spurlock7f8f22a2014-07-02 18:54:17 -040039
40import java.util.ArrayList;
41import java.util.Collections;
42import java.util.Comparator;
43import java.util.List;
44
Jeff Davidsondf9ca462014-11-23 15:53:14 -080045
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.
Jason Monkd2263cd2014-11-10 14:22:56 -050048public class AccessPointControllerImpl implements NetworkController.AccessPointController {
Jason Monk5d325072014-10-27 11:38:47 -040049 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";
John Spurlock7f8f22a2014-07-02 18:54:17 -040055
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;
Jason Monk5d325072014-10-27 11:38:47 -040067 private final UserManager mUserManager;
John Spurlock7f8f22a2014-07-02 18:54:17 -040068 private final Receiver mReceiver = new Receiver();
69
Jason Monk0e2400f2014-11-21 11:08:55 -050070 private NetworkControllerImpl mNetworkController;
John Spurlock7f8f22a2014-07-02 18:54:17 -040071 private boolean mScanning;
Jason Monk5d325072014-10-27 11:38:47 -040072 private int mCurrentUser;
John Spurlock7f8f22a2014-07-02 18:54:17 -040073
Jason Monkd2263cd2014-11-10 14:22:56 -050074 public AccessPointControllerImpl(Context context) {
John Spurlock7f8f22a2014-07-02 18:54:17 -040075 mContext = context;
76 mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
Jason Monk5d325072014-10-27 11:38:47 -040077 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
78 mCurrentUser = ActivityManager.getCurrentUser();
79 }
80
Jason Monk0e2400f2014-11-21 11:08:55 -050081 void setNetworkController(NetworkControllerImpl networkController) {
82 mNetworkController = networkController;
83 }
84
Jason Monk5d325072014-10-27 11:38:47 -040085 public boolean canConfigWifi() {
86 return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI,
87 new UserHandle(mCurrentUser));
88 }
89
Jason Monkd2263cd2014-11-10 14:22:56 -050090 public void onUserSwitched(int newUserId) {
Jason Monk5d325072014-10-27 11:38:47 -040091 mCurrentUser = newUserId;
John Spurlock7f8f22a2014-07-02 18:54:17 -040092 }
93
Jason Monkd2263cd2014-11-10 14:22:56 -050094 @Override
95 public void addAccessPointCallback(AccessPointCallback callback) {
John Spurlock7f8f22a2014-07-02 18:54:17 -040096 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
Jason Monkd2263cd2014-11-10 14:22:56 -0500102 @Override
103 public void removeAccessPointCallback(AccessPointCallback callback) {
John Spurlock7f8f22a2014-07-02 18:54:17 -0400104 if (callback == null) return;
105 if (DEBUG) Log.d(TAG, "removeCallback " + callback);
106 mCallbacks.remove(callback);
107 mReceiver.setListening(!mCallbacks.isEmpty());
108 }
109
Jason Monkd2263cd2014-11-10 14:22:56 -0500110 @Override
111 public void scanForAccessPoints() {
John Spurlock7f8f22a2014-07-02 18:54:17 -0400112 if (mScanning) return;
113 if (DEBUG) Log.d(TAG, "scan!");
114 mScanning = mWifiManager.startScan();
Jason Monk5d325072014-10-27 11:38:47 -0400115 // Grab current networks immediately while we wait for scan.
116 updateAccessPoints();
John Spurlock7f8f22a2014-07-02 18:54:17 -0400117 }
118
Jason Monk5d325072014-10-27 11:38:47 -0400119 public boolean connect(AccessPoint ap) {
120 if (ap == null) return false;
John Spurlock7f8f22a2014-07-02 18:54:17 -0400121 if (DEBUG) Log.d(TAG, "connect networkId=" + ap.networkId);
Jason Monk5d325072014-10-27 11:38:47 -0400122 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);
Jason Monk17f3c3f2015-01-14 10:13:22 -0500128 fireSettingsIntentCallback(intent);
Jason Monk5d325072014-10-27 11:38:47 -0400129 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);
John Spurlock7f8f22a2014-07-02 18:54:17 -0400135 }
Jason Monk5d325072014-10-27 11:38:47 -0400136 } else {
137 mWifiManager.connect(ap.networkId, mConnectListener);
138 }
139 return false;
John Spurlock7f8f22a2014-07-02 18:54:17 -0400140 }
141
Jason Monk17f3c3f2015-01-14 10:13:22 -0500142 private void fireSettingsIntentCallback(Intent intent) {
143 for (AccessPointCallback callback : mCallbacks) {
144 callback.onSettingsActivityTriggered(intent);
145 }
146 }
147
148 private void fireAcccessPointsCallback(AccessPoint[] aps) {
John Spurlock7f8f22a2014-07-02 18:54:17 -0400149 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
Jeff Davidsondf9ca462014-11-23 15:53:14 -0800159 private int getConnectedNetworkId(WifiInfo wifiInfo) {
John Spurlock7f8f22a2014-07-02 18:54:17 -0400160 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() {
Jeff Davidsondf9ca462014-11-23 15:53:14 -0800174 final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
175 final int connectedNetworkId = getConnectedNetworkId(wifiInfo);
John Spurlock7f8f22a2014-07-02 18:54:17 -0400176 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) {
Craig Mautner99b69282014-08-26 17:03:39 -0700183 if (scanResult == null) {
184 continue;
185 }
John Spurlock7f8f22a2014-07-02 18:54:17 -0400186 final String ssid = scanResult.SSID;
187 if (TextUtils.isEmpty(ssid) || ssids.contains(ssid)) continue;
John Spurlock7f8f22a2014-07-02 18:54:17 -0400188 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();
Jason Monk5d325072014-10-27 11:38:47 -0400192 ap.isConfigured = config != null;
John Spurlock7f8f22a2014-07-02 18:54:17 -0400193 ap.networkId = config != null ? config.networkId : AccessPoint.NO_NETWORK;
194 ap.ssid = ssid;
Jeff Davidsondf9ca462014-11-23 15:53:14 -0800195 // 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())));
Jason Monk0e2400f2014-11-21 11:08:55 -0500202 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];
Jason Monk5d325072014-10-27 11:38:47 -0400209 // 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");
John Spurlock7f8f22a2014-07-02 18:54:17 -0400214 aps.add(ap);
215 }
216 Collections.sort(aps, mByStrength);
Jason Monk17f3c3f2015-01-14 10:13:22 -0500217 fireAcccessPointsCallback(aps.toArray(new AccessPoint[aps.size()]));
John Spurlock7f8f22a2014-07-02 18:54:17 -0400218 }
219
Jason Monk5d325072014-10-27 11:38:47 -0400220 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
John Spurlock7f8f22a2014-07-02 18:54:17 -0400232 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) {
Jason Monk5d325072014-10-27 11:38:47 -0400239 return ap.level + (ap.isConnected ? 20 : 0) + (ap.isConfigured ? 10 : 0);
John Spurlock7f8f22a2014-07-02 18:54:17 -0400240 }
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}