blob: 4eb529c48a7ede62aa08ef569764832ee9a736c5 [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
Irfan Sheriff5321aef2010-02-12 12:35:59 -080025import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
26import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
27import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
28import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
29import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
30
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.app.AlarmManager;
32import android.app.PendingIntent;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -070033import android.bluetooth.BluetoothA2dp;
Jaikumar Ganesh084c6652009-12-07 10:58:18 -080034import android.bluetooth.BluetoothDevice;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.content.BroadcastReceiver;
36import android.content.ContentResolver;
37import android.content.Context;
38import android.content.Intent;
39import android.content.IntentFilter;
40import android.content.pm.PackageManager;
41import android.net.wifi.IWifiManager;
42import android.net.wifi.WifiInfo;
43import android.net.wifi.WifiManager;
44import android.net.wifi.WifiNative;
45import android.net.wifi.WifiStateTracker;
46import android.net.wifi.ScanResult;
47import android.net.wifi.WifiConfiguration;
San Mehat0310f9a2009-07-07 10:49:47 -070048import android.net.wifi.SupplicantState;
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -080049import android.net.wifi.WifiConfiguration.KeyMgmt;
Irfan Sheriff5321aef2010-02-12 12:35:59 -080050import android.net.ConnectivityManager;
51import android.net.InterfaceConfiguration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.net.NetworkStateTracker;
53import android.net.DhcpInfo;
Mike Lockwood0900f362009-07-10 17:24:07 -040054import android.net.NetworkUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.os.Binder;
56import android.os.Handler;
57import android.os.HandlerThread;
58import android.os.IBinder;
Irfan Sheriff5321aef2010-02-12 12:35:59 -080059import android.os.INetworkManagementService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060import android.os.Looper;
61import android.os.Message;
62import android.os.PowerManager;
Dianne Hackborn617f8772009-03-31 15:04:46 -070063import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064import android.os.RemoteException;
Amith Yamasani47873e52009-07-02 12:05:32 -070065import android.os.ServiceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066import android.provider.Settings;
Joe Onorato8a9b2202010-02-26 18:56:32 -080067import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import android.text.TextUtils;
69
70import java.util.ArrayList;
71import java.util.BitSet;
72import java.util.HashMap;
73import java.util.LinkedHashMap;
74import java.util.List;
75import java.util.Map;
Jaikumar Ganesh084c6652009-12-07 10:58:18 -080076import java.util.Set;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077import java.util.regex.Pattern;
78import java.io.FileDescriptor;
79import java.io.PrintWriter;
Irfan Sheriff5321aef2010-02-12 12:35:59 -080080import java.net.UnknownHostException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
The Android Open Source Project10592532009-03-18 17:39:46 -070082import com.android.internal.app.IBatteryStats;
Christopher Tate45281862010-03-05 15:46:30 -080083import android.app.backup.IBackupManager;
The Android Open Source Project10592532009-03-18 17:39:46 -070084import com.android.server.am.BatteryStatsService;
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -080085import com.android.internal.R;
The Android Open Source Project10592532009-03-18 17:39:46 -070086
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087/**
88 * WifiService handles remote WiFi operation requests by implementing
89 * the IWifiManager interface. It also creates a WifiMonitor to listen
90 * for Wifi-related events.
91 *
92 * @hide
93 */
94public class WifiService extends IWifiManager.Stub {
95 private static final String TAG = "WifiService";
96 private static final boolean DBG = false;
97 private static final Pattern scanResultPattern = Pattern.compile("\t+");
98 private final WifiStateTracker mWifiStateTracker;
99
100 private Context mContext;
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800101 private int mWifiApState;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102
103 private AlarmManager mAlarmManager;
104 private PendingIntent mIdleIntent;
105 private static final int IDLE_REQUEST = 0;
106 private boolean mScreenOff;
107 private boolean mDeviceIdle;
108 private int mPluggedType;
109
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800110 private enum DriverAction {DRIVER_UNLOAD, NO_DRIVER_UNLOAD};
111
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -0700112 // true if the user enabled Wifi while in airplane mode
113 private boolean mAirplaneModeOverwridden;
114
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 private final LockList mLocks = new LockList();
Eric Shienbrood5711fad2009-03-27 20:25:31 -0700116 // some wifi lock statistics
117 private int mFullLocksAcquired;
118 private int mFullLocksReleased;
119 private int mScanLocksAcquired;
120 private int mScanLocksReleased;
The Android Open Source Project10592532009-03-18 17:39:46 -0700121
Robert Greenwalt58ff0212009-05-19 15:53:54 -0700122 private final List<Multicaster> mMulticasters =
123 new ArrayList<Multicaster>();
Robert Greenwalt5347bd42009-05-13 15:10:16 -0700124 private int mMulticastEnabled;
125 private int mMulticastDisabled;
126
The Android Open Source Project10592532009-03-18 17:39:46 -0700127 private final IBatteryStats mBatteryStats;
Jaikumar Ganesh084c6652009-12-07 10:58:18 -0800128
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800129 private INetworkManagementService nwService;
130 ConnectivityManager mCm;
Irfan Sheriff7b009782010-03-11 16:37:45 -0800131 private WifiWatchdogService mWifiWatchdogService = null;
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800132 private String[] mWifiRegexs;
133
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 /**
Doug Zongker43866e02010-01-07 12:09:54 -0800135 * See {@link Settings.Secure#WIFI_IDLE_MS}. This is the default value if a
136 * Settings.Secure value is not present. This timeout value is chosen as
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 * the approximate point at which the battery drain caused by Wi-Fi
138 * being enabled but not active exceeds the battery drain caused by
139 * re-establishing a connection to the mobile data network.
140 */
141 private static final long DEFAULT_IDLE_MILLIS = 15 * 60 * 1000; /* 15 minutes */
142
143 private static final String WAKELOCK_TAG = "WifiService";
144
145 /**
146 * The maximum amount of time to hold the wake lock after a disconnect
147 * caused by stopping the driver. Establishing an EDGE connection has been
148 * observed to take about 5 seconds under normal circumstances. This
149 * provides a bit of extra margin.
150 * <p>
151 * See {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS}.
152 * This is the default value if a Settings.Secure value is not present.
153 */
154 private static final int DEFAULT_WAKELOCK_TIMEOUT = 8000;
155
156 // Wake lock used by driver-stop operation
157 private static PowerManager.WakeLock sDriverStopWakeLock;
158 // Wake lock used by other operations
159 private static PowerManager.WakeLock sWakeLock;
160
161 private static final int MESSAGE_ENABLE_WIFI = 0;
162 private static final int MESSAGE_DISABLE_WIFI = 1;
163 private static final int MESSAGE_STOP_WIFI = 2;
164 private static final int MESSAGE_START_WIFI = 3;
165 private static final int MESSAGE_RELEASE_WAKELOCK = 4;
Robert Greenwaltf75aa36fc2009-10-22 17:03:47 -0700166 private static final int MESSAGE_UPDATE_STATE = 5;
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800167 private static final int MESSAGE_START_ACCESS_POINT = 6;
168 private static final int MESSAGE_STOP_ACCESS_POINT = 7;
169
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170
171 private final WifiHandler mWifiHandler;
172
173 /*
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 * Cache of scan results objects (size is somewhat arbitrary)
175 */
176 private static final int SCAN_RESULT_CACHE_SIZE = 80;
177 private final LinkedHashMap<String, ScanResult> mScanResultCache;
178
179 /*
180 * Character buffer used to parse scan results (optimization)
181 */
182 private static final int SCAN_RESULT_BUFFER_SIZE = 512;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 private boolean mNeedReconfig;
184
Dianne Hackborn617f8772009-03-31 15:04:46 -0700185 /*
186 * Last UID that asked to enable WIFI.
187 */
188 private int mLastEnableUid = Process.myUid();
Jaikumar Ganesh084c6652009-12-07 10:58:18 -0800189
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 /**
191 * Number of allowed radio frequency channels in various regulatory domains.
192 * This list is sufficient for 802.11b/g networks (2.4GHz range).
193 */
194 private static int[] sValidRegulatoryChannelCounts = new int[] {11, 13, 14};
195
196 private static final String ACTION_DEVICE_IDLE =
197 "com.android.server.WifiManager.action.DEVICE_IDLE";
198
199 WifiService(Context context, WifiStateTracker tracker) {
200 mContext = context;
201 mWifiStateTracker = tracker;
Mike Lockwoodf32be162009-07-14 17:44:37 -0400202 mWifiStateTracker.enableRssiPolling(true);
The Android Open Source Project10592532009-03-18 17:39:46 -0700203 mBatteryStats = BatteryStatsService.getService();
Jaikumar Ganesh084c6652009-12-07 10:58:18 -0800204
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800205 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
206 nwService = INetworkManagementService.Stub.asInterface(b);
207
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 mScanResultCache = new LinkedHashMap<String, ScanResult>(
209 SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
210 /*
211 * Limit the cache size by SCAN_RESULT_CACHE_SIZE
212 * elements
213 */
214 public boolean removeEldestEntry(Map.Entry eldest) {
215 return SCAN_RESULT_CACHE_SIZE < this.size();
216 }
217 };
218
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 HandlerThread wifiThread = new HandlerThread("WifiService");
220 wifiThread.start();
221 mWifiHandler = new WifiHandler(wifiThread.getLooper());
222
Irfan Sheriff0f344060092010-03-10 10:05:51 -0800223 mWifiStateTracker.setWifiState(WIFI_STATE_DISABLED);
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800224 mWifiApState = WIFI_AP_STATE_DISABLED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225
226 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
227 Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
228 mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
229
230 PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
231 sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
232 sDriverStopWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
233 mWifiStateTracker.setReleaseWakeLockCallback(
234 new Runnable() {
235 public void run() {
236 mWifiHandler.removeMessages(MESSAGE_RELEASE_WAKELOCK);
237 synchronized (sDriverStopWakeLock) {
238 if (sDriverStopWakeLock.isHeld()) {
239 sDriverStopWakeLock.release();
240 }
241 }
242 }
243 }
244 );
245
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246 mContext.registerReceiver(
247 new BroadcastReceiver() {
248 @Override
249 public void onReceive(Context context, Intent intent) {
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -0700250 // clear our flag indicating the user has overwridden airplane mode
251 mAirplaneModeOverwridden = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 updateWifiState();
253 }
254 },
255 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
256
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800257 mContext.registerReceiver(
258 new BroadcastReceiver() {
259 @Override
260 public void onReceive(Context context, Intent intent) {
261
262 ArrayList<String> available = intent.getStringArrayListExtra(
263 ConnectivityManager.EXTRA_AVAILABLE_TETHER);
264 ArrayList<String> active = intent.getStringArrayListExtra(
265 ConnectivityManager.EXTRA_ACTIVE_TETHER);
266 updateTetherState(available, active);
267
268 }
269 },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
Irfan Sheriff7b009782010-03-11 16:37:45 -0800270 }
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800271
Irfan Sheriff7b009782010-03-11 16:37:45 -0800272 /**
273 * Check if Wi-Fi needs to be enabled and start
274 * if needed
275 */
276 public void startWifi() {
277 boolean wifiEnabled = getPersistedWifiEnabled();
Irfan Sheriff7b009782010-03-11 16:37:45 -0800278 Slog.i(TAG, "WifiService starting up with Wi-Fi " +
279 (wifiEnabled ? "enabled" : "disabled"));
Dianne Hackborn617f8772009-03-31 15:04:46 -0700280 setWifiEnabledBlocking(wifiEnabled, false, Process.myUid());
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800281 }
282
283 private void updateTetherState(ArrayList<String> available, ArrayList<String> tethered) {
284
285 boolean wifiTethered = false;
286 boolean wifiAvailable = false;
287
288 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
289 INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
290
291 mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
292 mWifiRegexs = mCm.getTetherableWifiRegexs();
293
294 for (String intf : available) {
295 for (String regex : mWifiRegexs) {
296 if (intf.matches(regex)) {
297
298 InterfaceConfiguration ifcg = null;
299 try {
300 ifcg = service.getInterfaceConfig(intf);
301 if (ifcg != null) {
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800302 /* IP/netmask: 169.254.2.2/255.255.255.0 */
303 ifcg.ipAddr = (169 << 24) + (254 << 16) + (2 << 8) + 2;
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800304 ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0;
305 ifcg.interfaceFlags = "up";
306
307 service.setInterfaceConfig(intf, ifcg);
308 }
309 } catch (Exception e) {
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800310 Slog.e(TAG, "Error configuring interface " + intf + ", :" + e);
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800311 setWifiApEnabledState(WIFI_AP_STATE_FAILED, 0, DriverAction.DRIVER_UNLOAD);
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800312 return;
313 }
314
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800315 if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800316 Slog.e(TAG, "Error tethering "+intf);
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800317 setWifiApEnabledState(WIFI_AP_STATE_FAILED, 0, DriverAction.DRIVER_UNLOAD);
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800318 }
319 break;
320 }
321 }
322 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 }
324
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325 private boolean getPersistedWifiEnabled() {
326 final ContentResolver cr = mContext.getContentResolver();
327 try {
328 return Settings.Secure.getInt(cr, Settings.Secure.WIFI_ON) == 1;
329 } catch (Settings.SettingNotFoundException e) {
330 Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, 0);
331 return false;
332 }
333 }
334
335 private void persistWifiEnabled(boolean enabled) {
336 final ContentResolver cr = mContext.getContentResolver();
337 Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0);
338 }
339
340 NetworkStateTracker getNetworkStateTracker() {
341 return mWifiStateTracker;
342 }
343
344 /**
345 * see {@link android.net.wifi.WifiManager#pingSupplicant()}
346 * @return {@code true} if the operation succeeds
347 */
348 public boolean pingSupplicant() {
349 enforceChangePermission();
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800350
351 return mWifiStateTracker.ping();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 }
353
354 /**
355 * see {@link android.net.wifi.WifiManager#startScan()}
356 * @return {@code true} if the operation succeeds
357 */
Mike Lockwooda5ec95c2009-07-08 17:11:17 -0400358 public boolean startScan(boolean forceActive) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 enforceChangePermission();
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800360
361 switch (mWifiStateTracker.getSupplicantState()) {
362 case DISCONNECTED:
363 case INACTIVE:
364 case SCANNING:
365 case DORMANT:
366 break;
367 default:
368 mWifiStateTracker.setScanResultHandling(
369 WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);
370 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371 }
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800372 return mWifiStateTracker.scan(forceActive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 }
374
375 /**
376 * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
377 * @param enable {@code true} to enable, {@code false} to disable.
378 * @return {@code true} if the enable/disable operation was
379 * started or is already in the queue.
380 */
381 public boolean setWifiEnabled(boolean enable) {
382 enforceChangePermission();
383 if (mWifiHandler == null) return false;
384
385 synchronized (mWifiHandler) {
Robert Greenwalta99f4612009-09-19 18:14:32 -0700386 // caller may not have WAKE_LOCK permission - it's not required here
387 long ident = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 sWakeLock.acquire();
Robert Greenwalta99f4612009-09-19 18:14:32 -0700389 Binder.restoreCallingIdentity(ident);
390
Dianne Hackborn617f8772009-03-31 15:04:46 -0700391 mLastEnableUid = Binder.getCallingUid();
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -0700392 // set a flag if the user is enabling Wifi while in airplane mode
393 mAirplaneModeOverwridden = (enable && isAirplaneModeOn() && isAirplaneToggleable());
Dianne Hackborn617f8772009-03-31 15:04:46 -0700394 sendEnableMessage(enable, true, Binder.getCallingUid());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395 }
396
397 return true;
398 }
399
400 /**
401 * Enables/disables Wi-Fi synchronously.
402 * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off.
403 * @param persist {@code true} if the setting should be persisted.
Dianne Hackborn617f8772009-03-31 15:04:46 -0700404 * @param uid The UID of the process making the request.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 * @return {@code true} if the operation succeeds (or if the existing state
406 * is the same as the requested state)
407 */
Dianne Hackborn617f8772009-03-31 15:04:46 -0700408 private boolean setWifiEnabledBlocking(boolean enable, boolean persist, int uid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 final int eventualWifiState = enable ? WIFI_STATE_ENABLED : WIFI_STATE_DISABLED;
Irfan Sheriff0f344060092010-03-10 10:05:51 -0800410 final int wifiState = mWifiStateTracker.getWifiState();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411
Irfan Sheriff0f344060092010-03-10 10:05:51 -0800412 if (wifiState == eventualWifiState) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 return true;
414 }
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -0700415 if (enable && isAirplaneModeOn() && !mAirplaneModeOverwridden) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 return false;
417 }
418
Irfan Sheriffcd770372010-01-08 09:36:04 -0800419 /**
420 * Multiple calls to unregisterReceiver() cause exception and a system crash.
421 * This can happen if a supplicant is lost (or firmware crash occurs) and user indicates
422 * disable wifi at the same time.
423 * Avoid doing a disable when the current Wifi state is UNKNOWN
424 * TODO: Handle driver load fail and supplicant lost as seperate states
425 */
Irfan Sheriff0f344060092010-03-10 10:05:51 -0800426 if ((wifiState == WIFI_STATE_UNKNOWN) && !enable) {
Irfan Sheriffcd770372010-01-08 09:36:04 -0800427 return false;
428 }
429
Dianne Hackborn617f8772009-03-31 15:04:46 -0700430 setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING, uid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800432 if ((mWifiApState == WIFI_AP_STATE_ENABLED) && enable) {
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800433 setWifiApEnabledBlocking(false, Process.myUid(), null);
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800434 }
435
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 if (enable) {
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800437 if (!mWifiStateTracker.loadDriver()) {
438 Slog.e(TAG, "Failed to load Wi-Fi driver.");
439 setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
440 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441 }
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800442 if (!mWifiStateTracker.startSupplicant()) {
443 mWifiStateTracker.unloadDriver();
444 Slog.e(TAG, "Failed to start supplicant daemon.");
445 setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
446 return false;
447 }
448
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 registerForBroadcasts();
450 mWifiStateTracker.startEventLoop();
Irfan Sheriff7b009782010-03-11 16:37:45 -0800451
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 } else {
453
454 mContext.unregisterReceiver(mReceiver);
455 // Remove notification (it will no-op if it isn't visible)
456 mWifiStateTracker.setNotificationVisible(false, 0, false, 0);
457
458 boolean failedToStopSupplicantOrUnloadDriver = false;
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800459
460 if (!mWifiStateTracker.stopSupplicant()) {
461 Slog.e(TAG, "Failed to stop supplicant daemon.");
462 setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
463 failedToStopSupplicantOrUnloadDriver = true;
464 }
465
466 /**
467 * Reset connections and disable interface
468 * before we unload the driver
469 */
470 mWifiStateTracker.resetConnections(true);
471
472 if (!mWifiStateTracker.unloadDriver()) {
473 Slog.e(TAG, "Failed to unload Wi-Fi driver.");
474 if (!failedToStopSupplicantOrUnloadDriver) {
Dianne Hackborn617f8772009-03-31 15:04:46 -0700475 setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 failedToStopSupplicantOrUnloadDriver = true;
477 }
478 }
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800479
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 if (failedToStopSupplicantOrUnloadDriver) {
481 return false;
482 }
483 }
484
485 // Success!
486
487 if (persist) {
488 persistWifiEnabled(enable);
489 }
Dianne Hackborn617f8772009-03-31 15:04:46 -0700490 setWifiEnabledState(eventualWifiState, uid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 return true;
492 }
493
Dianne Hackborn617f8772009-03-31 15:04:46 -0700494 private void setWifiEnabledState(int wifiState, int uid) {
Irfan Sheriff0f344060092010-03-10 10:05:51 -0800495 final int previousWifiState = mWifiStateTracker.getWifiState();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496
The Android Open Source Project10592532009-03-18 17:39:46 -0700497 long ident = Binder.clearCallingIdentity();
498 try {
499 if (wifiState == WIFI_STATE_ENABLED) {
Dianne Hackborn617f8772009-03-31 15:04:46 -0700500 mBatteryStats.noteWifiOn(uid);
The Android Open Source Project10592532009-03-18 17:39:46 -0700501 } else if (wifiState == WIFI_STATE_DISABLED) {
Dianne Hackborn617f8772009-03-31 15:04:46 -0700502 mBatteryStats.noteWifiOff(uid);
The Android Open Source Project10592532009-03-18 17:39:46 -0700503 }
504 } catch (RemoteException e) {
505 } finally {
506 Binder.restoreCallingIdentity(ident);
507 }
Jaikumar Ganesh084c6652009-12-07 10:58:18 -0800508
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 // Update state
Irfan Sheriff0f344060092010-03-10 10:05:51 -0800510 mWifiStateTracker.setWifiState(wifiState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511
512 // Broadcast
513 final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
514 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
515 intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
516 intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
517 mContext.sendStickyBroadcast(intent);
518 }
519
520 private void enforceAccessPermission() {
521 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
522 "WifiService");
523 }
524
525 private void enforceChangePermission() {
526 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
527 "WifiService");
528
529 }
530
Robert Greenwaltfc1b15c2009-05-22 15:09:51 -0700531 private void enforceMulticastChangePermission() {
532 mContext.enforceCallingOrSelfPermission(
533 android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
534 "WifiService");
535 }
536
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 /**
538 * see {@link WifiManager#getWifiState()}
539 * @return One of {@link WifiManager#WIFI_STATE_DISABLED},
540 * {@link WifiManager#WIFI_STATE_DISABLING},
541 * {@link WifiManager#WIFI_STATE_ENABLED},
542 * {@link WifiManager#WIFI_STATE_ENABLING},
543 * {@link WifiManager#WIFI_STATE_UNKNOWN}
544 */
545 public int getWifiEnabledState() {
546 enforceAccessPermission();
Irfan Sheriff0f344060092010-03-10 10:05:51 -0800547 return mWifiStateTracker.getWifiState();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548 }
549
550 /**
551 * see {@link android.net.wifi.WifiManager#disconnect()}
552 * @return {@code true} if the operation succeeds
553 */
554 public boolean disconnect() {
555 enforceChangePermission();
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800556
557 return mWifiStateTracker.disconnect();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 }
559
560 /**
561 * see {@link android.net.wifi.WifiManager#reconnect()}
562 * @return {@code true} if the operation succeeds
563 */
564 public boolean reconnect() {
565 enforceChangePermission();
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800566
567 return mWifiStateTracker.reconnectCommand();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 }
569
570 /**
571 * see {@link android.net.wifi.WifiManager#reassociate()}
572 * @return {@code true} if the operation succeeds
573 */
574 public boolean reassociate() {
575 enforceChangePermission();
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800576
577 return mWifiStateTracker.reassociate();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 }
579
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800580 /**
581 * see {@link android.net.wifi.WifiManager#startAccessPoint(WifiConfiguration)}
582 * @param wifiConfig SSID, security and channel details as
583 * part of WifiConfiguration
584 * @return {@code true} if the start operation was
585 * started or is already in the queue.
586 */
587 public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
588 enforceChangePermission();
589 if (mWifiHandler == null) return false;
590
591 synchronized (mWifiHandler) {
592
593 long ident = Binder.clearCallingIdentity();
594 sWakeLock.acquire();
595 Binder.restoreCallingIdentity(ident);
596
597 mLastEnableUid = Binder.getCallingUid();
598
599 sendAccessPointMessage(enabled, wifiConfig, Binder.getCallingUid());
600 }
601
602 return true;
603 }
604
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800605 public WifiConfiguration getWifiApConfiguration() {
606 final ContentResolver cr = mContext.getContentResolver();
607 WifiConfiguration wifiConfig = new WifiConfiguration();
608 int authType;
609 try {
610 wifiConfig.SSID = Settings.Secure.getString(cr, Settings.Secure.WIFI_AP_SSID);
611 if (wifiConfig.SSID == null)
612 return null;
613 authType = Settings.Secure.getInt(cr, Settings.Secure.WIFI_AP_SECURITY);
614 wifiConfig.allowedKeyManagement.set(authType);
615 wifiConfig.preSharedKey = Settings.Secure.getString(cr, Settings.Secure.WIFI_AP_PASSWD);
616 return wifiConfig;
617 } catch (Settings.SettingNotFoundException e) {
618 Slog.e(TAG,"AP settings not found, returning");
619 return null;
620 }
621 }
622
623 private void persistApConfiguration(WifiConfiguration wifiConfig) {
624 final ContentResolver cr = mContext.getContentResolver();
625 boolean isWpa;
626 if (wifiConfig == null)
627 return;
628 Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_SSID, wifiConfig.SSID);
629 isWpa = wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK);
630 Settings.Secure.putInt(cr,
631 Settings.Secure.WIFI_AP_SECURITY,
632 isWpa ? KeyMgmt.WPA_PSK : KeyMgmt.NONE);
633 if (isWpa)
634 Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_PASSWD, wifiConfig.preSharedKey);
635 }
636
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800637 /**
638 * Enables/disables Wi-Fi AP synchronously. The driver is loaded
639 * and soft access point configured as a single operation.
640 * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off.
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800641 * @param uid The UID of the process making the request.
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800642 * @param wifiConfig The WifiConfiguration for AP
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800643 * @return {@code true} if the operation succeeds (or if the existing state
644 * is the same as the requested state)
645 */
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800646 private boolean setWifiApEnabledBlocking(boolean enable,
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800647 int uid, WifiConfiguration wifiConfig) {
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800648 final int eventualWifiApState = enable ? WIFI_AP_STATE_ENABLED : WIFI_AP_STATE_DISABLED;
649
650 if (mWifiApState == eventualWifiApState) {
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800651 /* Configuration changed on a running access point */
652 if(enable && (wifiConfig != null)) {
653 try {
654 persistApConfiguration(wifiConfig);
655 nwService.stopAccessPoint();
656 nwService.startAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName());
657 return true;
658 } catch(Exception e) {
659 Slog.e(TAG, "Exception in nwService during AP restart");
660 setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD);
661 return false;
662 }
663 } else {
664 return true;
665 }
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800666 }
667
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800668 setWifiApEnabledState(enable ? WIFI_AP_STATE_ENABLING :
669 WIFI_AP_STATE_DISABLING, uid, DriverAction.NO_DRIVER_UNLOAD);
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800670
671 if (enable) {
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800672
673 /**
674 * Disable client mode for starting AP
675 */
676 if (mWifiStateTracker.getWifiState() == WIFI_STATE_ENABLED) {
677 setWifiEnabledBlocking(false, true, Process.myUid());
678 }
679
680 /* Use default config if there is no existing config */
681 if (wifiConfig == null && ((wifiConfig = getWifiApConfiguration()) == null)) {
682 wifiConfig = new WifiConfiguration();
683 wifiConfig.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default);
684 wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE);
685 }
686 persistApConfiguration(wifiConfig);
687
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800688 if (!mWifiStateTracker.loadDriver()) {
689 Slog.e(TAG, "Failed to load Wi-Fi driver for AP mode");
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800690 setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD);
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800691 return false;
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800692 }
693
694 try {
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800695 nwService.startAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName());
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800696 } catch(Exception e) {
697 Slog.e(TAG, "Exception in startAccessPoint()");
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800698 setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD);
699 return false;
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800700 }
701
702 } else {
703
704 try {
705 nwService.stopAccessPoint();
706 } catch(Exception e) {
707 Slog.e(TAG, "Exception in stopAccessPoint()");
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800708 setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD);
709 return false;
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800710 }
711
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800712 if (!mWifiStateTracker.unloadDriver()) {
713 Slog.e(TAG, "Failed to unload Wi-Fi driver for AP mode");
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800714 setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD);
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800715 return false;
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800716 }
717 }
718
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800719 setWifiApEnabledState(eventualWifiApState, uid, DriverAction.NO_DRIVER_UNLOAD);
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800720 return true;
721 }
722
723 /**
724 * see {@link WifiManager#getWifiApState()}
725 * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
726 * {@link WifiManager#WIFI_AP_STATE_DISABLING},
727 * {@link WifiManager#WIFI_AP_STATE_ENABLED},
728 * {@link WifiManager#WIFI_AP_STATE_ENABLING},
729 * {@link WifiManager#WIFI_AP_STATE_FAILED}
730 */
731 public int getWifiApEnabledState() {
732 enforceAccessPermission();
733 return mWifiApState;
734 }
735
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800736 private void setWifiApEnabledState(int wifiAPState, int uid, DriverAction flag) {
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800737 final int previousWifiApState = mWifiApState;
738
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800739 /**
740 * Unload the driver if going to a failed state
741 */
742 if ((mWifiApState == WIFI_AP_STATE_FAILED) && (flag == DriverAction.DRIVER_UNLOAD)) {
743 mWifiStateTracker.unloadDriver();
744 }
745
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800746 long ident = Binder.clearCallingIdentity();
747 try {
748 if (wifiAPState == WIFI_AP_STATE_ENABLED) {
749 mBatteryStats.noteWifiOn(uid);
750 } else if (wifiAPState == WIFI_AP_STATE_DISABLED) {
751 mBatteryStats.noteWifiOff(uid);
752 }
753 } catch (RemoteException e) {
754 } finally {
755 Binder.restoreCallingIdentity(ident);
756 }
757
758 // Update state
759 mWifiApState = wifiAPState;
760
761 // Broadcast
762 final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
763 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
764 intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiAPState);
765 intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
766 mContext.sendStickyBroadcast(intent);
767 }
768
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769 /**
770 * see {@link android.net.wifi.WifiManager#getConfiguredNetworks()}
771 * @return the list of configured networks
772 */
773 public List<WifiConfiguration> getConfiguredNetworks() {
774 enforceAccessPermission();
775 String listStr;
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800776
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777 /*
778 * We don't cache the list, because we want to allow
779 * for the possibility that the configuration file
780 * has been modified through some external means,
781 * such as the wpa_cli command line program.
782 */
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800783 listStr = mWifiStateTracker.listNetworks();
784
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 List<WifiConfiguration> networks =
786 new ArrayList<WifiConfiguration>();
787 if (listStr == null)
788 return networks;
789
790 String[] lines = listStr.split("\n");
791 // Skip the first line, which is a header
792 for (int i = 1; i < lines.length; i++) {
793 String[] result = lines[i].split("\t");
794 // network-id | ssid | bssid | flags
795 WifiConfiguration config = new WifiConfiguration();
796 try {
797 config.networkId = Integer.parseInt(result[0]);
798 } catch(NumberFormatException e) {
799 continue;
800 }
801 if (result.length > 3) {
802 if (result[3].indexOf("[CURRENT]") != -1)
803 config.status = WifiConfiguration.Status.CURRENT;
804 else if (result[3].indexOf("[DISABLED]") != -1)
805 config.status = WifiConfiguration.Status.DISABLED;
806 else
807 config.status = WifiConfiguration.Status.ENABLED;
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800808 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 config.status = WifiConfiguration.Status.ENABLED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 }
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800811 readNetworkVariables(config);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 networks.add(config);
813 }
814
815 return networks;
816 }
817
818 /**
819 * Read the variables from the supplicant daemon that are needed to
820 * fill in the WifiConfiguration object.
821 * <p/>
822 * The caller must hold the synchronization monitor.
823 * @param config the {@link WifiConfiguration} object to be filled in.
824 */
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800825 private void readNetworkVariables(WifiConfiguration config) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826
827 int netId = config.networkId;
828 if (netId < 0)
829 return;
830
831 /*
832 * TODO: maybe should have a native method that takes an array of
833 * variable names and returns an array of values. But we'd still
834 * be doing a round trip to the supplicant daemon for each variable.
835 */
836 String value;
837
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800838 value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.ssidVarName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839 if (!TextUtils.isEmpty(value)) {
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800840 config.SSID = removeDoubleQuotes(value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800841 } else {
842 config.SSID = null;
843 }
844
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800845 value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.bssidVarName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800846 if (!TextUtils.isEmpty(value)) {
847 config.BSSID = value;
848 } else {
849 config.BSSID = null;
850 }
851
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800852 value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.priorityVarName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853 config.priority = -1;
854 if (!TextUtils.isEmpty(value)) {
855 try {
856 config.priority = Integer.parseInt(value);
857 } catch (NumberFormatException ignore) {
858 }
859 }
860
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800861 value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862 config.hiddenSSID = false;
863 if (!TextUtils.isEmpty(value)) {
864 try {
865 config.hiddenSSID = Integer.parseInt(value) != 0;
866 } catch (NumberFormatException ignore) {
867 }
868 }
869
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800870 value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800871 config.wepTxKeyIndex = -1;
872 if (!TextUtils.isEmpty(value)) {
873 try {
874 config.wepTxKeyIndex = Integer.parseInt(value);
875 } catch (NumberFormatException ignore) {
876 }
877 }
878
879 /*
880 * Get up to 4 WEP keys. Note that the actual keys are not passed back,
881 * just a "*" if the key is set, or the null string otherwise.
882 */
883 for (int i = 0; i < 4; i++) {
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800884 value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.wepKeyVarNames[i]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800885 if (!TextUtils.isEmpty(value)) {
886 config.wepKeys[i] = value;
887 } else {
888 config.wepKeys[i] = null;
889 }
890 }
891
892 /*
893 * Get the private shared key. Note that the actual keys are not passed back,
894 * just a "*" if the key is set, or the null string otherwise.
895 */
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800896 value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.pskVarName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800897 if (!TextUtils.isEmpty(value)) {
898 config.preSharedKey = value;
899 } else {
900 config.preSharedKey = null;
901 }
902
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800903 value = mWifiStateTracker.getNetworkVariable(config.networkId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 WifiConfiguration.Protocol.varName);
905 if (!TextUtils.isEmpty(value)) {
906 String vals[] = value.split(" ");
907 for (String val : vals) {
908 int index =
909 lookupString(val, WifiConfiguration.Protocol.strings);
910 if (0 <= index) {
911 config.allowedProtocols.set(index);
912 }
913 }
914 }
915
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800916 value = mWifiStateTracker.getNetworkVariable(config.networkId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917 WifiConfiguration.KeyMgmt.varName);
918 if (!TextUtils.isEmpty(value)) {
919 String vals[] = value.split(" ");
920 for (String val : vals) {
921 int index =
922 lookupString(val, WifiConfiguration.KeyMgmt.strings);
923 if (0 <= index) {
924 config.allowedKeyManagement.set(index);
925 }
926 }
927 }
928
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800929 value = mWifiStateTracker.getNetworkVariable(config.networkId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800930 WifiConfiguration.AuthAlgorithm.varName);
931 if (!TextUtils.isEmpty(value)) {
932 String vals[] = value.split(" ");
933 for (String val : vals) {
934 int index =
935 lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
936 if (0 <= index) {
937 config.allowedAuthAlgorithms.set(index);
938 }
939 }
940 }
941
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800942 value = mWifiStateTracker.getNetworkVariable(config.networkId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800943 WifiConfiguration.PairwiseCipher.varName);
944 if (!TextUtils.isEmpty(value)) {
945 String vals[] = value.split(" ");
946 for (String val : vals) {
947 int index =
948 lookupString(val, WifiConfiguration.PairwiseCipher.strings);
949 if (0 <= index) {
950 config.allowedPairwiseCiphers.set(index);
951 }
952 }
953 }
954
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800955 value = mWifiStateTracker.getNetworkVariable(config.networkId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956 WifiConfiguration.GroupCipher.varName);
957 if (!TextUtils.isEmpty(value)) {
958 String vals[] = value.split(" ");
959 for (String val : vals) {
960 int index =
961 lookupString(val, WifiConfiguration.GroupCipher.strings);
962 if (0 <= index) {
963 config.allowedGroupCiphers.set(index);
964 }
965 }
966 }
Chung-yih Wang43374762009-09-16 14:28:42 +0800967
968 for (WifiConfiguration.EnterpriseField field :
969 config.enterpriseFields) {
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800970 value = mWifiStateTracker.getNetworkVariable(netId,
Chung-yih Wang43374762009-09-16 14:28:42 +0800971 field.varName());
972 if (!TextUtils.isEmpty(value)) {
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800973 if (field != config.eap) value = removeDoubleQuotes(value);
Chung-yih Wang43374762009-09-16 14:28:42 +0800974 field.setValue(value);
975 }
976 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800977 }
978
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800979 private static String removeDoubleQuotes(String string) {
980 if (string.length() <= 2) return "";
981 return string.substring(1, string.length() - 1);
982 }
983
984 private static String convertToQuotedString(String string) {
985 return "\"" + string + "\"";
986 }
987
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 /**
989 * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)}
990 * @return the supplicant-assigned identifier for the new or updated
991 * network if the operation succeeds, or {@code -1} if it fails
992 */
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800993 public int addOrUpdateNetwork(WifiConfiguration config) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 enforceChangePermission();
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800995
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 /*
997 * If the supplied networkId is -1, we create a new empty
998 * network configuration. Otherwise, the networkId should
999 * refer to an existing configuration.
1000 */
1001 int netId = config.networkId;
1002 boolean newNetwork = netId == -1;
Irfan Sheriff44113ba32010-03-16 14:54:07 -07001003 boolean doReconfig = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 // networkId of -1 means we want to create a new network
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001005 synchronized (mWifiStateTracker) {
1006 if (newNetwork) {
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001007 netId = mWifiStateTracker.addNetwork();
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001008 if (netId < 0) {
1009 if (DBG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001010 Slog.d(TAG, "Failed to add a network!");
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001011 }
1012 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013 }
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001014 doReconfig = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015 }
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001016 mNeedReconfig = mNeedReconfig || doReconfig;
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001017 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001018
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001019 setVariables: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 /*
1021 * Note that if a networkId for a non-existent network
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001022 * was supplied, then the first setNetworkVariable()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001023 * will fail, so we don't bother to make a separate check
1024 * for the validity of the ID up front.
1025 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 if (config.SSID != null &&
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001027 !mWifiStateTracker.setNetworkVariable(
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001028 netId,
1029 WifiConfiguration.ssidVarName,
1030 convertToQuotedString(config.SSID))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001031 if (DBG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001032 Slog.d(TAG, "failed to set SSID: "+config.SSID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 }
1034 break setVariables;
1035 }
1036
1037 if (config.BSSID != null &&
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001038 !mWifiStateTracker.setNetworkVariable(
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001039 netId,
1040 WifiConfiguration.bssidVarName,
1041 config.BSSID)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 if (DBG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001043 Slog.d(TAG, "failed to set BSSID: "+config.BSSID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 }
1045 break setVariables;
1046 }
1047
1048 String allowedKeyManagementString =
1049 makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
1050 if (config.allowedKeyManagement.cardinality() != 0 &&
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001051 !mWifiStateTracker.setNetworkVariable(
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001052 netId,
1053 WifiConfiguration.KeyMgmt.varName,
1054 allowedKeyManagementString)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 if (DBG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001056 Slog.d(TAG, "failed to set key_mgmt: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001057 allowedKeyManagementString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058 }
1059 break setVariables;
1060 }
1061
1062 String allowedProtocolsString =
1063 makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
1064 if (config.allowedProtocols.cardinality() != 0 &&
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001065 !mWifiStateTracker.setNetworkVariable(
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001066 netId,
1067 WifiConfiguration.Protocol.varName,
1068 allowedProtocolsString)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001069 if (DBG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001070 Slog.d(TAG, "failed to set proto: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001071 allowedProtocolsString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072 }
1073 break setVariables;
1074 }
1075
1076 String allowedAuthAlgorithmsString =
1077 makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
1078 if (config.allowedAuthAlgorithms.cardinality() != 0 &&
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001079 !mWifiStateTracker.setNetworkVariable(
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001080 netId,
1081 WifiConfiguration.AuthAlgorithm.varName,
1082 allowedAuthAlgorithmsString)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001083 if (DBG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001084 Slog.d(TAG, "failed to set auth_alg: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001085 allowedAuthAlgorithmsString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 }
1087 break setVariables;
1088 }
1089
1090 String allowedPairwiseCiphersString =
1091 makeString(config.allowedPairwiseCiphers, WifiConfiguration.PairwiseCipher.strings);
1092 if (config.allowedPairwiseCiphers.cardinality() != 0 &&
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001093 !mWifiStateTracker.setNetworkVariable(
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001094 netId,
1095 WifiConfiguration.PairwiseCipher.varName,
1096 allowedPairwiseCiphersString)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097 if (DBG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001098 Slog.d(TAG, "failed to set pairwise: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001099 allowedPairwiseCiphersString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001100 }
1101 break setVariables;
1102 }
1103
1104 String allowedGroupCiphersString =
1105 makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
1106 if (config.allowedGroupCiphers.cardinality() != 0 &&
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001107 !mWifiStateTracker.setNetworkVariable(
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001108 netId,
1109 WifiConfiguration.GroupCipher.varName,
1110 allowedGroupCiphersString)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001111 if (DBG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001112 Slog.d(TAG, "failed to set group: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001113 allowedGroupCiphersString);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114 }
1115 break setVariables;
1116 }
1117
1118 // Prevent client screw-up by passing in a WifiConfiguration we gave it
1119 // by preventing "*" as a key.
1120 if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001121 !mWifiStateTracker.setNetworkVariable(
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001122 netId,
1123 WifiConfiguration.pskVarName,
1124 config.preSharedKey)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001125 if (DBG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001126 Slog.d(TAG, "failed to set psk: "+config.preSharedKey);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127 }
1128 break setVariables;
1129 }
1130
1131 boolean hasSetKey = false;
1132 if (config.wepKeys != null) {
1133 for (int i = 0; i < config.wepKeys.length; i++) {
1134 // Prevent client screw-up by passing in a WifiConfiguration we gave it
1135 // by preventing "*" as a key.
1136 if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001137 if (!mWifiStateTracker.setNetworkVariable(
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001138 netId,
1139 WifiConfiguration.wepKeyVarNames[i],
1140 config.wepKeys[i])) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001141 if (DBG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001142 Slog.d(TAG,
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001143 "failed to set wep_key"+i+": " +
1144 config.wepKeys[i]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001145 }
1146 break setVariables;
1147 }
1148 hasSetKey = true;
1149 }
1150 }
1151 }
1152
1153 if (hasSetKey) {
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001154 if (!mWifiStateTracker.setNetworkVariable(
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001155 netId,
1156 WifiConfiguration.wepTxKeyIdxVarName,
1157 Integer.toString(config.wepTxKeyIndex))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001158 if (DBG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001159 Slog.d(TAG,
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001160 "failed to set wep_tx_keyidx: "+
1161 config.wepTxKeyIndex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001162 }
1163 break setVariables;
1164 }
1165 }
1166
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001167 if (!mWifiStateTracker.setNetworkVariable(
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001168 netId,
1169 WifiConfiguration.priorityVarName,
1170 Integer.toString(config.priority))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001171 if (DBG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001172 Slog.d(TAG, config.SSID + ": failed to set priority: "
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001173 +config.priority);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001174 }
1175 break setVariables;
1176 }
1177
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001178 if (config.hiddenSSID && !mWifiStateTracker.setNetworkVariable(
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001179 netId,
1180 WifiConfiguration.hiddenSSIDVarName,
1181 Integer.toString(config.hiddenSSID ? 1 : 0))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182 if (DBG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001183 Slog.d(TAG, config.SSID + ": failed to set hiddenSSID: "+
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001184 config.hiddenSSID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001185 }
1186 break setVariables;
1187 }
1188
Chung-yih Wang43374762009-09-16 14:28:42 +08001189 for (WifiConfiguration.EnterpriseField field
1190 : config.enterpriseFields) {
1191 String varName = field.varName();
1192 String value = field.value();
Chung-yih Wanga8d15942009-10-09 11:01:49 +08001193 if (value != null) {
1194 if (field != config.eap) {
Chia-chi Yeh784d53e2010-01-29 16:26:28 +08001195 value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
Chung-yih Wang43374762009-09-16 14:28:42 +08001196 }
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001197 if (!mWifiStateTracker.setNetworkVariable(
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001198 netId,
1199 varName,
1200 value)) {
Chung-yih Wanga8d15942009-10-09 11:01:49 +08001201 if (DBG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001202 Slog.d(TAG, config.SSID + ": failed to set " + varName +
Irfan Sheriff7aac5542009-12-22 21:42:17 -08001203 ": " + value);
Chung-yih Wanga8d15942009-10-09 11:01:49 +08001204 }
1205 break setVariables;
1206 }
Chung-yih Wang5069cc72009-06-03 17:33:47 +08001207 }
Chung-yih Wang5069cc72009-06-03 17:33:47 +08001208 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001209 return netId;
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001210 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001211
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001212 /*
1213 * For an update, if one of the setNetworkVariable operations fails,
1214 * we might want to roll back all the changes already made. But the
1215 * chances are that if anything is going to go wrong, it'll happen
1216 * the first time we try to set one of the variables.
1217 */
1218 if (newNetwork) {
1219 removeNetwork(netId);
1220 if (DBG) {
1221 Slog.d(TAG,
1222 "Failed to set a network variable, removed network: "
1223 + netId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001224 }
1225 }
1226 return -1;
1227 }
1228
1229 private static String makeString(BitSet set, String[] strings) {
1230 StringBuffer buf = new StringBuffer();
1231 int nextSetBit = -1;
1232
1233 /* Make sure all set bits are in [0, strings.length) to avoid
1234 * going out of bounds on strings. (Shouldn't happen, but...) */
1235 set = set.get(0, strings.length);
1236
1237 while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
1238 buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
1239 }
1240
1241 // remove trailing space
1242 if (set.cardinality() > 0) {
1243 buf.setLength(buf.length() - 1);
1244 }
1245
1246 return buf.toString();
1247 }
1248
1249 private static int lookupString(String string, String[] strings) {
1250 int size = strings.length;
1251
1252 string = string.replace('-', '_');
1253
1254 for (int i = 0; i < size; i++)
1255 if (string.equals(strings[i]))
1256 return i;
1257
1258 if (DBG) {
1259 // if we ever get here, we should probably add the
1260 // value to WifiConfiguration to reflect that it's
1261 // supported by the WPA supplicant
Joe Onorato8a9b2202010-02-26 18:56:32 -08001262 Slog.w(TAG, "Failed to look-up a string: " + string);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001263 }
1264
1265 return -1;
1266 }
1267
1268 /**
1269 * See {@link android.net.wifi.WifiManager#removeNetwork(int)}
1270 * @param netId the integer that identifies the network configuration
1271 * to the supplicant
1272 * @return {@code true} if the operation succeeded
1273 */
1274 public boolean removeNetwork(int netId) {
1275 enforceChangePermission();
1276
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001277 return mWifiStateTracker.removeNetwork(netId);
1278 }
1279
1280 /**
1281 * See {@link android.net.wifi.WifiManager#enableNetwork(int, boolean)}
1282 * @param netId the integer that identifies the network configuration
1283 * to the supplicant
1284 * @param disableOthers if true, disable all other networks.
1285 * @return {@code true} if the operation succeeded
1286 */
1287 public boolean enableNetwork(int netId, boolean disableOthers) {
1288 enforceChangePermission();
1289
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001290 String ifname = mWifiStateTracker.getInterfaceName();
1291 NetworkUtils.enableInterface(ifname);
1292 boolean result = mWifiStateTracker.enableNetwork(netId, disableOthers);
1293 if (!result) {
1294 NetworkUtils.disableInterface(ifname);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295 }
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001296 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001297 }
1298
1299 /**
1300 * See {@link android.net.wifi.WifiManager#disableNetwork(int)}
1301 * @param netId the integer that identifies the network configuration
1302 * to the supplicant
1303 * @return {@code true} if the operation succeeded
1304 */
1305 public boolean disableNetwork(int netId) {
1306 enforceChangePermission();
1307
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001308 return mWifiStateTracker.disableNetwork(netId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001309 }
1310
1311 /**
1312 * See {@link android.net.wifi.WifiManager#getConnectionInfo()}
1313 * @return the Wi-Fi information, contained in {@link WifiInfo}.
1314 */
1315 public WifiInfo getConnectionInfo() {
1316 enforceAccessPermission();
1317 /*
1318 * Make sure we have the latest information, by sending
1319 * a status request to the supplicant.
1320 */
1321 return mWifiStateTracker.requestConnectionInfo();
1322 }
1323
1324 /**
1325 * Return the results of the most recent access point scan, in the form of
1326 * a list of {@link ScanResult} objects.
1327 * @return the list of results
1328 */
1329 public List<ScanResult> getScanResults() {
1330 enforceAccessPermission();
1331 String reply;
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001332
1333 reply = mWifiStateTracker.scanResults();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001334 if (reply == null) {
1335 return null;
1336 }
1337
1338 List<ScanResult> scanList = new ArrayList<ScanResult>();
1339
1340 int lineCount = 0;
1341
1342 int replyLen = reply.length();
1343 // Parse the result string, keeping in mind that the last line does
1344 // not end with a newline.
1345 for (int lineBeg = 0, lineEnd = 0; lineEnd <= replyLen; ++lineEnd) {
1346 if (lineEnd == replyLen || reply.charAt(lineEnd) == '\n') {
1347 ++lineCount;
1348 /*
1349 * Skip the first line, which is a header
1350 */
1351 if (lineCount == 1) {
1352 lineBeg = lineEnd + 1;
1353 continue;
1354 }
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001355 if (lineEnd > lineBeg) {
1356 String line = reply.substring(lineBeg, lineEnd);
1357 ScanResult scanResult = parseScanResult(line);
1358 if (scanResult != null) {
1359 scanList.add(scanResult);
1360 } else if (DBG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001361 Slog.w(TAG, "misformatted scan result for: " + line);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001362 }
1363 }
1364 lineBeg = lineEnd + 1;
1365 }
1366 }
1367 mWifiStateTracker.setScanResultsList(scanList);
1368 return scanList;
1369 }
1370
1371 /**
1372 * Parse the scan result line passed to us by wpa_supplicant (helper).
1373 * @param line the line to parse
1374 * @return the {@link ScanResult} object
1375 */
1376 private ScanResult parseScanResult(String line) {
1377 ScanResult scanResult = null;
1378 if (line != null) {
1379 /*
1380 * Cache implementation (LinkedHashMap) is not synchronized, thus,
1381 * must synchronized here!
1382 */
1383 synchronized (mScanResultCache) {
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001384 String[] result = scanResultPattern.split(line);
1385 if (3 <= result.length && result.length <= 5) {
1386 String bssid = result[0];
1387 // bssid | frequency | level | flags | ssid
1388 int frequency;
1389 int level;
1390 try {
1391 frequency = Integer.parseInt(result[1]);
1392 level = Integer.parseInt(result[2]);
1393 /* some implementations avoid negative values by adding 256
1394 * so we need to adjust for that here.
1395 */
1396 if (level > 0) level -= 256;
1397 } catch (NumberFormatException e) {
1398 frequency = 0;
1399 level = 0;
1400 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001401
Mike Lockwood1a645052009-06-25 13:01:12 -04001402 /*
1403 * The formatting of the results returned by
1404 * wpa_supplicant is intended to make the fields
1405 * line up nicely when printed,
1406 * not to make them easy to parse. So we have to
1407 * apply some heuristics to figure out which field
1408 * is the SSID and which field is the flags.
1409 */
1410 String ssid;
1411 String flags;
1412 if (result.length == 4) {
1413 if (result[3].charAt(0) == '[') {
1414 flags = result[3];
1415 ssid = "";
1416 } else {
1417 flags = "";
1418 ssid = result[3];
1419 }
1420 } else if (result.length == 5) {
1421 flags = result[3];
1422 ssid = result[4];
1423 } else {
1424 // Here, we must have 3 fields: no flags and ssid
1425 // set
1426 flags = "";
1427 ssid = "";
1428 }
1429
Mike Lockwood00717e22009-08-17 10:09:36 -04001430 // bssid + ssid is the hash key
1431 String key = bssid + ssid;
1432 scanResult = mScanResultCache.get(key);
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001433 if (scanResult != null) {
1434 scanResult.level = level;
Mike Lockwood1a645052009-06-25 13:01:12 -04001435 scanResult.SSID = ssid;
1436 scanResult.capabilities = flags;
1437 scanResult.frequency = frequency;
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001438 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001439 // Do not add scan results that have no SSID set
1440 if (0 < ssid.trim().length()) {
1441 scanResult =
1442 new ScanResult(
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001443 ssid, bssid, flags, level, frequency);
Mike Lockwood00717e22009-08-17 10:09:36 -04001444 mScanResultCache.put(key, scanResult);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001445 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001446 }
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001447 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001448 Slog.w(TAG, "Misformatted scan result text with " +
Mike Lockwoodb30475e2009-04-21 13:55:07 -07001449 result.length + " fields: " + line);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001450 }
1451 }
1452 }
1453
1454 return scanResult;
1455 }
1456
1457 /**
1458 * Parse the "flags" field passed back in a scan result by wpa_supplicant,
1459 * and construct a {@code WifiConfiguration} that describes the encryption,
1460 * key management, and authenticaion capabilities of the access point.
1461 * @param flags the string returned by wpa_supplicant
1462 * @return the {@link WifiConfiguration} object, filled in
1463 */
1464 WifiConfiguration parseScanFlags(String flags) {
1465 WifiConfiguration config = new WifiConfiguration();
1466
1467 if (flags.length() == 0) {
1468 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
1469 }
1470 // ... to be implemented
1471 return config;
1472 }
1473
1474 /**
1475 * Tell the supplicant to persist the current list of configured networks.
1476 * @return {@code true} if the operation succeeded
1477 */
1478 public boolean saveConfiguration() {
1479 boolean result;
1480 enforceChangePermission();
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001481
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001482 synchronized (mWifiStateTracker) {
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001483 result = mWifiStateTracker.saveConfig();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001484 if (result && mNeedReconfig) {
1485 mNeedReconfig = false;
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001486 result = mWifiStateTracker.reloadConfig();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001487
1488 if (result) {
1489 Intent intent = new Intent(WifiManager.NETWORK_IDS_CHANGED_ACTION);
1490 mContext.sendBroadcast(intent);
1491 }
1492 }
1493 }
Amith Yamasani47873e52009-07-02 12:05:32 -07001494 // Inform the backup manager about a data change
1495 IBackupManager ibm = IBackupManager.Stub.asInterface(
1496 ServiceManager.getService(Context.BACKUP_SERVICE));
1497 if (ibm != null) {
1498 try {
1499 ibm.dataChanged("com.android.providers.settings");
1500 } catch (Exception e) {
1501 // Try again later
1502 }
1503 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001504 return result;
1505 }
1506
1507 /**
1508 * Set the number of radio frequency channels that are allowed to be used
1509 * in the current regulatory domain. This method should be used only
1510 * if the correct number of channels cannot be determined automatically
Robert Greenwaltb5010cc2009-05-21 15:11:40 -07001511 * for some reason. If the operation is successful, the new value may be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001512 * persisted as a Secure setting.
1513 * @param numChannels the number of allowed channels. Must be greater than 0
1514 * and less than or equal to 16.
Robert Greenwaltb5010cc2009-05-21 15:11:40 -07001515 * @param persist {@code true} if the setting should be remembered.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001516 * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
1517 * {@code numChannels} is outside the valid range.
1518 */
Robert Greenwaltb5010cc2009-05-21 15:11:40 -07001519 public boolean setNumAllowedChannels(int numChannels, boolean persist) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001520 Slog.i(TAG, "WifiService trying to setNumAllowed to "+numChannels+
Robert Greenwaltb5010cc2009-05-21 15:11:40 -07001521 " with persist set to "+persist);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001522 enforceChangePermission();
1523 /*
1524 * Validate the argument. We'd like to let the Wi-Fi driver do this,
1525 * but if Wi-Fi isn't currently enabled, that's not possible, and
1526 * we want to persist the setting anyway,so that it will take
1527 * effect when Wi-Fi does become enabled.
1528 */
1529 boolean found = false;
1530 for (int validChan : sValidRegulatoryChannelCounts) {
1531 if (validChan == numChannels) {
1532 found = true;
1533 break;
1534 }
1535 }
1536 if (!found) {
1537 return false;
1538 }
1539
Robert Greenwaltb5010cc2009-05-21 15:11:40 -07001540 if (persist) {
1541 Settings.Secure.putInt(mContext.getContentResolver(),
1542 Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
1543 numChannels);
1544 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001545 mWifiStateTracker.setNumAllowedChannels(numChannels);
1546 return true;
1547 }
1548
1549 /**
1550 * Return the number of frequency channels that are allowed
1551 * to be used in the current regulatory domain.
1552 * @return the number of allowed channels, or {@code -1} if an error occurs
1553 */
1554 public int getNumAllowedChannels() {
1555 int numChannels;
1556
1557 enforceAccessPermission();
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001558
1559 /*
1560 * If we can't get the value from the driver (e.g., because
1561 * Wi-Fi is not currently enabled), get the value from
1562 * Settings.
1563 */
1564 numChannels = mWifiStateTracker.getNumAllowedChannels();
1565 if (numChannels < 0) {
1566 numChannels = Settings.Secure.getInt(mContext.getContentResolver(),
1567 Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
1568 -1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001569 }
1570 return numChannels;
1571 }
1572
1573 /**
1574 * Return the list of valid values for the number of allowed radio channels
1575 * for various regulatory domains.
1576 * @return the list of channel counts
1577 */
1578 public int[] getValidChannelCounts() {
1579 enforceAccessPermission();
1580 return sValidRegulatoryChannelCounts;
1581 }
1582
1583 /**
1584 * Return the DHCP-assigned addresses from the last successful DHCP request,
1585 * if any.
1586 * @return the DHCP information
1587 */
1588 public DhcpInfo getDhcpInfo() {
1589 enforceAccessPermission();
1590 return mWifiStateTracker.getDhcpInfo();
1591 }
1592
1593 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1594 @Override
1595 public void onReceive(Context context, Intent intent) {
1596 String action = intent.getAction();
1597
Doug Zongker43866e02010-01-07 12:09:54 -08001598 long idleMillis =
1599 Settings.Secure.getLong(mContext.getContentResolver(),
1600 Settings.Secure.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001601 int stayAwakeConditions =
Doug Zongker43866e02010-01-07 12:09:54 -08001602 Settings.System.getInt(mContext.getContentResolver(),
1603 Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001604 if (action.equals(Intent.ACTION_SCREEN_ON)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001605 Slog.d(TAG, "ACTION_SCREEN_ON");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001606 mAlarmManager.cancel(mIdleIntent);
1607 mDeviceIdle = false;
1608 mScreenOff = false;
Mike Lockwoodf32be162009-07-14 17:44:37 -04001609 mWifiStateTracker.enableRssiPolling(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001610 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001611 Slog.d(TAG, "ACTION_SCREEN_OFF");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001612 mScreenOff = true;
Mike Lockwoodf32be162009-07-14 17:44:37 -04001613 mWifiStateTracker.enableRssiPolling(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001614 /*
1615 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
1616 * AND the "stay on while plugged in" setting doesn't match the
1617 * current power conditions (i.e, not plugged in, plugged in to USB,
1618 * or plugged in to AC).
1619 */
1620 if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
San Mehatfa6c7112009-07-07 09:34:44 -07001621 WifiInfo info = mWifiStateTracker.requestConnectionInfo();
1622 if (info.getSupplicantState() != SupplicantState.COMPLETED) {
Robert Greenwalt84612ea62009-09-30 09:04:22 -07001623 // we used to go to sleep immediately, but this caused some race conditions
1624 // we don't have time to track down for this release. Delay instead, but not
1625 // as long as we would if connected (below)
1626 // TODO - fix the race conditions and switch back to the immediate turn-off
1627 long triggerTime = System.currentTimeMillis() + (2*60*1000); // 2 min
Joe Onorato8a9b2202010-02-26 18:56:32 -08001628 Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for 120,000 ms");
Robert Greenwalt84612ea62009-09-30 09:04:22 -07001629 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
1630 // // do not keep Wifi awake when screen is off if Wifi is not associated
1631 // mDeviceIdle = true;
1632 // updateWifiState();
Mike Lockwoodd9c32bc2009-05-18 14:14:15 -04001633 } else {
1634 long triggerTime = System.currentTimeMillis() + idleMillis;
Joe Onorato8a9b2202010-02-26 18:56:32 -08001635 Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
Mike Lockwoodd9c32bc2009-05-18 14:14:15 -04001636 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
1637 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001638 }
1639 /* we can return now -- there's nothing to do until we get the idle intent back */
1640 return;
1641 } else if (action.equals(ACTION_DEVICE_IDLE)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001642 Slog.d(TAG, "got ACTION_DEVICE_IDLE");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001643 mDeviceIdle = true;
1644 } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
1645 /*
1646 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
1647 * AND we are transitioning from a state in which the device was supposed
1648 * to stay awake to a state in which it is not supposed to stay awake.
1649 * If "stay awake" state is not changing, we do nothing, to avoid resetting
1650 * the already-set timer.
1651 */
1652 int pluggedType = intent.getIntExtra("plugged", 0);
Joe Onorato8a9b2202010-02-26 18:56:32 -08001653 Slog.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001654 if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) &&
1655 !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) {
1656 long triggerTime = System.currentTimeMillis() + idleMillis;
Joe Onorato8a9b2202010-02-26 18:56:32 -08001657 Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001658 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
1659 mPluggedType = pluggedType;
1660 return;
1661 }
1662 mPluggedType = pluggedType;
Nick Pelly005b2282009-09-10 10:21:56 -07001663 } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
Jaikumar Ganesh084c6652009-12-07 10:58:18 -08001664 BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
1665 Set<BluetoothDevice> sinks = a2dp.getConnectedSinks();
1666 boolean isBluetoothPlaying = false;
1667 for (BluetoothDevice sink : sinks) {
1668 if (a2dp.getSinkState(sink) == BluetoothA2dp.STATE_PLAYING) {
1669 isBluetoothPlaying = true;
1670 }
1671 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001672 mWifiStateTracker.setBluetoothScanMode(isBluetoothPlaying);
Jaikumar Ganesh084c6652009-12-07 10:58:18 -08001673
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001674 } else {
1675 return;
1676 }
1677
1678 updateWifiState();
1679 }
1680
1681 /**
1682 * Determines whether the Wi-Fi chipset should stay awake or be put to
1683 * sleep. Looks at the setting for the sleep policy and the current
1684 * conditions.
Jaikumar Ganesh084c6652009-12-07 10:58:18 -08001685 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001686 * @see #shouldDeviceStayAwake(int, int)
1687 */
1688 private boolean shouldWifiStayAwake(int stayAwakeConditions, int pluggedType) {
1689 int wifiSleepPolicy = Settings.System.getInt(mContext.getContentResolver(),
1690 Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_DEFAULT);
1691
1692 if (wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER) {
1693 // Never sleep
1694 return true;
1695 } else if ((wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) &&
1696 (pluggedType != 0)) {
1697 // Never sleep while plugged, and we're plugged
1698 return true;
1699 } else {
1700 // Default
1701 return shouldDeviceStayAwake(stayAwakeConditions, pluggedType);
1702 }
1703 }
Jaikumar Ganesh084c6652009-12-07 10:58:18 -08001704
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001705 /**
1706 * Determine whether the bit value corresponding to {@code pluggedType} is set in
1707 * the bit string {@code stayAwakeConditions}. Because a {@code pluggedType} value
1708 * of {@code 0} isn't really a plugged type, but rather an indication that the
1709 * device isn't plugged in at all, there is no bit value corresponding to a
1710 * {@code pluggedType} value of {@code 0}. That is why we shift by
1711 * {@code pluggedType&nbsp;&#8212;&nbsp;1} instead of by {@code pluggedType}.
1712 * @param stayAwakeConditions a bit string specifying which "plugged types" should
1713 * keep the device (and hence Wi-Fi) awake.
1714 * @param pluggedType the type of plug (USB, AC, or none) for which the check is
1715 * being made
1716 * @return {@code true} if {@code pluggedType} indicates that the device is
1717 * supposed to stay awake, {@code false} otherwise.
1718 */
1719 private boolean shouldDeviceStayAwake(int stayAwakeConditions, int pluggedType) {
1720 return (stayAwakeConditions & pluggedType) != 0;
1721 }
1722 };
1723
Dianne Hackborn617f8772009-03-31 15:04:46 -07001724 private void sendEnableMessage(boolean enable, boolean persist, int uid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001725 Message msg = Message.obtain(mWifiHandler,
1726 (enable ? MESSAGE_ENABLE_WIFI : MESSAGE_DISABLE_WIFI),
Dianne Hackborn617f8772009-03-31 15:04:46 -07001727 (persist ? 1 : 0), uid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001728 msg.sendToTarget();
1729 }
1730
1731 private void sendStartMessage(boolean scanOnlyMode) {
1732 Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget();
1733 }
1734
Irfan Sheriff5321aef2010-02-12 12:35:59 -08001735 private void sendAccessPointMessage(boolean enable, WifiConfiguration wifiConfig, int uid) {
1736 Message.obtain(mWifiHandler,
1737 (enable ? MESSAGE_START_ACCESS_POINT : MESSAGE_STOP_ACCESS_POINT),
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -08001738 uid, 0, wifiConfig).sendToTarget();
Irfan Sheriff5321aef2010-02-12 12:35:59 -08001739 }
1740
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001741 private void updateWifiState() {
Robert Greenwaltf75aa36fc2009-10-22 17:03:47 -07001742 // send a message so it's all serialized
1743 Message.obtain(mWifiHandler, MESSAGE_UPDATE_STATE, 0, 0).sendToTarget();
1744 }
1745
1746 private void doUpdateWifiState() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001747 boolean wifiEnabled = getPersistedWifiEnabled();
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -07001748 boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001749 boolean lockHeld = mLocks.hasLocks();
1750 int strongestLockMode;
1751 boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
1752 boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
1753 if (mDeviceIdle && lockHeld) {
1754 strongestLockMode = mLocks.getStrongestLockMode();
1755 } else {
1756 strongestLockMode = WifiManager.WIFI_MODE_FULL;
1757 }
1758
1759 synchronized (mWifiHandler) {
Irfan Sheriff0f344060092010-03-10 10:05:51 -08001760 if ((mWifiStateTracker.getWifiState() == WIFI_STATE_ENABLING) && !airplaneMode) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001761 return;
1762 }
1763 if (wifiShouldBeEnabled) {
1764 if (wifiShouldBeStarted) {
1765 sWakeLock.acquire();
Dianne Hackborn617f8772009-03-31 15:04:46 -07001766 sendEnableMessage(true, false, mLastEnableUid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001767 sWakeLock.acquire();
1768 sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
1769 } else {
1770 int wakeLockTimeout =
1771 Settings.Secure.getInt(
1772 mContext.getContentResolver(),
1773 Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
1774 DEFAULT_WAKELOCK_TIMEOUT);
1775 /*
1776 * The following wakelock is held in order to ensure
1777 * that the connectivity manager has time to fail over
1778 * to the mobile data network. The connectivity manager
1779 * releases it once mobile data connectivity has been
1780 * established. If connectivity cannot be established,
1781 * the wakelock is released after wakeLockTimeout
1782 * milliseconds have elapsed.
1783 */
1784 sDriverStopWakeLock.acquire();
1785 mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI);
1786 mWifiHandler.sendEmptyMessageDelayed(MESSAGE_RELEASE_WAKELOCK, wakeLockTimeout);
1787 }
1788 } else {
1789 sWakeLock.acquire();
Dianne Hackborn617f8772009-03-31 15:04:46 -07001790 sendEnableMessage(false, false, mLastEnableUid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001791 }
1792 }
1793 }
1794
1795 private void registerForBroadcasts() {
1796 IntentFilter intentFilter = new IntentFilter();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001797 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
1798 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
1799 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
1800 intentFilter.addAction(ACTION_DEVICE_IDLE);
Nick Pelly005b2282009-09-10 10:21:56 -07001801 intentFilter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001802 mContext.registerReceiver(mReceiver, intentFilter);
1803 }
Jaikumar Ganesh084c6652009-12-07 10:58:18 -08001804
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001805 private boolean isAirplaneSensitive() {
1806 String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
1807 Settings.System.AIRPLANE_MODE_RADIOS);
1808 return airplaneModeRadios == null
1809 || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
1810 }
1811
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -07001812 private boolean isAirplaneToggleable() {
1813 String toggleableRadios = Settings.System.getString(mContext.getContentResolver(),
1814 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
1815 return toggleableRadios != null
1816 && toggleableRadios.contains(Settings.System.RADIO_WIFI);
1817 }
1818
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001819 /**
1820 * Returns true if Wi-Fi is sensitive to airplane mode, and airplane mode is
1821 * currently on.
1822 * @return {@code true} if airplane mode is on.
1823 */
1824 private boolean isAirplaneModeOn() {
1825 return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
1826 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1827 }
1828
1829 /**
1830 * Handler that allows posting to the WifiThread.
1831 */
1832 private class WifiHandler extends Handler {
1833 public WifiHandler(Looper looper) {
1834 super(looper);
1835 }
1836
1837 @Override
1838 public void handleMessage(Message msg) {
1839 switch (msg.what) {
1840
1841 case MESSAGE_ENABLE_WIFI:
Irfan Sheriff7b009782010-03-11 16:37:45 -08001842 if (mWifiWatchdogService == null) {
1843 mWifiWatchdogService = new WifiWatchdogService(mContext, mWifiStateTracker);
1844 }
Dianne Hackborn617f8772009-03-31 15:04:46 -07001845 setWifiEnabledBlocking(true, msg.arg1 == 1, msg.arg2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001846 sWakeLock.release();
1847 break;
1848
1849 case MESSAGE_START_WIFI:
1850 mWifiStateTracker.setScanOnlyMode(msg.arg1 != 0);
1851 mWifiStateTracker.restart();
1852 sWakeLock.release();
1853 break;
1854
Robert Greenwaltf75aa36fc2009-10-22 17:03:47 -07001855 case MESSAGE_UPDATE_STATE:
1856 doUpdateWifiState();
1857 break;
1858
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001859 case MESSAGE_DISABLE_WIFI:
1860 // a non-zero msg.arg1 value means the "enabled" setting
1861 // should be persisted
Dianne Hackborn617f8772009-03-31 15:04:46 -07001862 setWifiEnabledBlocking(false, msg.arg1 == 1, msg.arg2);
Irfan Sheriff7b009782010-03-11 16:37:45 -08001863 if (mWifiWatchdogService != null) {
1864 mWifiWatchdogService.quit();
1865 mWifiWatchdogService = null;
1866 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001867 sWakeLock.release();
1868 break;
1869
1870 case MESSAGE_STOP_WIFI:
1871 mWifiStateTracker.disconnectAndStop();
1872 // don't release wakelock
1873 break;
1874
1875 case MESSAGE_RELEASE_WAKELOCK:
1876 synchronized (sDriverStopWakeLock) {
1877 if (sDriverStopWakeLock.isHeld()) {
1878 sDriverStopWakeLock.release();
1879 }
1880 }
1881 break;
Irfan Sheriff5321aef2010-02-12 12:35:59 -08001882
1883 case MESSAGE_START_ACCESS_POINT:
1884 setWifiApEnabledBlocking(true,
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -08001885 msg.arg1,
Irfan Sheriff5321aef2010-02-12 12:35:59 -08001886 (WifiConfiguration) msg.obj);
1887 break;
1888
1889 case MESSAGE_STOP_ACCESS_POINT:
1890 setWifiApEnabledBlocking(false,
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -08001891 msg.arg1,
Irfan Sheriff5321aef2010-02-12 12:35:59 -08001892 (WifiConfiguration) msg.obj);
1893 sWakeLock.release();
1894 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001895 }
1896 }
1897 }
1898
1899 @Override
1900 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1901 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1902 != PackageManager.PERMISSION_GRANTED) {
1903 pw.println("Permission Denial: can't dump WifiService from from pid="
1904 + Binder.getCallingPid()
1905 + ", uid=" + Binder.getCallingUid());
1906 return;
1907 }
Irfan Sheriff0f344060092010-03-10 10:05:51 -08001908 pw.println("Wi-Fi is " + stateName(mWifiStateTracker.getWifiState()));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001909 pw.println("Stay-awake conditions: " +
1910 Settings.System.getInt(mContext.getContentResolver(),
1911 Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
1912 pw.println();
1913
1914 pw.println("Internal state:");
1915 pw.println(mWifiStateTracker);
1916 pw.println();
1917 pw.println("Latest scan results:");
1918 List<ScanResult> scanResults = mWifiStateTracker.getScanResultsList();
1919 if (scanResults != null && scanResults.size() != 0) {
1920 pw.println(" BSSID Frequency RSSI Flags SSID");
1921 for (ScanResult r : scanResults) {
1922 pw.printf(" %17s %9d %5d %-16s %s%n",
1923 r.BSSID,
1924 r.frequency,
1925 r.level,
1926 r.capabilities,
1927 r.SSID == null ? "" : r.SSID);
1928 }
1929 }
1930 pw.println();
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001931 pw.println("Locks acquired: " + mFullLocksAcquired + " full, " +
1932 mScanLocksAcquired + " scan");
1933 pw.println("Locks released: " + mFullLocksReleased + " full, " +
1934 mScanLocksReleased + " scan");
1935 pw.println();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001936 pw.println("Locks held:");
1937 mLocks.dump(pw);
1938 }
1939
1940 private static String stateName(int wifiState) {
1941 switch (wifiState) {
1942 case WIFI_STATE_DISABLING:
1943 return "disabling";
1944 case WIFI_STATE_DISABLED:
1945 return "disabled";
1946 case WIFI_STATE_ENABLING:
1947 return "enabling";
1948 case WIFI_STATE_ENABLED:
1949 return "enabled";
1950 case WIFI_STATE_UNKNOWN:
1951 return "unknown state";
1952 default:
1953 return "[invalid state]";
1954 }
1955 }
1956
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001957 private class WifiLock extends DeathRecipient {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001958 WifiLock(int lockMode, String tag, IBinder binder) {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001959 super(lockMode, tag, binder);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001960 }
1961
1962 public void binderDied() {
1963 synchronized (mLocks) {
1964 releaseWifiLockLocked(mBinder);
1965 }
1966 }
1967
1968 public String toString() {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001969 return "WifiLock{" + mTag + " type=" + mMode + " binder=" + mBinder + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001970 }
1971 }
1972
1973 private class LockList {
1974 private List<WifiLock> mList;
1975
1976 private LockList() {
1977 mList = new ArrayList<WifiLock>();
1978 }
1979
1980 private synchronized boolean hasLocks() {
1981 return !mList.isEmpty();
1982 }
1983
1984 private synchronized int getStrongestLockMode() {
1985 if (mList.isEmpty()) {
1986 return WifiManager.WIFI_MODE_FULL;
1987 }
1988 for (WifiLock l : mList) {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001989 if (l.mMode == WifiManager.WIFI_MODE_FULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001990 return WifiManager.WIFI_MODE_FULL;
1991 }
1992 }
1993 return WifiManager.WIFI_MODE_SCAN_ONLY;
1994 }
1995
1996 private void addLock(WifiLock lock) {
1997 if (findLockByBinder(lock.mBinder) < 0) {
1998 mList.add(lock);
1999 }
2000 }
2001
2002 private WifiLock removeLock(IBinder binder) {
2003 int index = findLockByBinder(binder);
2004 if (index >= 0) {
Suchi Amalapurapufff2fda2009-06-30 21:36:16 -07002005 WifiLock ret = mList.remove(index);
2006 ret.unlinkDeathRecipient();
2007 return ret;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002008 } else {
2009 return null;
2010 }
2011 }
2012
2013 private int findLockByBinder(IBinder binder) {
2014 int size = mList.size();
2015 for (int i = size - 1; i >= 0; i--)
2016 if (mList.get(i).mBinder == binder)
2017 return i;
2018 return -1;
2019 }
2020
2021 private void dump(PrintWriter pw) {
2022 for (WifiLock l : mList) {
2023 pw.print(" ");
2024 pw.println(l);
2025 }
2026 }
2027 }
2028
2029 public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) {
2030 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
2031 if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) {
2032 return false;
2033 }
2034 WifiLock wifiLock = new WifiLock(lockMode, tag, binder);
2035 synchronized (mLocks) {
2036 return acquireWifiLockLocked(wifiLock);
2037 }
2038 }
2039
2040 private boolean acquireWifiLockLocked(WifiLock wifiLock) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002041 Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07002042
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002043 mLocks.addLock(wifiLock);
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07002044
The Android Open Source Project10592532009-03-18 17:39:46 -07002045 int uid = Binder.getCallingUid();
2046 long ident = Binder.clearCallingIdentity();
2047 try {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07002048 switch(wifiLock.mMode) {
Eric Shienbrood5711fad2009-03-27 20:25:31 -07002049 case WifiManager.WIFI_MODE_FULL:
2050 ++mFullLocksAcquired;
2051 mBatteryStats.noteFullWifiLockAcquired(uid);
2052 break;
2053 case WifiManager.WIFI_MODE_SCAN_ONLY:
2054 ++mScanLocksAcquired;
2055 mBatteryStats.noteScanWifiLockAcquired(uid);
2056 break;
The Android Open Source Project10592532009-03-18 17:39:46 -07002057 }
2058 } catch (RemoteException e) {
2059 } finally {
2060 Binder.restoreCallingIdentity(ident);
2061 }
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07002062
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002063 updateWifiState();
2064 return true;
2065 }
2066
2067 public boolean releaseWifiLock(IBinder lock) {
2068 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
2069 synchronized (mLocks) {
2070 return releaseWifiLockLocked(lock);
2071 }
2072 }
2073
2074 private boolean releaseWifiLockLocked(IBinder lock) {
Eric Shienbroodd4c5f892009-03-24 18:13:20 -07002075 boolean hadLock;
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07002076
The Android Open Source Project10592532009-03-18 17:39:46 -07002077 WifiLock wifiLock = mLocks.removeLock(lock);
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07002078
Joe Onorato8a9b2202010-02-26 18:56:32 -08002079 Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock);
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07002080
Eric Shienbroodd4c5f892009-03-24 18:13:20 -07002081 hadLock = (wifiLock != null);
2082
2083 if (hadLock) {
2084 int uid = Binder.getCallingUid();
2085 long ident = Binder.clearCallingIdentity();
2086 try {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07002087 switch(wifiLock.mMode) {
Eric Shienbrood5711fad2009-03-27 20:25:31 -07002088 case WifiManager.WIFI_MODE_FULL:
2089 ++mFullLocksReleased;
2090 mBatteryStats.noteFullWifiLockReleased(uid);
2091 break;
2092 case WifiManager.WIFI_MODE_SCAN_ONLY:
2093 ++mScanLocksReleased;
2094 mBatteryStats.noteScanWifiLockReleased(uid);
2095 break;
Eric Shienbroodd4c5f892009-03-24 18:13:20 -07002096 }
2097 } catch (RemoteException e) {
2098 } finally {
2099 Binder.restoreCallingIdentity(ident);
The Android Open Source Project10592532009-03-18 17:39:46 -07002100 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002101 }
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07002102 // TODO - should this only happen if you hadLock?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002103 updateWifiState();
Eric Shienbroodd4c5f892009-03-24 18:13:20 -07002104 return hadLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002105 }
Robert Greenwalt5347bd42009-05-13 15:10:16 -07002106
Robert Greenwalt58ff0212009-05-19 15:53:54 -07002107 private abstract class DeathRecipient
Robert Greenwalt5347bd42009-05-13 15:10:16 -07002108 implements IBinder.DeathRecipient {
2109 String mTag;
2110 int mMode;
2111 IBinder mBinder;
2112
Robert Greenwalt58ff0212009-05-19 15:53:54 -07002113 DeathRecipient(int mode, String tag, IBinder binder) {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07002114 super();
2115 mTag = tag;
2116 mMode = mode;
2117 mBinder = binder;
2118 try {
2119 mBinder.linkToDeath(this, 0);
2120 } catch (RemoteException e) {
2121 binderDied();
2122 }
2123 }
Suchi Amalapurapufff2fda2009-06-30 21:36:16 -07002124
2125 void unlinkDeathRecipient() {
2126 mBinder.unlinkToDeath(this, 0);
2127 }
Robert Greenwalt5347bd42009-05-13 15:10:16 -07002128 }
2129
Robert Greenwalt58ff0212009-05-19 15:53:54 -07002130 private class Multicaster extends DeathRecipient {
2131 Multicaster(String tag, IBinder binder) {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07002132 super(Binder.getCallingUid(), tag, binder);
2133 }
2134
2135 public void binderDied() {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002136 Slog.e(TAG, "Multicaster binderDied");
Robert Greenwalt5347bd42009-05-13 15:10:16 -07002137 synchronized (mMulticasters) {
2138 int i = mMulticasters.indexOf(this);
2139 if (i != -1) {
2140 removeMulticasterLocked(i, mMode);
2141 }
2142 }
2143 }
2144
2145 public String toString() {
Robert Greenwalt58ff0212009-05-19 15:53:54 -07002146 return "Multicaster{" + mTag + " binder=" + mBinder + "}";
Robert Greenwalt5347bd42009-05-13 15:10:16 -07002147 }
2148
2149 public int getUid() {
2150 return mMode;
2151 }
2152 }
2153
Robert Greenwalte2d155a2009-10-21 14:58:34 -07002154 public void initializeMulticastFiltering() {
2155 enforceMulticastChangePermission();
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08002156
Robert Greenwalte2d155a2009-10-21 14:58:34 -07002157 synchronized (mMulticasters) {
2158 // if anybody had requested filters be off, leave off
2159 if (mMulticasters.size() != 0) {
2160 return;
2161 } else {
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08002162 mWifiStateTracker.startPacketFiltering();
Robert Greenwalte2d155a2009-10-21 14:58:34 -07002163 }
2164 }
2165 }
2166
Robert Greenwaltfc1b15c2009-05-22 15:09:51 -07002167 public void acquireMulticastLock(IBinder binder, String tag) {
2168 enforceMulticastChangePermission();
Robert Greenwalt5347bd42009-05-13 15:10:16 -07002169
2170 synchronized (mMulticasters) {
2171 mMulticastEnabled++;
Robert Greenwalt58ff0212009-05-19 15:53:54 -07002172 mMulticasters.add(new Multicaster(tag, binder));
Robert Greenwalt5347bd42009-05-13 15:10:16 -07002173 // Note that we could call stopPacketFiltering only when
2174 // our new size == 1 (first call), but this function won't
2175 // be called often and by making the stopPacket call each
2176 // time we're less fragile and self-healing.
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08002177 mWifiStateTracker.stopPacketFiltering();
Robert Greenwalt5347bd42009-05-13 15:10:16 -07002178 }
2179
2180 int uid = Binder.getCallingUid();
2181 Long ident = Binder.clearCallingIdentity();
2182 try {
2183 mBatteryStats.noteWifiMulticastEnabled(uid);
2184 } catch (RemoteException e) {
2185 } finally {
2186 Binder.restoreCallingIdentity(ident);
2187 }
2188 }
2189
Robert Greenwaltfc1b15c2009-05-22 15:09:51 -07002190 public void releaseMulticastLock() {
2191 enforceMulticastChangePermission();
Robert Greenwalt5347bd42009-05-13 15:10:16 -07002192
2193 int uid = Binder.getCallingUid();
2194 synchronized (mMulticasters) {
2195 mMulticastDisabled++;
2196 int size = mMulticasters.size();
2197 for (int i = size - 1; i >= 0; i--) {
Robert Greenwalt58ff0212009-05-19 15:53:54 -07002198 Multicaster m = mMulticasters.get(i);
Robert Greenwalt5347bd42009-05-13 15:10:16 -07002199 if ((m != null) && (m.getUid() == uid)) {
2200 removeMulticasterLocked(i, uid);
2201 }
2202 }
2203 }
2204 }
2205
2206 private void removeMulticasterLocked(int i, int uid)
2207 {
Suchi Amalapurapufff2fda2009-06-30 21:36:16 -07002208 Multicaster removed = mMulticasters.remove(i);
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08002209
Suchi Amalapurapufff2fda2009-06-30 21:36:16 -07002210 if (removed != null) {
2211 removed.unlinkDeathRecipient();
2212 }
Robert Greenwalt5347bd42009-05-13 15:10:16 -07002213 if (mMulticasters.size() == 0) {
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08002214 mWifiStateTracker.startPacketFiltering();
Robert Greenwalt5347bd42009-05-13 15:10:16 -07002215 }
2216
2217 Long ident = Binder.clearCallingIdentity();
2218 try {
2219 mBatteryStats.noteWifiMulticastDisabled(uid);
2220 } catch (RemoteException e) {
2221 } finally {
2222 Binder.restoreCallingIdentity(ident);
2223 }
2224 }
2225
Robert Greenwalt58ff0212009-05-19 15:53:54 -07002226 public boolean isMulticastEnabled() {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07002227 enforceAccessPermission();
2228
2229 synchronized (mMulticasters) {
2230 return (mMulticasters.size() > 0);
2231 }
2232 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002233}