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