blob: 2d61b1e537640a343461017b9ed4ff057f813c0b [file] [log] [blame]
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001/*
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
25import android.app.AlarmManager;
26import android.app.PendingIntent;
27import android.content.BroadcastReceiver;
28import android.content.ContentResolver;
29import android.content.Context;
30import android.content.Intent;
31import android.content.IntentFilter;
32import android.content.pm.PackageManager;
33import android.net.wifi.IWifiManager;
34import android.net.wifi.WifiInfo;
35import android.net.wifi.WifiManager;
36import android.net.wifi.WifiNative;
37import android.net.wifi.WifiStateTracker;
38import android.net.wifi.ScanResult;
39import android.net.wifi.WifiConfiguration;
40import android.net.NetworkStateTracker;
41import android.net.DhcpInfo;
42import android.os.Binder;
43import android.os.Handler;
44import android.os.HandlerThread;
45import android.os.IBinder;
46import android.os.Looper;
47import android.os.Message;
48import android.os.PowerManager;
49import android.os.RemoteException;
50import android.provider.Settings;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070051import android.util.Log;
52import android.text.TextUtils;
53
54import java.util.ArrayList;
55import java.util.BitSet;
56import java.util.HashMap;
57import java.util.LinkedHashMap;
58import java.util.List;
59import java.util.Map;
60import java.util.regex.Pattern;
61import java.io.FileDescriptor;
62import java.io.PrintWriter;
63
64/**
65 * WifiService handles remote WiFi operation requests by implementing
66 * the IWifiManager interface. It also creates a WifiMonitor to listen
67 * for Wifi-related events.
68 *
The Android Open Source Projectb7986892009-01-09 17:51:23 -080069 * @hide
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070070 */
71public class WifiService extends IWifiManager.Stub {
72 private static final String TAG = "WifiService";
73 private static final boolean DBG = false;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080074 private static final Pattern scanResultPattern = Pattern.compile("\t+");
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070075 private final WifiStateTracker mWifiStateTracker;
76
77 private Context mContext;
78 private int mWifiState;
79
80 private AlarmManager mAlarmManager;
81 private PendingIntent mIdleIntent;
82 private static final int IDLE_REQUEST = 0;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080083 private boolean mScreenOff;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070084 private boolean mDeviceIdle;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080085 private int mPluggedType;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070086
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080087 private final LockList mLocks = new LockList();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070088 /**
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080089 * See {@link Settings.System#WIFI_IDLE_MS}. This is the default value if a
90 * Settings.System value is not present.
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070091 */
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080092 private static final long DEFAULT_IDLE_MILLIS = 2 * 60 * 1000; /* 2 minutes */
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070093
94 private static final String WAKELOCK_TAG = "WifiService";
95
96 /**
97 * The maximum amount of time to hold the wake lock after a disconnect
98 * caused by stopping the driver. Establishing an EDGE connection has been
99 * observed to take about 5 seconds under normal circumstances. This
100 * provides a bit of extra margin.
101 * <p>
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800102 * See {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS}.
103 * This is the default value if a Settings.System value is not present.
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700104 */
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800105 private static final int DEFAULT_WAKELOCK_TIMEOUT = 8000;
106
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700107 // Wake lock used by driver-stop operation
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800108 private static PowerManager.WakeLock sDriverStopWakeLock;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700109 // Wake lock used by other operations
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800110 private static PowerManager.WakeLock sWakeLock;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700111
112 private static final int MESSAGE_ENABLE_WIFI = 0;
113 private static final int MESSAGE_DISABLE_WIFI = 1;
114 private static final int MESSAGE_STOP_WIFI = 2;
115 private static final int MESSAGE_START_WIFI = 3;
116 private static final int MESSAGE_RELEASE_WAKELOCK = 4;
117
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800118 private final WifiHandler mWifiHandler;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700119
120 /*
121 * Map used to keep track of hidden networks presence, which
122 * is needed to switch between active and passive scan modes.
123 * If there is at least one hidden network that is currently
124 * present (enabled), we want to do active scans instead of
125 * passive.
126 */
127 private final Map<Integer, Boolean> mIsHiddenNetworkPresent;
128 /*
129 * The number of currently present hidden networks. When this
130 * counter goes from 0 to 1 or from 1 to 0, we change the
131 * scan mode to active or passive respectively. Initially, we
132 * set the counter to 0 and we increment it every time we add
133 * a new present (enabled) hidden network.
134 */
135 private int mNumHiddenNetworkPresent;
136 /*
137 * Whether we change the scan mode is due to a hidden network
138 * (in this class, this is always the case)
139 */
140 private final static boolean SET_DUE_TO_A_HIDDEN_NETWORK = true;
141
142 /*
143 * Cache of scan results objects (size is somewhat arbitrary)
144 */
145 private static final int SCAN_RESULT_CACHE_SIZE = 80;
146 private final LinkedHashMap<String, ScanResult> mScanResultCache;
147
148 /*
149 * Character buffer used to parse scan results (optimization)
150 */
151 private static final int SCAN_RESULT_BUFFER_SIZE = 512;
152 private char[] mScanResultBuffer;
153 private boolean mNeedReconfig;
154
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800155 /**
156 * Number of allowed radio frequency channels in various regulatory domains.
157 * This list is sufficient for 802.11b/g networks (2.4GHz range).
158 */
159 private static int[] sValidRegulatoryChannelCounts = new int[] {11, 13, 14};
160
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700161 private static final String ACTION_DEVICE_IDLE =
162 "com.android.server.WifiManager.action.DEVICE_IDLE";
163
164 WifiService(Context context, WifiStateTracker tracker) {
165 mContext = context;
166 mWifiStateTracker = tracker;
167
168 /*
169 * Initialize the hidden-networks state
170 */
171 mIsHiddenNetworkPresent = new HashMap<Integer, Boolean>();
172 mNumHiddenNetworkPresent = 0;
173
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700174 mScanResultCache = new LinkedHashMap<String, ScanResult>(
175 SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
176 /*
177 * Limit the cache size by SCAN_RESULT_CACHE_SIZE
178 * elements
179 */
180 public boolean removeEldestEntry(Map.Entry eldest) {
181 return SCAN_RESULT_CACHE_SIZE < this.size();
182 }
183 };
184
185 mScanResultBuffer = new char [SCAN_RESULT_BUFFER_SIZE];
186
187 HandlerThread wifiThread = new HandlerThread("WifiService");
188 wifiThread.start();
189 mWifiHandler = new WifiHandler(wifiThread.getLooper());
190
191 mWifiState = WIFI_STATE_DISABLED;
192 boolean wifiEnabled = getPersistedWifiEnabled();
193
194 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
195 Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
196 mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
197
198 PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800199 sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
200 sDriverStopWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700201 mWifiStateTracker.setReleaseWakeLockCallback(
202 new Runnable() {
203 public void run() {
204 mWifiHandler.removeMessages(MESSAGE_RELEASE_WAKELOCK);
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800205 synchronized (sDriverStopWakeLock) {
206 if (sDriverStopWakeLock.isHeld()) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700207 if (DBG) Log.d(TAG, "Releasing driver-stop wakelock " +
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800208 sDriverStopWakeLock);
209 sDriverStopWakeLock.release();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700210 }
211 }
212 }
213 }
214 );
215
216 Log.d(TAG, "WifiService starting up with Wi-Fi " +
217 (wifiEnabled ? "enabled" : "disabled"));
218 registerForBroadcasts();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800219 setWifiEnabledBlocking(wifiEnabled, false);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700220 }
221
222 /**
223 * Initializes the hidden networks state. Must be called when we
224 * enable Wi-Fi.
225 */
226 private synchronized void initializeHiddenNetworksState() {
227 // First, reset the state
228 resetHiddenNetworksState();
229
230 // ... then add networks that are marked as hidden
231 List<WifiConfiguration> networks = getConfiguredNetworks();
232 if (!networks.isEmpty()) {
233 for (WifiConfiguration config : networks) {
234 if (config != null && config.hiddenSSID) {
235 addOrUpdateHiddenNetwork(
236 config.networkId,
237 config.status != WifiConfiguration.Status.DISABLED);
238 }
239 }
240
241 }
242 }
243
244 /**
245 * Resets the hidden networks state.
246 */
247 private synchronized void resetHiddenNetworksState() {
248 mNumHiddenNetworkPresent = 0;
249 mIsHiddenNetworkPresent.clear();
250 }
251
252 /**
253 * Marks all but netId network as not present.
254 */
255 private synchronized void markAllHiddenNetworksButOneAsNotPresent(int netId) {
256 for (Map.Entry<Integer, Boolean> entry : mIsHiddenNetworkPresent.entrySet()) {
257 if (entry != null) {
258 Integer networkId = entry.getKey();
259 if (networkId != netId) {
260 updateNetworkIfHidden(
261 networkId, false);
262 }
263 }
264 }
265 }
266
267 /**
268 * Updates the netId network presence status if netId is an existing
269 * hidden network.
270 */
271 private synchronized void updateNetworkIfHidden(int netId, boolean present) {
272 if (isHiddenNetwork(netId)) {
273 addOrUpdateHiddenNetwork(netId, present);
274 }
275 }
276
277 /**
278 * Updates the netId network presence status if netId is an existing
279 * hidden network. If the network does not exist, adds the network.
280 */
281 private synchronized void addOrUpdateHiddenNetwork(int netId, boolean present) {
282 if (0 <= netId) {
283
284 // If we are adding a new entry or modifying an existing one
285 Boolean isPresent = mIsHiddenNetworkPresent.get(netId);
286 if (isPresent == null || isPresent != present) {
287 if (present) {
288 incrementHiddentNetworkPresentCounter();
289 } else {
290 // If we add a new hidden network, no need to change
291 // the counter (it must be 0)
292 if (isPresent != null) {
293 decrementHiddentNetworkPresentCounter();
294 }
295 }
296 mIsHiddenNetworkPresent.put(netId, new Boolean(present));
297 }
298 } else {
299 Log.e(TAG, "addOrUpdateHiddenNetwork(): Invalid (negative) network id!");
300 }
301 }
302
303 /**
304 * Removes the netId network if it is hidden (being kept track of).
305 */
306 private synchronized void removeNetworkIfHidden(int netId) {
307 if (isHiddenNetwork(netId)) {
308 removeHiddenNetwork(netId);
309 }
310 }
311
312 /**
313 * Removes the netId network. For the call to be successful, the network
314 * must be hidden.
315 */
316 private synchronized void removeHiddenNetwork(int netId) {
317 if (0 <= netId) {
318 Boolean isPresent =
319 mIsHiddenNetworkPresent.remove(netId);
320 if (isPresent != null) {
321 // If we remove an existing hidden network that is not
322 // present, no need to change the counter
323 if (isPresent) {
324 decrementHiddentNetworkPresentCounter();
325 }
326 } else {
327 if (DBG) {
328 Log.d(TAG, "removeHiddenNetwork(): Removing a non-existent network!");
329 }
330 }
331 } else {
332 Log.e(TAG, "removeHiddenNetwork(): Invalid (negative) network id!");
333 }
334 }
335
336 /**
337 * Returns true if netId is an existing hidden network.
338 */
339 private synchronized boolean isHiddenNetwork(int netId) {
340 return mIsHiddenNetworkPresent.containsKey(netId);
341 }
342
343 /**
344 * Increments the present (enabled) hidden networks counter. If the
345 * counter value goes from 0 to 1, changes the scan mode to active.
346 */
347 private void incrementHiddentNetworkPresentCounter() {
348 ++mNumHiddenNetworkPresent;
349 if (1 == mNumHiddenNetworkPresent) {
350 // Switch the scan mode to "active"
351 mWifiStateTracker.setScanMode(true, SET_DUE_TO_A_HIDDEN_NETWORK);
352 }
353 }
354
355 /**
356 * Decrements the present (enabled) hidden networks counter. If the
357 * counter goes from 1 to 0, changes the scan mode back to passive.
358 */
359 private void decrementHiddentNetworkPresentCounter() {
360 if (0 < mNumHiddenNetworkPresent) {
361 --mNumHiddenNetworkPresent;
362 if (0 == mNumHiddenNetworkPresent) {
363 // Switch the scan mode to "passive"
364 mWifiStateTracker.setScanMode(false, SET_DUE_TO_A_HIDDEN_NETWORK);
365 }
366 } else {
367 Log.e(TAG, "Hidden-network counter invariant violation!");
368 }
369 }
370
371 private boolean getPersistedWifiEnabled() {
372 final ContentResolver cr = mContext.getContentResolver();
373 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800374 return Settings.Secure.getInt(cr, Settings.Secure.WIFI_ON) == 1;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700375 } catch (Settings.SettingNotFoundException e) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800376 Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, 0);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700377 return false;
378 }
379 }
380
381 private void persistWifiEnabled(boolean enabled) {
382 final ContentResolver cr = mContext.getContentResolver();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800383 Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700384 }
385
386 NetworkStateTracker getNetworkStateTracker() {
387 return mWifiStateTracker;
388 }
389
390 /**
391 * see {@link android.net.wifi.WifiManager#pingSupplicant()}
392 * @return {@code true} if the operation succeeds
393 */
394 public boolean pingSupplicant() {
395 enforceChangePermission();
396 synchronized (mWifiStateTracker) {
397 return WifiNative.pingCommand();
398 }
399 }
400
401 /**
402 * see {@link android.net.wifi.WifiManager#startScan()}
403 * @return {@code true} if the operation succeeds
404 */
405 public boolean startScan() {
406 enforceChangePermission();
407 synchronized (mWifiStateTracker) {
408 switch (mWifiStateTracker.getSupplicantState()) {
409 case DISCONNECTED:
410 case INACTIVE:
411 case SCANNING:
412 case DORMANT:
413 break;
414 default:
415 WifiNative.setScanResultHandlingCommand(
416 WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);
417 break;
418 }
419 return WifiNative.scanCommand();
420 }
421 }
422
423 /**
424 * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
425 * @return {@code true} if the enable/disable operation was
426 * started or is already in the queue.
427 */
428 public boolean setWifiEnabled(boolean enable) {
429 enforceChangePermission();
430 if (mWifiHandler == null) return false;
431
432 /*
433 * Remove any enable/disable Wi-Fi messages we may have in the queue
434 * before adding a new one
435 */
436 synchronized (mWifiHandler) {
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800437 sWakeLock.acquire();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800438 sendEnableMessage(enable, true);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700439 }
440
441 return true;
442 }
443
444 /**
445 * Enables/disables Wi-Fi synchronously.
446 * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off.
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800447 * @param persist {@code true} if the setting should be persisted.
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700448 * @return {@code true} if the operation succeeds (or if the existing state
449 * is the same as the requested state)
450 */
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800451 private boolean setWifiEnabledBlocking(boolean enable, boolean persist) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700452 final int eventualWifiState = enable ? WIFI_STATE_ENABLED : WIFI_STATE_DISABLED;
453
454 if (mWifiState == eventualWifiState) {
455 return true;
456 }
457 if (enable && isAirplaneModeOn()) {
458 return false;
459 }
460
461 updateWifiState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING);
462
463 if (enable) {
464 if (!WifiNative.loadDriver()) {
465 Log.e(TAG, "Failed to load Wi-Fi driver.");
466 updateWifiState(WIFI_STATE_UNKNOWN);
467 return false;
468 }
469 if (!WifiNative.startSupplicant()) {
470 WifiNative.unloadDriver();
471 Log.e(TAG, "Failed to start supplicant daemon.");
472 updateWifiState(WIFI_STATE_UNKNOWN);
473 return false;
474 }
475 mWifiStateTracker.startEventLoop();
476 } else {
477
478 // Remove notification (it will no-op if it isn't visible)
479 mWifiStateTracker.setNotificationVisible(false, 0, false, 0);
480
481 boolean failedToStopSupplicantOrUnloadDriver = false;
482 if (!WifiNative.stopSupplicant()) {
483 Log.e(TAG, "Failed to stop supplicant daemon.");
484 updateWifiState(WIFI_STATE_UNKNOWN);
485 failedToStopSupplicantOrUnloadDriver = true;
486 }
487
488 // We must reset the interface before we unload the driver
489 mWifiStateTracker.resetInterface();
490
491 if (!WifiNative.unloadDriver()) {
492 Log.e(TAG, "Failed to unload Wi-Fi driver.");
493 if (!failedToStopSupplicantOrUnloadDriver) {
494 updateWifiState(WIFI_STATE_UNKNOWN);
495 failedToStopSupplicantOrUnloadDriver = true;
496 }
497 }
498 if (failedToStopSupplicantOrUnloadDriver) {
499 return false;
500 }
501 }
502
503 // Success!
504
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800505 if (persist) {
506 persistWifiEnabled(enable);
507 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700508 updateWifiState(eventualWifiState);
509
510 /*
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800511 * Initialize the hidden networks state and the number of allowed
512 * radio channels if Wi-Fi is being turned on.
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700513 */
514 if (enable) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800515 mWifiStateTracker.setNumAllowedChannels();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700516 initializeHiddenNetworksState();
517 }
518
519 return true;
520 }
521
522 private void updateWifiState(int wifiState) {
523 final int previousWifiState = mWifiState;
524
525 // Update state
526 mWifiState = wifiState;
527
528 // Broadcast
529 final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
530 intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
531 intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
532 mContext.sendStickyBroadcast(intent);
533 }
534
535 private void enforceAccessPermission() {
536 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
537 "WifiService");
538 }
539
540 private void enforceChangePermission() {
541 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
542 "WifiService");
543
544 }
545
546 /**
547 * see {@link WifiManager#getWifiState()}
548 * @return One of {@link WifiManager#WIFI_STATE_DISABLED},
549 * {@link WifiManager#WIFI_STATE_DISABLING},
550 * {@link WifiManager#WIFI_STATE_ENABLED},
551 * {@link WifiManager#WIFI_STATE_ENABLING},
552 * {@link WifiManager#WIFI_STATE_UNKNOWN}
553 */
554 public int getWifiState() {
555 enforceAccessPermission();
556 return mWifiState;
557 }
558
559 /**
560 * see {@link android.net.wifi.WifiManager#disconnect()}
561 * @return {@code true} if the operation succeeds
562 */
563 public boolean disconnect() {
564 enforceChangePermission();
565 synchronized (mWifiStateTracker) {
566 return WifiNative.disconnectCommand();
567 }
568 }
569
570 /**
571 * see {@link android.net.wifi.WifiManager#reconnect()}
572 * @return {@code true} if the operation succeeds
573 */
574 public boolean reconnect() {
575 enforceChangePermission();
576 synchronized (mWifiStateTracker) {
577 return WifiNative.reconnectCommand();
578 }
579 }
580
581 /**
582 * see {@link android.net.wifi.WifiManager#reassociate()}
583 * @return {@code true} if the operation succeeds
584 */
585 public boolean reassociate() {
586 enforceChangePermission();
587 synchronized (mWifiStateTracker) {
588 return WifiNative.reassociateCommand();
589 }
590 }
591
592 /**
593 * see {@link android.net.wifi.WifiManager#getConfiguredNetworks()}
594 * @return the list of configured networks
595 */
596 public List<WifiConfiguration> getConfiguredNetworks() {
597 enforceAccessPermission();
598 String listStr;
599 /*
600 * We don't cache the list, because we want to allow
601 * for the possibility that the configuration file
602 * has been modified through some external means,
603 * such as the wpa_cli command line program.
604 */
605 synchronized (mWifiStateTracker) {
606 listStr = WifiNative.listNetworksCommand();
607 }
608 List<WifiConfiguration> networks =
609 new ArrayList<WifiConfiguration>();
610 if (listStr == null)
611 return networks;
612
613 String[] lines = listStr.split("\n");
614 // Skip the first line, which is a header
615 for (int i = 1; i < lines.length; i++) {
616 String[] result = lines[i].split("\t");
617 // network-id | ssid | bssid | flags
618 WifiConfiguration config = new WifiConfiguration();
619 try {
620 config.networkId = Integer.parseInt(result[0]);
621 } catch(NumberFormatException e) {
622 continue;
623 }
624 if (result.length > 3) {
625 if (result[3].indexOf("[CURRENT]") != -1)
626 config.status = WifiConfiguration.Status.CURRENT;
627 else if (result[3].indexOf("[DISABLED]") != -1)
628 config.status = WifiConfiguration.Status.DISABLED;
629 else
630 config.status = WifiConfiguration.Status.ENABLED;
631 } else
632 config.status = WifiConfiguration.Status.ENABLED;
633 synchronized (mWifiStateTracker) {
634 readNetworkVariables(config);
635 }
636 networks.add(config);
637 }
638
639 return networks;
640 }
641
642 /**
643 * Read the variables from the supplicant daemon that are needed to
644 * fill in the WifiConfiguration object.
645 * <p/>
646 * The caller must hold the synchronization monitor.
647 * @param config the {@link WifiConfiguration} object to be filled in.
648 */
649 private static void readNetworkVariables(WifiConfiguration config) {
650
651 int netId = config.networkId;
652 if (netId < 0)
653 return;
654
655 /*
656 * TODO: maybe should have a native method that takes an array of
657 * variable names and returns an array of values. But we'd still
658 * be doing a round trip to the supplicant daemon for each variable.
659 */
660 String value;
661
662 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
663 if (!TextUtils.isEmpty(value)) {
664 config.SSID = value;
665 } else {
666 config.SSID = null;
667 }
668
669 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName);
670 if (!TextUtils.isEmpty(value)) {
671 config.BSSID = value;
672 } else {
673 config.BSSID = null;
674 }
675
676 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
677 config.priority = -1;
678 if (!TextUtils.isEmpty(value)) {
679 try {
680 config.priority = Integer.parseInt(value);
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800681 } catch (NumberFormatException ignore) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700682 }
683 }
684
685 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName);
686 config.hiddenSSID = false;
687 if (!TextUtils.isEmpty(value)) {
688 try {
689 config.hiddenSSID = Integer.parseInt(value) != 0;
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800690 } catch (NumberFormatException ignore) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700691 }
692 }
693
694 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName);
695 config.wepTxKeyIndex = -1;
696 if (!TextUtils.isEmpty(value)) {
697 try {
698 config.wepTxKeyIndex = Integer.parseInt(value);
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800699 } catch (NumberFormatException ignore) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700700 }
701 }
702
703 /*
704 * Get up to 4 WEP keys. Note that the actual keys are not passed back,
705 * just a "*" if the key is set, or the null string otherwise.
706 */
707 for (int i = 0; i < 4; i++) {
708 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepKeyVarNames[i]);
709 if (!TextUtils.isEmpty(value)) {
710 config.wepKeys[i] = value;
711 } else {
712 config.wepKeys[i] = null;
713 }
714 }
715
716 /*
717 * Get the private shared key. Note that the actual keys are not passed back,
718 * just a "*" if the key is set, or the null string otherwise.
719 */
720 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName);
721 if (!TextUtils.isEmpty(value)) {
722 config.preSharedKey = value;
723 } else {
724 config.preSharedKey = null;
725 }
726
727 value = WifiNative.getNetworkVariableCommand(config.networkId,
728 WifiConfiguration.Protocol.varName);
729 if (!TextUtils.isEmpty(value)) {
730 String vals[] = value.split(" ");
731 for (String val : vals) {
732 int index =
733 lookupString(val, WifiConfiguration.Protocol.strings);
734 if (0 <= index) {
735 config.allowedProtocols.set(index);
736 }
737 }
738 }
739
740 value = WifiNative.getNetworkVariableCommand(config.networkId,
741 WifiConfiguration.KeyMgmt.varName);
742 if (!TextUtils.isEmpty(value)) {
743 String vals[] = value.split(" ");
744 for (String val : vals) {
745 int index =
746 lookupString(val, WifiConfiguration.KeyMgmt.strings);
747 if (0 <= index) {
748 config.allowedKeyManagement.set(index);
749 }
750 }
751 }
752
753 value = WifiNative.getNetworkVariableCommand(config.networkId,
754 WifiConfiguration.AuthAlgorithm.varName);
755 if (!TextUtils.isEmpty(value)) {
756 String vals[] = value.split(" ");
757 for (String val : vals) {
758 int index =
759 lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
760 if (0 <= index) {
761 config.allowedAuthAlgorithms.set(index);
762 }
763 }
764 }
765
766 value = WifiNative.getNetworkVariableCommand(config.networkId,
767 WifiConfiguration.PairwiseCipher.varName);
768 if (!TextUtils.isEmpty(value)) {
769 String vals[] = value.split(" ");
770 for (String val : vals) {
771 int index =
772 lookupString(val, WifiConfiguration.PairwiseCipher.strings);
773 if (0 <= index) {
774 config.allowedPairwiseCiphers.set(index);
775 }
776 }
777 }
778
779 value = WifiNative.getNetworkVariableCommand(config.networkId,
780 WifiConfiguration.GroupCipher.varName);
781 if (!TextUtils.isEmpty(value)) {
782 String vals[] = value.split(" ");
783 for (String val : vals) {
784 int index =
785 lookupString(val, WifiConfiguration.GroupCipher.strings);
786 if (0 <= index) {
787 config.allowedGroupCiphers.set(index);
788 }
789 }
790 }
791 }
792
793 /**
794 * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)}
795 * @return the supplicant-assigned identifier for the new or updated
796 * network if the operation succeeds, or {@code -1} if it fails
797 */
798 public synchronized int addOrUpdateNetwork(WifiConfiguration config) {
799 enforceChangePermission();
800 /*
801 * If the supplied networkId is -1, we create a new empty
802 * network configuration. Otherwise, the networkId should
803 * refer to an existing configuration.
804 */
805 int netId = config.networkId;
806 boolean newNetwork = netId == -1;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800807 boolean doReconfig;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700808 int currentPriority;
809 // networkId of -1 means we want to create a new network
810 if (newNetwork) {
811 netId = WifiNative.addNetworkCommand();
812 if (netId < 0) {
813 if (DBG) {
814 Log.d(TAG, "Failed to add a network!");
815 }
816 return -1;
817 }
818 doReconfig = true;
819 } else {
820 String priorityVal = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
821 currentPriority = -1;
822 if (!TextUtils.isEmpty(priorityVal)) {
823 try {
824 currentPriority = Integer.parseInt(priorityVal);
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800825 } catch (NumberFormatException ignore) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700826 }
827 }
828 doReconfig = currentPriority != config.priority;
829 }
830 mNeedReconfig = mNeedReconfig || doReconfig;
831
832 /*
833 * If we have hidden networks, we may have to change the scan mode
834 */
835 if (config.hiddenSSID) {
836 // Mark the network as present unless it is disabled
837 addOrUpdateHiddenNetwork(
838 netId, config.status != WifiConfiguration.Status.DISABLED);
839 }
840
841 setVariables: {
842 /*
843 * Note that if a networkId for a non-existent network
844 * was supplied, then the first setNetworkVariableCommand()
845 * will fail, so we don't bother to make a separate check
846 * for the validity of the ID up front.
847 */
848
849 if (config.SSID != null &&
850 !WifiNative.setNetworkVariableCommand(
851 netId,
852 WifiConfiguration.ssidVarName,
853 config.SSID)) {
854 if (DBG) {
855 Log.d(TAG, "failed to set SSID: "+config.SSID);
856 }
857 break setVariables;
858 }
859
860 if (config.BSSID != null &&
861 !WifiNative.setNetworkVariableCommand(
862 netId,
863 WifiConfiguration.bssidVarName,
864 config.BSSID)) {
865 if (DBG) {
866 Log.d(TAG, "failed to set BSSID: "+config.BSSID);
867 }
868 break setVariables;
869 }
870
871 String allowedKeyManagementString =
872 makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
873 if (config.allowedKeyManagement.cardinality() != 0 &&
874 !WifiNative.setNetworkVariableCommand(
875 netId,
876 WifiConfiguration.KeyMgmt.varName,
877 allowedKeyManagementString)) {
878 if (DBG) {
879 Log.d(TAG, "failed to set key_mgmt: "+
880 allowedKeyManagementString);
881 }
882 break setVariables;
883 }
884
885 String allowedProtocolsString =
886 makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
887 if (config.allowedProtocols.cardinality() != 0 &&
888 !WifiNative.setNetworkVariableCommand(
889 netId,
890 WifiConfiguration.Protocol.varName,
891 allowedProtocolsString)) {
892 if (DBG) {
893 Log.d(TAG, "failed to set proto: "+
894 allowedProtocolsString);
895 }
896 break setVariables;
897 }
898
899 String allowedAuthAlgorithmsString =
900 makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
901 if (config.allowedAuthAlgorithms.cardinality() != 0 &&
902 !WifiNative.setNetworkVariableCommand(
903 netId,
904 WifiConfiguration.AuthAlgorithm.varName,
905 allowedAuthAlgorithmsString)) {
906 if (DBG) {
907 Log.d(TAG, "failed to set auth_alg: "+
908 allowedAuthAlgorithmsString);
909 }
910 break setVariables;
911 }
912
913 String allowedPairwiseCiphersString =
914 makeString(config.allowedPairwiseCiphers, WifiConfiguration.PairwiseCipher.strings);
915 if (config.allowedPairwiseCiphers.cardinality() != 0 &&
916 !WifiNative.setNetworkVariableCommand(
917 netId,
918 WifiConfiguration.PairwiseCipher.varName,
919 allowedPairwiseCiphersString)) {
920 if (DBG) {
921 Log.d(TAG, "failed to set pairwise: "+
922 allowedPairwiseCiphersString);
923 }
924 break setVariables;
925 }
926
927 String allowedGroupCiphersString =
928 makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
929 if (config.allowedGroupCiphers.cardinality() != 0 &&
930 !WifiNative.setNetworkVariableCommand(
931 netId,
932 WifiConfiguration.GroupCipher.varName,
933 allowedGroupCiphersString)) {
934 if (DBG) {
935 Log.d(TAG, "failed to set group: "+
936 allowedGroupCiphersString);
937 }
938 break setVariables;
939 }
940
941 // Prevent client screw-up by passing in a WifiConfiguration we gave it
942 // by preventing "*" as a key.
943 if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
944 !WifiNative.setNetworkVariableCommand(
945 netId,
946 WifiConfiguration.pskVarName,
947 config.preSharedKey)) {
948 if (DBG) {
949 Log.d(TAG, "failed to set psk: "+config.preSharedKey);
950 }
951 break setVariables;
952 }
953
954 boolean hasSetKey = false;
955 if (config.wepKeys != null) {
956 for (int i = 0; i < config.wepKeys.length; i++) {
957 // Prevent client screw-up by passing in a WifiConfiguration we gave it
958 // by preventing "*" as a key.
959 if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
960 if (!WifiNative.setNetworkVariableCommand(
961 netId,
962 WifiConfiguration.wepKeyVarNames[i],
963 config.wepKeys[i])) {
964 if (DBG) {
965 Log.d(TAG,
966 "failed to set wep_key"+i+": " +
967 config.wepKeys[i]);
968 }
969 break setVariables;
970 }
971 hasSetKey = true;
972 }
973 }
974 }
975
976 if (hasSetKey) {
977 if (!WifiNative.setNetworkVariableCommand(
978 netId,
979 WifiConfiguration.wepTxKeyIdxVarName,
980 Integer.toString(config.wepTxKeyIndex))) {
981 if (DBG) {
982 Log.d(TAG,
983 "failed to set wep_tx_keyidx: "+
984 config.wepTxKeyIndex);
985 }
986 break setVariables;
987 }
988 }
989
990 if (!WifiNative.setNetworkVariableCommand(
991 netId,
992 WifiConfiguration.priorityVarName,
993 Integer.toString(config.priority))) {
994 if (DBG) {
995 Log.d(TAG, config.SSID + ": failed to set priority: "
996 +config.priority);
997 }
998 break setVariables;
999 }
1000
1001 if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand(
1002 netId,
1003 WifiConfiguration.hiddenSSIDVarName,
1004 Integer.toString(config.hiddenSSID ? 1 : 0))) {
1005 if (DBG) {
1006 Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+
1007 config.hiddenSSID);
1008 }
1009 break setVariables;
1010 }
1011
1012 return netId;
1013 }
1014
1015 /*
1016 * For an update, if one of the setNetworkVariable operations fails,
1017 * we might want to roll back all the changes already made. But the
1018 * chances are that if anything is going to go wrong, it'll happen
1019 * the first time we try to set one of the variables.
1020 */
1021 if (newNetwork) {
1022 removeNetwork(netId);
1023 if (DBG) {
1024 Log.d(TAG,
1025 "Failed to set a network variable, removed network: "
1026 + netId);
1027 }
1028 }
1029 return -1;
1030 }
1031
1032 private static String makeString(BitSet set, String[] strings) {
1033 StringBuffer buf = new StringBuffer();
1034 int nextSetBit = -1;
1035
1036 /* Make sure all set bits are in [0, strings.length) to avoid
1037 * going out of bounds on strings. (Shouldn't happen, but...) */
1038 set = set.get(0, strings.length);
1039
1040 while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
1041 buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
1042 }
1043
1044 // remove trailing space
1045 if (set.cardinality() > 0) {
1046 buf.setLength(buf.length() - 1);
1047 }
1048
1049 return buf.toString();
1050 }
1051
1052 private static int lookupString(String string, String[] strings) {
1053 int size = strings.length;
1054
1055 string = string.replace('-', '_');
1056
1057 for (int i = 0; i < size; i++)
1058 if (string.equals(strings[i]))
1059 return i;
1060
1061 if (DBG) {
1062 // if we ever get here, we should probably add the
1063 // value to WifiConfiguration to reflect that it's
1064 // supported by the WPA supplicant
1065 Log.w(TAG, "Failed to look-up a string: " + string);
1066 }
1067
1068 return -1;
1069 }
1070
1071 /**
1072 * See {@link android.net.wifi.WifiManager#removeNetwork(int)}
1073 * @param netId the integer that identifies the network configuration
1074 * to the supplicant
1075 * @return {@code true} if the operation succeeded
1076 */
1077 public boolean removeNetwork(int netId) {
1078 enforceChangePermission();
1079
1080 /*
1081 * If we have hidden networks, we may have to change the scan mode
1082 */
1083 removeNetworkIfHidden(netId);
1084
1085 synchronized (mWifiStateTracker) {
1086 return WifiNative.removeNetworkCommand(netId);
1087 }
1088 }
1089
1090 /**
1091 * See {@link android.net.wifi.WifiManager#enableNetwork(int, boolean)}
1092 * @param netId the integer that identifies the network configuration
1093 * to the supplicant
1094 * @param disableOthers if true, disable all other networks.
1095 * @return {@code true} if the operation succeeded
1096 */
1097 public boolean enableNetwork(int netId, boolean disableOthers) {
1098 enforceChangePermission();
1099
1100 /*
1101 * If we have hidden networks, we may have to change the scan mode
1102 */
1103 synchronized(this) {
1104 if (disableOthers) {
1105 markAllHiddenNetworksButOneAsNotPresent(netId);
1106 }
1107 updateNetworkIfHidden(netId, true);
1108 }
1109
1110 synchronized (mWifiStateTracker) {
1111 return WifiNative.enableNetworkCommand(netId, disableOthers);
1112 }
1113 }
1114
1115 /**
1116 * See {@link android.net.wifi.WifiManager#disableNetwork(int)}
1117 * @param netId the integer that identifies the network configuration
1118 * to the supplicant
1119 * @return {@code true} if the operation succeeded
1120 */
1121 public boolean disableNetwork(int netId) {
1122 enforceChangePermission();
1123
1124 /*
1125 * If we have hidden networks, we may have to change the scan mode
1126 */
1127 updateNetworkIfHidden(netId, false);
1128
1129 synchronized (mWifiStateTracker) {
1130 return WifiNative.disableNetworkCommand(netId);
1131 }
1132 }
1133
1134 /**
1135 * See {@link android.net.wifi.WifiManager#getConnectionInfo()}
1136 * @return the Wi-Fi information, contained in {@link WifiInfo}.
1137 */
1138 public WifiInfo getConnectionInfo() {
1139 enforceAccessPermission();
1140 /*
1141 * Make sure we have the latest information, by sending
1142 * a status request to the supplicant.
1143 */
1144 return mWifiStateTracker.requestConnectionInfo();
1145 }
1146
1147 /**
1148 * Return the results of the most recent access point scan, in the form of
1149 * a list of {@link ScanResult} objects.
1150 * @return the list of results
1151 */
1152 public List<ScanResult> getScanResults() {
1153 enforceAccessPermission();
1154 String reply;
1155 synchronized (mWifiStateTracker) {
1156 reply = WifiNative.scanResultsCommand();
1157 }
1158 if (reply == null) {
1159 return null;
1160 }
1161
1162 List<ScanResult> scanList = new ArrayList<ScanResult>();
1163
1164 int lineCount = 0;
1165
1166 int replyLen = reply.length();
1167 // Parse the result string, keeping in mind that the last line does
1168 // not end with a newline.
1169 for (int lineBeg = 0, lineEnd = 0; lineEnd <= replyLen; ++lineEnd) {
1170 if (lineEnd == replyLen || reply.charAt(lineEnd) == '\n') {
1171 ++lineCount;
1172 /*
1173 * Skip the first line, which is a header
1174 */
1175 if (lineCount == 1) {
1176 lineBeg = lineEnd + 1;
1177 continue;
1178 }
1179 int lineLen = lineEnd - lineBeg;
1180 if (0 < lineLen && lineLen <= SCAN_RESULT_BUFFER_SIZE) {
1181 int scanResultLevel = 0;
1182 /*
1183 * At most one thread should have access to the buffer at a time!
1184 */
1185 synchronized(mScanResultBuffer) {
1186 boolean parsingScanResultLevel = false;
1187 for (int i = lineBeg; i < lineEnd; ++i) {
1188 char ch = reply.charAt(i);
1189 /*
1190 * Assume that the signal level starts with a '-'
1191 */
1192 if (ch == '-') {
1193 /*
1194 * Skip whatever instances of '-' we may have
1195 * after we parse the signal level
1196 */
1197 parsingScanResultLevel = (scanResultLevel == 0);
1198 } else if (parsingScanResultLevel) {
1199 int digit = Character.digit(ch, 10);
1200 if (0 <= digit) {
1201 scanResultLevel =
1202 10 * scanResultLevel + digit;
1203 /*
1204 * Replace the signal level number in
1205 * the string with 0's for caching
1206 */
1207 ch = '0';
1208 } else {
1209 /*
1210 * Reset the flag if we meet a non-digit
1211 * character
1212 */
1213 parsingScanResultLevel = false;
1214 }
1215 }
1216 mScanResultBuffer[i - lineBeg] = ch;
1217 }
1218 if (scanResultLevel != 0) {
1219 ScanResult scanResult = parseScanResult(
1220 new String(mScanResultBuffer, 0, lineLen));
1221 if (scanResult != null) {
1222 scanResult.level = -scanResultLevel;
1223 scanList.add(scanResult);
1224 //if (DBG) Log.d(TAG, "ScanResult: " + scanResult);
1225 }
1226 } else if (DBG) {
1227 Log.w(TAG,
1228 "ScanResult.level=0: misformatted scan result?");
1229 }
1230 }
1231 } else if (0 < lineLen) {
1232 if (DBG) {
1233 Log.w(TAG, "Scan result line is too long: " +
1234 (lineEnd - lineBeg) + ", skipping the line!");
1235 }
1236 }
1237 lineBeg = lineEnd + 1;
1238 }
1239 }
1240 mWifiStateTracker.setScanResultsList(scanList);
1241 return scanList;
1242 }
1243
1244 /**
1245 * Parse the scan result line passed to us by wpa_supplicant (helper).
1246 * @param line the line to parse
1247 * @return the {@link ScanResult} object
1248 */
1249 private ScanResult parseScanResult(String line) {
1250 ScanResult scanResult = null;
1251 if (line != null) {
1252 /*
1253 * Cache implementation (LinkedHashMap) is not synchronized, thus,
1254 * must synchronized here!
1255 */
1256 synchronized (mScanResultCache) {
1257 scanResult = mScanResultCache.get(line);
1258 if (scanResult == null) {
1259 String[] result = scanResultPattern.split(line);
1260 if (3 <= result.length && result.length <= 5) {
1261 // bssid | frequency | level | flags | ssid
1262 int frequency;
1263 int level;
1264 try {
1265 frequency = Integer.parseInt(result[1]);
1266 level = Integer.parseInt(result[2]);
1267 } catch (NumberFormatException e) {
1268 frequency = 0;
1269 level = 0;
1270 }
1271
1272 /*
1273 * The formatting of the results returned by
1274 * wpa_supplicant is intended to make the fields
1275 * line up nicely when printed,
1276 * not to make them easy to parse. So we have to
1277 * apply some heuristics to figure out which field
1278 * is the SSID and which field is the flags.
1279 */
1280 String ssid;
1281 String flags;
1282 if (result.length == 4) {
1283 if (result[3].charAt(0) == '[') {
1284 flags = result[3];
1285 ssid = "";
1286 } else {
1287 flags = "";
1288 ssid = result[3];
1289 }
1290 } else if (result.length == 5) {
1291 flags = result[3];
1292 ssid = result[4];
1293 } else {
1294 // Here, we must have 3 fields: no flags and ssid
1295 // set
1296 flags = "";
1297 ssid = "";
1298 }
1299
1300 // Do not add scan results that have no SSID set
1301 if (0 < ssid.trim().length()) {
1302 scanResult =
1303 new ScanResult(
1304 ssid, result[0], flags, level, frequency);
1305 mScanResultCache.put(line, scanResult);
1306 }
1307 } else {
1308 Log.w(TAG, "Misformatted scan result text with " +
1309 result.length + " fields: " + line);
1310 }
1311 }
1312 }
1313 }
1314
1315 return scanResult;
1316 }
1317
1318 /**
1319 * Parse the "flags" field passed back in a scan result by wpa_supplicant,
1320 * and construct a {@code WifiConfiguration} that describes the encryption,
1321 * key management, and authenticaion capabilities of the access point.
1322 * @param flags the string returned by wpa_supplicant
1323 * @return the {@link WifiConfiguration} object, filled in
1324 */
1325 WifiConfiguration parseScanFlags(String flags) {
1326 WifiConfiguration config = new WifiConfiguration();
1327
1328 if (flags.length() == 0) {
1329 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
1330 }
1331 // ... to be implemented
1332 return config;
1333 }
1334
1335 /**
1336 * Tell the supplicant to persist the current list of configured networks.
1337 * @return {@code true} if the operation succeeded
1338 */
1339 public boolean saveConfiguration() {
1340 boolean result;
1341 enforceChangePermission();
1342 synchronized (mWifiStateTracker) {
1343 result = WifiNative.saveConfigCommand();
1344 if (result && mNeedReconfig) {
1345 mNeedReconfig = false;
1346 result = WifiNative.reloadConfigCommand();
1347
1348 if (result) {
1349 Intent intent = new Intent(WifiManager.NETWORK_IDS_CHANGED_ACTION);
1350 mContext.sendBroadcast(intent);
1351 }
1352 }
1353 }
1354 return result;
1355 }
1356
1357 /**
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001358 * Set the number of radio frequency channels that are allowed to be used
1359 * in the current regulatory domain. This method should be used only
1360 * if the correct number of channels cannot be determined automatically
1361 * for some reason. If the operation is successful, the new value is
1362 * persisted as a System setting.
1363 * @param numChannels the number of allowed channels. Must be greater than 0
1364 * and less than or equal to 16.
1365 * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
1366 * {@code numChannels} is outside the valid range.
1367 */
1368 public boolean setNumAllowedChannels(int numChannels) {
1369 enforceChangePermission();
1370 /*
1371 * Validate the argument. We'd like to let the Wi-Fi driver do this,
1372 * but if Wi-Fi isn't currently enabled, that's not possible, and
1373 * we want to persist the setting anyway,so that it will take
1374 * effect when Wi-Fi does become enabled.
1375 */
1376 boolean found = false;
1377 for (int validChan : sValidRegulatoryChannelCounts) {
1378 if (validChan == numChannels) {
1379 found = true;
1380 break;
1381 }
1382 }
1383 if (!found) {
1384 return false;
1385 }
1386
1387 Settings.Secure.putInt(mContext.getContentResolver(),
1388 Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
1389 numChannels);
1390 mWifiStateTracker.setNumAllowedChannels(numChannels);
1391 return true;
1392 }
1393
1394 /**
1395 * Return the number of frequency channels that are allowed
1396 * to be used in the current regulatory domain.
1397 * @return the number of allowed channels, or {@code -1} if an error occurs
1398 */
1399 public int getNumAllowedChannels() {
1400 int numChannels;
1401
1402 enforceAccessPermission();
1403 synchronized (mWifiStateTracker) {
1404 /*
1405 * If we can't get the value from the driver (e.g., because
1406 * Wi-Fi is not currently enabled), get the value from
1407 * Settings.
1408 */
1409 numChannels = WifiNative.getNumAllowedChannelsCommand();
1410 if (numChannels < 0) {
1411 numChannels = Settings.Secure.getInt(mContext.getContentResolver(),
1412 Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
1413 -1);
1414 }
1415 }
1416 return numChannels;
1417 }
1418
1419 /**
1420 * Return the list of valid values for the number of allowed radio channels
1421 * for various regulatory domains.
1422 * @return the list of channel counts
1423 */
1424 public int[] getValidChannelCounts() {
1425 enforceAccessPermission();
1426 return sValidRegulatoryChannelCounts;
1427 }
1428
1429 /**
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001430 * Return the DHCP-assigned addresses from the last successful DHCP request,
1431 * if any.
1432 * @return the DHCP information
1433 */
1434 public DhcpInfo getDhcpInfo() {
1435 enforceAccessPermission();
1436 return mWifiStateTracker.getDhcpInfo();
1437 }
1438
1439 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1440 @Override
1441 public void onReceive(Context context, Intent intent) {
1442 String action = intent.getAction();
1443
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001444 long idleMillis = Settings.System.getLong(mContext.getContentResolver(),
1445 Settings.System.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS);
1446 int stayAwakeConditions =
1447 Settings.System.getInt(mContext.getContentResolver(),
1448 Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001449 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
1450 /* do nothing, we'll check isAirplaneModeOn later. */
1451 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1452 mAlarmManager.cancel(mIdleIntent);
1453 mDeviceIdle = false;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001454 mScreenOff = false;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001455 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001456 mScreenOff = true;
1457 /*
1458 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
1459 * AND the "stay on while plugged in" setting doesn't match the
1460 * current power conditions (i.e, not plugged in, plugged in to USB,
1461 * or plugged in to AC).
1462 */
The Android Open Source Project9266c5582009-01-15 16:12:10 -08001463 if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001464 long triggerTime = System.currentTimeMillis() + idleMillis;
1465 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
1466 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001467 /* we can return now -- there's nothing to do until we get the idle intent back */
1468 return;
1469 } else if (action.equals(ACTION_DEVICE_IDLE)) {
1470 mDeviceIdle = true;
1471 } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001472 /*
1473 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
1474 * AND we are transitioning from a state in which the device was supposed
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001475 * to stay awake to a state in which it is not supposed to stay awake.
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001476 * If "stay awake" state is not changing, we do nothing, to avoid resetting
1477 * the already-set timer.
1478 */
1479 int pluggedType = intent.getIntExtra("plugged", 0);
The Android Open Source Project9266c5582009-01-15 16:12:10 -08001480 if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) &&
1481 !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001482 long triggerTime = System.currentTimeMillis() + idleMillis;
1483 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
1484 mPluggedType = pluggedType;
1485 return;
1486 }
1487 mPluggedType = pluggedType;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001488 } else {
1489 return;
1490 }
1491
1492 updateWifiState();
1493 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001494
1495 /**
The Android Open Source Project9266c5582009-01-15 16:12:10 -08001496 * Determines whether the Wi-Fi chipset should stay awake or be put to
1497 * sleep. Looks at the setting for the sleep policy and the current
1498 * conditions.
1499 *
1500 * @see #shouldDeviceStayAwake(int, int)
1501 */
1502 private boolean shouldWifiStayAwake(int stayAwakeConditions, int pluggedType) {
1503 int wifiSleepPolicy = Settings.System.getInt(mContext.getContentResolver(),
1504 Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_DEFAULT);
1505
1506 if (wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER) {
1507 // Never sleep
1508 return true;
1509 } else if ((wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) &&
1510 (pluggedType != 0)) {
1511 // Never sleep while plugged, and we're plugged
1512 return true;
1513 } else {
1514 // Default
1515 return shouldDeviceStayAwake(stayAwakeConditions, pluggedType);
1516 }
1517 }
1518
1519 /**
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001520 * Determine whether the bit value corresponding to {@code pluggedType} is set in
1521 * the bit string {@code stayAwakeConditions}. Because a {@code pluggedType} value
1522 * of {@code 0} isn't really a plugged type, but rather an indication that the
1523 * device isn't plugged in at all, there is no bit value corresponding to a
1524 * {@code pluggedType} value of {@code 0}. That is why we shift by
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001525 * {@code pluggedType&nbsp;&#8212;&nbsp;1} instead of by {@code pluggedType}.
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001526 * @param stayAwakeConditions a bit string specifying which "plugged types" should
1527 * keep the device (and hence Wi-Fi) awake.
1528 * @param pluggedType the type of plug (USB, AC, or none) for which the check is
1529 * being made
1530 * @return {@code true} if {@code pluggedType} indicates that the device is
1531 * supposed to stay awake, {@code false} otherwise.
1532 */
The Android Open Source Project9266c5582009-01-15 16:12:10 -08001533 private boolean shouldDeviceStayAwake(int stayAwakeConditions, int pluggedType) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001534 return (stayAwakeConditions & pluggedType) != 0;
1535 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001536 };
1537
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001538 private void sendEnableMessage(boolean enable, boolean persist) {
1539 Message msg = Message.obtain(mWifiHandler,
1540 (enable ? MESSAGE_ENABLE_WIFI : MESSAGE_DISABLE_WIFI),
1541 (persist ? 1 : 0), 0);
1542 msg.sendToTarget();
1543 }
1544
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001545 private void sendStartMessage(boolean scanOnlyMode) {
1546 if (DBG) Log.d(TAG, "sendStartMessage(" + scanOnlyMode + ")");
1547 Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget();
1548 }
1549
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001550 private void updateWifiState() {
1551 boolean wifiEnabled = getPersistedWifiEnabled();
1552 boolean airplaneMode = isAirplaneModeOn();
1553 boolean lockHeld = mLocks.hasLocks();
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001554 int strongestLockMode;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001555 boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
1556 boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001557 if (mDeviceIdle && lockHeld) {
1558 strongestLockMode = mLocks.getStrongestLockMode();
1559 } else {
1560 strongestLockMode = WifiManager.WIFI_MODE_FULL;
1561 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001562
1563 synchronized (mWifiHandler) {
1564 if (wifiShouldBeEnabled) {
1565 if (wifiShouldBeStarted) {
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001566 sWakeLock.acquire();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001567 sendEnableMessage(true, false);
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001568 sWakeLock.acquire();
1569 sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001570 } else {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001571 int wakeLockTimeout =
1572 Settings.Secure.getInt(
1573 mContext.getContentResolver(),
1574 Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
1575 DEFAULT_WAKELOCK_TIMEOUT);
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001576 sDriverStopWakeLock.acquire();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001577 mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI);
1578 mWifiHandler.sendEmptyMessageDelayed(MESSAGE_RELEASE_WAKELOCK, wakeLockTimeout);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001579 }
1580 } else {
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001581 sWakeLock.acquire();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001582 sendEnableMessage(false, false);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001583 }
1584 }
1585 }
1586
1587 private void registerForBroadcasts() {
The Android Open Source Project9266c5582009-01-15 16:12:10 -08001588 if (isAirplaneSensitive()) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001589 mContext.registerReceiver(mReceiver,
1590 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
1591 }
1592 mContext.registerReceiver(mReceiver,
1593 new IntentFilter(Intent.ACTION_SCREEN_ON));
1594 mContext.registerReceiver(mReceiver,
1595 new IntentFilter(Intent.ACTION_SCREEN_OFF));
1596 mContext.registerReceiver(mReceiver,
1597 new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
1598 mContext.registerReceiver(mReceiver,
1599 new IntentFilter(ACTION_DEVICE_IDLE));
1600 }
The Android Open Source Project9266c5582009-01-15 16:12:10 -08001601
1602 private boolean isAirplaneSensitive() {
1603 String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
1604 Settings.System.AIRPLANE_MODE_RADIOS);
1605 return airplaneModeRadios == null
1606 || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
1607 }
1608
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001609 /**
The Android Open Source Project9266c5582009-01-15 16:12:10 -08001610 * Returns true if Wi-Fi is sensitive to airplane mode, and airplane mode is
1611 * currently on.
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001612 * @return {@code true} if airplane mode is on.
1613 */
1614 private boolean isAirplaneModeOn() {
The Android Open Source Project9266c5582009-01-15 16:12:10 -08001615 return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001616 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1617 }
1618
1619 /**
1620 * Handler that allows posting to the WifiThread.
1621 */
1622 private class WifiHandler extends Handler {
1623 public WifiHandler(Looper looper) {
1624 super(looper);
1625 }
1626
1627 @Override
1628 public void handleMessage(Message msg) {
1629 switch (msg.what) {
1630
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001631 case MESSAGE_ENABLE_WIFI:
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001632 setWifiEnabledBlocking(true, msg.arg1 == 1);
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001633 sWakeLock.release();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001634 break;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001635
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001636 case MESSAGE_START_WIFI:
1637 mWifiStateTracker.setScanOnlyMode(msg.arg1 != 0);
1638 mWifiStateTracker.restart();
1639 sWakeLock.release();
1640 break;
1641
1642 case MESSAGE_DISABLE_WIFI:
1643 // a non-zero msg.arg1 value means the "enabled" setting
1644 // should be persisted
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001645 setWifiEnabledBlocking(false, msg.arg1 == 1);
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001646 sWakeLock.release();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001647 break;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001648
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001649 case MESSAGE_STOP_WIFI:
1650 mWifiStateTracker.disconnectAndStop();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001651 // don't release wakelock
1652 break;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001653
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001654 case MESSAGE_RELEASE_WAKELOCK:
1655 synchronized (sDriverStopWakeLock) {
1656 if (sDriverStopWakeLock.isHeld()) {
1657 sDriverStopWakeLock.release();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001658 }
1659 }
1660 break;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001661 }
1662 }
1663 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001664
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001665 @Override
1666 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1667 if (mContext.checkCallingPermission(android.Manifest.permission.DUMP)
1668 != PackageManager.PERMISSION_GRANTED) {
1669 pw.println("Permission Denial: can't dump WifiService from from pid="
1670 + Binder.getCallingPid()
1671 + ", uid=" + Binder.getCallingUid());
1672 return;
1673 }
1674 pw.println("Wi-Fi is " + stateName(mWifiState));
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001675 pw.println("stay-awake conditions: " +
1676 Settings.System.getInt(mContext.getContentResolver(),
1677 Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001678 pw.println();
1679
1680 pw.println("Latest scan results:");
1681 List<ScanResult> scanResults = mWifiStateTracker.getScanResultsList();
1682 if (scanResults != null && scanResults.size() != 0) {
1683 pw.println(" BSSID Frequency RSSI Flags SSID");
1684 for (ScanResult r : scanResults) {
1685 pw.printf(" %17s %9d %5d %-16s %s%n",
1686 r.BSSID,
1687 r.frequency,
1688 r.level,
1689 r.capabilities,
1690 r.SSID == null ? "" : r.SSID);
1691 }
1692 }
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001693 pw.println();
1694 pw.println("Locks held:");
1695 mLocks.dump(pw);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001696 }
1697
1698 private static String stateName(int wifiState) {
1699 switch (wifiState) {
1700 case WIFI_STATE_DISABLING:
1701 return "disabling";
1702 case WIFI_STATE_DISABLED:
1703 return "disabled";
1704 case WIFI_STATE_ENABLING:
1705 return "enabling";
1706 case WIFI_STATE_ENABLED:
1707 return "enabled";
1708 case WIFI_STATE_UNKNOWN:
1709 return "unknown state";
1710 default:
1711 return "[invalid state]";
1712 }
1713 }
1714
1715 private class WifiLock implements IBinder.DeathRecipient {
1716 String mTag;
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001717 int mLockMode;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001718 IBinder mBinder;
1719
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001720 WifiLock(int lockMode, String tag, IBinder binder) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001721 super();
1722 mTag = tag;
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001723 mLockMode = lockMode;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001724 mBinder = binder;
1725 try {
1726 mBinder.linkToDeath(this, 0);
1727 } catch (RemoteException e) {
1728 binderDied();
1729 }
1730 }
1731
1732 public void binderDied() {
1733 synchronized (mLocks) {
1734 releaseWifiLockLocked(mBinder);
1735 }
1736 }
1737
1738 public String toString() {
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001739 return "WifiLock{" + mTag + " type=" + mLockMode + " binder=" + mBinder + "}";
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001740 }
1741 }
1742
1743 private class LockList {
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001744 private List<WifiLock> mList;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001745
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001746 private LockList() {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001747 mList = new ArrayList<WifiLock>();
1748 }
1749
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001750 private synchronized boolean hasLocks() {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001751 return !mList.isEmpty();
1752 }
1753
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001754 private synchronized int getStrongestLockMode() {
1755 if (mList.isEmpty()) {
1756 return WifiManager.WIFI_MODE_FULL;
1757 }
1758 for (WifiLock l : mList) {
1759 if (l.mLockMode == WifiManager.WIFI_MODE_FULL) {
1760 return WifiManager.WIFI_MODE_FULL;
1761 }
1762 }
1763 return WifiManager.WIFI_MODE_SCAN_ONLY;
1764 }
1765
1766 private void addLock(WifiLock lock) {
1767 if (findLockByBinder(lock.mBinder) < 0) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001768 mList.add(lock);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001769 }
1770 }
1771
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001772 private WifiLock removeLock(IBinder binder) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001773 int index = findLockByBinder(binder);
1774 if (index >= 0) {
1775 return mList.remove(index);
1776 } else {
1777 return null;
1778 }
1779 }
1780
1781 private int findLockByBinder(IBinder binder) {
1782 int size = mList.size();
1783 for (int i = size - 1; i >= 0; i--)
1784 if (mList.get(i).mBinder == binder)
1785 return i;
1786 return -1;
1787 }
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001788
1789 private synchronized void clear() {
1790 if (!mList.isEmpty()) {
1791 mList.clear();
1792 updateWifiState();
1793 }
1794 }
1795
1796 private void dump(PrintWriter pw) {
1797 for (WifiLock l : mList) {
1798 pw.print(" ");
1799 pw.println(l);
1800 }
1801 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001802 }
1803
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001804 public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001805 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001806 if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) {
1807 return false;
1808 }
1809 WifiLock wifiLock = new WifiLock(lockMode, tag, binder);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001810 synchronized (mLocks) {
1811 return acquireWifiLockLocked(wifiLock);
1812 }
1813 }
1814
1815 private boolean acquireWifiLockLocked(WifiLock wifiLock) {
1816 mLocks.addLock(wifiLock);
1817 updateWifiState();
1818 return true;
1819 }
1820
1821 public boolean releaseWifiLock(IBinder lock) {
1822 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
1823 synchronized (mLocks) {
1824 return releaseWifiLockLocked(lock);
1825 }
1826 }
1827
1828 private boolean releaseWifiLockLocked(IBinder lock) {
1829 boolean result;
1830 result = (mLocks.removeLock(lock) != null);
1831 updateWifiState();
1832 return result;
1833 }
1834}