blob: de3dcb01cee8c5e4aae6952dea82b3d68c5d4c87 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 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.server;
18
19import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
20import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
21import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
22import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
23import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
24
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.app.AlarmManager;
26import android.app.PendingIntent;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -070027import android.bluetooth.BluetoothA2dp;
Jaikumar Ganesh084c6652009-12-07 10:58:18 -080028import android.bluetooth.BluetoothDevice;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.content.BroadcastReceiver;
30import android.content.ContentResolver;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
34import android.content.pm.PackageManager;
35import android.net.wifi.IWifiManager;
36import android.net.wifi.WifiInfo;
37import android.net.wifi.WifiManager;
38import android.net.wifi.WifiNative;
39import android.net.wifi.WifiStateTracker;
40import android.net.wifi.ScanResult;
41import android.net.wifi.WifiConfiguration;
San Mehat0310f9a2009-07-07 10:49:47 -070042import android.net.wifi.SupplicantState;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.net.NetworkStateTracker;
44import android.net.DhcpInfo;
Mike Lockwood0900f362009-07-10 17:24:07 -040045import android.net.NetworkUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.os.Binder;
47import android.os.Handler;
48import android.os.HandlerThread;
49import android.os.IBinder;
50import android.os.Looper;
51import android.os.Message;
52import android.os.PowerManager;
Dianne Hackborn617f8772009-03-31 15:04:46 -070053import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.os.RemoteException;
Amith Yamasani47873e52009-07-02 12:05:32 -070055import android.os.ServiceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.provider.Settings;
57import android.util.Log;
58import android.text.TextUtils;
59
60import java.util.ArrayList;
61import java.util.BitSet;
62import java.util.HashMap;
63import java.util.LinkedHashMap;
64import java.util.List;
65import java.util.Map;
Jaikumar Ganesh084c6652009-12-07 10:58:18 -080066import java.util.Set;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import java.util.regex.Pattern;
68import java.io.FileDescriptor;
69import java.io.PrintWriter;
70
The Android Open Source Project10592532009-03-18 17:39:46 -070071import com.android.internal.app.IBatteryStats;
Amith Yamasani47873e52009-07-02 12:05:32 -070072import android.backup.IBackupManager;
The Android Open Source Project10592532009-03-18 17:39:46 -070073import com.android.server.am.BatteryStatsService;
74
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075/**
76 * WifiService handles remote WiFi operation requests by implementing
77 * the IWifiManager interface. It also creates a WifiMonitor to listen
78 * for Wifi-related events.
79 *
80 * @hide
81 */
82public class WifiService extends IWifiManager.Stub {
83 private static final String TAG = "WifiService";
84 private static final boolean DBG = false;
85 private static final Pattern scanResultPattern = Pattern.compile("\t+");
86 private final WifiStateTracker mWifiStateTracker;
87
88 private Context mContext;
89 private int mWifiState;
90
91 private AlarmManager mAlarmManager;
92 private PendingIntent mIdleIntent;
93 private static final int IDLE_REQUEST = 0;
94 private boolean mScreenOff;
95 private boolean mDeviceIdle;
96 private int mPluggedType;
97
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -070098 // true if the user enabled Wifi while in airplane mode
99 private boolean mAirplaneModeOverwridden;
100
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 private final LockList mLocks = new LockList();
Eric Shienbrood5711fad2009-03-27 20:25:31 -0700102 // some wifi lock statistics
103 private int mFullLocksAcquired;
104 private int mFullLocksReleased;
105 private int mScanLocksAcquired;
106 private int mScanLocksReleased;
The Android Open Source Project10592532009-03-18 17:39:46 -0700107
Robert Greenwalt58ff0212009-05-19 15:53:54 -0700108 private final List<Multicaster> mMulticasters =
109 new ArrayList<Multicaster>();
Robert Greenwalt5347bd42009-05-13 15:10:16 -0700110 private int mMulticastEnabled;
111 private int mMulticastDisabled;
112
The Android Open Source Project10592532009-03-18 17:39:46 -0700113 private final IBatteryStats mBatteryStats;
Jaikumar Ganesh084c6652009-12-07 10:58:18 -0800114
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 /**
Doug Zongker43866e02010-01-07 12:09:54 -0800116 * See {@link Settings.Secure#WIFI_IDLE_MS}. This is the default value if a
117 * Settings.Secure value is not present. This timeout value is chosen as
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 * the approximate point at which the battery drain caused by Wi-Fi
119 * being enabled but not active exceeds the battery drain caused by
120 * re-establishing a connection to the mobile data network.
121 */
122 private static final long DEFAULT_IDLE_MILLIS = 15 * 60 * 1000; /* 15 minutes */
123
124 private static final String WAKELOCK_TAG = "WifiService";
125
126 /**
127 * The maximum amount of time to hold the wake lock after a disconnect
128 * caused by stopping the driver. Establishing an EDGE connection has been
129 * observed to take about 5 seconds under normal circumstances. This
130 * provides a bit of extra margin.
131 * <p>
132 * See {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS}.
133 * This is the default value if a Settings.Secure value is not present.
134 */
135 private static final int DEFAULT_WAKELOCK_TIMEOUT = 8000;
136
137 // Wake lock used by driver-stop operation
138 private static PowerManager.WakeLock sDriverStopWakeLock;
139 // Wake lock used by other operations
140 private static PowerManager.WakeLock sWakeLock;
141
142 private static final int MESSAGE_ENABLE_WIFI = 0;
143 private static final int MESSAGE_DISABLE_WIFI = 1;
144 private static final int MESSAGE_STOP_WIFI = 2;
145 private static final int MESSAGE_START_WIFI = 3;
146 private static final int MESSAGE_RELEASE_WAKELOCK = 4;
Robert Greenwaltf75aa36fc2009-10-22 17:03:47 -0700147 private static final int MESSAGE_UPDATE_STATE = 5;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148
149 private final WifiHandler mWifiHandler;
150
151 /*
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 * Cache of scan results objects (size is somewhat arbitrary)
153 */
154 private static final int SCAN_RESULT_CACHE_SIZE = 80;
155 private final LinkedHashMap<String, ScanResult> mScanResultCache;
156
157 /*
158 * Character buffer used to parse scan results (optimization)
159 */
160 private static final int SCAN_RESULT_BUFFER_SIZE = 512;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 private boolean mNeedReconfig;
162
Dianne Hackborn617f8772009-03-31 15:04:46 -0700163 /*
164 * Last UID that asked to enable WIFI.
165 */
166 private int mLastEnableUid = Process.myUid();
Jaikumar Ganesh084c6652009-12-07 10:58:18 -0800167
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 /**
169 * Number of allowed radio frequency channels in various regulatory domains.
170 * This list is sufficient for 802.11b/g networks (2.4GHz range).
171 */
172 private static int[] sValidRegulatoryChannelCounts = new int[] {11, 13, 14};
173
174 private static final String ACTION_DEVICE_IDLE =
175 "com.android.server.WifiManager.action.DEVICE_IDLE";
176
177 WifiService(Context context, WifiStateTracker tracker) {
178 mContext = context;
179 mWifiStateTracker = tracker;
Mike Lockwoodf32be162009-07-14 17:44:37 -0400180 mWifiStateTracker.enableRssiPolling(true);
The Android Open Source Project10592532009-03-18 17:39:46 -0700181 mBatteryStats = BatteryStatsService.getService();
Jaikumar Ganesh084c6652009-12-07 10:58:18 -0800182
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 mScanResultCache = new LinkedHashMap<String, ScanResult>(
184 SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
185 /*
186 * Limit the cache size by SCAN_RESULT_CACHE_SIZE
187 * elements
188 */
189 public boolean removeEldestEntry(Map.Entry eldest) {
190 return SCAN_RESULT_CACHE_SIZE < this.size();
191 }
192 };
193
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 HandlerThread wifiThread = new HandlerThread("WifiService");
195 wifiThread.start();
196 mWifiHandler = new WifiHandler(wifiThread.getLooper());
197
198 mWifiState = WIFI_STATE_DISABLED;
199 boolean wifiEnabled = getPersistedWifiEnabled();
200
201 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
202 Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
203 mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
204
205 PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
206 sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
207 sDriverStopWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
208 mWifiStateTracker.setReleaseWakeLockCallback(
209 new Runnable() {
210 public void run() {
211 mWifiHandler.removeMessages(MESSAGE_RELEASE_WAKELOCK);
212 synchronized (sDriverStopWakeLock) {
213 if (sDriverStopWakeLock.isHeld()) {
214 sDriverStopWakeLock.release();
215 }
216 }
217 }
218 }
219 );
220
221 Log.i(TAG, "WifiService starting up with Wi-Fi " +
222 (wifiEnabled ? "enabled" : "disabled"));
223
224 mContext.registerReceiver(
225 new BroadcastReceiver() {
226 @Override
227 public void onReceive(Context context, Intent intent) {
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -0700228 // clear our flag indicating the user has overwridden airplane mode
229 mAirplaneModeOverwridden = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 updateWifiState();
231 }
232 },
233 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
234
Dianne Hackborn617f8772009-03-31 15:04:46 -0700235 setWifiEnabledBlocking(wifiEnabled, false, Process.myUid());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 }
237
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 private boolean getPersistedWifiEnabled() {
239 final ContentResolver cr = mContext.getContentResolver();
240 try {
241 return Settings.Secure.getInt(cr, Settings.Secure.WIFI_ON) == 1;
242 } catch (Settings.SettingNotFoundException e) {
243 Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, 0);
244 return false;
245 }
246 }
247
248 private void persistWifiEnabled(boolean enabled) {
249 final ContentResolver cr = mContext.getContentResolver();
250 Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0);
251 }
252
253 NetworkStateTracker getNetworkStateTracker() {
254 return mWifiStateTracker;
255 }
256
257 /**
258 * see {@link android.net.wifi.WifiManager#pingSupplicant()}
259 * @return {@code true} if the operation succeeds
260 */
261 public boolean pingSupplicant() {
262 enforceChangePermission();
263 synchronized (mWifiStateTracker) {
264 return WifiNative.pingCommand();
265 }
266 }
267
268 /**
269 * see {@link android.net.wifi.WifiManager#startScan()}
270 * @return {@code true} if the operation succeeds
271 */
Mike Lockwooda5ec95c2009-07-08 17:11:17 -0400272 public boolean startScan(boolean forceActive) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 enforceChangePermission();
274 synchronized (mWifiStateTracker) {
275 switch (mWifiStateTracker.getSupplicantState()) {
276 case DISCONNECTED:
277 case INACTIVE:
278 case SCANNING:
279 case DORMANT:
280 break;
281 default:
282 WifiNative.setScanResultHandlingCommand(
283 WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);
284 break;
285 }
Mike Lockwooda5ec95c2009-07-08 17:11:17 -0400286 return WifiNative.scanCommand(forceActive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 }
288 }
289
290 /**
291 * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
292 * @param enable {@code true} to enable, {@code false} to disable.
293 * @return {@code true} if the enable/disable operation was
294 * started or is already in the queue.
295 */
296 public boolean setWifiEnabled(boolean enable) {
297 enforceChangePermission();
298 if (mWifiHandler == null) return false;
299
300 synchronized (mWifiHandler) {
Robert Greenwalta99f4612009-09-19 18:14:32 -0700301 // caller may not have WAKE_LOCK permission - it's not required here
302 long ident = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 sWakeLock.acquire();
Robert Greenwalta99f4612009-09-19 18:14:32 -0700304 Binder.restoreCallingIdentity(ident);
305
Dianne Hackborn617f8772009-03-31 15:04:46 -0700306 mLastEnableUid = Binder.getCallingUid();
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -0700307 // set a flag if the user is enabling Wifi while in airplane mode
308 mAirplaneModeOverwridden = (enable && isAirplaneModeOn() && isAirplaneToggleable());
Dianne Hackborn617f8772009-03-31 15:04:46 -0700309 sendEnableMessage(enable, true, Binder.getCallingUid());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 }
311
312 return true;
313 }
314
315 /**
316 * Enables/disables Wi-Fi synchronously.
317 * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off.
318 * @param persist {@code true} if the setting should be persisted.
Dianne Hackborn617f8772009-03-31 15:04:46 -0700319 * @param uid The UID of the process making the request.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 * @return {@code true} if the operation succeeds (or if the existing state
321 * is the same as the requested state)
322 */
Dianne Hackborn617f8772009-03-31 15:04:46 -0700323 private boolean setWifiEnabledBlocking(boolean enable, boolean persist, int uid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 final int eventualWifiState = enable ? WIFI_STATE_ENABLED : WIFI_STATE_DISABLED;
325
326 if (mWifiState == eventualWifiState) {
327 return true;
328 }
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -0700329 if (enable && isAirplaneModeOn() && !mAirplaneModeOverwridden) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 return false;
331 }
332
Dianne Hackborn617f8772009-03-31 15:04:46 -0700333 setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING, uid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334
335 if (enable) {
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800336 synchronized (mWifiStateTracker) {
337 if (!WifiNative.loadDriver()) {
338 Log.e(TAG, "Failed to load Wi-Fi driver.");
339 setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
340 return false;
341 }
342 if (!WifiNative.startSupplicant()) {
343 WifiNative.unloadDriver();
344 Log.e(TAG, "Failed to start supplicant daemon.");
345 setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
346 return false;
347 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 }
349 registerForBroadcasts();
350 mWifiStateTracker.startEventLoop();
351 } else {
352
353 mContext.unregisterReceiver(mReceiver);
354 // Remove notification (it will no-op if it isn't visible)
355 mWifiStateTracker.setNotificationVisible(false, 0, false, 0);
356
357 boolean failedToStopSupplicantOrUnloadDriver = false;
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800358 synchronized (mWifiStateTracker) {
359 if (!WifiNative.stopSupplicant()) {
360 Log.e(TAG, "Failed to stop supplicant daemon.");
Dianne Hackborn617f8772009-03-31 15:04:46 -0700361 setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 failedToStopSupplicantOrUnloadDriver = true;
363 }
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800364
365 // We must reset the interface before we unload the driver
366 mWifiStateTracker.resetInterface(false);
367
368 if (!WifiNative.unloadDriver()) {
369 Log.e(TAG, "Failed to unload Wi-Fi driver.");
370 if (!failedToStopSupplicantOrUnloadDriver) {
371 setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
372 failedToStopSupplicantOrUnloadDriver = true;
373 }
374 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 }
376 if (failedToStopSupplicantOrUnloadDriver) {
377 return false;
378 }
379 }
380
381 // Success!
382
383 if (persist) {
384 persistWifiEnabled(enable);
385 }
Dianne Hackborn617f8772009-03-31 15:04:46 -0700386 setWifiEnabledState(eventualWifiState, uid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387 return true;
388 }
389
Dianne Hackborn617f8772009-03-31 15:04:46 -0700390 private void setWifiEnabledState(int wifiState, int uid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 final int previousWifiState = mWifiState;
392
The Android Open Source Project10592532009-03-18 17:39:46 -0700393 long ident = Binder.clearCallingIdentity();
394 try {
395 if (wifiState == WIFI_STATE_ENABLED) {
Dianne Hackborn617f8772009-03-31 15:04:46 -0700396 mBatteryStats.noteWifiOn(uid);
The Android Open Source Project10592532009-03-18 17:39:46 -0700397 } else if (wifiState == WIFI_STATE_DISABLED) {
Dianne Hackborn617f8772009-03-31 15:04:46 -0700398 mBatteryStats.noteWifiOff(uid);
The Android Open Source Project10592532009-03-18 17:39:46 -0700399 }
400 } catch (RemoteException e) {
401 } finally {
402 Binder.restoreCallingIdentity(ident);
403 }
Jaikumar Ganesh084c6652009-12-07 10:58:18 -0800404
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 // Update state
406 mWifiState = wifiState;
407
408 // Broadcast
409 final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
410 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
411 intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
412 intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
413 mContext.sendStickyBroadcast(intent);
414 }
415
416 private void enforceAccessPermission() {
417 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
418 "WifiService");
419 }
420
421 private void enforceChangePermission() {
422 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
423 "WifiService");
424
425 }
426
Robert Greenwaltfc1b15c2009-05-22 15:09:51 -0700427 private void enforceMulticastChangePermission() {
428 mContext.enforceCallingOrSelfPermission(
429 android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
430 "WifiService");
431 }
432
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433 /**
434 * see {@link WifiManager#getWifiState()}
435 * @return One of {@link WifiManager#WIFI_STATE_DISABLED},
436 * {@link WifiManager#WIFI_STATE_DISABLING},
437 * {@link WifiManager#WIFI_STATE_ENABLED},
438 * {@link WifiManager#WIFI_STATE_ENABLING},
439 * {@link WifiManager#WIFI_STATE_UNKNOWN}
440 */
441 public int getWifiEnabledState() {
442 enforceAccessPermission();
443 return mWifiState;
444 }
445
446 /**
447 * see {@link android.net.wifi.WifiManager#disconnect()}
448 * @return {@code true} if the operation succeeds
449 */
450 public boolean disconnect() {
451 enforceChangePermission();
452 synchronized (mWifiStateTracker) {
453 return WifiNative.disconnectCommand();
454 }
455 }
456
457 /**
458 * see {@link android.net.wifi.WifiManager#reconnect()}
459 * @return {@code true} if the operation succeeds
460 */
461 public boolean reconnect() {
462 enforceChangePermission();
463 synchronized (mWifiStateTracker) {
464 return WifiNative.reconnectCommand();
465 }
466 }
467
468 /**
469 * see {@link android.net.wifi.WifiManager#reassociate()}
470 * @return {@code true} if the operation succeeds
471 */
472 public boolean reassociate() {
473 enforceChangePermission();
474 synchronized (mWifiStateTracker) {
475 return WifiNative.reassociateCommand();
476 }
477 }
478
479 /**
480 * see {@link android.net.wifi.WifiManager#getConfiguredNetworks()}
481 * @return the list of configured networks
482 */
483 public List<WifiConfiguration> getConfiguredNetworks() {
484 enforceAccessPermission();
485 String listStr;
486 /*
487 * We don't cache the list, because we want to allow
488 * for the possibility that the configuration file
489 * has been modified through some external means,
490 * such as the wpa_cli command line program.
491 */
492 synchronized (mWifiStateTracker) {
493 listStr = WifiNative.listNetworksCommand();
494 }
495 List<WifiConfiguration> networks =
496 new ArrayList<WifiConfiguration>();
497 if (listStr == null)
498 return networks;
499
500 String[] lines = listStr.split("\n");
501 // Skip the first line, which is a header
502 for (int i = 1; i < lines.length; i++) {
503 String[] result = lines[i].split("\t");
504 // network-id | ssid | bssid | flags
505 WifiConfiguration config = new WifiConfiguration();
506 try {
507 config.networkId = Integer.parseInt(result[0]);
508 } catch(NumberFormatException e) {
509 continue;
510 }
511 if (result.length > 3) {
512 if (result[3].indexOf("[CURRENT]") != -1)
513 config.status = WifiConfiguration.Status.CURRENT;
514 else if (result[3].indexOf("[DISABLED]") != -1)
515 config.status = WifiConfiguration.Status.DISABLED;
516 else
517 config.status = WifiConfiguration.Status.ENABLED;
518 } else
519 config.status = WifiConfiguration.Status.ENABLED;
520 synchronized (mWifiStateTracker) {
521 readNetworkVariables(config);
522 }
523 networks.add(config);
524 }
525
526 return networks;
527 }
528
529 /**
530 * Read the variables from the supplicant daemon that are needed to
531 * fill in the WifiConfiguration object.
532 * <p/>
533 * The caller must hold the synchronization monitor.
534 * @param config the {@link WifiConfiguration} object to be filled in.
535 */
536 private static void readNetworkVariables(WifiConfiguration config) {
537
538 int netId = config.networkId;
539 if (netId < 0)
540 return;
541
542 /*
543 * TODO: maybe should have a native method that takes an array of
544 * variable names and returns an array of values. But we'd still
545 * be doing a round trip to the supplicant daemon for each variable.
546 */
547 String value;
548
549 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
550 if (!TextUtils.isEmpty(value)) {
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800551 config.SSID = removeDoubleQuotes(value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 } else {
553 config.SSID = null;
554 }
555
556 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName);
557 if (!TextUtils.isEmpty(value)) {
558 config.BSSID = value;
559 } else {
560 config.BSSID = null;
561 }
562
563 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
564 config.priority = -1;
565 if (!TextUtils.isEmpty(value)) {
566 try {
567 config.priority = Integer.parseInt(value);
568 } catch (NumberFormatException ignore) {
569 }
570 }
571
572 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName);
573 config.hiddenSSID = false;
574 if (!TextUtils.isEmpty(value)) {
575 try {
576 config.hiddenSSID = Integer.parseInt(value) != 0;
577 } catch (NumberFormatException ignore) {
578 }
579 }
580
581 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName);
582 config.wepTxKeyIndex = -1;
583 if (!TextUtils.isEmpty(value)) {
584 try {
585 config.wepTxKeyIndex = Integer.parseInt(value);
586 } catch (NumberFormatException ignore) {
587 }
588 }
589
590 /*
591 * Get up to 4 WEP keys. Note that the actual keys are not passed back,
592 * just a "*" if the key is set, or the null string otherwise.
593 */
594 for (int i = 0; i < 4; i++) {
595 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepKeyVarNames[i]);
596 if (!TextUtils.isEmpty(value)) {
597 config.wepKeys[i] = value;
598 } else {
599 config.wepKeys[i] = null;
600 }
601 }
602
603 /*
604 * Get the private shared key. Note that the actual keys are not passed back,
605 * just a "*" if the key is set, or the null string otherwise.
606 */
607 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName);
608 if (!TextUtils.isEmpty(value)) {
609 config.preSharedKey = value;
610 } else {
611 config.preSharedKey = null;
612 }
613
614 value = WifiNative.getNetworkVariableCommand(config.networkId,
615 WifiConfiguration.Protocol.varName);
616 if (!TextUtils.isEmpty(value)) {
617 String vals[] = value.split(" ");
618 for (String val : vals) {
619 int index =
620 lookupString(val, WifiConfiguration.Protocol.strings);
621 if (0 <= index) {
622 config.allowedProtocols.set(index);
623 }
624 }
625 }
626
627 value = WifiNative.getNetworkVariableCommand(config.networkId,
628 WifiConfiguration.KeyMgmt.varName);
629 if (!TextUtils.isEmpty(value)) {
630 String vals[] = value.split(" ");
631 for (String val : vals) {
632 int index =
633 lookupString(val, WifiConfiguration.KeyMgmt.strings);
634 if (0 <= index) {
635 config.allowedKeyManagement.set(index);
636 }
637 }
638 }
639
640 value = WifiNative.getNetworkVariableCommand(config.networkId,
641 WifiConfiguration.AuthAlgorithm.varName);
642 if (!TextUtils.isEmpty(value)) {
643 String vals[] = value.split(" ");
644 for (String val : vals) {
645 int index =
646 lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
647 if (0 <= index) {
648 config.allowedAuthAlgorithms.set(index);
649 }
650 }
651 }
652
653 value = WifiNative.getNetworkVariableCommand(config.networkId,
654 WifiConfiguration.PairwiseCipher.varName);
655 if (!TextUtils.isEmpty(value)) {
656 String vals[] = value.split(" ");
657 for (String val : vals) {
658 int index =
659 lookupString(val, WifiConfiguration.PairwiseCipher.strings);
660 if (0 <= index) {
661 config.allowedPairwiseCiphers.set(index);
662 }
663 }
664 }
665
666 value = WifiNative.getNetworkVariableCommand(config.networkId,
667 WifiConfiguration.GroupCipher.varName);
668 if (!TextUtils.isEmpty(value)) {
669 String vals[] = value.split(" ");
670 for (String val : vals) {
671 int index =
672 lookupString(val, WifiConfiguration.GroupCipher.strings);
673 if (0 <= index) {
674 config.allowedGroupCiphers.set(index);
675 }
676 }
677 }
Chung-yih Wang43374762009-09-16 14:28:42 +0800678
679 for (WifiConfiguration.EnterpriseField field :
680 config.enterpriseFields) {
681 value = WifiNative.getNetworkVariableCommand(netId,
682 field.varName());
683 if (!TextUtils.isEmpty(value)) {
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800684 if (field != config.eap) value = removeDoubleQuotes(value);
Chung-yih Wang43374762009-09-16 14:28:42 +0800685 field.setValue(value);
686 }
687 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 }
689
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800690 private static String removeDoubleQuotes(String string) {
691 if (string.length() <= 2) return "";
692 return string.substring(1, string.length() - 1);
693 }
694
695 private static String convertToQuotedString(String string) {
696 return "\"" + string + "\"";
697 }
698
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 /**
700 * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)}
701 * @return the supplicant-assigned identifier for the new or updated
702 * network if the operation succeeds, or {@code -1} if it fails
703 */
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800704 public int addOrUpdateNetwork(WifiConfiguration config) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 enforceChangePermission();
706 /*
707 * If the supplied networkId is -1, we create a new empty
708 * network configuration. Otherwise, the networkId should
709 * refer to an existing configuration.
710 */
711 int netId = config.networkId;
712 boolean newNetwork = netId == -1;
713 boolean doReconfig;
714 int currentPriority;
715 // networkId of -1 means we want to create a new network
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800716 synchronized (mWifiStateTracker) {
717 if (newNetwork) {
718 netId = WifiNative.addNetworkCommand();
719 if (netId < 0) {
720 if (DBG) {
721 Log.d(TAG, "Failed to add a network!");
722 }
723 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800724 }
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800725 doReconfig = true;
726 } else {
727 String priorityVal = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
728 currentPriority = -1;
729 if (!TextUtils.isEmpty(priorityVal)) {
730 try {
731 currentPriority = Integer.parseInt(priorityVal);
732 } catch (NumberFormatException ignore) {
733 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 }
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800735 doReconfig = currentPriority != config.priority;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800736 }
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800737 mNeedReconfig = mNeedReconfig || doReconfig;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800738
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800739 setVariables: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800740 /*
741 * Note that if a networkId for a non-existent network
742 * was supplied, then the first setNetworkVariableCommand()
743 * will fail, so we don't bother to make a separate check
744 * for the validity of the ID up front.
745 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800746 if (config.SSID != null &&
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800747 !WifiNative.setNetworkVariableCommand(
748 netId,
749 WifiConfiguration.ssidVarName,
750 convertToQuotedString(config.SSID))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 if (DBG) {
752 Log.d(TAG, "failed to set SSID: "+config.SSID);
753 }
754 break setVariables;
755 }
756
757 if (config.BSSID != null &&
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800758 !WifiNative.setNetworkVariableCommand(
759 netId,
760 WifiConfiguration.bssidVarName,
761 config.BSSID)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 if (DBG) {
763 Log.d(TAG, "failed to set BSSID: "+config.BSSID);
764 }
765 break setVariables;
766 }
767
768 String allowedKeyManagementString =
769 makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
770 if (config.allowedKeyManagement.cardinality() != 0 &&
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800771 !WifiNative.setNetworkVariableCommand(
772 netId,
773 WifiConfiguration.KeyMgmt.varName,
774 allowedKeyManagementString)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 if (DBG) {
776 Log.d(TAG, "failed to set key_mgmt: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800777 allowedKeyManagementString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778 }
779 break setVariables;
780 }
781
782 String allowedProtocolsString =
783 makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
784 if (config.allowedProtocols.cardinality() != 0 &&
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800785 !WifiNative.setNetworkVariableCommand(
786 netId,
787 WifiConfiguration.Protocol.varName,
788 allowedProtocolsString)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 if (DBG) {
790 Log.d(TAG, "failed to set proto: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800791 allowedProtocolsString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792 }
793 break setVariables;
794 }
795
796 String allowedAuthAlgorithmsString =
797 makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
798 if (config.allowedAuthAlgorithms.cardinality() != 0 &&
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800799 !WifiNative.setNetworkVariableCommand(
800 netId,
801 WifiConfiguration.AuthAlgorithm.varName,
802 allowedAuthAlgorithmsString)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 if (DBG) {
804 Log.d(TAG, "failed to set auth_alg: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800805 allowedAuthAlgorithmsString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 }
807 break setVariables;
808 }
809
810 String allowedPairwiseCiphersString =
811 makeString(config.allowedPairwiseCiphers, WifiConfiguration.PairwiseCipher.strings);
812 if (config.allowedPairwiseCiphers.cardinality() != 0 &&
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800813 !WifiNative.setNetworkVariableCommand(
814 netId,
815 WifiConfiguration.PairwiseCipher.varName,
816 allowedPairwiseCiphersString)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800817 if (DBG) {
818 Log.d(TAG, "failed to set pairwise: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800819 allowedPairwiseCiphersString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800820 }
821 break setVariables;
822 }
823
824 String allowedGroupCiphersString =
825 makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
826 if (config.allowedGroupCiphers.cardinality() != 0 &&
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800827 !WifiNative.setNetworkVariableCommand(
828 netId,
829 WifiConfiguration.GroupCipher.varName,
830 allowedGroupCiphersString)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800831 if (DBG) {
832 Log.d(TAG, "failed to set group: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800833 allowedGroupCiphersString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800834 }
835 break setVariables;
836 }
837
838 // Prevent client screw-up by passing in a WifiConfiguration we gave it
839 // by preventing "*" as a key.
840 if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800841 !WifiNative.setNetworkVariableCommand(
842 netId,
843 WifiConfiguration.pskVarName,
844 config.preSharedKey)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800845 if (DBG) {
846 Log.d(TAG, "failed to set psk: "+config.preSharedKey);
847 }
848 break setVariables;
849 }
850
851 boolean hasSetKey = false;
852 if (config.wepKeys != null) {
853 for (int i = 0; i < config.wepKeys.length; i++) {
854 // Prevent client screw-up by passing in a WifiConfiguration we gave it
855 // by preventing "*" as a key.
856 if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
857 if (!WifiNative.setNetworkVariableCommand(
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800858 netId,
859 WifiConfiguration.wepKeyVarNames[i],
860 config.wepKeys[i])) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800861 if (DBG) {
862 Log.d(TAG,
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800863 "failed to set wep_key"+i+": " +
864 config.wepKeys[i]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800865 }
866 break setVariables;
867 }
868 hasSetKey = true;
869 }
870 }
871 }
872
873 if (hasSetKey) {
874 if (!WifiNative.setNetworkVariableCommand(
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800875 netId,
876 WifiConfiguration.wepTxKeyIdxVarName,
877 Integer.toString(config.wepTxKeyIndex))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 if (DBG) {
879 Log.d(TAG,
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800880 "failed to set wep_tx_keyidx: "+
881 config.wepTxKeyIndex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882 }
883 break setVariables;
884 }
885 }
886
887 if (!WifiNative.setNetworkVariableCommand(
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800888 netId,
889 WifiConfiguration.priorityVarName,
890 Integer.toString(config.priority))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800891 if (DBG) {
892 Log.d(TAG, config.SSID + ": failed to set priority: "
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800893 +config.priority);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 }
895 break setVariables;
896 }
897
898 if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand(
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800899 netId,
900 WifiConfiguration.hiddenSSIDVarName,
901 Integer.toString(config.hiddenSSID ? 1 : 0))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800902 if (DBG) {
903 Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800904 config.hiddenSSID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 }
906 break setVariables;
907 }
908
Chung-yih Wang43374762009-09-16 14:28:42 +0800909 for (WifiConfiguration.EnterpriseField field
910 : config.enterpriseFields) {
911 String varName = field.varName();
912 String value = field.value();
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800913 if (value != null) {
914 if (field != config.eap) {
915 value = convertToQuotedString(value);
Chung-yih Wang43374762009-09-16 14:28:42 +0800916 }
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800917 if (!WifiNative.setNetworkVariableCommand(
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800918 netId,
919 varName,
920 value)) {
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800921 if (DBG) {
922 Log.d(TAG, config.SSID + ": failed to set " + varName +
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800923 ": " + value);
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800924 }
925 break setVariables;
926 }
Chung-yih Wang5069cc72009-06-03 17:33:47 +0800927 }
Chung-yih Wang5069cc72009-06-03 17:33:47 +0800928 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 return netId;
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800930 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800932 /*
933 * For an update, if one of the setNetworkVariable operations fails,
934 * we might want to roll back all the changes already made. But the
935 * chances are that if anything is going to go wrong, it'll happen
936 * the first time we try to set one of the variables.
937 */
938 if (newNetwork) {
939 removeNetwork(netId);
940 if (DBG) {
941 Log.d(TAG,
942 "Failed to set a network variable, removed network: "
943 + netId);
944 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800945 }
946 }
947 return -1;
948 }
949
950 private static String makeString(BitSet set, String[] strings) {
951 StringBuffer buf = new StringBuffer();
952 int nextSetBit = -1;
953
954 /* Make sure all set bits are in [0, strings.length) to avoid
955 * going out of bounds on strings. (Shouldn't happen, but...) */
956 set = set.get(0, strings.length);
957
958 while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
959 buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
960 }
961
962 // remove trailing space
963 if (set.cardinality() > 0) {
964 buf.setLength(buf.length() - 1);
965 }
966
967 return buf.toString();
968 }
969
970 private static int lookupString(String string, String[] strings) {
971 int size = strings.length;
972
973 string = string.replace('-', '_');
974
975 for (int i = 0; i < size; i++)
976 if (string.equals(strings[i]))
977 return i;
978
979 if (DBG) {
980 // if we ever get here, we should probably add the
981 // value to WifiConfiguration to reflect that it's
982 // supported by the WPA supplicant
983 Log.w(TAG, "Failed to look-up a string: " + string);
984 }
985
986 return -1;
987 }
988
989 /**
990 * See {@link android.net.wifi.WifiManager#removeNetwork(int)}
991 * @param netId the integer that identifies the network configuration
992 * to the supplicant
993 * @return {@code true} if the operation succeeded
994 */
995 public boolean removeNetwork(int netId) {
996 enforceChangePermission();
997
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 return mWifiStateTracker.removeNetwork(netId);
999 }
1000
1001 /**
1002 * See {@link android.net.wifi.WifiManager#enableNetwork(int, boolean)}
1003 * @param netId the integer that identifies the network configuration
1004 * to the supplicant
1005 * @param disableOthers if true, disable all other networks.
1006 * @return {@code true} if the operation succeeded
1007 */
1008 public boolean enableNetwork(int netId, boolean disableOthers) {
1009 enforceChangePermission();
1010
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001011 synchronized (mWifiStateTracker) {
Mike Lockwood0900f362009-07-10 17:24:07 -04001012 String ifname = mWifiStateTracker.getInterfaceName();
1013 NetworkUtils.enableInterface(ifname);
1014 boolean result = WifiNative.enableNetworkCommand(netId, disableOthers);
1015 if (!result) {
1016 NetworkUtils.disableInterface(ifname);
1017 }
1018 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 }
1020 }
1021
1022 /**
1023 * See {@link android.net.wifi.WifiManager#disableNetwork(int)}
1024 * @param netId the integer that identifies the network configuration
1025 * to the supplicant
1026 * @return {@code true} if the operation succeeded
1027 */
1028 public boolean disableNetwork(int netId) {
1029 enforceChangePermission();
1030
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001031 synchronized (mWifiStateTracker) {
1032 return WifiNative.disableNetworkCommand(netId);
1033 }
1034 }
1035
1036 /**
1037 * See {@link android.net.wifi.WifiManager#getConnectionInfo()}
1038 * @return the Wi-Fi information, contained in {@link WifiInfo}.
1039 */
1040 public WifiInfo getConnectionInfo() {
1041 enforceAccessPermission();
1042 /*
1043 * Make sure we have the latest information, by sending
1044 * a status request to the supplicant.
1045 */
1046 return mWifiStateTracker.requestConnectionInfo();
1047 }
1048
1049 /**
1050 * Return the results of the most recent access point scan, in the form of
1051 * a list of {@link ScanResult} objects.
1052 * @return the list of results
1053 */
1054 public List<ScanResult> getScanResults() {
1055 enforceAccessPermission();
1056 String reply;
1057 synchronized (mWifiStateTracker) {
1058 reply = WifiNative.scanResultsCommand();
1059 }
1060 if (reply == null) {
1061 return null;
1062 }
1063
1064 List<ScanResult> scanList = new ArrayList<ScanResult>();
1065
1066 int lineCount = 0;
1067
1068 int replyLen = reply.length();
1069 // Parse the result string, keeping in mind that the last line does
1070 // not end with a newline.
1071 for (int lineBeg = 0, lineEnd = 0; lineEnd <= replyLen; ++lineEnd) {
1072 if (lineEnd == replyLen || reply.charAt(lineEnd) == '\n') {
1073 ++lineCount;
1074 /*
1075 * Skip the first line, which is a header
1076 */
1077 if (lineCount == 1) {
1078 lineBeg = lineEnd + 1;
1079 continue;
1080 }
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001081 if (lineEnd > lineBeg) {
1082 String line = reply.substring(lineBeg, lineEnd);
1083 ScanResult scanResult = parseScanResult(line);
1084 if (scanResult != null) {
1085 scanList.add(scanResult);
1086 } else if (DBG) {
1087 Log.w(TAG, "misformatted scan result for: " + line);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001088 }
1089 }
1090 lineBeg = lineEnd + 1;
1091 }
1092 }
1093 mWifiStateTracker.setScanResultsList(scanList);
1094 return scanList;
1095 }
1096
1097 /**
1098 * Parse the scan result line passed to us by wpa_supplicant (helper).
1099 * @param line the line to parse
1100 * @return the {@link ScanResult} object
1101 */
1102 private ScanResult parseScanResult(String line) {
1103 ScanResult scanResult = null;
1104 if (line != null) {
1105 /*
1106 * Cache implementation (LinkedHashMap) is not synchronized, thus,
1107 * must synchronized here!
1108 */
1109 synchronized (mScanResultCache) {
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001110 String[] result = scanResultPattern.split(line);
1111 if (3 <= result.length && result.length <= 5) {
1112 String bssid = result[0];
1113 // bssid | frequency | level | flags | ssid
1114 int frequency;
1115 int level;
1116 try {
1117 frequency = Integer.parseInt(result[1]);
1118 level = Integer.parseInt(result[2]);
1119 /* some implementations avoid negative values by adding 256
1120 * so we need to adjust for that here.
1121 */
1122 if (level > 0) level -= 256;
1123 } catch (NumberFormatException e) {
1124 frequency = 0;
1125 level = 0;
1126 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127
Mike Lockwood1a645052009-06-25 13:01:12 -04001128 /*
1129 * The formatting of the results returned by
1130 * wpa_supplicant is intended to make the fields
1131 * line up nicely when printed,
1132 * not to make them easy to parse. So we have to
1133 * apply some heuristics to figure out which field
1134 * is the SSID and which field is the flags.
1135 */
1136 String ssid;
1137 String flags;
1138 if (result.length == 4) {
1139 if (result[3].charAt(0) == '[') {
1140 flags = result[3];
1141 ssid = "";
1142 } else {
1143 flags = "";
1144 ssid = result[3];
1145 }
1146 } else if (result.length == 5) {
1147 flags = result[3];
1148 ssid = result[4];
1149 } else {
1150 // Here, we must have 3 fields: no flags and ssid
1151 // set
1152 flags = "";
1153 ssid = "";
1154 }
1155
Mike Lockwood00717e22009-08-17 10:09:36 -04001156 // bssid + ssid is the hash key
1157 String key = bssid + ssid;
1158 scanResult = mScanResultCache.get(key);
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001159 if (scanResult != null) {
1160 scanResult.level = level;
Mike Lockwood1a645052009-06-25 13:01:12 -04001161 scanResult.SSID = ssid;
1162 scanResult.capabilities = flags;
1163 scanResult.frequency = frequency;
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001164 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001165 // Do not add scan results that have no SSID set
1166 if (0 < ssid.trim().length()) {
1167 scanResult =
1168 new ScanResult(
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001169 ssid, bssid, flags, level, frequency);
Mike Lockwood00717e22009-08-17 10:09:36 -04001170 mScanResultCache.put(key, scanResult);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001171 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001172 }
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001173 } else {
1174 Log.w(TAG, "Misformatted scan result text with " +
1175 result.length + " fields: " + line);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001176 }
1177 }
1178 }
1179
1180 return scanResult;
1181 }
1182
1183 /**
1184 * Parse the "flags" field passed back in a scan result by wpa_supplicant,
1185 * and construct a {@code WifiConfiguration} that describes the encryption,
1186 * key management, and authenticaion capabilities of the access point.
1187 * @param flags the string returned by wpa_supplicant
1188 * @return the {@link WifiConfiguration} object, filled in
1189 */
1190 WifiConfiguration parseScanFlags(String flags) {
1191 WifiConfiguration config = new WifiConfiguration();
1192
1193 if (flags.length() == 0) {
1194 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
1195 }
1196 // ... to be implemented
1197 return config;
1198 }
1199
1200 /**
1201 * Tell the supplicant to persist the current list of configured networks.
1202 * @return {@code true} if the operation succeeded
1203 */
1204 public boolean saveConfiguration() {
1205 boolean result;
1206 enforceChangePermission();
1207 synchronized (mWifiStateTracker) {
1208 result = WifiNative.saveConfigCommand();
1209 if (result && mNeedReconfig) {
1210 mNeedReconfig = false;
1211 result = WifiNative.reloadConfigCommand();
1212
1213 if (result) {
1214 Intent intent = new Intent(WifiManager.NETWORK_IDS_CHANGED_ACTION);
1215 mContext.sendBroadcast(intent);
1216 }
1217 }
1218 }
Amith Yamasani47873e52009-07-02 12:05:32 -07001219 // Inform the backup manager about a data change
1220 IBackupManager ibm = IBackupManager.Stub.asInterface(
1221 ServiceManager.getService(Context.BACKUP_SERVICE));
1222 if (ibm != null) {
1223 try {
1224 ibm.dataChanged("com.android.providers.settings");
1225 } catch (Exception e) {
1226 // Try again later
1227 }
1228 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001229 return result;
1230 }
1231
1232 /**
1233 * Set the number of radio frequency channels that are allowed to be used
1234 * in the current regulatory domain. This method should be used only
1235 * if the correct number of channels cannot be determined automatically
Robert Greenwaltb5010cc2009-05-21 15:11:40 -07001236 * for some reason. If the operation is successful, the new value may be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001237 * persisted as a Secure setting.
1238 * @param numChannels the number of allowed channels. Must be greater than 0
1239 * and less than or equal to 16.
Robert Greenwaltb5010cc2009-05-21 15:11:40 -07001240 * @param persist {@code true} if the setting should be remembered.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001241 * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
1242 * {@code numChannels} is outside the valid range.
1243 */
Robert Greenwaltb5010cc2009-05-21 15:11:40 -07001244 public boolean setNumAllowedChannels(int numChannels, boolean persist) {
1245 Log.i(TAG, "WifiService trying to setNumAllowed to "+numChannels+
1246 " with persist set to "+persist);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001247 enforceChangePermission();
1248 /*
1249 * Validate the argument. We'd like to let the Wi-Fi driver do this,
1250 * but if Wi-Fi isn't currently enabled, that's not possible, and
1251 * we want to persist the setting anyway,so that it will take
1252 * effect when Wi-Fi does become enabled.
1253 */
1254 boolean found = false;
1255 for (int validChan : sValidRegulatoryChannelCounts) {
1256 if (validChan == numChannels) {
1257 found = true;
1258 break;
1259 }
1260 }
1261 if (!found) {
1262 return false;
1263 }
1264
Robert Greenwaltb5010cc2009-05-21 15:11:40 -07001265 if (persist) {
1266 Settings.Secure.putInt(mContext.getContentResolver(),
1267 Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
1268 numChannels);
1269 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001270 mWifiStateTracker.setNumAllowedChannels(numChannels);
1271 return true;
1272 }
1273
1274 /**
1275 * Return the number of frequency channels that are allowed
1276 * to be used in the current regulatory domain.
1277 * @return the number of allowed channels, or {@code -1} if an error occurs
1278 */
1279 public int getNumAllowedChannels() {
1280 int numChannels;
1281
1282 enforceAccessPermission();
1283 synchronized (mWifiStateTracker) {
1284 /*
1285 * If we can't get the value from the driver (e.g., because
1286 * Wi-Fi is not currently enabled), get the value from
1287 * Settings.
1288 */
1289 numChannels = WifiNative.getNumAllowedChannelsCommand();
1290 if (numChannels < 0) {
1291 numChannels = Settings.Secure.getInt(mContext.getContentResolver(),
1292 Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
1293 -1);
1294 }
1295 }
1296 return numChannels;
1297 }
1298
1299 /**
1300 * Return the list of valid values for the number of allowed radio channels
1301 * for various regulatory domains.
1302 * @return the list of channel counts
1303 */
1304 public int[] getValidChannelCounts() {
1305 enforceAccessPermission();
1306 return sValidRegulatoryChannelCounts;
1307 }
1308
1309 /**
1310 * Return the DHCP-assigned addresses from the last successful DHCP request,
1311 * if any.
1312 * @return the DHCP information
1313 */
1314 public DhcpInfo getDhcpInfo() {
1315 enforceAccessPermission();
1316 return mWifiStateTracker.getDhcpInfo();
1317 }
1318
1319 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1320 @Override
1321 public void onReceive(Context context, Intent intent) {
1322 String action = intent.getAction();
1323
Doug Zongker43866e02010-01-07 12:09:54 -08001324 long idleMillis =
1325 Settings.Secure.getLong(mContext.getContentResolver(),
1326 Settings.Secure.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 int stayAwakeConditions =
Doug Zongker43866e02010-01-07 12:09:54 -08001328 Settings.System.getInt(mContext.getContentResolver(),
1329 Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001330 if (action.equals(Intent.ACTION_SCREEN_ON)) {
Mike Lockwoodd9c32bc2009-05-18 14:14:15 -04001331 Log.d(TAG, "ACTION_SCREEN_ON");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001332 mAlarmManager.cancel(mIdleIntent);
1333 mDeviceIdle = false;
1334 mScreenOff = false;
Mike Lockwoodf32be162009-07-14 17:44:37 -04001335 mWifiStateTracker.enableRssiPolling(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001336 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
Mike Lockwoodd9c32bc2009-05-18 14:14:15 -04001337 Log.d(TAG, "ACTION_SCREEN_OFF");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001338 mScreenOff = true;
Mike Lockwoodf32be162009-07-14 17:44:37 -04001339 mWifiStateTracker.enableRssiPolling(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001340 /*
1341 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
1342 * AND the "stay on while plugged in" setting doesn't match the
1343 * current power conditions (i.e, not plugged in, plugged in to USB,
1344 * or plugged in to AC).
1345 */
1346 if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
San Mehatfa6c7112009-07-07 09:34:44 -07001347 WifiInfo info = mWifiStateTracker.requestConnectionInfo();
1348 if (info.getSupplicantState() != SupplicantState.COMPLETED) {
Robert Greenwalt84612ea62009-09-30 09:04:22 -07001349 // we used to go to sleep immediately, but this caused some race conditions
1350 // we don't have time to track down for this release. Delay instead, but not
1351 // as long as we would if connected (below)
1352 // TODO - fix the race conditions and switch back to the immediate turn-off
1353 long triggerTime = System.currentTimeMillis() + (2*60*1000); // 2 min
1354 Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for 120,000 ms");
1355 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
1356 // // do not keep Wifi awake when screen is off if Wifi is not associated
1357 // mDeviceIdle = true;
1358 // updateWifiState();
Mike Lockwoodd9c32bc2009-05-18 14:14:15 -04001359 } else {
1360 long triggerTime = System.currentTimeMillis() + idleMillis;
1361 Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
1362 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
1363 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001364 }
1365 /* we can return now -- there's nothing to do until we get the idle intent back */
1366 return;
1367 } else if (action.equals(ACTION_DEVICE_IDLE)) {
Mike Lockwoodd9c32bc2009-05-18 14:14:15 -04001368 Log.d(TAG, "got ACTION_DEVICE_IDLE");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001369 mDeviceIdle = true;
1370 } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
1371 /*
1372 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
1373 * AND we are transitioning from a state in which the device was supposed
1374 * to stay awake to a state in which it is not supposed to stay awake.
1375 * If "stay awake" state is not changing, we do nothing, to avoid resetting
1376 * the already-set timer.
1377 */
1378 int pluggedType = intent.getIntExtra("plugged", 0);
Mike Lockwoodd9c32bc2009-05-18 14:14:15 -04001379 Log.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001380 if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) &&
1381 !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) {
1382 long triggerTime = System.currentTimeMillis() + idleMillis;
Mike Lockwoodd9c32bc2009-05-18 14:14:15 -04001383 Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001384 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
1385 mPluggedType = pluggedType;
1386 return;
1387 }
1388 mPluggedType = pluggedType;
Nick Pelly005b2282009-09-10 10:21:56 -07001389 } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
Jaikumar Ganesh084c6652009-12-07 10:58:18 -08001390 BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
1391 Set<BluetoothDevice> sinks = a2dp.getConnectedSinks();
1392 boolean isBluetoothPlaying = false;
1393 for (BluetoothDevice sink : sinks) {
1394 if (a2dp.getSinkState(sink) == BluetoothA2dp.STATE_PLAYING) {
1395 isBluetoothPlaying = true;
1396 }
1397 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001398 mWifiStateTracker.setBluetoothScanMode(isBluetoothPlaying);
Jaikumar Ganesh084c6652009-12-07 10:58:18 -08001399
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001400 } else {
1401 return;
1402 }
1403
1404 updateWifiState();
1405 }
1406
1407 /**
1408 * Determines whether the Wi-Fi chipset should stay awake or be put to
1409 * sleep. Looks at the setting for the sleep policy and the current
1410 * conditions.
Jaikumar Ganesh084c6652009-12-07 10:58:18 -08001411 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001412 * @see #shouldDeviceStayAwake(int, int)
1413 */
1414 private boolean shouldWifiStayAwake(int stayAwakeConditions, int pluggedType) {
1415 int wifiSleepPolicy = Settings.System.getInt(mContext.getContentResolver(),
1416 Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_DEFAULT);
1417
1418 if (wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER) {
1419 // Never sleep
1420 return true;
1421 } else if ((wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) &&
1422 (pluggedType != 0)) {
1423 // Never sleep while plugged, and we're plugged
1424 return true;
1425 } else {
1426 // Default
1427 return shouldDeviceStayAwake(stayAwakeConditions, pluggedType);
1428 }
1429 }
Jaikumar Ganesh084c6652009-12-07 10:58:18 -08001430
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001431 /**
1432 * Determine whether the bit value corresponding to {@code pluggedType} is set in
1433 * the bit string {@code stayAwakeConditions}. Because a {@code pluggedType} value
1434 * of {@code 0} isn't really a plugged type, but rather an indication that the
1435 * device isn't plugged in at all, there is no bit value corresponding to a
1436 * {@code pluggedType} value of {@code 0}. That is why we shift by
1437 * {@code pluggedType&nbsp;&#8212;&nbsp;1} instead of by {@code pluggedType}.
1438 * @param stayAwakeConditions a bit string specifying which "plugged types" should
1439 * keep the device (and hence Wi-Fi) awake.
1440 * @param pluggedType the type of plug (USB, AC, or none) for which the check is
1441 * being made
1442 * @return {@code true} if {@code pluggedType} indicates that the device is
1443 * supposed to stay awake, {@code false} otherwise.
1444 */
1445 private boolean shouldDeviceStayAwake(int stayAwakeConditions, int pluggedType) {
1446 return (stayAwakeConditions & pluggedType) != 0;
1447 }
1448 };
1449
Dianne Hackborn617f8772009-03-31 15:04:46 -07001450 private void sendEnableMessage(boolean enable, boolean persist, int uid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001451 Message msg = Message.obtain(mWifiHandler,
1452 (enable ? MESSAGE_ENABLE_WIFI : MESSAGE_DISABLE_WIFI),
Dianne Hackborn617f8772009-03-31 15:04:46 -07001453 (persist ? 1 : 0), uid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001454 msg.sendToTarget();
1455 }
1456
1457 private void sendStartMessage(boolean scanOnlyMode) {
1458 Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget();
1459 }
1460
1461 private void updateWifiState() {
Robert Greenwaltf75aa36fc2009-10-22 17:03:47 -07001462 // send a message so it's all serialized
1463 Message.obtain(mWifiHandler, MESSAGE_UPDATE_STATE, 0, 0).sendToTarget();
1464 }
1465
1466 private void doUpdateWifiState() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001467 boolean wifiEnabled = getPersistedWifiEnabled();
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -07001468 boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001469 boolean lockHeld = mLocks.hasLocks();
1470 int strongestLockMode;
1471 boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
1472 boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
1473 if (mDeviceIdle && lockHeld) {
1474 strongestLockMode = mLocks.getStrongestLockMode();
1475 } else {
1476 strongestLockMode = WifiManager.WIFI_MODE_FULL;
1477 }
1478
1479 synchronized (mWifiHandler) {
1480 if (mWifiState == WIFI_STATE_ENABLING && !airplaneMode) {
1481 return;
1482 }
1483 if (wifiShouldBeEnabled) {
1484 if (wifiShouldBeStarted) {
1485 sWakeLock.acquire();
Dianne Hackborn617f8772009-03-31 15:04:46 -07001486 sendEnableMessage(true, false, mLastEnableUid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001487 sWakeLock.acquire();
1488 sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
1489 } else {
1490 int wakeLockTimeout =
1491 Settings.Secure.getInt(
1492 mContext.getContentResolver(),
1493 Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
1494 DEFAULT_WAKELOCK_TIMEOUT);
1495 /*
1496 * The following wakelock is held in order to ensure
1497 * that the connectivity manager has time to fail over
1498 * to the mobile data network. The connectivity manager
1499 * releases it once mobile data connectivity has been
1500 * established. If connectivity cannot be established,
1501 * the wakelock is released after wakeLockTimeout
1502 * milliseconds have elapsed.
1503 */
1504 sDriverStopWakeLock.acquire();
1505 mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI);
1506 mWifiHandler.sendEmptyMessageDelayed(MESSAGE_RELEASE_WAKELOCK, wakeLockTimeout);
1507 }
1508 } else {
1509 sWakeLock.acquire();
Dianne Hackborn617f8772009-03-31 15:04:46 -07001510 sendEnableMessage(false, false, mLastEnableUid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001511 }
1512 }
1513 }
1514
1515 private void registerForBroadcasts() {
1516 IntentFilter intentFilter = new IntentFilter();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001517 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
1518 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
1519 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
1520 intentFilter.addAction(ACTION_DEVICE_IDLE);
Nick Pelly005b2282009-09-10 10:21:56 -07001521 intentFilter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001522 mContext.registerReceiver(mReceiver, intentFilter);
1523 }
Jaikumar Ganesh084c6652009-12-07 10:58:18 -08001524
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001525 private boolean isAirplaneSensitive() {
1526 String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
1527 Settings.System.AIRPLANE_MODE_RADIOS);
1528 return airplaneModeRadios == null
1529 || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
1530 }
1531
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -07001532 private boolean isAirplaneToggleable() {
1533 String toggleableRadios = Settings.System.getString(mContext.getContentResolver(),
1534 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
1535 return toggleableRadios != null
1536 && toggleableRadios.contains(Settings.System.RADIO_WIFI);
1537 }
1538
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001539 /**
1540 * Returns true if Wi-Fi is sensitive to airplane mode, and airplane mode is
1541 * currently on.
1542 * @return {@code true} if airplane mode is on.
1543 */
1544 private boolean isAirplaneModeOn() {
1545 return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
1546 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1547 }
1548
1549 /**
1550 * Handler that allows posting to the WifiThread.
1551 */
1552 private class WifiHandler extends Handler {
1553 public WifiHandler(Looper looper) {
1554 super(looper);
1555 }
1556
1557 @Override
1558 public void handleMessage(Message msg) {
1559 switch (msg.what) {
1560
1561 case MESSAGE_ENABLE_WIFI:
Dianne Hackborn617f8772009-03-31 15:04:46 -07001562 setWifiEnabledBlocking(true, msg.arg1 == 1, msg.arg2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001563 sWakeLock.release();
1564 break;
1565
1566 case MESSAGE_START_WIFI:
1567 mWifiStateTracker.setScanOnlyMode(msg.arg1 != 0);
1568 mWifiStateTracker.restart();
1569 sWakeLock.release();
1570 break;
1571
Robert Greenwaltf75aa36fc2009-10-22 17:03:47 -07001572 case MESSAGE_UPDATE_STATE:
1573 doUpdateWifiState();
1574 break;
1575
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001576 case MESSAGE_DISABLE_WIFI:
1577 // a non-zero msg.arg1 value means the "enabled" setting
1578 // should be persisted
Dianne Hackborn617f8772009-03-31 15:04:46 -07001579 setWifiEnabledBlocking(false, msg.arg1 == 1, msg.arg2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001580 sWakeLock.release();
1581 break;
1582
1583 case MESSAGE_STOP_WIFI:
1584 mWifiStateTracker.disconnectAndStop();
1585 // don't release wakelock
1586 break;
1587
1588 case MESSAGE_RELEASE_WAKELOCK:
1589 synchronized (sDriverStopWakeLock) {
1590 if (sDriverStopWakeLock.isHeld()) {
1591 sDriverStopWakeLock.release();
1592 }
1593 }
1594 break;
1595 }
1596 }
1597 }
1598
1599 @Override
1600 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1601 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1602 != PackageManager.PERMISSION_GRANTED) {
1603 pw.println("Permission Denial: can't dump WifiService from from pid="
1604 + Binder.getCallingPid()
1605 + ", uid=" + Binder.getCallingUid());
1606 return;
1607 }
1608 pw.println("Wi-Fi is " + stateName(mWifiState));
1609 pw.println("Stay-awake conditions: " +
1610 Settings.System.getInt(mContext.getContentResolver(),
1611 Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
1612 pw.println();
1613
1614 pw.println("Internal state:");
1615 pw.println(mWifiStateTracker);
1616 pw.println();
1617 pw.println("Latest scan results:");
1618 List<ScanResult> scanResults = mWifiStateTracker.getScanResultsList();
1619 if (scanResults != null && scanResults.size() != 0) {
1620 pw.println(" BSSID Frequency RSSI Flags SSID");
1621 for (ScanResult r : scanResults) {
1622 pw.printf(" %17s %9d %5d %-16s %s%n",
1623 r.BSSID,
1624 r.frequency,
1625 r.level,
1626 r.capabilities,
1627 r.SSID == null ? "" : r.SSID);
1628 }
1629 }
1630 pw.println();
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001631 pw.println("Locks acquired: " + mFullLocksAcquired + " full, " +
1632 mScanLocksAcquired + " scan");
1633 pw.println("Locks released: " + mFullLocksReleased + " full, " +
1634 mScanLocksReleased + " scan");
1635 pw.println();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001636 pw.println("Locks held:");
1637 mLocks.dump(pw);
1638 }
1639
1640 private static String stateName(int wifiState) {
1641 switch (wifiState) {
1642 case WIFI_STATE_DISABLING:
1643 return "disabling";
1644 case WIFI_STATE_DISABLED:
1645 return "disabled";
1646 case WIFI_STATE_ENABLING:
1647 return "enabling";
1648 case WIFI_STATE_ENABLED:
1649 return "enabled";
1650 case WIFI_STATE_UNKNOWN:
1651 return "unknown state";
1652 default:
1653 return "[invalid state]";
1654 }
1655 }
1656
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001657 private class WifiLock extends DeathRecipient {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001658 WifiLock(int lockMode, String tag, IBinder binder) {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001659 super(lockMode, tag, binder);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001660 }
1661
1662 public void binderDied() {
1663 synchronized (mLocks) {
1664 releaseWifiLockLocked(mBinder);
1665 }
1666 }
1667
1668 public String toString() {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001669 return "WifiLock{" + mTag + " type=" + mMode + " binder=" + mBinder + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001670 }
1671 }
1672
1673 private class LockList {
1674 private List<WifiLock> mList;
1675
1676 private LockList() {
1677 mList = new ArrayList<WifiLock>();
1678 }
1679
1680 private synchronized boolean hasLocks() {
1681 return !mList.isEmpty();
1682 }
1683
1684 private synchronized int getStrongestLockMode() {
1685 if (mList.isEmpty()) {
1686 return WifiManager.WIFI_MODE_FULL;
1687 }
1688 for (WifiLock l : mList) {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001689 if (l.mMode == WifiManager.WIFI_MODE_FULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001690 return WifiManager.WIFI_MODE_FULL;
1691 }
1692 }
1693 return WifiManager.WIFI_MODE_SCAN_ONLY;
1694 }
1695
1696 private void addLock(WifiLock lock) {
1697 if (findLockByBinder(lock.mBinder) < 0) {
1698 mList.add(lock);
1699 }
1700 }
1701
1702 private WifiLock removeLock(IBinder binder) {
1703 int index = findLockByBinder(binder);
1704 if (index >= 0) {
Suchi Amalapurapufff2fda2009-06-30 21:36:16 -07001705 WifiLock ret = mList.remove(index);
1706 ret.unlinkDeathRecipient();
1707 return ret;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001708 } else {
1709 return null;
1710 }
1711 }
1712
1713 private int findLockByBinder(IBinder binder) {
1714 int size = mList.size();
1715 for (int i = size - 1; i >= 0; i--)
1716 if (mList.get(i).mBinder == binder)
1717 return i;
1718 return -1;
1719 }
1720
1721 private void dump(PrintWriter pw) {
1722 for (WifiLock l : mList) {
1723 pw.print(" ");
1724 pw.println(l);
1725 }
1726 }
1727 }
1728
1729 public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) {
1730 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
1731 if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) {
1732 return false;
1733 }
1734 WifiLock wifiLock = new WifiLock(lockMode, tag, binder);
1735 synchronized (mLocks) {
1736 return acquireWifiLockLocked(wifiLock);
1737 }
1738 }
1739
1740 private boolean acquireWifiLockLocked(WifiLock wifiLock) {
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07001741 Log.d(TAG, "acquireWifiLockLocked: " + wifiLock);
1742
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001743 mLocks.addLock(wifiLock);
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07001744
The Android Open Source Project10592532009-03-18 17:39:46 -07001745 int uid = Binder.getCallingUid();
1746 long ident = Binder.clearCallingIdentity();
1747 try {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001748 switch(wifiLock.mMode) {
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001749 case WifiManager.WIFI_MODE_FULL:
1750 ++mFullLocksAcquired;
1751 mBatteryStats.noteFullWifiLockAcquired(uid);
1752 break;
1753 case WifiManager.WIFI_MODE_SCAN_ONLY:
1754 ++mScanLocksAcquired;
1755 mBatteryStats.noteScanWifiLockAcquired(uid);
1756 break;
The Android Open Source Project10592532009-03-18 17:39:46 -07001757 }
1758 } catch (RemoteException e) {
1759 } finally {
1760 Binder.restoreCallingIdentity(ident);
1761 }
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07001762
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001763 updateWifiState();
1764 return true;
1765 }
1766
1767 public boolean releaseWifiLock(IBinder lock) {
1768 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
1769 synchronized (mLocks) {
1770 return releaseWifiLockLocked(lock);
1771 }
1772 }
1773
1774 private boolean releaseWifiLockLocked(IBinder lock) {
Eric Shienbroodd4c5f892009-03-24 18:13:20 -07001775 boolean hadLock;
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07001776
The Android Open Source Project10592532009-03-18 17:39:46 -07001777 WifiLock wifiLock = mLocks.removeLock(lock);
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07001778
1779 Log.d(TAG, "releaseWifiLockLocked: " + wifiLock);
1780
Eric Shienbroodd4c5f892009-03-24 18:13:20 -07001781 hadLock = (wifiLock != null);
1782
1783 if (hadLock) {
1784 int uid = Binder.getCallingUid();
1785 long ident = Binder.clearCallingIdentity();
1786 try {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001787 switch(wifiLock.mMode) {
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001788 case WifiManager.WIFI_MODE_FULL:
1789 ++mFullLocksReleased;
1790 mBatteryStats.noteFullWifiLockReleased(uid);
1791 break;
1792 case WifiManager.WIFI_MODE_SCAN_ONLY:
1793 ++mScanLocksReleased;
1794 mBatteryStats.noteScanWifiLockReleased(uid);
1795 break;
Eric Shienbroodd4c5f892009-03-24 18:13:20 -07001796 }
1797 } catch (RemoteException e) {
1798 } finally {
1799 Binder.restoreCallingIdentity(ident);
The Android Open Source Project10592532009-03-18 17:39:46 -07001800 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001801 }
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07001802 // TODO - should this only happen if you hadLock?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001803 updateWifiState();
Eric Shienbroodd4c5f892009-03-24 18:13:20 -07001804 return hadLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001805 }
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001806
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001807 private abstract class DeathRecipient
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001808 implements IBinder.DeathRecipient {
1809 String mTag;
1810 int mMode;
1811 IBinder mBinder;
1812
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001813 DeathRecipient(int mode, String tag, IBinder binder) {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001814 super();
1815 mTag = tag;
1816 mMode = mode;
1817 mBinder = binder;
1818 try {
1819 mBinder.linkToDeath(this, 0);
1820 } catch (RemoteException e) {
1821 binderDied();
1822 }
1823 }
Suchi Amalapurapufff2fda2009-06-30 21:36:16 -07001824
1825 void unlinkDeathRecipient() {
1826 mBinder.unlinkToDeath(this, 0);
1827 }
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001828 }
1829
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001830 private class Multicaster extends DeathRecipient {
1831 Multicaster(String tag, IBinder binder) {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001832 super(Binder.getCallingUid(), tag, binder);
1833 }
1834
1835 public void binderDied() {
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001836 Log.e(TAG, "Multicaster binderDied");
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001837 synchronized (mMulticasters) {
1838 int i = mMulticasters.indexOf(this);
1839 if (i != -1) {
1840 removeMulticasterLocked(i, mMode);
1841 }
1842 }
1843 }
1844
1845 public String toString() {
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001846 return "Multicaster{" + mTag + " binder=" + mBinder + "}";
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001847 }
1848
1849 public int getUid() {
1850 return mMode;
1851 }
1852 }
1853
Robert Greenwalte2d155a2009-10-21 14:58:34 -07001854 public void initializeMulticastFiltering() {
1855 enforceMulticastChangePermission();
Robert Greenwalte2d155a2009-10-21 14:58:34 -07001856 synchronized (mMulticasters) {
1857 // if anybody had requested filters be off, leave off
1858 if (mMulticasters.size() != 0) {
1859 return;
1860 } else {
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001861 synchronized (mWifiStateTracker) {
1862 WifiNative.startPacketFiltering();
1863 }
Robert Greenwalte2d155a2009-10-21 14:58:34 -07001864 }
1865 }
1866 }
1867
Robert Greenwaltfc1b15c2009-05-22 15:09:51 -07001868 public void acquireMulticastLock(IBinder binder, String tag) {
1869 enforceMulticastChangePermission();
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001870
1871 synchronized (mMulticasters) {
1872 mMulticastEnabled++;
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001873 mMulticasters.add(new Multicaster(tag, binder));
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001874 // Note that we could call stopPacketFiltering only when
1875 // our new size == 1 (first call), but this function won't
1876 // be called often and by making the stopPacket call each
1877 // time we're less fragile and self-healing.
Zheng BaoZhong8d1668d2009-08-05 08:57:49 -04001878 synchronized (mWifiStateTracker) {
1879 WifiNative.stopPacketFiltering();
1880 }
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001881 }
1882
1883 int uid = Binder.getCallingUid();
1884 Long ident = Binder.clearCallingIdentity();
1885 try {
1886 mBatteryStats.noteWifiMulticastEnabled(uid);
1887 } catch (RemoteException e) {
1888 } finally {
1889 Binder.restoreCallingIdentity(ident);
1890 }
1891 }
1892
Robert Greenwaltfc1b15c2009-05-22 15:09:51 -07001893 public void releaseMulticastLock() {
1894 enforceMulticastChangePermission();
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001895
1896 int uid = Binder.getCallingUid();
1897 synchronized (mMulticasters) {
1898 mMulticastDisabled++;
1899 int size = mMulticasters.size();
1900 for (int i = size - 1; i >= 0; i--) {
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001901 Multicaster m = mMulticasters.get(i);
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001902 if ((m != null) && (m.getUid() == uid)) {
1903 removeMulticasterLocked(i, uid);
1904 }
1905 }
1906 }
1907 }
1908
1909 private void removeMulticasterLocked(int i, int uid)
1910 {
Suchi Amalapurapufff2fda2009-06-30 21:36:16 -07001911 Multicaster removed = mMulticasters.remove(i);
1912 if (removed != null) {
1913 removed.unlinkDeathRecipient();
1914 }
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001915 if (mMulticasters.size() == 0) {
Zheng BaoZhong8d1668d2009-08-05 08:57:49 -04001916 synchronized (mWifiStateTracker) {
1917 WifiNative.startPacketFiltering();
1918 }
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001919 }
1920
1921 Long ident = Binder.clearCallingIdentity();
1922 try {
1923 mBatteryStats.noteWifiMulticastDisabled(uid);
1924 } catch (RemoteException e) {
1925 } finally {
1926 Binder.restoreCallingIdentity(ident);
1927 }
1928 }
1929
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001930 public boolean isMulticastEnabled() {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001931 enforceAccessPermission();
1932
1933 synchronized (mMulticasters) {
1934 return (mMulticasters.size() > 0);
1935 }
1936 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001937}