blob: 7951fb71b084d66aade50ca7c8560711f6f9e779 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
20import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
21import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
22import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
23import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
24
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.app.AlarmManager;
26import android.app.PendingIntent;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -070027import android.bluetooth.BluetoothA2dp;
Jaikumar Ganesh084c6652009-12-07 10:58:18 -080028import android.bluetooth.BluetoothDevice;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.content.BroadcastReceiver;
30import android.content.ContentResolver;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
34import android.content.pm.PackageManager;
35import android.net.wifi.IWifiManager;
36import android.net.wifi.WifiInfo;
37import android.net.wifi.WifiManager;
38import android.net.wifi.WifiNative;
39import android.net.wifi.WifiStateTracker;
40import android.net.wifi.ScanResult;
41import android.net.wifi.WifiConfiguration;
San Mehat0310f9a2009-07-07 10:49:47 -070042import android.net.wifi.SupplicantState;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.net.NetworkStateTracker;
44import android.net.DhcpInfo;
Mike Lockwood0900f362009-07-10 17:24:07 -040045import android.net.NetworkUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.os.Binder;
47import android.os.Handler;
48import android.os.HandlerThread;
49import android.os.IBinder;
50import android.os.Looper;
51import android.os.Message;
52import android.os.PowerManager;
Dianne Hackborn617f8772009-03-31 15:04:46 -070053import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.os.RemoteException;
Amith Yamasani47873e52009-07-02 12:05:32 -070055import android.os.ServiceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.provider.Settings;
57import android.util.Log;
58import android.text.TextUtils;
59
60import java.util.ArrayList;
61import java.util.BitSet;
62import java.util.HashMap;
63import java.util.LinkedHashMap;
64import java.util.List;
65import java.util.Map;
Jaikumar Ganesh084c6652009-12-07 10:58:18 -080066import java.util.Set;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import java.util.regex.Pattern;
68import java.io.FileDescriptor;
69import java.io.PrintWriter;
70
The Android Open Source Project10592532009-03-18 17:39:46 -070071import com.android.internal.app.IBatteryStats;
Amith Yamasani47873e52009-07-02 12:05:32 -070072import android.backup.IBackupManager;
The Android Open Source Project10592532009-03-18 17:39:46 -070073import com.android.server.am.BatteryStatsService;
74
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075/**
76 * WifiService handles remote WiFi operation requests by implementing
77 * the IWifiManager interface. It also creates a WifiMonitor to listen
78 * for Wifi-related events.
79 *
80 * @hide
81 */
82public class WifiService extends IWifiManager.Stub {
83 private static final String TAG = "WifiService";
84 private static final boolean DBG = false;
85 private static final Pattern scanResultPattern = Pattern.compile("\t+");
86 private final WifiStateTracker mWifiStateTracker;
87
88 private Context mContext;
89 private int mWifiState;
90
91 private AlarmManager mAlarmManager;
92 private PendingIntent mIdleIntent;
93 private static final int IDLE_REQUEST = 0;
94 private boolean mScreenOff;
95 private boolean mDeviceIdle;
96 private int mPluggedType;
97
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -070098 // true if the user enabled Wifi while in airplane mode
99 private boolean mAirplaneModeOverwridden;
100
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 private final LockList mLocks = new LockList();
Eric Shienbrood5711fad2009-03-27 20:25:31 -0700102 // some wifi lock statistics
103 private int mFullLocksAcquired;
104 private int mFullLocksReleased;
105 private int mScanLocksAcquired;
106 private int mScanLocksReleased;
The Android Open Source Project10592532009-03-18 17:39:46 -0700107
Robert Greenwalt58ff0212009-05-19 15:53:54 -0700108 private final List<Multicaster> mMulticasters =
109 new ArrayList<Multicaster>();
Robert Greenwalt5347bd42009-05-13 15:10:16 -0700110 private int mMulticastEnabled;
111 private int mMulticastDisabled;
112
The Android Open Source Project10592532009-03-18 17:39:46 -0700113 private final IBatteryStats mBatteryStats;
Jaikumar Ganesh084c6652009-12-07 10:58:18 -0800114
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 /**
Doug Zongker43866e02010-01-07 12:09:54 -0800116 * See {@link Settings.Secure#WIFI_IDLE_MS}. This is the default value if a
117 * Settings.Secure value is not present. This timeout value is chosen as
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 * the approximate point at which the battery drain caused by Wi-Fi
119 * being enabled but not active exceeds the battery drain caused by
120 * re-establishing a connection to the mobile data network.
121 */
122 private static final long DEFAULT_IDLE_MILLIS = 15 * 60 * 1000; /* 15 minutes */
123
124 private static final String WAKELOCK_TAG = "WifiService";
125
126 /**
127 * The maximum amount of time to hold the wake lock after a disconnect
128 * caused by stopping the driver. Establishing an EDGE connection has been
129 * observed to take about 5 seconds under normal circumstances. This
130 * provides a bit of extra margin.
131 * <p>
132 * See {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS}.
133 * This is the default value if a Settings.Secure value is not present.
134 */
135 private static final int DEFAULT_WAKELOCK_TIMEOUT = 8000;
136
137 // Wake lock used by driver-stop operation
138 private static PowerManager.WakeLock sDriverStopWakeLock;
139 // Wake lock used by other operations
140 private static PowerManager.WakeLock sWakeLock;
141
142 private static final int MESSAGE_ENABLE_WIFI = 0;
143 private static final int MESSAGE_DISABLE_WIFI = 1;
144 private static final int MESSAGE_STOP_WIFI = 2;
145 private static final int MESSAGE_START_WIFI = 3;
146 private static final int MESSAGE_RELEASE_WAKELOCK = 4;
Robert Greenwaltf75aa36f2009-10-22 17:03:47 -0700147 private static final int MESSAGE_UPDATE_STATE = 5;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148
149 private final WifiHandler mWifiHandler;
150
151 /*
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 * Cache of scan results objects (size is somewhat arbitrary)
153 */
154 private static final int SCAN_RESULT_CACHE_SIZE = 80;
155 private final LinkedHashMap<String, ScanResult> mScanResultCache;
156
157 /*
158 * Character buffer used to parse scan results (optimization)
159 */
160 private static final int SCAN_RESULT_BUFFER_SIZE = 512;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 private boolean mNeedReconfig;
162
Dianne Hackborn617f8772009-03-31 15:04:46 -0700163 /*
164 * Last UID that asked to enable WIFI.
165 */
166 private int mLastEnableUid = Process.myUid();
Jaikumar Ganesh084c6652009-12-07 10:58:18 -0800167
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 /**
169 * Number of allowed radio frequency channels in various regulatory domains.
170 * This list is sufficient for 802.11b/g networks (2.4GHz range).
171 */
172 private static int[] sValidRegulatoryChannelCounts = new int[] {11, 13, 14};
173
174 private static final String ACTION_DEVICE_IDLE =
175 "com.android.server.WifiManager.action.DEVICE_IDLE";
176
177 WifiService(Context context, WifiStateTracker tracker) {
178 mContext = context;
179 mWifiStateTracker = tracker;
Mike Lockwoodf32be162009-07-14 17:44:37 -0400180 mWifiStateTracker.enableRssiPolling(true);
The Android Open Source Project10592532009-03-18 17:39:46 -0700181 mBatteryStats = BatteryStatsService.getService();
Jaikumar Ganesh084c6652009-12-07 10:58:18 -0800182
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 mScanResultCache = new LinkedHashMap<String, ScanResult>(
184 SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
185 /*
186 * Limit the cache size by SCAN_RESULT_CACHE_SIZE
187 * elements
188 */
189 public boolean removeEldestEntry(Map.Entry eldest) {
190 return SCAN_RESULT_CACHE_SIZE < this.size();
191 }
192 };
193
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 HandlerThread wifiThread = new HandlerThread("WifiService");
195 wifiThread.start();
196 mWifiHandler = new WifiHandler(wifiThread.getLooper());
197
198 mWifiState = WIFI_STATE_DISABLED;
199 boolean wifiEnabled = getPersistedWifiEnabled();
200
201 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
202 Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
203 mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
204
205 PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
206 sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
207 sDriverStopWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
208 mWifiStateTracker.setReleaseWakeLockCallback(
209 new Runnable() {
210 public void run() {
211 mWifiHandler.removeMessages(MESSAGE_RELEASE_WAKELOCK);
212 synchronized (sDriverStopWakeLock) {
213 if (sDriverStopWakeLock.isHeld()) {
214 sDriverStopWakeLock.release();
215 }
216 }
217 }
218 }
219 );
220
221 Log.i(TAG, "WifiService starting up with Wi-Fi " +
222 (wifiEnabled ? "enabled" : "disabled"));
223
224 mContext.registerReceiver(
225 new BroadcastReceiver() {
226 @Override
227 public void onReceive(Context context, Intent intent) {
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -0700228 // clear our flag indicating the user has overwridden airplane mode
229 mAirplaneModeOverwridden = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 updateWifiState();
231 }
232 },
233 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
234
Dianne Hackborn617f8772009-03-31 15:04:46 -0700235 setWifiEnabledBlocking(wifiEnabled, false, Process.myUid());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 }
237
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 private boolean getPersistedWifiEnabled() {
239 final ContentResolver cr = mContext.getContentResolver();
240 try {
241 return Settings.Secure.getInt(cr, Settings.Secure.WIFI_ON) == 1;
242 } catch (Settings.SettingNotFoundException e) {
243 Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, 0);
244 return false;
245 }
246 }
247
248 private void persistWifiEnabled(boolean enabled) {
249 final ContentResolver cr = mContext.getContentResolver();
250 Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0);
251 }
252
253 NetworkStateTracker getNetworkStateTracker() {
254 return mWifiStateTracker;
255 }
256
257 /**
258 * see {@link android.net.wifi.WifiManager#pingSupplicant()}
259 * @return {@code true} if the operation succeeds
260 */
261 public boolean pingSupplicant() {
262 enforceChangePermission();
263 synchronized (mWifiStateTracker) {
264 return WifiNative.pingCommand();
265 }
266 }
267
268 /**
269 * see {@link android.net.wifi.WifiManager#startScan()}
270 * @return {@code true} if the operation succeeds
271 */
Mike Lockwooda5ec95c2009-07-08 17:11:17 -0400272 public boolean startScan(boolean forceActive) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 enforceChangePermission();
274 synchronized (mWifiStateTracker) {
275 switch (mWifiStateTracker.getSupplicantState()) {
276 case DISCONNECTED:
277 case INACTIVE:
278 case SCANNING:
279 case DORMANT:
280 break;
281 default:
282 WifiNative.setScanResultHandlingCommand(
283 WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);
284 break;
285 }
Mike Lockwooda5ec95c2009-07-08 17:11:17 -0400286 return WifiNative.scanCommand(forceActive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 }
288 }
289
290 /**
291 * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
292 * @param enable {@code true} to enable, {@code false} to disable.
293 * @return {@code true} if the enable/disable operation was
294 * started or is already in the queue.
295 */
296 public boolean setWifiEnabled(boolean enable) {
297 enforceChangePermission();
298 if (mWifiHandler == null) return false;
299
300 synchronized (mWifiHandler) {
Robert Greenwalta99f4612009-09-19 18:14:32 -0700301 // caller may not have WAKE_LOCK permission - it's not required here
302 long ident = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 sWakeLock.acquire();
Robert Greenwalta99f4612009-09-19 18:14:32 -0700304 Binder.restoreCallingIdentity(ident);
305
Dianne Hackborn617f8772009-03-31 15:04:46 -0700306 mLastEnableUid = Binder.getCallingUid();
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -0700307 // set a flag if the user is enabling Wifi while in airplane mode
308 mAirplaneModeOverwridden = (enable && isAirplaneModeOn() && isAirplaneToggleable());
Dianne Hackborn617f8772009-03-31 15:04:46 -0700309 sendEnableMessage(enable, true, Binder.getCallingUid());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 }
311
312 return true;
313 }
314
315 /**
316 * Enables/disables Wi-Fi synchronously.
317 * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off.
318 * @param persist {@code true} if the setting should be persisted.
Dianne Hackborn617f8772009-03-31 15:04:46 -0700319 * @param uid The UID of the process making the request.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 * @return {@code true} if the operation succeeds (or if the existing state
321 * is the same as the requested state)
322 */
Dianne Hackborn617f8772009-03-31 15:04:46 -0700323 private boolean setWifiEnabledBlocking(boolean enable, boolean persist, int uid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 final int eventualWifiState = enable ? WIFI_STATE_ENABLED : WIFI_STATE_DISABLED;
325
326 if (mWifiState == eventualWifiState) {
327 return true;
328 }
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -0700329 if (enable && isAirplaneModeOn() && !mAirplaneModeOverwridden) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 return false;
331 }
332
Irfan Sheriffcd770372010-01-08 09:36:04 -0800333 /**
334 * Multiple calls to unregisterReceiver() cause exception and a system crash.
335 * This can happen if a supplicant is lost (or firmware crash occurs) and user indicates
336 * disable wifi at the same time.
337 * Avoid doing a disable when the current Wifi state is UNKNOWN
338 * TODO: Handle driver load fail and supplicant lost as seperate states
339 */
340 if (mWifiState == WIFI_STATE_UNKNOWN && !enable) {
341 return false;
342 }
343
Dianne Hackborn617f8772009-03-31 15:04:46 -0700344 setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING, uid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345
346 if (enable) {
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800347 synchronized (mWifiStateTracker) {
348 if (!WifiNative.loadDriver()) {
349 Log.e(TAG, "Failed to load Wi-Fi driver.");
350 setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
351 return false;
352 }
353 if (!WifiNative.startSupplicant()) {
354 WifiNative.unloadDriver();
355 Log.e(TAG, "Failed to start supplicant daemon.");
356 setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
357 return false;
358 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 }
360 registerForBroadcasts();
361 mWifiStateTracker.startEventLoop();
362 } else {
363
364 mContext.unregisterReceiver(mReceiver);
365 // Remove notification (it will no-op if it isn't visible)
366 mWifiStateTracker.setNotificationVisible(false, 0, false, 0);
367
368 boolean failedToStopSupplicantOrUnloadDriver = false;
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800369 synchronized (mWifiStateTracker) {
370 if (!WifiNative.stopSupplicant()) {
371 Log.e(TAG, "Failed to stop supplicant daemon.");
Dianne Hackborn617f8772009-03-31 15:04:46 -0700372 setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 failedToStopSupplicantOrUnloadDriver = true;
374 }
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800375
376 // We must reset the interface before we unload the driver
377 mWifiStateTracker.resetInterface(false);
378
379 if (!WifiNative.unloadDriver()) {
380 Log.e(TAG, "Failed to unload Wi-Fi driver.");
381 if (!failedToStopSupplicantOrUnloadDriver) {
382 setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
383 failedToStopSupplicantOrUnloadDriver = true;
384 }
385 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 }
387 if (failedToStopSupplicantOrUnloadDriver) {
388 return false;
389 }
390 }
391
392 // Success!
393
394 if (persist) {
395 persistWifiEnabled(enable);
396 }
Dianne Hackborn617f8772009-03-31 15:04:46 -0700397 setWifiEnabledState(eventualWifiState, uid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 return true;
399 }
400
Dianne Hackborn617f8772009-03-31 15:04:46 -0700401 private void setWifiEnabledState(int wifiState, int uid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 final int previousWifiState = mWifiState;
403
The Android Open Source Project10592532009-03-18 17:39:46 -0700404 long ident = Binder.clearCallingIdentity();
405 try {
406 if (wifiState == WIFI_STATE_ENABLED) {
Dianne Hackborn617f8772009-03-31 15:04:46 -0700407 mBatteryStats.noteWifiOn(uid);
The Android Open Source Project10592532009-03-18 17:39:46 -0700408 } else if (wifiState == WIFI_STATE_DISABLED) {
Dianne Hackborn617f8772009-03-31 15:04:46 -0700409 mBatteryStats.noteWifiOff(uid);
The Android Open Source Project10592532009-03-18 17:39:46 -0700410 }
411 } catch (RemoteException e) {
412 } finally {
413 Binder.restoreCallingIdentity(ident);
414 }
Jaikumar Ganesh084c6652009-12-07 10:58:18 -0800415
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 // Update state
417 mWifiState = wifiState;
418
419 // Broadcast
420 final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
421 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
422 intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
423 intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
424 mContext.sendStickyBroadcast(intent);
425 }
426
427 private void enforceAccessPermission() {
428 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
429 "WifiService");
430 }
431
432 private void enforceChangePermission() {
433 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
434 "WifiService");
435
436 }
437
Robert Greenwaltfc1b15c2009-05-22 15:09:51 -0700438 private void enforceMulticastChangePermission() {
439 mContext.enforceCallingOrSelfPermission(
440 android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
441 "WifiService");
442 }
443
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 /**
445 * see {@link WifiManager#getWifiState()}
446 * @return One of {@link WifiManager#WIFI_STATE_DISABLED},
447 * {@link WifiManager#WIFI_STATE_DISABLING},
448 * {@link WifiManager#WIFI_STATE_ENABLED},
449 * {@link WifiManager#WIFI_STATE_ENABLING},
450 * {@link WifiManager#WIFI_STATE_UNKNOWN}
451 */
452 public int getWifiEnabledState() {
453 enforceAccessPermission();
454 return mWifiState;
455 }
456
457 /**
458 * see {@link android.net.wifi.WifiManager#disconnect()}
459 * @return {@code true} if the operation succeeds
460 */
461 public boolean disconnect() {
462 enforceChangePermission();
463 synchronized (mWifiStateTracker) {
464 return WifiNative.disconnectCommand();
465 }
466 }
467
468 /**
469 * see {@link android.net.wifi.WifiManager#reconnect()}
470 * @return {@code true} if the operation succeeds
471 */
472 public boolean reconnect() {
473 enforceChangePermission();
474 synchronized (mWifiStateTracker) {
475 return WifiNative.reconnectCommand();
476 }
477 }
478
479 /**
480 * see {@link android.net.wifi.WifiManager#reassociate()}
481 * @return {@code true} if the operation succeeds
482 */
483 public boolean reassociate() {
484 enforceChangePermission();
485 synchronized (mWifiStateTracker) {
486 return WifiNative.reassociateCommand();
487 }
488 }
489
490 /**
491 * see {@link android.net.wifi.WifiManager#getConfiguredNetworks()}
492 * @return the list of configured networks
493 */
494 public List<WifiConfiguration> getConfiguredNetworks() {
495 enforceAccessPermission();
496 String listStr;
497 /*
498 * We don't cache the list, because we want to allow
499 * for the possibility that the configuration file
500 * has been modified through some external means,
501 * such as the wpa_cli command line program.
502 */
503 synchronized (mWifiStateTracker) {
504 listStr = WifiNative.listNetworksCommand();
505 }
506 List<WifiConfiguration> networks =
507 new ArrayList<WifiConfiguration>();
508 if (listStr == null)
509 return networks;
510
511 String[] lines = listStr.split("\n");
512 // Skip the first line, which is a header
513 for (int i = 1; i < lines.length; i++) {
514 String[] result = lines[i].split("\t");
515 // network-id | ssid | bssid | flags
516 WifiConfiguration config = new WifiConfiguration();
517 try {
518 config.networkId = Integer.parseInt(result[0]);
519 } catch(NumberFormatException e) {
520 continue;
521 }
522 if (result.length > 3) {
523 if (result[3].indexOf("[CURRENT]") != -1)
524 config.status = WifiConfiguration.Status.CURRENT;
525 else if (result[3].indexOf("[DISABLED]") != -1)
526 config.status = WifiConfiguration.Status.DISABLED;
527 else
528 config.status = WifiConfiguration.Status.ENABLED;
529 } else
530 config.status = WifiConfiguration.Status.ENABLED;
531 synchronized (mWifiStateTracker) {
532 readNetworkVariables(config);
533 }
534 networks.add(config);
535 }
536
537 return networks;
538 }
539
540 /**
541 * Read the variables from the supplicant daemon that are needed to
542 * fill in the WifiConfiguration object.
543 * <p/>
544 * The caller must hold the synchronization monitor.
545 * @param config the {@link WifiConfiguration} object to be filled in.
546 */
547 private static void readNetworkVariables(WifiConfiguration config) {
548
549 int netId = config.networkId;
550 if (netId < 0)
551 return;
552
553 /*
554 * TODO: maybe should have a native method that takes an array of
555 * variable names and returns an array of values. But we'd still
556 * be doing a round trip to the supplicant daemon for each variable.
557 */
558 String value;
559
560 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
561 if (!TextUtils.isEmpty(value)) {
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800562 config.SSID = removeDoubleQuotes(value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563 } else {
564 config.SSID = null;
565 }
566
567 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName);
568 if (!TextUtils.isEmpty(value)) {
569 config.BSSID = value;
570 } else {
571 config.BSSID = null;
572 }
573
574 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
575 config.priority = -1;
576 if (!TextUtils.isEmpty(value)) {
577 try {
578 config.priority = Integer.parseInt(value);
579 } catch (NumberFormatException ignore) {
580 }
581 }
582
583 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName);
584 config.hiddenSSID = false;
585 if (!TextUtils.isEmpty(value)) {
586 try {
587 config.hiddenSSID = Integer.parseInt(value) != 0;
588 } catch (NumberFormatException ignore) {
589 }
590 }
591
592 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName);
593 config.wepTxKeyIndex = -1;
594 if (!TextUtils.isEmpty(value)) {
595 try {
596 config.wepTxKeyIndex = Integer.parseInt(value);
597 } catch (NumberFormatException ignore) {
598 }
599 }
600
601 /*
602 * Get up to 4 WEP keys. Note that the actual keys are not passed back,
603 * just a "*" if the key is set, or the null string otherwise.
604 */
605 for (int i = 0; i < 4; i++) {
606 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepKeyVarNames[i]);
607 if (!TextUtils.isEmpty(value)) {
608 config.wepKeys[i] = value;
609 } else {
610 config.wepKeys[i] = null;
611 }
612 }
613
614 /*
615 * Get the private shared key. Note that the actual keys are not passed back,
616 * just a "*" if the key is set, or the null string otherwise.
617 */
618 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName);
619 if (!TextUtils.isEmpty(value)) {
620 config.preSharedKey = value;
621 } else {
622 config.preSharedKey = null;
623 }
624
625 value = WifiNative.getNetworkVariableCommand(config.networkId,
626 WifiConfiguration.Protocol.varName);
627 if (!TextUtils.isEmpty(value)) {
628 String vals[] = value.split(" ");
629 for (String val : vals) {
630 int index =
631 lookupString(val, WifiConfiguration.Protocol.strings);
632 if (0 <= index) {
633 config.allowedProtocols.set(index);
634 }
635 }
636 }
637
638 value = WifiNative.getNetworkVariableCommand(config.networkId,
639 WifiConfiguration.KeyMgmt.varName);
640 if (!TextUtils.isEmpty(value)) {
641 String vals[] = value.split(" ");
642 for (String val : vals) {
643 int index =
644 lookupString(val, WifiConfiguration.KeyMgmt.strings);
645 if (0 <= index) {
646 config.allowedKeyManagement.set(index);
647 }
648 }
649 }
650
651 value = WifiNative.getNetworkVariableCommand(config.networkId,
652 WifiConfiguration.AuthAlgorithm.varName);
653 if (!TextUtils.isEmpty(value)) {
654 String vals[] = value.split(" ");
655 for (String val : vals) {
656 int index =
657 lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
658 if (0 <= index) {
659 config.allowedAuthAlgorithms.set(index);
660 }
661 }
662 }
663
664 value = WifiNative.getNetworkVariableCommand(config.networkId,
665 WifiConfiguration.PairwiseCipher.varName);
666 if (!TextUtils.isEmpty(value)) {
667 String vals[] = value.split(" ");
668 for (String val : vals) {
669 int index =
670 lookupString(val, WifiConfiguration.PairwiseCipher.strings);
671 if (0 <= index) {
672 config.allowedPairwiseCiphers.set(index);
673 }
674 }
675 }
676
677 value = WifiNative.getNetworkVariableCommand(config.networkId,
678 WifiConfiguration.GroupCipher.varName);
679 if (!TextUtils.isEmpty(value)) {
680 String vals[] = value.split(" ");
681 for (String val : vals) {
682 int index =
683 lookupString(val, WifiConfiguration.GroupCipher.strings);
684 if (0 <= index) {
685 config.allowedGroupCiphers.set(index);
686 }
687 }
688 }
Chung-yih Wang43374762009-09-16 14:28:42 +0800689
690 for (WifiConfiguration.EnterpriseField field :
691 config.enterpriseFields) {
692 value = WifiNative.getNetworkVariableCommand(netId,
693 field.varName());
694 if (!TextUtils.isEmpty(value)) {
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800695 if (field != config.eap) value = removeDoubleQuotes(value);
Chung-yih Wang43374762009-09-16 14:28:42 +0800696 field.setValue(value);
697 }
698 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 }
700
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800701 private static String removeDoubleQuotes(String string) {
702 if (string.length() <= 2) return "";
703 return string.substring(1, string.length() - 1);
704 }
705
706 private static String convertToQuotedString(String string) {
707 return "\"" + string + "\"";
708 }
709
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800710 /**
711 * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)}
712 * @return the supplicant-assigned identifier for the new or updated
713 * network if the operation succeeds, or {@code -1} if it fails
714 */
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800715 public int addOrUpdateNetwork(WifiConfiguration config) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716 enforceChangePermission();
717 /*
718 * If the supplied networkId is -1, we create a new empty
719 * network configuration. Otherwise, the networkId should
720 * refer to an existing configuration.
721 */
722 int netId = config.networkId;
723 boolean newNetwork = netId == -1;
724 boolean doReconfig;
725 int currentPriority;
726 // networkId of -1 means we want to create a new network
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800727 synchronized (mWifiStateTracker) {
728 if (newNetwork) {
729 netId = WifiNative.addNetworkCommand();
730 if (netId < 0) {
731 if (DBG) {
732 Log.d(TAG, "Failed to add a network!");
733 }
734 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735 }
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800736 doReconfig = true;
737 } else {
738 String priorityVal = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
739 currentPriority = -1;
740 if (!TextUtils.isEmpty(priorityVal)) {
741 try {
742 currentPriority = Integer.parseInt(priorityVal);
743 } catch (NumberFormatException ignore) {
744 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800745 }
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800746 doReconfig = currentPriority != config.priority;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800747 }
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800748 mNeedReconfig = mNeedReconfig || doReconfig;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800750 setVariables: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 /*
752 * Note that if a networkId for a non-existent network
753 * was supplied, then the first setNetworkVariableCommand()
754 * will fail, so we don't bother to make a separate check
755 * for the validity of the ID up front.
756 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800757 if (config.SSID != null &&
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800758 !WifiNative.setNetworkVariableCommand(
759 netId,
760 WifiConfiguration.ssidVarName,
761 convertToQuotedString(config.SSID))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 if (DBG) {
763 Log.d(TAG, "failed to set SSID: "+config.SSID);
764 }
765 break setVariables;
766 }
767
768 if (config.BSSID != null &&
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800769 !WifiNative.setNetworkVariableCommand(
770 netId,
771 WifiConfiguration.bssidVarName,
772 config.BSSID)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773 if (DBG) {
774 Log.d(TAG, "failed to set BSSID: "+config.BSSID);
775 }
776 break setVariables;
777 }
778
779 String allowedKeyManagementString =
780 makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
781 if (config.allowedKeyManagement.cardinality() != 0 &&
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800782 !WifiNative.setNetworkVariableCommand(
783 netId,
784 WifiConfiguration.KeyMgmt.varName,
785 allowedKeyManagementString)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 if (DBG) {
787 Log.d(TAG, "failed to set key_mgmt: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800788 allowedKeyManagementString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 }
790 break setVariables;
791 }
792
793 String allowedProtocolsString =
794 makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
795 if (config.allowedProtocols.cardinality() != 0 &&
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800796 !WifiNative.setNetworkVariableCommand(
797 netId,
798 WifiConfiguration.Protocol.varName,
799 allowedProtocolsString)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800800 if (DBG) {
801 Log.d(TAG, "failed to set proto: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800802 allowedProtocolsString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 }
804 break setVariables;
805 }
806
807 String allowedAuthAlgorithmsString =
808 makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
809 if (config.allowedAuthAlgorithms.cardinality() != 0 &&
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800810 !WifiNative.setNetworkVariableCommand(
811 netId,
812 WifiConfiguration.AuthAlgorithm.varName,
813 allowedAuthAlgorithmsString)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800814 if (DBG) {
815 Log.d(TAG, "failed to set auth_alg: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800816 allowedAuthAlgorithmsString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800817 }
818 break setVariables;
819 }
820
821 String allowedPairwiseCiphersString =
822 makeString(config.allowedPairwiseCiphers, WifiConfiguration.PairwiseCipher.strings);
823 if (config.allowedPairwiseCiphers.cardinality() != 0 &&
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800824 !WifiNative.setNetworkVariableCommand(
825 netId,
826 WifiConfiguration.PairwiseCipher.varName,
827 allowedPairwiseCiphersString)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 if (DBG) {
829 Log.d(TAG, "failed to set pairwise: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800830 allowedPairwiseCiphersString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800831 }
832 break setVariables;
833 }
834
835 String allowedGroupCiphersString =
836 makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
837 if (config.allowedGroupCiphers.cardinality() != 0 &&
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800838 !WifiNative.setNetworkVariableCommand(
839 netId,
840 WifiConfiguration.GroupCipher.varName,
841 allowedGroupCiphersString)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800842 if (DBG) {
843 Log.d(TAG, "failed to set group: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800844 allowedGroupCiphersString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800845 }
846 break setVariables;
847 }
848
849 // Prevent client screw-up by passing in a WifiConfiguration we gave it
850 // by preventing "*" as a key.
851 if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800852 !WifiNative.setNetworkVariableCommand(
853 netId,
854 WifiConfiguration.pskVarName,
855 config.preSharedKey)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856 if (DBG) {
857 Log.d(TAG, "failed to set psk: "+config.preSharedKey);
858 }
859 break setVariables;
860 }
861
862 boolean hasSetKey = false;
863 if (config.wepKeys != null) {
864 for (int i = 0; i < config.wepKeys.length; i++) {
865 // Prevent client screw-up by passing in a WifiConfiguration we gave it
866 // by preventing "*" as a key.
867 if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
868 if (!WifiNative.setNetworkVariableCommand(
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800869 netId,
870 WifiConfiguration.wepKeyVarNames[i],
871 config.wepKeys[i])) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872 if (DBG) {
873 Log.d(TAG,
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800874 "failed to set wep_key"+i+": " +
875 config.wepKeys[i]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800876 }
877 break setVariables;
878 }
879 hasSetKey = true;
880 }
881 }
882 }
883
884 if (hasSetKey) {
885 if (!WifiNative.setNetworkVariableCommand(
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800886 netId,
887 WifiConfiguration.wepTxKeyIdxVarName,
888 Integer.toString(config.wepTxKeyIndex))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889 if (DBG) {
890 Log.d(TAG,
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800891 "failed to set wep_tx_keyidx: "+
892 config.wepTxKeyIndex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800893 }
894 break setVariables;
895 }
896 }
897
898 if (!WifiNative.setNetworkVariableCommand(
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800899 netId,
900 WifiConfiguration.priorityVarName,
901 Integer.toString(config.priority))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800902 if (DBG) {
903 Log.d(TAG, config.SSID + ": failed to set priority: "
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800904 +config.priority);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 }
906 break setVariables;
907 }
908
909 if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand(
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800910 netId,
911 WifiConfiguration.hiddenSSIDVarName,
912 Integer.toString(config.hiddenSSID ? 1 : 0))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 if (DBG) {
914 Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800915 config.hiddenSSID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 }
917 break setVariables;
918 }
919
Chung-yih Wang43374762009-09-16 14:28:42 +0800920 for (WifiConfiguration.EnterpriseField field
921 : config.enterpriseFields) {
922 String varName = field.varName();
923 String value = field.value();
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800924 if (value != null) {
925 if (field != config.eap) {
Chia-chi Yeh784d53e2010-01-29 16:26:28 +0800926 value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
Chung-yih Wang43374762009-09-16 14:28:42 +0800927 }
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800928 if (!WifiNative.setNetworkVariableCommand(
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800929 netId,
930 varName,
931 value)) {
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800932 if (DBG) {
933 Log.d(TAG, config.SSID + ": failed to set " + varName +
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800934 ": " + value);
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800935 }
936 break setVariables;
937 }
Chung-yih Wang5069cc72009-06-03 17:33:47 +0800938 }
Chung-yih Wang5069cc72009-06-03 17:33:47 +0800939 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 return netId;
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800941 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800942
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800943 /*
944 * For an update, if one of the setNetworkVariable operations fails,
945 * we might want to roll back all the changes already made. But the
946 * chances are that if anything is going to go wrong, it'll happen
947 * the first time we try to set one of the variables.
948 */
949 if (newNetwork) {
950 removeNetwork(netId);
951 if (DBG) {
952 Log.d(TAG,
953 "Failed to set a network variable, removed network: "
954 + netId);
955 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956 }
957 }
958 return -1;
959 }
960
961 private static String makeString(BitSet set, String[] strings) {
962 StringBuffer buf = new StringBuffer();
963 int nextSetBit = -1;
964
965 /* Make sure all set bits are in [0, strings.length) to avoid
966 * going out of bounds on strings. (Shouldn't happen, but...) */
967 set = set.get(0, strings.length);
968
969 while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
970 buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
971 }
972
973 // remove trailing space
974 if (set.cardinality() > 0) {
975 buf.setLength(buf.length() - 1);
976 }
977
978 return buf.toString();
979 }
980
981 private static int lookupString(String string, String[] strings) {
982 int size = strings.length;
983
984 string = string.replace('-', '_');
985
986 for (int i = 0; i < size; i++)
987 if (string.equals(strings[i]))
988 return i;
989
990 if (DBG) {
991 // if we ever get here, we should probably add the
992 // value to WifiConfiguration to reflect that it's
993 // supported by the WPA supplicant
994 Log.w(TAG, "Failed to look-up a string: " + string);
995 }
996
997 return -1;
998 }
999
1000 /**
1001 * See {@link android.net.wifi.WifiManager#removeNetwork(int)}
1002 * @param netId the integer that identifies the network configuration
1003 * to the supplicant
1004 * @return {@code true} if the operation succeeded
1005 */
1006 public boolean removeNetwork(int netId) {
1007 enforceChangePermission();
1008
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001009 return mWifiStateTracker.removeNetwork(netId);
1010 }
1011
1012 /**
1013 * See {@link android.net.wifi.WifiManager#enableNetwork(int, boolean)}
1014 * @param netId the integer that identifies the network configuration
1015 * to the supplicant
1016 * @param disableOthers if true, disable all other networks.
1017 * @return {@code true} if the operation succeeded
1018 */
1019 public boolean enableNetwork(int netId, boolean disableOthers) {
1020 enforceChangePermission();
1021
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022 synchronized (mWifiStateTracker) {
Mike Lockwood0900f362009-07-10 17:24:07 -04001023 String ifname = mWifiStateTracker.getInterfaceName();
1024 NetworkUtils.enableInterface(ifname);
1025 boolean result = WifiNative.enableNetworkCommand(netId, disableOthers);
1026 if (!result) {
1027 NetworkUtils.disableInterface(ifname);
1028 }
1029 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030 }
1031 }
1032
1033 /**
1034 * See {@link android.net.wifi.WifiManager#disableNetwork(int)}
1035 * @param netId the integer that identifies the network configuration
1036 * to the supplicant
1037 * @return {@code true} if the operation succeeded
1038 */
1039 public boolean disableNetwork(int netId) {
1040 enforceChangePermission();
1041
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 synchronized (mWifiStateTracker) {
1043 return WifiNative.disableNetworkCommand(netId);
1044 }
1045 }
1046
1047 /**
1048 * See {@link android.net.wifi.WifiManager#getConnectionInfo()}
1049 * @return the Wi-Fi information, contained in {@link WifiInfo}.
1050 */
1051 public WifiInfo getConnectionInfo() {
1052 enforceAccessPermission();
1053 /*
1054 * Make sure we have the latest information, by sending
1055 * a status request to the supplicant.
1056 */
1057 return mWifiStateTracker.requestConnectionInfo();
1058 }
1059
1060 /**
1061 * Return the results of the most recent access point scan, in the form of
1062 * a list of {@link ScanResult} objects.
1063 * @return the list of results
1064 */
1065 public List<ScanResult> getScanResults() {
1066 enforceAccessPermission();
1067 String reply;
1068 synchronized (mWifiStateTracker) {
1069 reply = WifiNative.scanResultsCommand();
1070 }
1071 if (reply == null) {
1072 return null;
1073 }
1074
1075 List<ScanResult> scanList = new ArrayList<ScanResult>();
1076
1077 int lineCount = 0;
1078
1079 int replyLen = reply.length();
1080 // Parse the result string, keeping in mind that the last line does
1081 // not end with a newline.
1082 for (int lineBeg = 0, lineEnd = 0; lineEnd <= replyLen; ++lineEnd) {
1083 if (lineEnd == replyLen || reply.charAt(lineEnd) == '\n') {
1084 ++lineCount;
1085 /*
1086 * Skip the first line, which is a header
1087 */
1088 if (lineCount == 1) {
1089 lineBeg = lineEnd + 1;
1090 continue;
1091 }
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001092 if (lineEnd > lineBeg) {
1093 String line = reply.substring(lineBeg, lineEnd);
1094 ScanResult scanResult = parseScanResult(line);
1095 if (scanResult != null) {
1096 scanList.add(scanResult);
1097 } else if (DBG) {
1098 Log.w(TAG, "misformatted scan result for: " + line);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099 }
1100 }
1101 lineBeg = lineEnd + 1;
1102 }
1103 }
1104 mWifiStateTracker.setScanResultsList(scanList);
1105 return scanList;
1106 }
1107
1108 /**
1109 * Parse the scan result line passed to us by wpa_supplicant (helper).
1110 * @param line the line to parse
1111 * @return the {@link ScanResult} object
1112 */
1113 private ScanResult parseScanResult(String line) {
1114 ScanResult scanResult = null;
1115 if (line != null) {
1116 /*
1117 * Cache implementation (LinkedHashMap) is not synchronized, thus,
1118 * must synchronized here!
1119 */
1120 synchronized (mScanResultCache) {
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001121 String[] result = scanResultPattern.split(line);
1122 if (3 <= result.length && result.length <= 5) {
1123 String bssid = result[0];
1124 // bssid | frequency | level | flags | ssid
1125 int frequency;
1126 int level;
1127 try {
1128 frequency = Integer.parseInt(result[1]);
1129 level = Integer.parseInt(result[2]);
1130 /* some implementations avoid negative values by adding 256
1131 * so we need to adjust for that here.
1132 */
1133 if (level > 0) level -= 256;
1134 } catch (NumberFormatException e) {
1135 frequency = 0;
1136 level = 0;
1137 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138
Mike Lockwood1a645052009-06-25 13:01:12 -04001139 /*
1140 * The formatting of the results returned by
1141 * wpa_supplicant is intended to make the fields
1142 * line up nicely when printed,
1143 * not to make them easy to parse. So we have to
1144 * apply some heuristics to figure out which field
1145 * is the SSID and which field is the flags.
1146 */
1147 String ssid;
1148 String flags;
1149 if (result.length == 4) {
1150 if (result[3].charAt(0) == '[') {
1151 flags = result[3];
1152 ssid = "";
1153 } else {
1154 flags = "";
1155 ssid = result[3];
1156 }
1157 } else if (result.length == 5) {
1158 flags = result[3];
1159 ssid = result[4];
1160 } else {
1161 // Here, we must have 3 fields: no flags and ssid
1162 // set
1163 flags = "";
1164 ssid = "";
1165 }
1166
Mike Lockwood00717e22009-08-17 10:09:36 -04001167 // bssid + ssid is the hash key
1168 String key = bssid + ssid;
1169 scanResult = mScanResultCache.get(key);
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001170 if (scanResult != null) {
1171 scanResult.level = level;
Mike Lockwood1a645052009-06-25 13:01:12 -04001172 scanResult.SSID = ssid;
1173 scanResult.capabilities = flags;
1174 scanResult.frequency = frequency;
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001175 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001176 // Do not add scan results that have no SSID set
1177 if (0 < ssid.trim().length()) {
1178 scanResult =
1179 new ScanResult(
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001180 ssid, bssid, flags, level, frequency);
Mike Lockwood00717e22009-08-17 10:09:36 -04001181 mScanResultCache.put(key, scanResult);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001183 }
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001184 } else {
1185 Log.w(TAG, "Misformatted scan result text with " +
1186 result.length + " fields: " + line);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 }
1188 }
1189 }
1190
1191 return scanResult;
1192 }
1193
1194 /**
1195 * Parse the "flags" field passed back in a scan result by wpa_supplicant,
1196 * and construct a {@code WifiConfiguration} that describes the encryption,
1197 * key management, and authenticaion capabilities of the access point.
1198 * @param flags the string returned by wpa_supplicant
1199 * @return the {@link WifiConfiguration} object, filled in
1200 */
1201 WifiConfiguration parseScanFlags(String flags) {
1202 WifiConfiguration config = new WifiConfiguration();
1203
1204 if (flags.length() == 0) {
1205 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
1206 }
1207 // ... to be implemented
1208 return config;
1209 }
1210
1211 /**
1212 * Tell the supplicant to persist the current list of configured networks.
1213 * @return {@code true} if the operation succeeded
1214 */
1215 public boolean saveConfiguration() {
1216 boolean result;
1217 enforceChangePermission();
1218 synchronized (mWifiStateTracker) {
1219 result = WifiNative.saveConfigCommand();
1220 if (result && mNeedReconfig) {
1221 mNeedReconfig = false;
1222 result = WifiNative.reloadConfigCommand();
1223
1224 if (result) {
1225 Intent intent = new Intent(WifiManager.NETWORK_IDS_CHANGED_ACTION);
1226 mContext.sendBroadcast(intent);
1227 }
1228 }
1229 }
Amith Yamasani47873e52009-07-02 12:05:32 -07001230 // Inform the backup manager about a data change
1231 IBackupManager ibm = IBackupManager.Stub.asInterface(
1232 ServiceManager.getService(Context.BACKUP_SERVICE));
1233 if (ibm != null) {
1234 try {
1235 ibm.dataChanged("com.android.providers.settings");
1236 } catch (Exception e) {
1237 // Try again later
1238 }
1239 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001240 return result;
1241 }
1242
1243 /**
1244 * Set the number of radio frequency channels that are allowed to be used
1245 * in the current regulatory domain. This method should be used only
1246 * if the correct number of channels cannot be determined automatically
Robert Greenwaltb5010cc2009-05-21 15:11:40 -07001247 * for some reason. If the operation is successful, the new value may be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001248 * persisted as a Secure setting.
1249 * @param numChannels the number of allowed channels. Must be greater than 0
1250 * and less than or equal to 16.
Robert Greenwaltb5010cc2009-05-21 15:11:40 -07001251 * @param persist {@code true} if the setting should be remembered.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001252 * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
1253 * {@code numChannels} is outside the valid range.
1254 */
Robert Greenwaltb5010cc2009-05-21 15:11:40 -07001255 public boolean setNumAllowedChannels(int numChannels, boolean persist) {
1256 Log.i(TAG, "WifiService trying to setNumAllowed to "+numChannels+
1257 " with persist set to "+persist);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001258 enforceChangePermission();
1259 /*
1260 * Validate the argument. We'd like to let the Wi-Fi driver do this,
1261 * but if Wi-Fi isn't currently enabled, that's not possible, and
1262 * we want to persist the setting anyway,so that it will take
1263 * effect when Wi-Fi does become enabled.
1264 */
1265 boolean found = false;
1266 for (int validChan : sValidRegulatoryChannelCounts) {
1267 if (validChan == numChannels) {
1268 found = true;
1269 break;
1270 }
1271 }
1272 if (!found) {
1273 return false;
1274 }
1275
Robert Greenwaltb5010cc2009-05-21 15:11:40 -07001276 if (persist) {
1277 Settings.Secure.putInt(mContext.getContentResolver(),
1278 Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
1279 numChannels);
1280 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001281 mWifiStateTracker.setNumAllowedChannels(numChannels);
1282 return true;
1283 }
1284
1285 /**
1286 * Return the number of frequency channels that are allowed
1287 * to be used in the current regulatory domain.
1288 * @return the number of allowed channels, or {@code -1} if an error occurs
1289 */
1290 public int getNumAllowedChannels() {
1291 int numChannels;
1292
1293 enforceAccessPermission();
1294 synchronized (mWifiStateTracker) {
1295 /*
1296 * If we can't get the value from the driver (e.g., because
1297 * Wi-Fi is not currently enabled), get the value from
1298 * Settings.
1299 */
1300 numChannels = WifiNative.getNumAllowedChannelsCommand();
1301 if (numChannels < 0) {
1302 numChannels = Settings.Secure.getInt(mContext.getContentResolver(),
1303 Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
1304 -1);
1305 }
1306 }
1307 return numChannels;
1308 }
1309
1310 /**
1311 * Return the list of valid values for the number of allowed radio channels
1312 * for various regulatory domains.
1313 * @return the list of channel counts
1314 */
1315 public int[] getValidChannelCounts() {
1316 enforceAccessPermission();
1317 return sValidRegulatoryChannelCounts;
1318 }
1319
1320 /**
1321 * Return the DHCP-assigned addresses from the last successful DHCP request,
1322 * if any.
1323 * @return the DHCP information
1324 */
1325 public DhcpInfo getDhcpInfo() {
1326 enforceAccessPermission();
1327 return mWifiStateTracker.getDhcpInfo();
1328 }
1329
1330 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1331 @Override
1332 public void onReceive(Context context, Intent intent) {
1333 String action = intent.getAction();
1334
Doug Zongker43866e02010-01-07 12:09:54 -08001335 long idleMillis =
1336 Settings.Secure.getLong(mContext.getContentResolver(),
1337 Settings.Secure.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001338 int stayAwakeConditions =
Doug Zongker43866e02010-01-07 12:09:54 -08001339 Settings.System.getInt(mContext.getContentResolver(),
1340 Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001341 if (action.equals(Intent.ACTION_SCREEN_ON)) {
Mike Lockwoodd9c32bc2009-05-18 14:14:15 -04001342 Log.d(TAG, "ACTION_SCREEN_ON");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001343 mAlarmManager.cancel(mIdleIntent);
1344 mDeviceIdle = false;
1345 mScreenOff = false;
Mike Lockwoodf32be162009-07-14 17:44:37 -04001346 mWifiStateTracker.enableRssiPolling(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001347 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
Mike Lockwoodd9c32bc2009-05-18 14:14:15 -04001348 Log.d(TAG, "ACTION_SCREEN_OFF");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001349 mScreenOff = true;
Mike Lockwoodf32be162009-07-14 17:44:37 -04001350 mWifiStateTracker.enableRssiPolling(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001351 /*
1352 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
1353 * AND the "stay on while plugged in" setting doesn't match the
1354 * current power conditions (i.e, not plugged in, plugged in to USB,
1355 * or plugged in to AC).
1356 */
1357 if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
San Mehatfa6c7112009-07-07 09:34:44 -07001358 WifiInfo info = mWifiStateTracker.requestConnectionInfo();
1359 if (info.getSupplicantState() != SupplicantState.COMPLETED) {
Robert Greenwalt84612ea62009-09-30 09:04:22 -07001360 // we used to go to sleep immediately, but this caused some race conditions
1361 // we don't have time to track down for this release. Delay instead, but not
1362 // as long as we would if connected (below)
1363 // TODO - fix the race conditions and switch back to the immediate turn-off
1364 long triggerTime = System.currentTimeMillis() + (2*60*1000); // 2 min
1365 Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for 120,000 ms");
1366 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
1367 // // do not keep Wifi awake when screen is off if Wifi is not associated
1368 // mDeviceIdle = true;
1369 // updateWifiState();
Mike Lockwoodd9c32bc2009-05-18 14:14:15 -04001370 } else {
1371 long triggerTime = System.currentTimeMillis() + idleMillis;
1372 Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
1373 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
1374 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001375 }
1376 /* we can return now -- there's nothing to do until we get the idle intent back */
1377 return;
1378 } else if (action.equals(ACTION_DEVICE_IDLE)) {
Mike Lockwoodd9c32bc2009-05-18 14:14:15 -04001379 Log.d(TAG, "got ACTION_DEVICE_IDLE");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001380 mDeviceIdle = true;
1381 } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
1382 /*
1383 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
1384 * AND we are transitioning from a state in which the device was supposed
1385 * to stay awake to a state in which it is not supposed to stay awake.
1386 * If "stay awake" state is not changing, we do nothing, to avoid resetting
1387 * the already-set timer.
1388 */
1389 int pluggedType = intent.getIntExtra("plugged", 0);
Mike Lockwoodd9c32bc2009-05-18 14:14:15 -04001390 Log.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001391 if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) &&
1392 !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) {
1393 long triggerTime = System.currentTimeMillis() + idleMillis;
Mike Lockwoodd9c32bc2009-05-18 14:14:15 -04001394 Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001395 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
1396 mPluggedType = pluggedType;
1397 return;
1398 }
1399 mPluggedType = pluggedType;
Nick Pelly005b2282009-09-10 10:21:56 -07001400 } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
Jaikumar Ganesh084c6652009-12-07 10:58:18 -08001401 BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
1402 Set<BluetoothDevice> sinks = a2dp.getConnectedSinks();
1403 boolean isBluetoothPlaying = false;
1404 for (BluetoothDevice sink : sinks) {
1405 if (a2dp.getSinkState(sink) == BluetoothA2dp.STATE_PLAYING) {
1406 isBluetoothPlaying = true;
1407 }
1408 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001409 mWifiStateTracker.setBluetoothScanMode(isBluetoothPlaying);
Jaikumar Ganesh084c6652009-12-07 10:58:18 -08001410
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001411 } else {
1412 return;
1413 }
1414
1415 updateWifiState();
1416 }
1417
1418 /**
1419 * Determines whether the Wi-Fi chipset should stay awake or be put to
1420 * sleep. Looks at the setting for the sleep policy and the current
1421 * conditions.
Jaikumar Ganesh084c6652009-12-07 10:58:18 -08001422 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001423 * @see #shouldDeviceStayAwake(int, int)
1424 */
1425 private boolean shouldWifiStayAwake(int stayAwakeConditions, int pluggedType) {
1426 int wifiSleepPolicy = Settings.System.getInt(mContext.getContentResolver(),
1427 Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_DEFAULT);
1428
1429 if (wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER) {
1430 // Never sleep
1431 return true;
1432 } else if ((wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) &&
1433 (pluggedType != 0)) {
1434 // Never sleep while plugged, and we're plugged
1435 return true;
1436 } else {
1437 // Default
1438 return shouldDeviceStayAwake(stayAwakeConditions, pluggedType);
1439 }
1440 }
Jaikumar Ganesh084c6652009-12-07 10:58:18 -08001441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001442 /**
1443 * Determine whether the bit value corresponding to {@code pluggedType} is set in
1444 * the bit string {@code stayAwakeConditions}. Because a {@code pluggedType} value
1445 * of {@code 0} isn't really a plugged type, but rather an indication that the
1446 * device isn't plugged in at all, there is no bit value corresponding to a
1447 * {@code pluggedType} value of {@code 0}. That is why we shift by
1448 * {@code pluggedType&nbsp;&#8212;&nbsp;1} instead of by {@code pluggedType}.
1449 * @param stayAwakeConditions a bit string specifying which "plugged types" should
1450 * keep the device (and hence Wi-Fi) awake.
1451 * @param pluggedType the type of plug (USB, AC, or none) for which the check is
1452 * being made
1453 * @return {@code true} if {@code pluggedType} indicates that the device is
1454 * supposed to stay awake, {@code false} otherwise.
1455 */
1456 private boolean shouldDeviceStayAwake(int stayAwakeConditions, int pluggedType) {
1457 return (stayAwakeConditions & pluggedType) != 0;
1458 }
1459 };
1460
Dianne Hackborn617f8772009-03-31 15:04:46 -07001461 private void sendEnableMessage(boolean enable, boolean persist, int uid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001462 Message msg = Message.obtain(mWifiHandler,
1463 (enable ? MESSAGE_ENABLE_WIFI : MESSAGE_DISABLE_WIFI),
Dianne Hackborn617f8772009-03-31 15:04:46 -07001464 (persist ? 1 : 0), uid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001465 msg.sendToTarget();
1466 }
1467
1468 private void sendStartMessage(boolean scanOnlyMode) {
1469 Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget();
1470 }
1471
1472 private void updateWifiState() {
Robert Greenwaltf75aa36f2009-10-22 17:03:47 -07001473 // send a message so it's all serialized
1474 Message.obtain(mWifiHandler, MESSAGE_UPDATE_STATE, 0, 0).sendToTarget();
1475 }
1476
1477 private void doUpdateWifiState() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001478 boolean wifiEnabled = getPersistedWifiEnabled();
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -07001479 boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001480 boolean lockHeld = mLocks.hasLocks();
1481 int strongestLockMode;
1482 boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
1483 boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
1484 if (mDeviceIdle && lockHeld) {
1485 strongestLockMode = mLocks.getStrongestLockMode();
1486 } else {
1487 strongestLockMode = WifiManager.WIFI_MODE_FULL;
1488 }
1489
1490 synchronized (mWifiHandler) {
1491 if (mWifiState == WIFI_STATE_ENABLING && !airplaneMode) {
1492 return;
1493 }
1494 if (wifiShouldBeEnabled) {
1495 if (wifiShouldBeStarted) {
1496 sWakeLock.acquire();
Dianne Hackborn617f8772009-03-31 15:04:46 -07001497 sendEnableMessage(true, false, mLastEnableUid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001498 sWakeLock.acquire();
1499 sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
1500 } else {
1501 int wakeLockTimeout =
1502 Settings.Secure.getInt(
1503 mContext.getContentResolver(),
1504 Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
1505 DEFAULT_WAKELOCK_TIMEOUT);
1506 /*
1507 * The following wakelock is held in order to ensure
1508 * that the connectivity manager has time to fail over
1509 * to the mobile data network. The connectivity manager
1510 * releases it once mobile data connectivity has been
1511 * established. If connectivity cannot be established,
1512 * the wakelock is released after wakeLockTimeout
1513 * milliseconds have elapsed.
1514 */
1515 sDriverStopWakeLock.acquire();
1516 mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI);
1517 mWifiHandler.sendEmptyMessageDelayed(MESSAGE_RELEASE_WAKELOCK, wakeLockTimeout);
1518 }
1519 } else {
1520 sWakeLock.acquire();
Dianne Hackborn617f8772009-03-31 15:04:46 -07001521 sendEnableMessage(false, false, mLastEnableUid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001522 }
1523 }
1524 }
1525
1526 private void registerForBroadcasts() {
1527 IntentFilter intentFilter = new IntentFilter();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001528 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
1529 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
1530 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
1531 intentFilter.addAction(ACTION_DEVICE_IDLE);
Nick Pelly005b2282009-09-10 10:21:56 -07001532 intentFilter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001533 mContext.registerReceiver(mReceiver, intentFilter);
1534 }
Jaikumar Ganesh084c6652009-12-07 10:58:18 -08001535
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001536 private boolean isAirplaneSensitive() {
1537 String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
1538 Settings.System.AIRPLANE_MODE_RADIOS);
1539 return airplaneModeRadios == null
1540 || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
1541 }
1542
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -07001543 private boolean isAirplaneToggleable() {
1544 String toggleableRadios = Settings.System.getString(mContext.getContentResolver(),
1545 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
1546 return toggleableRadios != null
1547 && toggleableRadios.contains(Settings.System.RADIO_WIFI);
1548 }
1549
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001550 /**
1551 * Returns true if Wi-Fi is sensitive to airplane mode, and airplane mode is
1552 * currently on.
1553 * @return {@code true} if airplane mode is on.
1554 */
1555 private boolean isAirplaneModeOn() {
1556 return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
1557 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1558 }
1559
1560 /**
1561 * Handler that allows posting to the WifiThread.
1562 */
1563 private class WifiHandler extends Handler {
1564 public WifiHandler(Looper looper) {
1565 super(looper);
1566 }
1567
1568 @Override
1569 public void handleMessage(Message msg) {
1570 switch (msg.what) {
1571
1572 case MESSAGE_ENABLE_WIFI:
Dianne Hackborn617f8772009-03-31 15:04:46 -07001573 setWifiEnabledBlocking(true, msg.arg1 == 1, msg.arg2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001574 sWakeLock.release();
1575 break;
1576
1577 case MESSAGE_START_WIFI:
1578 mWifiStateTracker.setScanOnlyMode(msg.arg1 != 0);
1579 mWifiStateTracker.restart();
1580 sWakeLock.release();
1581 break;
1582
Robert Greenwaltf75aa36f2009-10-22 17:03:47 -07001583 case MESSAGE_UPDATE_STATE:
1584 doUpdateWifiState();
1585 break;
1586
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001587 case MESSAGE_DISABLE_WIFI:
1588 // a non-zero msg.arg1 value means the "enabled" setting
1589 // should be persisted
Dianne Hackborn617f8772009-03-31 15:04:46 -07001590 setWifiEnabledBlocking(false, msg.arg1 == 1, msg.arg2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001591 sWakeLock.release();
1592 break;
1593
1594 case MESSAGE_STOP_WIFI:
1595 mWifiStateTracker.disconnectAndStop();
1596 // don't release wakelock
1597 break;
1598
1599 case MESSAGE_RELEASE_WAKELOCK:
1600 synchronized (sDriverStopWakeLock) {
1601 if (sDriverStopWakeLock.isHeld()) {
1602 sDriverStopWakeLock.release();
1603 }
1604 }
1605 break;
1606 }
1607 }
1608 }
1609
1610 @Override
1611 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1612 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1613 != PackageManager.PERMISSION_GRANTED) {
1614 pw.println("Permission Denial: can't dump WifiService from from pid="
1615 + Binder.getCallingPid()
1616 + ", uid=" + Binder.getCallingUid());
1617 return;
1618 }
1619 pw.println("Wi-Fi is " + stateName(mWifiState));
1620 pw.println("Stay-awake conditions: " +
1621 Settings.System.getInt(mContext.getContentResolver(),
1622 Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
1623 pw.println();
1624
1625 pw.println("Internal state:");
1626 pw.println(mWifiStateTracker);
1627 pw.println();
1628 pw.println("Latest scan results:");
1629 List<ScanResult> scanResults = mWifiStateTracker.getScanResultsList();
1630 if (scanResults != null && scanResults.size() != 0) {
1631 pw.println(" BSSID Frequency RSSI Flags SSID");
1632 for (ScanResult r : scanResults) {
1633 pw.printf(" %17s %9d %5d %-16s %s%n",
1634 r.BSSID,
1635 r.frequency,
1636 r.level,
1637 r.capabilities,
1638 r.SSID == null ? "" : r.SSID);
1639 }
1640 }
1641 pw.println();
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001642 pw.println("Locks acquired: " + mFullLocksAcquired + " full, " +
1643 mScanLocksAcquired + " scan");
1644 pw.println("Locks released: " + mFullLocksReleased + " full, " +
1645 mScanLocksReleased + " scan");
1646 pw.println();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001647 pw.println("Locks held:");
1648 mLocks.dump(pw);
1649 }
1650
1651 private static String stateName(int wifiState) {
1652 switch (wifiState) {
1653 case WIFI_STATE_DISABLING:
1654 return "disabling";
1655 case WIFI_STATE_DISABLED:
1656 return "disabled";
1657 case WIFI_STATE_ENABLING:
1658 return "enabling";
1659 case WIFI_STATE_ENABLED:
1660 return "enabled";
1661 case WIFI_STATE_UNKNOWN:
1662 return "unknown state";
1663 default:
1664 return "[invalid state]";
1665 }
1666 }
1667
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001668 private class WifiLock extends DeathRecipient {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001669 WifiLock(int lockMode, String tag, IBinder binder) {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001670 super(lockMode, tag, binder);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001671 }
1672
1673 public void binderDied() {
1674 synchronized (mLocks) {
1675 releaseWifiLockLocked(mBinder);
1676 }
1677 }
1678
1679 public String toString() {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001680 return "WifiLock{" + mTag + " type=" + mMode + " binder=" + mBinder + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001681 }
1682 }
1683
1684 private class LockList {
1685 private List<WifiLock> mList;
1686
1687 private LockList() {
1688 mList = new ArrayList<WifiLock>();
1689 }
1690
1691 private synchronized boolean hasLocks() {
1692 return !mList.isEmpty();
1693 }
1694
1695 private synchronized int getStrongestLockMode() {
1696 if (mList.isEmpty()) {
1697 return WifiManager.WIFI_MODE_FULL;
1698 }
1699 for (WifiLock l : mList) {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001700 if (l.mMode == WifiManager.WIFI_MODE_FULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001701 return WifiManager.WIFI_MODE_FULL;
1702 }
1703 }
1704 return WifiManager.WIFI_MODE_SCAN_ONLY;
1705 }
1706
1707 private void addLock(WifiLock lock) {
1708 if (findLockByBinder(lock.mBinder) < 0) {
1709 mList.add(lock);
1710 }
1711 }
1712
1713 private WifiLock removeLock(IBinder binder) {
1714 int index = findLockByBinder(binder);
1715 if (index >= 0) {
Suchi Amalapurapufff2fda2009-06-30 21:36:16 -07001716 WifiLock ret = mList.remove(index);
1717 ret.unlinkDeathRecipient();
1718 return ret;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001719 } else {
1720 return null;
1721 }
1722 }
1723
1724 private int findLockByBinder(IBinder binder) {
1725 int size = mList.size();
1726 for (int i = size - 1; i >= 0; i--)
1727 if (mList.get(i).mBinder == binder)
1728 return i;
1729 return -1;
1730 }
1731
1732 private void dump(PrintWriter pw) {
1733 for (WifiLock l : mList) {
1734 pw.print(" ");
1735 pw.println(l);
1736 }
1737 }
1738 }
1739
1740 public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) {
1741 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
1742 if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) {
1743 return false;
1744 }
1745 WifiLock wifiLock = new WifiLock(lockMode, tag, binder);
1746 synchronized (mLocks) {
1747 return acquireWifiLockLocked(wifiLock);
1748 }
1749 }
1750
1751 private boolean acquireWifiLockLocked(WifiLock wifiLock) {
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07001752 Log.d(TAG, "acquireWifiLockLocked: " + wifiLock);
1753
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001754 mLocks.addLock(wifiLock);
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07001755
The Android Open Source Project10592532009-03-18 17:39:46 -07001756 int uid = Binder.getCallingUid();
1757 long ident = Binder.clearCallingIdentity();
1758 try {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001759 switch(wifiLock.mMode) {
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001760 case WifiManager.WIFI_MODE_FULL:
1761 ++mFullLocksAcquired;
1762 mBatteryStats.noteFullWifiLockAcquired(uid);
1763 break;
1764 case WifiManager.WIFI_MODE_SCAN_ONLY:
1765 ++mScanLocksAcquired;
1766 mBatteryStats.noteScanWifiLockAcquired(uid);
1767 break;
The Android Open Source Project10592532009-03-18 17:39:46 -07001768 }
1769 } catch (RemoteException e) {
1770 } finally {
1771 Binder.restoreCallingIdentity(ident);
1772 }
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07001773
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001774 updateWifiState();
1775 return true;
1776 }
1777
1778 public boolean releaseWifiLock(IBinder lock) {
1779 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
1780 synchronized (mLocks) {
1781 return releaseWifiLockLocked(lock);
1782 }
1783 }
1784
1785 private boolean releaseWifiLockLocked(IBinder lock) {
Eric Shienbroodd4c5f892009-03-24 18:13:20 -07001786 boolean hadLock;
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07001787
The Android Open Source Project10592532009-03-18 17:39:46 -07001788 WifiLock wifiLock = mLocks.removeLock(lock);
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07001789
1790 Log.d(TAG, "releaseWifiLockLocked: " + wifiLock);
1791
Eric Shienbroodd4c5f892009-03-24 18:13:20 -07001792 hadLock = (wifiLock != null);
1793
1794 if (hadLock) {
1795 int uid = Binder.getCallingUid();
1796 long ident = Binder.clearCallingIdentity();
1797 try {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001798 switch(wifiLock.mMode) {
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001799 case WifiManager.WIFI_MODE_FULL:
1800 ++mFullLocksReleased;
1801 mBatteryStats.noteFullWifiLockReleased(uid);
1802 break;
1803 case WifiManager.WIFI_MODE_SCAN_ONLY:
1804 ++mScanLocksReleased;
1805 mBatteryStats.noteScanWifiLockReleased(uid);
1806 break;
Eric Shienbroodd4c5f892009-03-24 18:13:20 -07001807 }
1808 } catch (RemoteException e) {
1809 } finally {
1810 Binder.restoreCallingIdentity(ident);
The Android Open Source Project10592532009-03-18 17:39:46 -07001811 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001812 }
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07001813 // TODO - should this only happen if you hadLock?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001814 updateWifiState();
Eric Shienbroodd4c5f892009-03-24 18:13:20 -07001815 return hadLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001816 }
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001817
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001818 private abstract class DeathRecipient
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001819 implements IBinder.DeathRecipient {
1820 String mTag;
1821 int mMode;
1822 IBinder mBinder;
1823
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001824 DeathRecipient(int mode, String tag, IBinder binder) {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001825 super();
1826 mTag = tag;
1827 mMode = mode;
1828 mBinder = binder;
1829 try {
1830 mBinder.linkToDeath(this, 0);
1831 } catch (RemoteException e) {
1832 binderDied();
1833 }
1834 }
Suchi Amalapurapufff2fda2009-06-30 21:36:16 -07001835
1836 void unlinkDeathRecipient() {
1837 mBinder.unlinkToDeath(this, 0);
1838 }
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001839 }
1840
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001841 private class Multicaster extends DeathRecipient {
1842 Multicaster(String tag, IBinder binder) {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001843 super(Binder.getCallingUid(), tag, binder);
1844 }
1845
1846 public void binderDied() {
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001847 Log.e(TAG, "Multicaster binderDied");
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001848 synchronized (mMulticasters) {
1849 int i = mMulticasters.indexOf(this);
1850 if (i != -1) {
1851 removeMulticasterLocked(i, mMode);
1852 }
1853 }
1854 }
1855
1856 public String toString() {
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001857 return "Multicaster{" + mTag + " binder=" + mBinder + "}";
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001858 }
1859
1860 public int getUid() {
1861 return mMode;
1862 }
1863 }
1864
Robert Greenwalte2d155a2009-10-21 14:58:34 -07001865 public void initializeMulticastFiltering() {
1866 enforceMulticastChangePermission();
Robert Greenwalte2d155a2009-10-21 14:58:34 -07001867 synchronized (mMulticasters) {
1868 // if anybody had requested filters be off, leave off
1869 if (mMulticasters.size() != 0) {
1870 return;
1871 } else {
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001872 synchronized (mWifiStateTracker) {
1873 WifiNative.startPacketFiltering();
1874 }
Robert Greenwalte2d155a2009-10-21 14:58:34 -07001875 }
1876 }
1877 }
1878
Robert Greenwaltfc1b15c2009-05-22 15:09:51 -07001879 public void acquireMulticastLock(IBinder binder, String tag) {
1880 enforceMulticastChangePermission();
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001881
1882 synchronized (mMulticasters) {
1883 mMulticastEnabled++;
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001884 mMulticasters.add(new Multicaster(tag, binder));
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001885 // Note that we could call stopPacketFiltering only when
1886 // our new size == 1 (first call), but this function won't
1887 // be called often and by making the stopPacket call each
1888 // time we're less fragile and self-healing.
Zheng BaoZhong8d1668d2009-08-05 08:57:49 -04001889 synchronized (mWifiStateTracker) {
1890 WifiNative.stopPacketFiltering();
1891 }
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001892 }
1893
1894 int uid = Binder.getCallingUid();
1895 Long ident = Binder.clearCallingIdentity();
1896 try {
1897 mBatteryStats.noteWifiMulticastEnabled(uid);
1898 } catch (RemoteException e) {
1899 } finally {
1900 Binder.restoreCallingIdentity(ident);
1901 }
1902 }
1903
Robert Greenwaltfc1b15c2009-05-22 15:09:51 -07001904 public void releaseMulticastLock() {
1905 enforceMulticastChangePermission();
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001906
1907 int uid = Binder.getCallingUid();
1908 synchronized (mMulticasters) {
1909 mMulticastDisabled++;
1910 int size = mMulticasters.size();
1911 for (int i = size - 1; i >= 0; i--) {
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001912 Multicaster m = mMulticasters.get(i);
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001913 if ((m != null) && (m.getUid() == uid)) {
1914 removeMulticasterLocked(i, uid);
1915 }
1916 }
1917 }
1918 }
1919
1920 private void removeMulticasterLocked(int i, int uid)
1921 {
Suchi Amalapurapufff2fda2009-06-30 21:36:16 -07001922 Multicaster removed = mMulticasters.remove(i);
1923 if (removed != null) {
1924 removed.unlinkDeathRecipient();
1925 }
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001926 if (mMulticasters.size() == 0) {
Zheng BaoZhong8d1668d2009-08-05 08:57:49 -04001927 synchronized (mWifiStateTracker) {
1928 WifiNative.startPacketFiltering();
1929 }
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001930 }
1931
1932 Long ident = Binder.clearCallingIdentity();
1933 try {
1934 mBatteryStats.noteWifiMulticastDisabled(uid);
1935 } catch (RemoteException e) {
1936 } finally {
1937 Binder.restoreCallingIdentity(ident);
1938 }
1939 }
1940
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001941 public boolean isMulticastEnabled() {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001942 enforceAccessPermission();
1943
1944 synchronized (mMulticasters) {
1945 return (mMulticasters.size() > 0);
1946 }
1947 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001948}