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