blob: 20082157b19312254da4d6f6ae1385ca2f115ab0 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
Irfan Sheriffa2a1b912010-06-07 09:03:04 -07002 * Copyright (C) 2010 The Android Open Source Project
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003 *
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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019import android.app.AlarmManager;
Irfan Sheriff0d255342010-07-28 09:35:20 -070020import android.app.Notification;
21import android.app.NotificationManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.app.PendingIntent;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -070023import android.bluetooth.BluetoothA2dp;
Jaikumar Ganesh7440fc22010-09-27 17:04:14 -070024import android.bluetooth.BluetoothAdapter;
Jaikumar Ganesh084c6652009-12-07 10:58:18 -080025import android.bluetooth.BluetoothDevice;
Jaikumar Ganesh7440fc22010-09-27 17:04:14 -070026import android.bluetooth.BluetoothProfile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.content.BroadcastReceiver;
28import android.content.ContentResolver;
29import android.content.Context;
30import android.content.Intent;
31import android.content.IntentFilter;
32import android.content.pm.PackageManager;
Irfan Sheriff0d255342010-07-28 09:35:20 -070033import android.database.ContentObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.net.wifi.IWifiManager;
35import android.net.wifi.WifiInfo;
36import android.net.wifi.WifiManager;
Irfan Sheriff0d255342010-07-28 09:35:20 -070037import android.net.wifi.WifiStateMachine;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.net.wifi.ScanResult;
39import android.net.wifi.WifiConfiguration;
San Mehat0310f9a2009-07-07 10:49:47 -070040import android.net.wifi.SupplicantState;
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -080041import android.net.wifi.WifiConfiguration.KeyMgmt;
Irfan Sheriff5321aef2010-02-12 12:35:59 -080042import android.net.ConnectivityManager;
43import android.net.InterfaceConfiguration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.net.DhcpInfo;
Irfan Sheriff0d255342010-07-28 09:35:20 -070045import android.net.NetworkInfo;
46import android.net.NetworkInfo.State;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.os.Binder;
Irfan Sheriff0d255342010-07-28 09:35:20 -070048import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.os.HandlerThread;
50import android.os.IBinder;
Irfan Sheriff5321aef2010-02-12 12:35:59 -080051import android.os.INetworkManagementService;
Irfan Sheriff0d255342010-07-28 09:35:20 -070052import android.os.Message;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.os.RemoteException;
Amith Yamasani47873e52009-07-02 12:05:32 -070054import android.os.ServiceManager;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070055import android.os.WorkSource;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.provider.Settings;
Irfan Sheriff0d255342010-07-28 09:35:20 -070057import android.text.TextUtils;
Joe Onorato8a9b2202010-02-26 18:56:32 -080058import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059
60import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061import java.util.List;
Jaikumar Ganesh084c6652009-12-07 10:58:18 -080062import java.util.Set;
Irfan Sheriffa2a1b912010-06-07 09:03:04 -070063import java.util.concurrent.atomic.AtomicBoolean;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064import java.io.FileDescriptor;
65import java.io.PrintWriter;
66
The Android Open Source Project10592532009-03-18 17:39:46 -070067import com.android.internal.app.IBatteryStats;
Wink Saville4b7ba092010-10-20 15:37:41 -070068import com.android.internal.util.AsyncChannel;
The Android Open Source Project10592532009-03-18 17:39:46 -070069import com.android.server.am.BatteryStatsService;
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -080070import com.android.internal.R;
The Android Open Source Project10592532009-03-18 17:39:46 -070071
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072/**
73 * WifiService handles remote WiFi operation requests by implementing
Irfan Sheriffa2a1b912010-06-07 09:03:04 -070074 * the IWifiManager interface.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 *
76 * @hide
77 */
Irfan Sheriffa2a1b912010-06-07 09:03:04 -070078//TODO: Clean up multiple locks and implement WifiService
79// as a SM to track soft AP/client/adhoc bring up based
80// on device idle state, airplane mode and boot.
81
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082public class WifiService extends IWifiManager.Stub {
83 private static final String TAG = "WifiService";
Irfan Sheriffa2a1b912010-06-07 09:03:04 -070084 private static final boolean DBG = true;
85
Irfan Sheriff0d255342010-07-28 09:35:20 -070086 private final WifiStateMachine mWifiStateMachine;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087
88 private Context mContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089
90 private AlarmManager mAlarmManager;
91 private PendingIntent mIdleIntent;
Irfan Sheriff4f5f7c92010-10-14 17:01:27 -070092 private PendingIntent mScanIntent;
Jaikumar Ganesh7440fc22010-09-27 17:04:14 -070093 private BluetoothA2dp mBluetoothA2dp;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 private static final int IDLE_REQUEST = 0;
Irfan Sheriff4f5f7c92010-10-14 17:01:27 -070095 private static final int SCAN_REQUEST = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 private boolean mScreenOff;
97 private boolean mDeviceIdle;
98 private int mPluggedType;
99
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -0700100 // true if the user enabled Wifi while in airplane mode
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700101 private AtomicBoolean mAirplaneModeOverwridden = new AtomicBoolean(false);
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -0700102
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 private final LockList mLocks = new LockList();
Eric Shienbrood5711fad2009-03-27 20:25:31 -0700104 // some wifi lock statistics
Irfan Sheriff5876a422010-08-12 20:26:23 -0700105 private int mFullHighPerfLocksAcquired;
106 private int mFullHighPerfLocksReleased;
Eric Shienbrood5711fad2009-03-27 20:25:31 -0700107 private int mFullLocksAcquired;
108 private int mFullLocksReleased;
109 private int mScanLocksAcquired;
110 private int mScanLocksReleased;
The Android Open Source Project10592532009-03-18 17:39:46 -0700111
Robert Greenwalt58ff0212009-05-19 15:53:54 -0700112 private final List<Multicaster> mMulticasters =
113 new ArrayList<Multicaster>();
Robert Greenwalt5347bd42009-05-13 15:10:16 -0700114 private int mMulticastEnabled;
115 private int mMulticastDisabled;
116
The Android Open Source Project10592532009-03-18 17:39:46 -0700117 private final IBatteryStats mBatteryStats;
Jaikumar Ganesh084c6652009-12-07 10:58:18 -0800118
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800119 ConnectivityManager mCm;
120 private String[] mWifiRegexs;
121
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 /**
Doug Zongker43866e02010-01-07 12:09:54 -0800123 * See {@link Settings.Secure#WIFI_IDLE_MS}. This is the default value if a
124 * Settings.Secure value is not present. This timeout value is chosen as
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 * the approximate point at which the battery drain caused by Wi-Fi
126 * being enabled but not active exceeds the battery drain caused by
127 * re-establishing a connection to the mobile data network.
128 */
Irfan Sheriff4f5f7c92010-10-14 17:01:27 -0700129 private static final long DEFAULT_IDLE_MS = 15 * 60 * 1000; /* 15 minutes */
130
131 /**
132 * See {@link Settings.Secure#WIFI_SCAN_INTERVAL_MS}. This is the default value if a
133 * Settings.Secure value is not present.
134 */
135 private static final long DEFAULT_SCAN_INTERVAL_MS = 60 * 1000; /* 1 minute */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 /**
138 * Number of allowed radio frequency channels in various regulatory domains.
139 * This list is sufficient for 802.11b/g networks (2.4GHz range).
140 */
141 private static int[] sValidRegulatoryChannelCounts = new int[] {11, 13, 14};
142
143 private static final String ACTION_DEVICE_IDLE =
144 "com.android.server.WifiManager.action.DEVICE_IDLE";
145
Irfan Sheriff4f5f7c92010-10-14 17:01:27 -0700146 private static final String ACTION_START_SCAN =
147 "com.android.server.WifiManager.action.START_SCAN";
148
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700149 private boolean mIsReceiverRegistered = false;
150
Irfan Sheriff0d255342010-07-28 09:35:20 -0700151
152 NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
153
154 // Variables relating to the 'available networks' notification
155 /**
156 * The icon to show in the 'available networks' notification. This will also
157 * be the ID of the Notification given to the NotificationManager.
158 */
159 private static final int ICON_NETWORKS_AVAILABLE =
160 com.android.internal.R.drawable.stat_notify_wifi_in_range;
161 /**
162 * When a notification is shown, we wait this amount before possibly showing it again.
163 */
164 private final long NOTIFICATION_REPEAT_DELAY_MS;
165 /**
166 * Whether the user has set the setting to show the 'available networks' notification.
167 */
168 private boolean mNotificationEnabled;
169 /**
170 * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
171 */
172 private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
173 /**
174 * The {@link System#currentTimeMillis()} must be at least this value for us
175 * to show the notification again.
176 */
177 private long mNotificationRepeatTime;
178 /**
179 * The Notification object given to the NotificationManager.
180 */
181 private Notification mNotification;
182 /**
183 * Whether the notification is being shown, as set by us. That is, if the
184 * user cancels the notification, we will not receive the callback so this
185 * will still be true. We only guarantee if this is false, then the
186 * notification is not showing.
187 */
188 private boolean mNotificationShown;
189 /**
190 * The number of continuous scans that must occur before consider the
191 * supplicant in a scanning state. This allows supplicant to associate with
192 * remembered networks that are in the scan results.
193 */
194 private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
195 /**
196 * The number of scans since the last network state change. When this
197 * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
198 * supplicant to actually be scanning. When the network state changes to
199 * something other than scanning, we reset this to 0.
200 */
201 private int mNumScansSinceNetworkStateChange;
Jaikumar Ganesh7440fc22010-09-27 17:04:14 -0700202
Dianne Hackborn03f3cb02010-09-17 23:12:26 -0700203 /**
Wink Saville4b7ba092010-10-20 15:37:41 -0700204 * Asynchronous channel to WifiStateMachine
205 */
206 private AsyncChannel mChannel;
207
208 /**
209 * TODO: Possibly change WifiService into an AsyncService.
210 */
211 private class WifiServiceHandler extends Handler {
212 private AsyncChannel mWshChannel;
213
214 WifiServiceHandler(android.os.Looper looper, Context context) {
215 super(looper);
216 mWshChannel = new AsyncChannel();
217 mWshChannel.connect(context, this, mWifiStateMachine.getHandler(), 0);
218 }
219
220 @Override
221 public void handleMessage(Message msg) {
222 switch (msg.what) {
223 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
224 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
225 mChannel = mWshChannel;
226 } else {
227 Slog.d(TAG, "WifiServicehandler.handleMessage could not connect error=" +
228 msg.arg1);
229 mChannel = null;
230 }
231 break;
232 }
233 default: {
234 Slog.d(TAG, "WifiServicehandler.handleMessage ignoring msg=" + msg);
235 break;
236 }
237 }
238 }
239 }
240 WifiServiceHandler mHandler;
241
242 /**
Dianne Hackborn03f3cb02010-09-17 23:12:26 -0700243 * Temporary for computing UIDS that are responsible for starting WIFI.
244 * Protected by mWifiStateTracker lock.
245 */
246 private final WorkSource mTmpWorkSource = new WorkSource();
Irfan Sheriff0d255342010-07-28 09:35:20 -0700247
248 WifiService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 mContext = context;
Irfan Sheriff0d255342010-07-28 09:35:20 -0700250 mWifiStateMachine = new WifiStateMachine(mContext);
251 mWifiStateMachine.enableRssiPolling(true);
The Android Open Source Project10592532009-03-18 17:39:46 -0700252 mBatteryStats = BatteryStatsService.getService();
Jaikumar Ganesh084c6652009-12-07 10:58:18 -0800253
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
255 Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
256 mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
257
Irfan Sheriff4f5f7c92010-10-14 17:01:27 -0700258 Intent scanIntent = new Intent(ACTION_START_SCAN, null);
259 mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0);
260
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700261 HandlerThread wifiThread = new HandlerThread("WifiService");
262 wifiThread.start();
Wink Saville4b7ba092010-10-20 15:37:41 -0700263 mHandler = new WifiServiceHandler(wifiThread.getLooper(), context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 mContext.registerReceiver(
266 new BroadcastReceiver() {
267 @Override
268 public void onReceive(Context context, Intent intent) {
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -0700269 // clear our flag indicating the user has overwridden airplane mode
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700270 mAirplaneModeOverwridden.set(false);
Irfan Sheriffb2e6c012010-04-05 11:57:56 -0700271 // on airplane disable, restore Wifi if the saved state indicates so
272 if (!isAirplaneModeOn() && testAndClearWifiSavedState()) {
273 persistWifiEnabled(true);
274 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 updateWifiState();
276 }
277 },
278 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
279
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800280 mContext.registerReceiver(
281 new BroadcastReceiver() {
282 @Override
283 public void onReceive(Context context, Intent intent) {
284
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700285 ArrayList<String> available = intent.getStringArrayListExtra(
286 ConnectivityManager.EXTRA_AVAILABLE_TETHER);
287 ArrayList<String> active = intent.getStringArrayListExtra(
288 ConnectivityManager.EXTRA_ACTIVE_TETHER);
289 updateTetherState(available, active);
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800290
291 }
292 },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
Irfan Sheriff0d255342010-07-28 09:35:20 -0700293
294 IntentFilter filter = new IntentFilter();
295 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
296 filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
297 filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
298
299 mContext.registerReceiver(
300 new BroadcastReceiver() {
301 @Override
302 public void onReceive(Context context, Intent intent) {
303 if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
304 // reset & clear notification on any wifi state change
305 resetNotification();
306 } else if (intent.getAction().equals(
307 WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
308 mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
309 WifiManager.EXTRA_NETWORK_INFO);
310 // reset & clear notification on a network connect & disconnect
311 switch(mNetworkInfo.getDetailedState()) {
312 case CONNECTED:
313 case DISCONNECTED:
314 resetNotification();
315 break;
316 }
317 } else if (intent.getAction().equals(
318 WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
319 checkAndSetNotification();
320 }
321 }
322 }, filter);
323
324 // Setting is in seconds
325 NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
326 Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
327 mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
328 mNotificationEnabledSettingObserver.register();
Irfan Sheriff7b009782010-03-11 16:37:45 -0800329 }
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800330
Irfan Sheriff7b009782010-03-11 16:37:45 -0800331 /**
332 * Check if Wi-Fi needs to be enabled and start
333 * if needed
Irfan Sheriff60e3ba02010-04-02 12:18:45 -0700334 *
335 * This function is used only at boot time
Irfan Sheriff7b009782010-03-11 16:37:45 -0800336 */
Irfan Sheriff0d255342010-07-28 09:35:20 -0700337 public void checkAndStartWifi() {
Irfan Sheriffa3bd4092010-03-24 17:58:59 -0700338 /* Start if Wi-Fi is enabled or the saved state indicates Wi-Fi was on */
Irfan Sheriff60e3ba02010-04-02 12:18:45 -0700339 boolean wifiEnabled = !isAirplaneModeOn()
340 && (getPersistedWifiEnabled() || testAndClearWifiSavedState());
Irfan Sheriff7b009782010-03-11 16:37:45 -0800341 Slog.i(TAG, "WifiService starting up with Wi-Fi " +
342 (wifiEnabled ? "enabled" : "disabled"));
Irfan Sheriffb99fe5e2010-03-26 14:56:07 -0700343 setWifiEnabled(wifiEnabled);
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800344 }
345
346 private void updateTetherState(ArrayList<String> available, ArrayList<String> tethered) {
347
348 boolean wifiTethered = false;
349 boolean wifiAvailable = false;
350
351 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
352 INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
353
Robert Greenwalt14f2ef42010-06-15 12:19:37 -0700354 if (mCm == null) {
355 mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
356 }
357
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800358 mWifiRegexs = mCm.getTetherableWifiRegexs();
359
360 for (String intf : available) {
361 for (String regex : mWifiRegexs) {
362 if (intf.matches(regex)) {
363
364 InterfaceConfiguration ifcg = null;
365 try {
366 ifcg = service.getInterfaceConfig(intf);
367 if (ifcg != null) {
Robert Greenwaltbfb7bfa2010-03-24 16:03:21 -0700368 /* IP/netmask: 192.168.43.1/255.255.255.0 */
369 ifcg.ipAddr = (192 << 24) + (168 << 16) + (43 << 8) + 1;
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800370 ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0;
Irfan Sheriff07bd5ae2010-10-28 14:45:56 -0700371 ifcg.interfaceFlags = "[up]";
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800372
373 service.setInterfaceConfig(intf, ifcg);
374 }
375 } catch (Exception e) {
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800376 Slog.e(TAG, "Error configuring interface " + intf + ", :" + e);
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700377 setWifiApEnabled(null, false);
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800378 return;
379 }
380
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800381 if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700382 Slog.e(TAG, "Error tethering on " + intf);
383 setWifiApEnabled(null, false);
384 return;
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800385 }
386 break;
387 }
388 }
389 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 }
391
Irfan Sheriffa3bd4092010-03-24 17:58:59 -0700392 private boolean testAndClearWifiSavedState() {
393 final ContentResolver cr = mContext.getContentResolver();
394 int wifiSavedState = 0;
395 try {
396 wifiSavedState = Settings.Secure.getInt(cr, Settings.Secure.WIFI_SAVED_STATE);
397 if(wifiSavedState == 1)
398 Settings.Secure.putInt(cr, Settings.Secure.WIFI_SAVED_STATE, 0);
399 } catch (Settings.SettingNotFoundException e) {
400 ;
401 }
402 return (wifiSavedState == 1);
403 }
404
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 private boolean getPersistedWifiEnabled() {
406 final ContentResolver cr = mContext.getContentResolver();
407 try {
408 return Settings.Secure.getInt(cr, Settings.Secure.WIFI_ON) == 1;
409 } catch (Settings.SettingNotFoundException e) {
410 Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, 0);
411 return false;
412 }
413 }
414
415 private void persistWifiEnabled(boolean enabled) {
416 final ContentResolver cr = mContext.getContentResolver();
417 Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0);
418 }
419
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 /**
421 * see {@link android.net.wifi.WifiManager#pingSupplicant()}
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700422 * @return {@code true} if the operation succeeds, {@code false} otherwise
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 */
424 public boolean pingSupplicant() {
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700425 enforceAccessPermission();
Irfan Sheriffd8134ff2010-08-22 17:06:34 -0700426 return mWifiStateMachine.syncPingSupplicant();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 }
428
429 /**
430 * see {@link android.net.wifi.WifiManager#startScan()}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 */
Irfan Sheriffe4984752010-08-19 11:29:22 -0700432 public void startScan(boolean forceActive) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433 enforceChangePermission();
Irfan Sheriffe4984752010-08-19 11:29:22 -0700434 mWifiStateMachine.startScan(forceActive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 }
436
437 private void enforceAccessPermission() {
438 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
439 "WifiService");
440 }
441
442 private void enforceChangePermission() {
443 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
444 "WifiService");
445
446 }
447
Robert Greenwaltfc1b15c2009-05-22 15:09:51 -0700448 private void enforceMulticastChangePermission() {
449 mContext.enforceCallingOrSelfPermission(
450 android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
451 "WifiService");
452 }
453
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 /**
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700455 * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
456 * @param enable {@code true} to enable, {@code false} to disable.
457 * @return {@code true} if the enable/disable operation was
458 * started or is already in the queue.
459 */
460 public synchronized boolean setWifiEnabled(boolean enable) {
461 enforceChangePermission();
462
463 if (DBG) {
Irfan Sheriff0d255342010-07-28 09:35:20 -0700464 Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700465 }
466
467 // set a flag if the user is enabling Wifi while in airplane mode
468 if (enable && isAirplaneModeOn() && isAirplaneToggleable()) {
469 mAirplaneModeOverwridden.set(true);
470 }
471
Dianne Hackborn03f3cb02010-09-17 23:12:26 -0700472 if (enable) {
473 reportStartWorkSource();
474 }
Irfan Sheriff0d255342010-07-28 09:35:20 -0700475 mWifiStateMachine.setWifiEnabled(enable);
Irfan Sheriff61180692010-08-18 16:07:39 -0700476
477 /*
478 * Caller might not have WRITE_SECURE_SETTINGS,
479 * only CHANGE_WIFI_STATE is enforced
480 */
481 long ident = Binder.clearCallingIdentity();
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700482 persistWifiEnabled(enable);
Irfan Sheriff61180692010-08-18 16:07:39 -0700483 Binder.restoreCallingIdentity(ident);
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700484
485 if (enable) {
486 if (!mIsReceiverRegistered) {
487 registerForBroadcasts();
488 mIsReceiverRegistered = true;
489 }
490 } else if (mIsReceiverRegistered){
491 mContext.unregisterReceiver(mReceiver);
492 mIsReceiverRegistered = false;
493 }
494
495 return true;
496 }
497
498 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 * see {@link WifiManager#getWifiState()}
500 * @return One of {@link WifiManager#WIFI_STATE_DISABLED},
501 * {@link WifiManager#WIFI_STATE_DISABLING},
502 * {@link WifiManager#WIFI_STATE_ENABLED},
503 * {@link WifiManager#WIFI_STATE_ENABLING},
504 * {@link WifiManager#WIFI_STATE_UNKNOWN}
505 */
506 public int getWifiEnabledState() {
507 enforceAccessPermission();
Irfan Sheriffd8134ff2010-08-22 17:06:34 -0700508 return mWifiStateMachine.syncGetWifiState();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 }
510
511 /**
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700512 * see {@link android.net.wifi.WifiManager#setWifiApEnabled(WifiConfiguration, boolean)}
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800513 * @param wifiConfig SSID, security and channel details as
514 * part of WifiConfiguration
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700515 * @param enabled true to enable and false to disable
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800516 * @return {@code true} if the start operation was
517 * started or is already in the queue.
518 */
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700519 public synchronized boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800520 enforceChangePermission();
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800521
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700522 if (enabled) {
523 /* Use default config if there is no existing config */
524 if (wifiConfig == null && ((wifiConfig = getWifiApConfiguration()) == null)) {
525 wifiConfig = new WifiConfiguration();
526 wifiConfig.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default);
527 wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE);
528 }
Irfan Sheriff61180692010-08-18 16:07:39 -0700529 /*
530 * Caller might not have WRITE_SECURE_SETTINGS,
531 * only CHANGE_WIFI_STATE is enforced
532 */
533 long ident = Binder.clearCallingIdentity();
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700534 setWifiApConfiguration(wifiConfig);
Irfan Sheriff61180692010-08-18 16:07:39 -0700535 Binder.restoreCallingIdentity(ident);
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800536 }
537
Irfan Sheriff0d255342010-07-28 09:35:20 -0700538 mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled);
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700539
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800540 return true;
541 }
542
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700543 /**
544 * see {@link WifiManager#getWifiApState()}
545 * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
546 * {@link WifiManager#WIFI_AP_STATE_DISABLING},
547 * {@link WifiManager#WIFI_AP_STATE_ENABLED},
548 * {@link WifiManager#WIFI_AP_STATE_ENABLING},
549 * {@link WifiManager#WIFI_AP_STATE_FAILED}
550 */
551 public int getWifiApEnabledState() {
Irfan Sheriff17b232b2010-06-24 11:32:26 -0700552 enforceAccessPermission();
Irfan Sheriffd8134ff2010-08-22 17:06:34 -0700553 return mWifiStateMachine.syncGetWifiApState();
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700554 }
555
556 /**
557 * see {@link WifiManager#getWifiApConfiguration()}
558 * @return soft access point configuration
559 */
560 public synchronized WifiConfiguration getWifiApConfiguration() {
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800561 final ContentResolver cr = mContext.getContentResolver();
562 WifiConfiguration wifiConfig = new WifiConfiguration();
563 int authType;
564 try {
565 wifiConfig.SSID = Settings.Secure.getString(cr, Settings.Secure.WIFI_AP_SSID);
566 if (wifiConfig.SSID == null)
567 return null;
568 authType = Settings.Secure.getInt(cr, Settings.Secure.WIFI_AP_SECURITY);
569 wifiConfig.allowedKeyManagement.set(authType);
570 wifiConfig.preSharedKey = Settings.Secure.getString(cr, Settings.Secure.WIFI_AP_PASSWD);
571 return wifiConfig;
572 } catch (Settings.SettingNotFoundException e) {
573 Slog.e(TAG,"AP settings not found, returning");
574 return null;
575 }
576 }
577
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700578 /**
579 * see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)}
580 * @param wifiConfig WifiConfiguration details for soft access point
581 */
582 public synchronized void setWifiApConfiguration(WifiConfiguration wifiConfig) {
Irfan Sheriff17b232b2010-06-24 11:32:26 -0700583 enforceChangePermission();
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800584 final ContentResolver cr = mContext.getContentResolver();
585 boolean isWpa;
586 if (wifiConfig == null)
587 return;
588 Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_SSID, wifiConfig.SSID);
589 isWpa = wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK);
590 Settings.Secure.putInt(cr,
591 Settings.Secure.WIFI_AP_SECURITY,
592 isWpa ? KeyMgmt.WPA_PSK : KeyMgmt.NONE);
593 if (isWpa)
594 Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_PASSWD, wifiConfig.preSharedKey);
595 }
596
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800597 /**
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700598 * see {@link android.net.wifi.WifiManager#disconnect()}
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800599 */
Irfan Sheriffe4984752010-08-19 11:29:22 -0700600 public void disconnect() {
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700601 enforceChangePermission();
Irfan Sheriffe4984752010-08-19 11:29:22 -0700602 mWifiStateMachine.disconnectCommand();
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800603 }
604
605 /**
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700606 * see {@link android.net.wifi.WifiManager#reconnect()}
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800607 */
Irfan Sheriffe4984752010-08-19 11:29:22 -0700608 public void reconnect() {
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700609 enforceChangePermission();
Irfan Sheriffe4984752010-08-19 11:29:22 -0700610 mWifiStateMachine.reconnectCommand();
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800611 }
612
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700613 /**
614 * see {@link android.net.wifi.WifiManager#reassociate()}
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700615 */
Irfan Sheriffe4984752010-08-19 11:29:22 -0700616 public void reassociate() {
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700617 enforceChangePermission();
Irfan Sheriffe4984752010-08-19 11:29:22 -0700618 mWifiStateMachine.reassociateCommand();
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800619 }
620
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621 /**
622 * see {@link android.net.wifi.WifiManager#getConfiguredNetworks()}
623 * @return the list of configured networks
624 */
625 public List<WifiConfiguration> getConfiguredNetworks() {
626 enforceAccessPermission();
Irfan Sheriffd8134ff2010-08-22 17:06:34 -0700627 return mWifiStateMachine.syncGetConfiguredNetworks();
Chung-yih Wanga8d15942009-10-09 11:01:49 +0800628 }
629
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800630 /**
631 * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)}
632 * @return the supplicant-assigned identifier for the new or updated
633 * network if the operation succeeds, or {@code -1} if it fails
634 */
Irfan Sheriff7aac5542009-12-22 21:42:17 -0800635 public int addOrUpdateNetwork(WifiConfiguration config) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800636 enforceChangePermission();
Irfan Sheriffd8134ff2010-08-22 17:06:34 -0700637 return mWifiStateMachine.syncAddOrUpdateNetwork(config);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800638 }
639
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700640 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641 * See {@link android.net.wifi.WifiManager#removeNetwork(int)}
642 * @param netId the integer that identifies the network configuration
643 * to the supplicant
644 * @return {@code true} if the operation succeeded
645 */
646 public boolean removeNetwork(int netId) {
647 enforceChangePermission();
Wink Saville4b7ba092010-10-20 15:37:41 -0700648 if (mChannel != null) {
649 return mWifiStateMachine.syncRemoveNetwork(mChannel, netId);
650 } else {
651 Slog.e(TAG, "mChannel is not initialized");
652 return false;
653 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800654 }
655
656 /**
657 * See {@link android.net.wifi.WifiManager#enableNetwork(int, boolean)}
658 * @param netId the integer that identifies the network configuration
659 * to the supplicant
660 * @param disableOthers if true, disable all other networks.
661 * @return {@code true} if the operation succeeded
662 */
663 public boolean enableNetwork(int netId, boolean disableOthers) {
664 enforceChangePermission();
Irfan Sheriffd8134ff2010-08-22 17:06:34 -0700665 return mWifiStateMachine.syncEnableNetwork(netId, disableOthers);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 }
667
668 /**
669 * See {@link android.net.wifi.WifiManager#disableNetwork(int)}
670 * @param netId the integer that identifies the network configuration
671 * to the supplicant
672 * @return {@code true} if the operation succeeded
673 */
674 public boolean disableNetwork(int netId) {
675 enforceChangePermission();
Irfan Sheriffd8134ff2010-08-22 17:06:34 -0700676 return mWifiStateMachine.syncDisableNetwork(netId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677 }
678
679 /**
680 * See {@link android.net.wifi.WifiManager#getConnectionInfo()}
681 * @return the Wi-Fi information, contained in {@link WifiInfo}.
682 */
683 public WifiInfo getConnectionInfo() {
684 enforceAccessPermission();
685 /*
686 * Make sure we have the latest information, by sending
687 * a status request to the supplicant.
688 */
Irfan Sheriffd8134ff2010-08-22 17:06:34 -0700689 return mWifiStateMachine.syncRequestConnectionInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800690 }
691
692 /**
693 * Return the results of the most recent access point scan, in the form of
694 * a list of {@link ScanResult} objects.
695 * @return the list of results
696 */
697 public List<ScanResult> getScanResults() {
698 enforceAccessPermission();
Irfan Sheriffd8134ff2010-08-22 17:06:34 -0700699 return mWifiStateMachine.syncGetScanResultsList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800700 }
701
702 /**
703 * Tell the supplicant to persist the current list of configured networks.
704 * @return {@code true} if the operation succeeded
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700705 *
706 * TODO: deprecate this
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707 */
708 public boolean saveConfiguration() {
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700709 boolean result = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800710 enforceChangePermission();
Irfan Sheriffd8134ff2010-08-22 17:06:34 -0700711 return mWifiStateMachine.syncSaveConfig();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 }
713
714 /**
715 * Set the number of radio frequency channels that are allowed to be used
716 * in the current regulatory domain. This method should be used only
717 * if the correct number of channels cannot be determined automatically
Robert Greenwaltb5010cc2009-05-21 15:11:40 -0700718 * for some reason. If the operation is successful, the new value may be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800719 * persisted as a Secure setting.
720 * @param numChannels the number of allowed channels. Must be greater than 0
721 * and less than or equal to 16.
Robert Greenwaltb5010cc2009-05-21 15:11:40 -0700722 * @param persist {@code true} if the setting should be remembered.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800723 * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
724 * {@code numChannels} is outside the valid range.
725 */
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700726 public synchronized boolean setNumAllowedChannels(int numChannels, boolean persist) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800727 Slog.i(TAG, "WifiService trying to setNumAllowed to "+numChannels+
Robert Greenwaltb5010cc2009-05-21 15:11:40 -0700728 " with persist set to "+persist);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800729 enforceChangePermission();
Irfan Sheriff59610c02010-03-30 11:00:41 -0700730
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800731 /*
732 * Validate the argument. We'd like to let the Wi-Fi driver do this,
733 * but if Wi-Fi isn't currently enabled, that's not possible, and
734 * we want to persist the setting anyway,so that it will take
735 * effect when Wi-Fi does become enabled.
736 */
737 boolean found = false;
738 for (int validChan : sValidRegulatoryChannelCounts) {
739 if (validChan == numChannels) {
740 found = true;
741 break;
742 }
743 }
744 if (!found) {
745 return false;
746 }
747
Robert Greenwaltb5010cc2009-05-21 15:11:40 -0700748 if (persist) {
749 Settings.Secure.putInt(mContext.getContentResolver(),
Irfan Sheriff59610c02010-03-30 11:00:41 -0700750 Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
751 numChannels);
Robert Greenwaltb5010cc2009-05-21 15:11:40 -0700752 }
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700753
Irfan Sheriff0d255342010-07-28 09:35:20 -0700754 mWifiStateMachine.setNumAllowedChannels(numChannels);
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700755
756 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800757 }
758
759 /**
760 * Return the number of frequency channels that are allowed
761 * to be used in the current regulatory domain.
762 * @return the number of allowed channels, or {@code -1} if an error occurs
763 */
764 public int getNumAllowedChannels() {
765 int numChannels;
766
767 enforceAccessPermission();
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800768
769 /*
770 * If we can't get the value from the driver (e.g., because
771 * Wi-Fi is not currently enabled), get the value from
772 * Settings.
773 */
Irfan Sheriff0d255342010-07-28 09:35:20 -0700774 numChannels = mWifiStateMachine.getNumAllowedChannels();
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -0800775 if (numChannels < 0) {
776 numChannels = Settings.Secure.getInt(mContext.getContentResolver(),
777 Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
778 -1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800779 }
780 return numChannels;
781 }
782
783 /**
784 * Return the list of valid values for the number of allowed radio channels
785 * for various regulatory domains.
786 * @return the list of channel counts
787 */
788 public int[] getValidChannelCounts() {
789 enforceAccessPermission();
790 return sValidRegulatoryChannelCounts;
791 }
792
793 /**
794 * Return the DHCP-assigned addresses from the last successful DHCP request,
795 * if any.
796 * @return the DHCP information
797 */
798 public DhcpInfo getDhcpInfo() {
799 enforceAccessPermission();
Irfan Sheriffd8134ff2010-08-22 17:06:34 -0700800 return mWifiStateMachine.syncGetDhcpInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800801 }
802
Irfan Sheriff0d255342010-07-28 09:35:20 -0700803 /**
804 * see {@link android.net.wifi.WifiManager#startWifi}
805 *
806 */
807 public void startWifi() {
808 enforceChangePermission();
809 /* TODO: may be add permissions for access only to connectivity service
810 * TODO: if a start issued, keep wifi alive until a stop issued irrespective
811 * of WifiLock & device idle status unless wifi enabled status is toggled
812 */
813
814 mWifiStateMachine.setDriverStart(true);
815 mWifiStateMachine.reconnectCommand();
816 }
817
818 /**
819 * see {@link android.net.wifi.WifiManager#stopWifi}
820 *
821 */
822 public void stopWifi() {
823 enforceChangePermission();
824 /* TODO: may be add permissions for access only to connectivity service
825 * TODO: if a stop is issued, wifi is brought up only by startWifi
826 * unless wifi enabled status is toggled
827 */
828 mWifiStateMachine.setDriverStart(false);
829 }
830
831
832 /**
833 * see {@link android.net.wifi.WifiManager#addToBlacklist}
834 *
835 */
836 public void addToBlacklist(String bssid) {
837 enforceChangePermission();
838
839 mWifiStateMachine.addToBlacklist(bssid);
840 }
841
842 /**
843 * see {@link android.net.wifi.WifiManager#clearBlacklist}
844 *
845 */
846 public void clearBlacklist() {
847 enforceChangePermission();
848
849 mWifiStateMachine.clearBlacklist();
850 }
851
Irfan Sheriffe04653c2010-08-09 09:09:59 -0700852 public void connectNetworkWithId(int networkId) {
853 enforceChangePermission();
854 mWifiStateMachine.connectNetwork(networkId);
855 }
856
857 public void connectNetworkWithConfig(WifiConfiguration config) {
858 enforceChangePermission();
859 mWifiStateMachine.connectNetwork(config);
860 }
861
862 public void saveNetwork(WifiConfiguration config) {
863 enforceChangePermission();
864 mWifiStateMachine.saveNetwork(config);
865 }
866
867 public void forgetNetwork(int netId) {
868 enforceChangePermission();
869 mWifiStateMachine.forgetNetwork(netId);
870 }
Irfan Sheriff0d255342010-07-28 09:35:20 -0700871
Irfan Sheriff5ee89802010-09-16 17:53:34 -0700872 public void startWpsPbc(String bssid) {
873 enforceChangePermission();
874 mWifiStateMachine.startWpsPbc(bssid);
875 }
876
Irfan Sherifff235c5a2010-10-21 16:44:48 -0700877 public void startWpsWithPinFromAccessPoint(String bssid, int apPin) {
Irfan Sheriff5ee89802010-09-16 17:53:34 -0700878 enforceChangePermission();
Irfan Sherifff235c5a2010-10-21 16:44:48 -0700879 mWifiStateMachine.startWpsWithPinFromAccessPoint(bssid, apPin);
880 }
881
882 public int startWpsWithPinFromDevice(String bssid) {
883 enforceChangePermission();
884 if (mChannel != null) {
885 return mWifiStateMachine.syncStartWpsWithPinFromDevice(mChannel, bssid);
886 } else {
887 Slog.e(TAG, "mChannel is not initialized");
888 return -1;
889 }
Irfan Sheriff5ee89802010-09-16 17:53:34 -0700890 }
891
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
893 @Override
894 public void onReceive(Context context, Intent intent) {
895 String action = intent.getAction();
896
Doug Zongker43866e02010-01-07 12:09:54 -0800897 long idleMillis =
898 Settings.Secure.getLong(mContext.getContentResolver(),
Irfan Sheriff4f5f7c92010-10-14 17:01:27 -0700899 Settings.Secure.WIFI_IDLE_MS, DEFAULT_IDLE_MS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900 int stayAwakeConditions =
Doug Zongker43866e02010-01-07 12:09:54 -0800901 Settings.System.getInt(mContext.getContentResolver(),
902 Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 if (action.equals(Intent.ACTION_SCREEN_ON)) {
Joe Onorato431bb222010-10-18 19:13:23 -0400904 if (DBG) {
905 Slog.d(TAG, "ACTION_SCREEN_ON");
906 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800907 mAlarmManager.cancel(mIdleIntent);
908 mDeviceIdle = false;
909 mScreenOff = false;
Dianne Hackborn58e0eef2010-09-16 01:22:10 -0700910 // Once the screen is on, we are not keeping WIFI running
911 // because of any locks so clear that tracking immediately.
912 reportStartWorkSource();
Irfan Sheriff0d255342010-07-28 09:35:20 -0700913 mWifiStateMachine.enableRssiPolling(true);
Irfan Sheriff4f5f7c92010-10-14 17:01:27 -0700914 updateWifiState();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800915 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
Joe Onorato431bb222010-10-18 19:13:23 -0400916 if (DBG) {
917 Slog.d(TAG, "ACTION_SCREEN_OFF");
918 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 mScreenOff = true;
Irfan Sheriff0d255342010-07-28 09:35:20 -0700920 mWifiStateMachine.enableRssiPolling(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921 /*
922 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
923 * AND the "stay on while plugged in" setting doesn't match the
924 * current power conditions (i.e, not plugged in, plugged in to USB,
925 * or plugged in to AC).
926 */
927 if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
Irfan Sheriffd8134ff2010-08-22 17:06:34 -0700928 WifiInfo info = mWifiStateMachine.syncRequestConnectionInfo();
San Mehatfa6c7112009-07-07 09:34:44 -0700929 if (info.getSupplicantState() != SupplicantState.COMPLETED) {
Robert Greenwalt84612ea62009-09-30 09:04:22 -0700930 // we used to go to sleep immediately, but this caused some race conditions
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700931 // we don't have time to track down for this release. Delay instead,
932 // but not as long as we would if connected (below)
Robert Greenwalt84612ea62009-09-30 09:04:22 -0700933 // TODO - fix the race conditions and switch back to the immediate turn-off
934 long triggerTime = System.currentTimeMillis() + (2*60*1000); // 2 min
Joe Onorato431bb222010-10-18 19:13:23 -0400935 if (DBG) {
936 Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for 120,000 ms");
937 }
Robert Greenwalt84612ea62009-09-30 09:04:22 -0700938 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
939 // // do not keep Wifi awake when screen is off if Wifi is not associated
940 // mDeviceIdle = true;
941 // updateWifiState();
Mike Lockwoodd9c32bc2009-05-18 14:14:15 -0400942 } else {
943 long triggerTime = System.currentTimeMillis() + idleMillis;
Joe Onorato431bb222010-10-18 19:13:23 -0400944 if (DBG) {
945 Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis
946 + "ms");
947 }
Mike Lockwoodd9c32bc2009-05-18 14:14:15 -0400948 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
949 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800950 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800951 } else if (action.equals(ACTION_DEVICE_IDLE)) {
Joe Onorato431bb222010-10-18 19:13:23 -0400952 if (DBG) {
953 Slog.d(TAG, "got ACTION_DEVICE_IDLE");
954 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955 mDeviceIdle = true;
Dianne Hackborn58e0eef2010-09-16 01:22:10 -0700956 reportStartWorkSource();
Irfan Sheriff4f5f7c92010-10-14 17:01:27 -0700957 updateWifiState();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800958 } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
959 /*
960 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
961 * AND we are transitioning from a state in which the device was supposed
962 * to stay awake to a state in which it is not supposed to stay awake.
963 * If "stay awake" state is not changing, we do nothing, to avoid resetting
964 * the already-set timer.
965 */
966 int pluggedType = intent.getIntExtra("plugged", 0);
Joe Onorato431bb222010-10-18 19:13:23 -0400967 if (DBG) {
968 Slog.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType);
969 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800970 if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) &&
971 !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) {
972 long triggerTime = System.currentTimeMillis() + idleMillis;
Joe Onorato431bb222010-10-18 19:13:23 -0400973 if (DBG) {
974 Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
975 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800976 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800977 }
978 mPluggedType = pluggedType;
Jaikumar Ganesh7440fc22010-09-27 17:04:14 -0700979 } else if (action.equals(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED)) {
980 int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
981 BluetoothA2dp.STATE_NOT_PLAYING);
982 mWifiStateMachine.setBluetoothScanMode(state == BluetoothA2dp.STATE_PLAYING);
Irfan Sheriff4f5f7c92010-10-14 17:01:27 -0700983 } else if (action.equals(ACTION_START_SCAN)) {
984 mWifiStateMachine.startScan(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800985 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800986 }
987
988 /**
989 * Determines whether the Wi-Fi chipset should stay awake or be put to
990 * sleep. Looks at the setting for the sleep policy and the current
991 * conditions.
Jaikumar Ganesh084c6652009-12-07 10:58:18 -0800992 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800993 * @see #shouldDeviceStayAwake(int, int)
994 */
995 private boolean shouldWifiStayAwake(int stayAwakeConditions, int pluggedType) {
996 int wifiSleepPolicy = Settings.System.getInt(mContext.getContentResolver(),
997 Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_DEFAULT);
998
999 if (wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER) {
1000 // Never sleep
1001 return true;
1002 } else if ((wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) &&
1003 (pluggedType != 0)) {
1004 // Never sleep while plugged, and we're plugged
1005 return true;
1006 } else {
1007 // Default
1008 return shouldDeviceStayAwake(stayAwakeConditions, pluggedType);
1009 }
1010 }
Jaikumar Ganesh084c6652009-12-07 10:58:18 -08001011
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001012 /**
1013 * Determine whether the bit value corresponding to {@code pluggedType} is set in
1014 * the bit string {@code stayAwakeConditions}. Because a {@code pluggedType} value
1015 * of {@code 0} isn't really a plugged type, but rather an indication that the
1016 * device isn't plugged in at all, there is no bit value corresponding to a
1017 * {@code pluggedType} value of {@code 0}. That is why we shift by
Ben Dodson4e8620f2010-08-25 10:55:47 -07001018 * {@code pluggedType - 1} instead of by {@code pluggedType}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 * @param stayAwakeConditions a bit string specifying which "plugged types" should
1020 * keep the device (and hence Wi-Fi) awake.
1021 * @param pluggedType the type of plug (USB, AC, or none) for which the check is
1022 * being made
1023 * @return {@code true} if {@code pluggedType} indicates that the device is
1024 * supposed to stay awake, {@code false} otherwise.
1025 */
1026 private boolean shouldDeviceStayAwake(int stayAwakeConditions, int pluggedType) {
1027 return (stayAwakeConditions & pluggedType) != 0;
1028 }
1029 };
1030
Dianne Hackborn03f3cb02010-09-17 23:12:26 -07001031 private synchronized void reportStartWorkSource() {
1032 mTmpWorkSource.clear();
1033 if (mDeviceIdle) {
1034 for (int i=0; i<mLocks.mList.size(); i++) {
1035 mTmpWorkSource.add(mLocks.mList.get(i).mWorkSource);
Dianne Hackborn58e0eef2010-09-16 01:22:10 -07001036 }
Dianne Hackborn58e0eef2010-09-16 01:22:10 -07001037 }
Dianne Hackborn03f3cb02010-09-17 23:12:26 -07001038 mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource);
Dianne Hackborn58e0eef2010-09-16 01:22:10 -07001039 }
Jaikumar Ganesh7440fc22010-09-27 17:04:14 -07001040
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001041 private void updateWifiState() {
1042 boolean wifiEnabled = getPersistedWifiEnabled();
Irfan Sheriffa2a1b912010-06-07 09:03:04 -07001043 boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden.get();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 boolean lockHeld = mLocks.hasLocks();
Irfan Sheriff5876a422010-08-12 20:26:23 -07001045 int strongestLockMode = WifiManager.WIFI_MODE_FULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001046 boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
1047 boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
Irfan Sheriff5876a422010-08-12 20:26:23 -07001048
1049 if (lockHeld) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 strongestLockMode = mLocks.getStrongestLockMode();
Irfan Sheriff5876a422010-08-12 20:26:23 -07001051 }
1052 /* If device is not idle, lockmode cannot be scan only */
1053 if (!mDeviceIdle && strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054 strongestLockMode = WifiManager.WIFI_MODE_FULL;
1055 }
1056
Irfan Sheriff4f5f7c92010-10-14 17:01:27 -07001057 /* Scan interval when driver is started */
1058 long scanMs = Settings.Secure.getLong(mContext.getContentResolver(),
1059 Settings.Secure.WIFI_SCAN_INTERVAL_MS, DEFAULT_SCAN_INTERVAL_MS);
1060
Irfan Sheriffa2a1b912010-06-07 09:03:04 -07001061 /* Disable tethering when airplane mode is enabled */
1062 if (airplaneMode) {
Irfan Sheriff0d255342010-07-28 09:35:20 -07001063 mWifiStateMachine.setWifiApEnabled(null, false);
Irfan Sheriffa2a1b912010-06-07 09:03:04 -07001064 }
Irfan Sheriffb2e6c012010-04-05 11:57:56 -07001065
Irfan Sheriffa2a1b912010-06-07 09:03:04 -07001066 if (wifiShouldBeEnabled) {
1067 if (wifiShouldBeStarted) {
Dianne Hackborn03f3cb02010-09-17 23:12:26 -07001068 reportStartWorkSource();
Irfan Sheriff0d255342010-07-28 09:35:20 -07001069 mWifiStateMachine.setWifiEnabled(true);
1070 mWifiStateMachine.setScanOnlyMode(
Irfan Sheriffa2a1b912010-06-07 09:03:04 -07001071 strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
Irfan Sheriff0d255342010-07-28 09:35:20 -07001072 mWifiStateMachine.setDriverStart(true);
Irfan Sheriff4f5f7c92010-10-14 17:01:27 -07001073 mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
1074 System.currentTimeMillis() + scanMs, scanMs, mScanIntent);
Irfan Sheriff5876a422010-08-12 20:26:23 -07001075 mWifiStateMachine.setHighPerfModeEnabled(strongestLockMode
1076 == WifiManager.WIFI_MODE_FULL_HIGH_PERF);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 } else {
Irfan Sheriff0d255342010-07-28 09:35:20 -07001078 mWifiStateMachine.requestCmWakeLock();
1079 mWifiStateMachine.setDriverStart(false);
Irfan Sheriff4f5f7c92010-10-14 17:01:27 -07001080 mAlarmManager.cancel(mScanIntent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001081 }
Irfan Sheriffa2a1b912010-06-07 09:03:04 -07001082 } else {
Irfan Sheriff0d255342010-07-28 09:35:20 -07001083 mWifiStateMachine.setWifiEnabled(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 }
1085 }
1086
1087 private void registerForBroadcasts() {
1088 IntentFilter intentFilter = new IntentFilter();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001089 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
1090 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
1091 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
1092 intentFilter.addAction(ACTION_DEVICE_IDLE);
Irfan Sheriff4f5f7c92010-10-14 17:01:27 -07001093 intentFilter.addAction(ACTION_START_SCAN);
Jaikumar Ganesh7440fc22010-09-27 17:04:14 -07001094 intentFilter.addAction(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001095 mContext.registerReceiver(mReceiver, intentFilter);
1096 }
Jaikumar Ganesh084c6652009-12-07 10:58:18 -08001097
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001098 private boolean isAirplaneSensitive() {
1099 String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
1100 Settings.System.AIRPLANE_MODE_RADIOS);
1101 return airplaneModeRadios == null
1102 || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
1103 }
1104
Mike Lockwoodbd5ddf02009-07-29 21:37:14 -07001105 private boolean isAirplaneToggleable() {
1106 String toggleableRadios = Settings.System.getString(mContext.getContentResolver(),
1107 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
1108 return toggleableRadios != null
1109 && toggleableRadios.contains(Settings.System.RADIO_WIFI);
1110 }
1111
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001112 /**
1113 * Returns true if Wi-Fi is sensitive to airplane mode, and airplane mode is
1114 * currently on.
1115 * @return {@code true} if airplane mode is on.
1116 */
1117 private boolean isAirplaneModeOn() {
1118 return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
1119 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1120 }
1121
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122 @Override
1123 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1124 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1125 != PackageManager.PERMISSION_GRANTED) {
1126 pw.println("Permission Denial: can't dump WifiService from from pid="
1127 + Binder.getCallingPid()
1128 + ", uid=" + Binder.getCallingUid());
1129 return;
1130 }
Irfan Sheriffd8134ff2010-08-22 17:06:34 -07001131 pw.println("Wi-Fi is " + mWifiStateMachine.syncGetWifiStateByName());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001132 pw.println("Stay-awake conditions: " +
1133 Settings.System.getInt(mContext.getContentResolver(),
1134 Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
1135 pw.println();
1136
1137 pw.println("Internal state:");
Irfan Sheriff0d255342010-07-28 09:35:20 -07001138 pw.println(mWifiStateMachine);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 pw.println();
1140 pw.println("Latest scan results:");
Irfan Sheriffd8134ff2010-08-22 17:06:34 -07001141 List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001142 if (scanResults != null && scanResults.size() != 0) {
1143 pw.println(" BSSID Frequency RSSI Flags SSID");
1144 for (ScanResult r : scanResults) {
1145 pw.printf(" %17s %9d %5d %-16s %s%n",
1146 r.BSSID,
1147 r.frequency,
1148 r.level,
1149 r.capabilities,
1150 r.SSID == null ? "" : r.SSID);
1151 }
1152 }
1153 pw.println();
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001154 pw.println("Locks acquired: " + mFullLocksAcquired + " full, " +
Irfan Sheriff5876a422010-08-12 20:26:23 -07001155 mFullHighPerfLocksAcquired + " full high perf, " +
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001156 mScanLocksAcquired + " scan");
1157 pw.println("Locks released: " + mFullLocksReleased + " full, " +
Irfan Sheriff5876a422010-08-12 20:26:23 -07001158 mFullHighPerfLocksReleased + " full high perf, " +
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001159 mScanLocksReleased + " scan");
1160 pw.println();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001161 pw.println("Locks held:");
1162 mLocks.dump(pw);
1163 }
1164
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001165 private class WifiLock extends DeathRecipient {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001166 WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) {
1167 super(lockMode, tag, binder, ws);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001168 }
1169
1170 public void binderDied() {
1171 synchronized (mLocks) {
1172 releaseWifiLockLocked(mBinder);
1173 }
1174 }
1175
1176 public String toString() {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001177 return "WifiLock{" + mTag + " type=" + mMode + " binder=" + mBinder + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001178 }
1179 }
1180
1181 private class LockList {
1182 private List<WifiLock> mList;
1183
1184 private LockList() {
1185 mList = new ArrayList<WifiLock>();
1186 }
1187
1188 private synchronized boolean hasLocks() {
1189 return !mList.isEmpty();
1190 }
1191
1192 private synchronized int getStrongestLockMode() {
1193 if (mList.isEmpty()) {
1194 return WifiManager.WIFI_MODE_FULL;
1195 }
Irfan Sheriff5876a422010-08-12 20:26:23 -07001196
1197 if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) {
1198 return WifiManager.WIFI_MODE_FULL_HIGH_PERF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001199 }
Irfan Sheriff5876a422010-08-12 20:26:23 -07001200
1201 if (mFullLocksAcquired > mFullLocksReleased) {
1202 return WifiManager.WIFI_MODE_FULL;
1203 }
1204
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001205 return WifiManager.WIFI_MODE_SCAN_ONLY;
1206 }
1207
1208 private void addLock(WifiLock lock) {
1209 if (findLockByBinder(lock.mBinder) < 0) {
1210 mList.add(lock);
1211 }
1212 }
1213
1214 private WifiLock removeLock(IBinder binder) {
1215 int index = findLockByBinder(binder);
1216 if (index >= 0) {
Suchi Amalapurapufff2fda2009-06-30 21:36:16 -07001217 WifiLock ret = mList.remove(index);
1218 ret.unlinkDeathRecipient();
1219 return ret;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001220 } else {
1221 return null;
1222 }
1223 }
1224
1225 private int findLockByBinder(IBinder binder) {
1226 int size = mList.size();
1227 for (int i = size - 1; i >= 0; i--)
1228 if (mList.get(i).mBinder == binder)
1229 return i;
1230 return -1;
1231 }
1232
1233 private void dump(PrintWriter pw) {
1234 for (WifiLock l : mList) {
1235 pw.print(" ");
1236 pw.println(l);
1237 }
1238 }
1239 }
1240
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001241 void enforceWakeSourcePermission(int uid, int pid) {
Dianne Hackborne746f032010-09-13 16:02:57 -07001242 if (uid == android.os.Process.myUid()) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001243 return;
1244 }
1245 mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
1246 pid, uid, null);
1247 }
1248
1249 public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001250 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
Irfan Sheriff5876a422010-08-12 20:26:23 -07001251 if (lockMode != WifiManager.WIFI_MODE_FULL &&
1252 lockMode != WifiManager.WIFI_MODE_SCAN_ONLY &&
1253 lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF) {
1254 Slog.e(TAG, "Illegal argument, lockMode= " + lockMode);
1255 if (DBG) throw new IllegalArgumentException("lockMode=" + lockMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001256 return false;
1257 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001258 if (ws != null && ws.size() == 0) {
1259 ws = null;
1260 }
Dianne Hackbornecfd7f72010-10-08 14:23:40 -07001261 if (ws != null) {
1262 enforceWakeSourcePermission(Binder.getCallingUid(), Binder.getCallingPid());
1263 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001264 if (ws == null) {
1265 ws = new WorkSource(Binder.getCallingUid());
1266 }
1267 WifiLock wifiLock = new WifiLock(lockMode, tag, binder, ws);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001268 synchronized (mLocks) {
1269 return acquireWifiLockLocked(wifiLock);
1270 }
1271 }
1272
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001273 private void noteAcquireWifiLock(WifiLock wifiLock) throws RemoteException {
1274 switch(wifiLock.mMode) {
1275 case WifiManager.WIFI_MODE_FULL:
Irfan Sheriff5876a422010-08-12 20:26:23 -07001276 case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001277 mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource);
1278 break;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001279 case WifiManager.WIFI_MODE_SCAN_ONLY:
1280 mBatteryStats.noteScanWifiLockAcquiredFromSource(wifiLock.mWorkSource);
1281 break;
1282 }
1283 }
1284
1285 private void noteReleaseWifiLock(WifiLock wifiLock) throws RemoteException {
1286 switch(wifiLock.mMode) {
1287 case WifiManager.WIFI_MODE_FULL:
Irfan Sheriff5876a422010-08-12 20:26:23 -07001288 case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001289 mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
1290 break;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001291 case WifiManager.WIFI_MODE_SCAN_ONLY:
1292 mBatteryStats.noteScanWifiLockReleasedFromSource(wifiLock.mWorkSource);
1293 break;
1294 }
1295 }
1296
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001297 private boolean acquireWifiLockLocked(WifiLock wifiLock) {
Irfan Sheriffc89dd542010-09-28 08:40:54 -07001298 if (DBG) Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07001299
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001300 mLocks.addLock(wifiLock);
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07001301
The Android Open Source Project10592532009-03-18 17:39:46 -07001302 long ident = Binder.clearCallingIdentity();
1303 try {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001304 noteAcquireWifiLock(wifiLock);
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001305 switch(wifiLock.mMode) {
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001306 case WifiManager.WIFI_MODE_FULL:
1307 ++mFullLocksAcquired;
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001308 break;
Irfan Sheriff5876a422010-08-12 20:26:23 -07001309 case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
1310 ++mFullHighPerfLocksAcquired;
1311 break;
1312
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001313 case WifiManager.WIFI_MODE_SCAN_ONLY:
1314 ++mScanLocksAcquired;
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001315 break;
The Android Open Source Project10592532009-03-18 17:39:46 -07001316 }
Dianne Hackbornecfd7f72010-10-08 14:23:40 -07001317
1318 // Be aggressive about adding new locks into the accounted state...
1319 // we want to over-report rather than under-report.
1320 reportStartWorkSource();
1321
1322 updateWifiState();
1323 return true;
The Android Open Source Project10592532009-03-18 17:39:46 -07001324 } catch (RemoteException e) {
Dianne Hackbornecfd7f72010-10-08 14:23:40 -07001325 return false;
The Android Open Source Project10592532009-03-18 17:39:46 -07001326 } finally {
1327 Binder.restoreCallingIdentity(ident);
1328 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001329 }
1330
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001331 public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) {
1332 int uid = Binder.getCallingUid();
1333 int pid = Binder.getCallingPid();
1334 if (ws != null && ws.size() == 0) {
1335 ws = null;
1336 }
1337 if (ws != null) {
1338 enforceWakeSourcePermission(uid, pid);
1339 }
1340 long ident = Binder.clearCallingIdentity();
1341 try {
1342 synchronized (mLocks) {
1343 int index = mLocks.findLockByBinder(lock);
1344 if (index < 0) {
1345 throw new IllegalArgumentException("Wifi lock not active");
1346 }
1347 WifiLock wl = mLocks.mList.get(index);
1348 noteReleaseWifiLock(wl);
1349 wl.mWorkSource = ws != null ? new WorkSource(ws) : new WorkSource(uid);
1350 noteAcquireWifiLock(wl);
1351 }
1352 } catch (RemoteException e) {
1353 } finally {
1354 Binder.restoreCallingIdentity(ident);
1355 }
1356 }
1357
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001358 public boolean releaseWifiLock(IBinder lock) {
1359 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
1360 synchronized (mLocks) {
1361 return releaseWifiLockLocked(lock);
1362 }
1363 }
1364
1365 private boolean releaseWifiLockLocked(IBinder lock) {
Eric Shienbroodd4c5f892009-03-24 18:13:20 -07001366 boolean hadLock;
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07001367
The Android Open Source Project10592532009-03-18 17:39:46 -07001368 WifiLock wifiLock = mLocks.removeLock(lock);
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07001369
Irfan Sheriffc89dd542010-09-28 08:40:54 -07001370 if (DBG) Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock);
Robert Greenwaltf1acb2d2009-10-13 08:20:55 -07001371
Eric Shienbroodd4c5f892009-03-24 18:13:20 -07001372 hadLock = (wifiLock != null);
1373
Dianne Hackbornecfd7f72010-10-08 14:23:40 -07001374 long ident = Binder.clearCallingIdentity();
1375 try {
1376 if (hadLock) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001377 noteAcquireWifiLock(wifiLock);
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001378 switch(wifiLock.mMode) {
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001379 case WifiManager.WIFI_MODE_FULL:
1380 ++mFullLocksReleased;
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001381 break;
Irfan Sheriff5876a422010-08-12 20:26:23 -07001382 case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
1383 ++mFullHighPerfLocksReleased;
1384 break;
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001385 case WifiManager.WIFI_MODE_SCAN_ONLY:
1386 ++mScanLocksReleased;
Eric Shienbrood5711fad2009-03-27 20:25:31 -07001387 break;
Eric Shienbroodd4c5f892009-03-24 18:13:20 -07001388 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001389 }
Dianne Hackbornecfd7f72010-10-08 14:23:40 -07001390
1391 // TODO - should this only happen if you hadLock?
1392 updateWifiState();
1393
1394 } catch (RemoteException e) {
1395 } finally {
1396 Binder.restoreCallingIdentity(ident);
The Android Open Source Project10592532009-03-18 17:39:46 -07001397 }
Dianne Hackbornecfd7f72010-10-08 14:23:40 -07001398
Eric Shienbroodd4c5f892009-03-24 18:13:20 -07001399 return hadLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001400 }
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001401
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001402 private abstract class DeathRecipient
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001403 implements IBinder.DeathRecipient {
1404 String mTag;
1405 int mMode;
1406 IBinder mBinder;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001407 WorkSource mWorkSource;
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001408
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001409 DeathRecipient(int mode, String tag, IBinder binder, WorkSource ws) {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001410 super();
1411 mTag = tag;
1412 mMode = mode;
1413 mBinder = binder;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001414 mWorkSource = ws;
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001415 try {
1416 mBinder.linkToDeath(this, 0);
1417 } catch (RemoteException e) {
1418 binderDied();
1419 }
1420 }
Suchi Amalapurapufff2fda2009-06-30 21:36:16 -07001421
1422 void unlinkDeathRecipient() {
1423 mBinder.unlinkToDeath(this, 0);
1424 }
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001425 }
1426
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001427 private class Multicaster extends DeathRecipient {
1428 Multicaster(String tag, IBinder binder) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001429 super(Binder.getCallingUid(), tag, binder, null);
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001430 }
1431
1432 public void binderDied() {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001433 Slog.e(TAG, "Multicaster binderDied");
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001434 synchronized (mMulticasters) {
1435 int i = mMulticasters.indexOf(this);
1436 if (i != -1) {
1437 removeMulticasterLocked(i, mMode);
1438 }
1439 }
1440 }
1441
1442 public String toString() {
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001443 return "Multicaster{" + mTag + " binder=" + mBinder + "}";
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001444 }
1445
1446 public int getUid() {
1447 return mMode;
1448 }
1449 }
1450
Robert Greenwalte2d155a2009-10-21 14:58:34 -07001451 public void initializeMulticastFiltering() {
1452 enforceMulticastChangePermission();
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001453
Robert Greenwalte2d155a2009-10-21 14:58:34 -07001454 synchronized (mMulticasters) {
1455 // if anybody had requested filters be off, leave off
1456 if (mMulticasters.size() != 0) {
1457 return;
1458 } else {
Irfan Sheriff0d255342010-07-28 09:35:20 -07001459 mWifiStateMachine.startPacketFiltering();
Robert Greenwalte2d155a2009-10-21 14:58:34 -07001460 }
1461 }
1462 }
1463
Robert Greenwaltfc1b15c2009-05-22 15:09:51 -07001464 public void acquireMulticastLock(IBinder binder, String tag) {
1465 enforceMulticastChangePermission();
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001466
1467 synchronized (mMulticasters) {
1468 mMulticastEnabled++;
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001469 mMulticasters.add(new Multicaster(tag, binder));
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001470 // Note that we could call stopPacketFiltering only when
1471 // our new size == 1 (first call), but this function won't
1472 // be called often and by making the stopPacket call each
1473 // time we're less fragile and self-healing.
Irfan Sheriff0d255342010-07-28 09:35:20 -07001474 mWifiStateMachine.stopPacketFiltering();
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001475 }
1476
1477 int uid = Binder.getCallingUid();
1478 Long ident = Binder.clearCallingIdentity();
1479 try {
1480 mBatteryStats.noteWifiMulticastEnabled(uid);
1481 } catch (RemoteException e) {
1482 } finally {
1483 Binder.restoreCallingIdentity(ident);
1484 }
1485 }
1486
Robert Greenwaltfc1b15c2009-05-22 15:09:51 -07001487 public void releaseMulticastLock() {
1488 enforceMulticastChangePermission();
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001489
1490 int uid = Binder.getCallingUid();
1491 synchronized (mMulticasters) {
1492 mMulticastDisabled++;
1493 int size = mMulticasters.size();
1494 for (int i = size - 1; i >= 0; i--) {
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001495 Multicaster m = mMulticasters.get(i);
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001496 if ((m != null) && (m.getUid() == uid)) {
1497 removeMulticasterLocked(i, uid);
1498 }
1499 }
1500 }
1501 }
1502
1503 private void removeMulticasterLocked(int i, int uid)
1504 {
Suchi Amalapurapufff2fda2009-06-30 21:36:16 -07001505 Multicaster removed = mMulticasters.remove(i);
Irfan Sheriffa8fbe1f2010-03-09 09:13:58 -08001506
Suchi Amalapurapufff2fda2009-06-30 21:36:16 -07001507 if (removed != null) {
1508 removed.unlinkDeathRecipient();
1509 }
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001510 if (mMulticasters.size() == 0) {
Irfan Sheriff0d255342010-07-28 09:35:20 -07001511 mWifiStateMachine.startPacketFiltering();
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001512 }
1513
1514 Long ident = Binder.clearCallingIdentity();
1515 try {
1516 mBatteryStats.noteWifiMulticastDisabled(uid);
1517 } catch (RemoteException e) {
1518 } finally {
1519 Binder.restoreCallingIdentity(ident);
1520 }
1521 }
1522
Robert Greenwalt58ff0212009-05-19 15:53:54 -07001523 public boolean isMulticastEnabled() {
Robert Greenwalt5347bd42009-05-13 15:10:16 -07001524 enforceAccessPermission();
1525
1526 synchronized (mMulticasters) {
1527 return (mMulticasters.size() > 0);
1528 }
1529 }
Irfan Sheriff0d255342010-07-28 09:35:20 -07001530
1531 private void checkAndSetNotification() {
1532 // If we shouldn't place a notification on available networks, then
1533 // don't bother doing any of the following
1534 if (!mNotificationEnabled) return;
1535
1536 State state = mNetworkInfo.getState();
1537 if ((state == NetworkInfo.State.DISCONNECTED)
1538 || (state == NetworkInfo.State.UNKNOWN)) {
1539 // Look for an open network
Irfan Sheriffd8134ff2010-08-22 17:06:34 -07001540 List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
Irfan Sheriff0d255342010-07-28 09:35:20 -07001541 if (scanResults != null) {
1542 int numOpenNetworks = 0;
1543 for (int i = scanResults.size() - 1; i >= 0; i--) {
1544 ScanResult scanResult = scanResults.get(i);
1545
1546 if (TextUtils.isEmpty(scanResult.capabilities)) {
1547 numOpenNetworks++;
1548 }
1549 }
1550
1551 if (numOpenNetworks > 0) {
1552 if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
1553 /*
1554 * We've scanned continuously at least
1555 * NUM_SCANS_BEFORE_NOTIFICATION times. The user
1556 * probably does not have a remembered network in range,
1557 * since otherwise supplicant would have tried to
1558 * associate and thus resetting this counter.
1559 */
1560 setNotificationVisible(true, numOpenNetworks, false, 0);
1561 }
1562 return;
1563 }
1564 }
1565 }
1566
1567 // No open networks in range, remove the notification
1568 setNotificationVisible(false, 0, false, 0);
1569 }
1570
1571 /**
1572 * Clears variables related to tracking whether a notification has been
1573 * shown recently and clears the current notification.
1574 */
1575 private void resetNotification() {
1576 mNotificationRepeatTime = 0;
1577 mNumScansSinceNetworkStateChange = 0;
1578 setNotificationVisible(false, 0, false, 0);
1579 }
1580
1581 /**
1582 * Display or don't display a notification that there are open Wi-Fi networks.
1583 * @param visible {@code true} if notification should be visible, {@code false} otherwise
1584 * @param numNetworks the number networks seen
1585 * @param force {@code true} to force notification to be shown/not-shown,
1586 * even if it is already shown/not-shown.
1587 * @param delay time in milliseconds after which the notification should be made
1588 * visible or invisible.
1589 */
1590 private void setNotificationVisible(boolean visible, int numNetworks, boolean force,
1591 int delay) {
1592
1593 // Since we use auto cancel on the notification, when the
1594 // mNetworksAvailableNotificationShown is true, the notification may
1595 // have actually been canceled. However, when it is false we know
1596 // for sure that it is not being shown (it will not be shown any other
1597 // place than here)
1598
1599 // If it should be hidden and it is already hidden, then noop
1600 if (!visible && !mNotificationShown && !force) {
1601 return;
1602 }
1603
1604 NotificationManager notificationManager = (NotificationManager) mContext
1605 .getSystemService(Context.NOTIFICATION_SERVICE);
1606
1607 Message message;
1608 if (visible) {
1609
1610 // Not enough time has passed to show the notification again
1611 if (System.currentTimeMillis() < mNotificationRepeatTime) {
1612 return;
1613 }
1614
1615 if (mNotification == null) {
Wink Savillec7a98342010-08-13 16:11:42 -07001616 // Cache the Notification object.
Irfan Sheriff0d255342010-07-28 09:35:20 -07001617 mNotification = new Notification();
1618 mNotification.when = 0;
1619 mNotification.icon = ICON_NETWORKS_AVAILABLE;
1620 mNotification.flags = Notification.FLAG_AUTO_CANCEL;
1621 mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
1622 new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
1623 }
1624
1625 CharSequence title = mContext.getResources().getQuantityText(
1626 com.android.internal.R.plurals.wifi_available, numNetworks);
1627 CharSequence details = mContext.getResources().getQuantityText(
1628 com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
1629 mNotification.tickerText = title;
1630 mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
1631
1632 mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
1633
1634 notificationManager.notify(ICON_NETWORKS_AVAILABLE, mNotification);
Irfan Sheriff0d255342010-07-28 09:35:20 -07001635 } else {
Irfan Sheriff0d255342010-07-28 09:35:20 -07001636 notificationManager.cancel(ICON_NETWORKS_AVAILABLE);
Irfan Sheriff0d255342010-07-28 09:35:20 -07001637 }
1638
Irfan Sheriff0d255342010-07-28 09:35:20 -07001639 mNotificationShown = visible;
1640 }
1641
1642 private class NotificationEnabledSettingObserver extends ContentObserver {
1643
1644 public NotificationEnabledSettingObserver(Handler handler) {
1645 super(handler);
1646 }
1647
1648 public void register() {
1649 ContentResolver cr = mContext.getContentResolver();
1650 cr.registerContentObserver(Settings.Secure.getUriFor(
1651 Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
1652 mNotificationEnabled = getValue();
1653 }
1654
1655 @Override
1656 public void onChange(boolean selfChange) {
1657 super.onChange(selfChange);
1658
1659 mNotificationEnabled = getValue();
1660 resetNotification();
1661 }
1662
1663 private boolean getValue() {
1664 return Settings.Secure.getInt(mContext.getContentResolver(),
1665 Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
1666 }
1667 }
1668
1669
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001670}