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