WifiService.java revision 35bbe27d58ccf01ffd170b0f617a568bc843a49e
1/*
2 * Copyright (C) 2010 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 android.app.AlarmManager;
20import android.app.Notification;
21import android.app.NotificationManager;
22import android.app.PendingIntent;
23import android.bluetooth.BluetoothAdapter;
24import android.content.BroadcastReceiver;
25import android.content.ContentResolver;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.pm.PackageManager;
30import android.database.ContentObserver;
31import android.net.wifi.IWifiManager;
32import android.net.wifi.ScanResult;
33import android.net.wifi.SupplicantState;
34import android.net.wifi.WifiInfo;
35import android.net.wifi.WifiManager;
36import android.net.wifi.WifiStateMachine;
37import android.net.wifi.WifiConfiguration;
38import android.net.wifi.WifiWatchdogStateMachine;
39import android.net.wifi.WifiConfiguration.KeyMgmt;
40import android.net.wifi.WpsInfo;
41import android.net.wifi.WpsResult;
42import android.net.ConnectivityManager;
43import android.net.DhcpInfo;
44import android.net.NetworkInfo;
45import android.net.NetworkInfo.State;
46import android.net.NetworkInfo.DetailedState;
47import android.net.TrafficStats;
48import android.os.Binder;
49import android.os.Handler;
50import android.os.Messenger;
51import android.os.HandlerThread;
52import android.os.IBinder;
53import android.os.INetworkManagementService;
54import android.os.Message;
55import android.os.RemoteException;
56import android.os.ServiceManager;
57import android.os.SystemProperties;
58import android.os.WorkSource;
59import android.provider.Settings;
60import android.text.TextUtils;
61import android.util.Log;
62import android.util.Slog;
63
64import java.util.ArrayList;
65import java.util.HashMap;
66import java.util.List;
67import java.util.Set;
68import java.util.concurrent.atomic.AtomicInteger;
69import java.util.concurrent.atomic.AtomicBoolean;
70import java.io.FileDescriptor;
71import java.io.PrintWriter;
72
73import com.android.internal.app.IBatteryStats;
74import com.android.internal.telephony.TelephonyIntents;
75import com.android.internal.util.AsyncChannel;
76import com.android.server.am.BatteryStatsService;
77import com.android.internal.R;
78
79/**
80 * WifiService handles remote WiFi operation requests by implementing
81 * the IWifiManager interface.
82 *
83 * @hide
84 */
85//TODO: Clean up multiple locks and implement WifiService
86// as a SM to track soft AP/client/adhoc bring up based
87// on device idle state, airplane mode and boot.
88
89public class WifiService extends IWifiManager.Stub {
90    private static final String TAG = "WifiService";
91    private static final boolean DBG = false;
92
93    private final WifiStateMachine mWifiStateMachine;
94
95    private Context mContext;
96
97    private AlarmManager mAlarmManager;
98    private PendingIntent mIdleIntent;
99    private static final int IDLE_REQUEST = 0;
100    private boolean mScreenOff;
101    private boolean mDeviceIdle;
102    private boolean mEmergencyCallbackMode = false;
103    private int mPluggedType;
104
105    private final LockList mLocks = new LockList();
106    // some wifi lock statistics
107    private int mFullHighPerfLocksAcquired;
108    private int mFullHighPerfLocksReleased;
109    private int mFullLocksAcquired;
110    private int mFullLocksReleased;
111    private int mScanLocksAcquired;
112    private int mScanLocksReleased;
113
114    private final List<Multicaster> mMulticasters =
115            new ArrayList<Multicaster>();
116    private int mMulticastEnabled;
117    private int mMulticastDisabled;
118
119    private final IBatteryStats mBatteryStats;
120
121    private boolean mEnableTrafficStatsPoll = false;
122    private int mTrafficStatsPollToken = 0;
123    private long mTxPkts;
124    private long mRxPkts;
125    /* Tracks last reported data activity */
126    private int mDataActivity;
127    private String mInterfaceName;
128
129    /**
130     * Interval in milliseconds between polling for traffic
131     * statistics
132     */
133    private static final int POLL_TRAFFIC_STATS_INTERVAL_MSECS = 1000;
134
135    /**
136     * See {@link Settings.Secure#WIFI_IDLE_MS}. This is the default value if a
137     * Settings.Secure value is not present. This timeout value is chosen as
138     * the approximate point at which the battery drain caused by Wi-Fi
139     * being enabled but not active exceeds the battery drain caused by
140     * re-establishing a connection to the mobile data network.
141     */
142    private static final long DEFAULT_IDLE_MS = 15 * 60 * 1000; /* 15 minutes */
143
144    private static final String ACTION_DEVICE_IDLE =
145            "com.android.server.WifiManager.action.DEVICE_IDLE";
146
147    private static final int WIFI_DISABLED                  = 0;
148    private static final int WIFI_ENABLED                   = 1;
149    /* Wifi enabled while in airplane mode */
150    private static final int WIFI_ENABLED_AIRPLANE_OVERRIDE = 2;
151    /* Wifi disabled due to airplane mode on */
152    private static final int WIFI_DISABLED_AIRPLANE_ON      = 3;
153
154    /* Persisted state that tracks the wifi & airplane interaction from settings */
155    private AtomicInteger mPersistWifiState = new AtomicInteger(WIFI_DISABLED);
156    /* Tracks current airplane mode state */
157    private AtomicBoolean mAirplaneModeOn = new AtomicBoolean(false);
158    /* Tracks whether wifi is enabled from WifiStateMachine's perspective */
159    private boolean mWifiEnabled;
160
161    /* The work source (UID) that triggered the current WIFI scan, synchronized
162     * on this */
163    private WorkSource mScanWorkSource;
164
165    private boolean mIsReceiverRegistered = false;
166
167
168    NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
169
170    // Variables relating to the 'available networks' notification
171    /**
172     * The icon to show in the 'available networks' notification. This will also
173     * be the ID of the Notification given to the NotificationManager.
174     */
175    private static final int ICON_NETWORKS_AVAILABLE =
176            com.android.internal.R.drawable.stat_notify_wifi_in_range;
177    /**
178     * When a notification is shown, we wait this amount before possibly showing it again.
179     */
180    private final long NOTIFICATION_REPEAT_DELAY_MS;
181    /**
182     * Whether the user has set the setting to show the 'available networks' notification.
183     */
184    private boolean mNotificationEnabled;
185    /**
186     * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
187     */
188    private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
189    /**
190     * The {@link System#currentTimeMillis()} must be at least this value for us
191     * to show the notification again.
192     */
193    private long mNotificationRepeatTime;
194    /**
195     * The Notification object given to the NotificationManager.
196     */
197    private Notification mNotification;
198    /**
199     * Whether the notification is being shown, as set by us. That is, if the
200     * user cancels the notification, we will not receive the callback so this
201     * will still be true. We only guarantee if this is false, then the
202     * notification is not showing.
203     */
204    private boolean mNotificationShown;
205    /**
206     * The number of continuous scans that must occur before consider the
207     * supplicant in a scanning state. This allows supplicant to associate with
208     * remembered networks that are in the scan results.
209     */
210    private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
211    /**
212     * The number of scans since the last network state change. When this
213     * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
214     * supplicant to actually be scanning. When the network state changes to
215     * something other than scanning, we reset this to 0.
216     */
217    private int mNumScansSinceNetworkStateChange;
218
219    /**
220     * Asynchronous channel to WifiStateMachine
221     */
222    private AsyncChannel mWifiStateMachineChannel;
223
224    /**
225     * Clients receiving asynchronous messages
226     */
227    private List<AsyncChannel> mClients = new ArrayList<AsyncChannel>();
228
229    /**
230     * Handles client connections
231     */
232    private class AsyncServiceHandler extends Handler {
233
234        AsyncServiceHandler(android.os.Looper looper) {
235            super(looper);
236        }
237
238        @Override
239        public void handleMessage(Message msg) {
240            switch (msg.what) {
241                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
242                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
243                        Slog.d(TAG, "New client listening to asynchronous messages");
244                        mClients.add((AsyncChannel) msg.obj);
245                    } else {
246                        Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
247                    }
248                    break;
249                }
250                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
251                    if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
252                        Slog.d(TAG, "Send failed, client connection lost");
253                    } else {
254                        Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
255                    }
256                    mClients.remove((AsyncChannel) msg.obj);
257                    break;
258                }
259                case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
260                    AsyncChannel ac = new AsyncChannel();
261                    ac.connect(mContext, this, msg.replyTo);
262                    break;
263                }
264                case WifiManager.ENABLE_TRAFFIC_STATS_POLL: {
265                    mEnableTrafficStatsPoll = (msg.arg1 == 1);
266                    mTrafficStatsPollToken++;
267                    if (mEnableTrafficStatsPoll) {
268                        notifyOnDataActivity();
269                        sendMessageDelayed(Message.obtain(this, WifiManager.TRAFFIC_STATS_POLL,
270                                mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
271                    }
272                    break;
273                }
274                case WifiManager.TRAFFIC_STATS_POLL: {
275                    if (msg.arg1 == mTrafficStatsPollToken) {
276                        notifyOnDataActivity();
277                        sendMessageDelayed(Message.obtain(this, WifiManager.TRAFFIC_STATS_POLL,
278                                mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
279                    }
280                    break;
281                }
282                case WifiManager.CONNECT_NETWORK: {
283                    mWifiStateMachine.sendMessage(Message.obtain(msg));
284                    break;
285                }
286                case WifiManager.SAVE_NETWORK: {
287                    mWifiStateMachine.sendMessage(Message.obtain(msg));
288                    break;
289                }
290                case WifiManager.FORGET_NETWORK: {
291                    mWifiStateMachine.sendMessage(Message.obtain(msg));
292                    break;
293                }
294                case WifiManager.START_WPS: {
295                    mWifiStateMachine.sendMessage(Message.obtain(msg));
296                    break;
297                }
298                case WifiManager.CANCEL_WPS: {
299                    mWifiStateMachine.sendMessage(Message.obtain(msg));
300                    break;
301                }
302                case WifiManager.DISABLE_NETWORK: {
303                    mWifiStateMachine.sendMessage(Message.obtain(msg));
304                    break;
305                }
306                case WifiManager.RSSI_PKTCNT_FETCH: {
307                    mWifiStateMachine.sendMessage(Message.obtain(msg));
308                    break;
309                }
310                default: {
311                    Slog.d(TAG, "WifiServicehandler.handleMessage ignoring msg=" + msg);
312                    break;
313                }
314            }
315        }
316    }
317    private AsyncServiceHandler mAsyncServiceHandler;
318
319    /**
320     * Handles interaction with WifiStateMachine
321     */
322    private class WifiStateMachineHandler extends Handler {
323        private AsyncChannel mWsmChannel;
324
325        WifiStateMachineHandler(android.os.Looper looper) {
326            super(looper);
327            mWsmChannel = new AsyncChannel();
328            mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
329        }
330
331        @Override
332        public void handleMessage(Message msg) {
333            switch (msg.what) {
334                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
335                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
336                        mWifiStateMachineChannel = mWsmChannel;
337                    } else {
338                        Slog.e(TAG, "WifiStateMachine connection failure, error=" + msg.arg1);
339                        mWifiStateMachineChannel = null;
340                    }
341                    break;
342                }
343                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
344                    Slog.e(TAG, "WifiStateMachine channel lost, msg.arg1 =" + msg.arg1);
345                    mWifiStateMachineChannel = null;
346                    //Re-establish connection to state machine
347                    mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
348                    break;
349                }
350                default: {
351                    Slog.d(TAG, "WifiStateMachineHandler.handleMessage ignoring msg=" + msg);
352                    break;
353                }
354            }
355        }
356    }
357    WifiStateMachineHandler mWifiStateMachineHandler;
358
359    /**
360     * Temporary for computing UIDS that are responsible for starting WIFI.
361     * Protected by mWifiStateTracker lock.
362     */
363    private final WorkSource mTmpWorkSource = new WorkSource();
364    private WifiWatchdogStateMachine mWifiWatchdogStateMachine;
365
366    WifiService(Context context) {
367        mContext = context;
368
369        mInterfaceName =  SystemProperties.get("wifi.interface", "wlan0");
370
371        mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName);
372        mWifiStateMachine.enableRssiPolling(true);
373        mBatteryStats = BatteryStatsService.getService();
374
375        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
376        Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
377        mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
378
379        mContext.registerReceiver(
380                new BroadcastReceiver() {
381                    @Override
382                    public void onReceive(Context context, Intent intent) {
383                        mAirplaneModeOn.set(isAirplaneModeOn());
384                        handleAirplaneModeToggled(mAirplaneModeOn.get());
385                        updateWifiState();
386                    }
387                },
388                new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
389
390        IntentFilter filter = new IntentFilter();
391        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
392        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
393        filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
394
395        mContext.registerReceiver(
396                new BroadcastReceiver() {
397                    @Override
398                    public void onReceive(Context context, Intent intent) {
399                        if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
400                            int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
401                                    WifiManager.WIFI_STATE_DISABLED);
402
403                            mWifiEnabled = (wifiState == WifiManager.WIFI_STATE_ENABLED);
404
405                           // reset & clear notification on any wifi state change
406                            resetNotification();
407                        } else if (intent.getAction().equals(
408                                WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
409                            mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
410                                    WifiManager.EXTRA_NETWORK_INFO);
411                            // reset & clear notification on a network connect & disconnect
412                            switch(mNetworkInfo.getDetailedState()) {
413                                case CONNECTED:
414                                case DISCONNECTED:
415                                    evaluateTrafficStatsPolling();
416                                    resetNotification();
417                                    break;
418                            }
419                        } else if (intent.getAction().equals(
420                                WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
421                            noteScanEnd();
422                            checkAndSetNotification();
423                        }
424                    }
425                }, filter);
426
427        HandlerThread wifiThread = new HandlerThread("WifiService");
428        wifiThread.start();
429        mAsyncServiceHandler = new AsyncServiceHandler(wifiThread.getLooper());
430        mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper());
431
432        // Setting is in seconds
433        NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
434                Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
435        mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
436        mNotificationEnabledSettingObserver.register();
437    }
438
439    /** Tell battery stats about a new WIFI scan */
440    private void noteScanStart() {
441        WorkSource scanWorkSource = null;
442        synchronized (WifiService.this) {
443            if (mScanWorkSource != null) {
444                // Scan already in progress, don't add this one to battery stats
445                return;
446            }
447            scanWorkSource = new WorkSource(Binder.getCallingUid());
448            mScanWorkSource = scanWorkSource;
449        }
450
451        long id = Binder.clearCallingIdentity();
452        try {
453            mBatteryStats.noteWifiScanStartedFromSource(scanWorkSource);
454        } catch (RemoteException e) {
455            Log.w(TAG, e);
456        } finally {
457            Binder.restoreCallingIdentity(id);
458        }
459    }
460
461    /** Tell battery stats that the current WIFI scan has completed */
462    private void noteScanEnd() {
463        WorkSource scanWorkSource = null;
464        synchronized (WifiService.this) {
465            scanWorkSource = mScanWorkSource;
466            mScanWorkSource = null;
467        }
468        if (scanWorkSource != null) {
469            try {
470                mBatteryStats.noteWifiScanStoppedFromSource(scanWorkSource);
471            } catch (RemoteException e) {
472                Log.w(TAG, e);
473            }
474        }
475    }
476
477    /**
478     * Check if Wi-Fi needs to be enabled and start
479     * if needed
480     *
481     * This function is used only at boot time
482     */
483    public void checkAndStartWifi() {
484        mAirplaneModeOn.set(isAirplaneModeOn());
485        mPersistWifiState.set(getPersistedWifiState());
486        /* Start if Wi-Fi should be enabled or the saved state indicates Wi-Fi was on */
487        boolean wifiEnabled = shouldWifiBeEnabled() || testAndClearWifiSavedState();
488        Slog.i(TAG, "WifiService starting up with Wi-Fi " +
489                (wifiEnabled ? "enabled" : "disabled"));
490
491        // If we are already disabled (could be due to airplane mode), avoid changing persist
492        // state here
493        if (wifiEnabled) setWifiEnabled(wifiEnabled);
494
495        mWifiWatchdogStateMachine = WifiWatchdogStateMachine.
496               makeWifiWatchdogStateMachine(mContext);
497
498    }
499
500    private boolean testAndClearWifiSavedState() {
501        final ContentResolver cr = mContext.getContentResolver();
502        int wifiSavedState = 0;
503        try {
504            wifiSavedState = Settings.Secure.getInt(cr, Settings.Secure.WIFI_SAVED_STATE);
505            if(wifiSavedState == 1)
506                Settings.Secure.putInt(cr, Settings.Secure.WIFI_SAVED_STATE, 0);
507        } catch (Settings.SettingNotFoundException e) {
508            ;
509        }
510        return (wifiSavedState == 1);
511    }
512
513    private int getPersistedWifiState() {
514        final ContentResolver cr = mContext.getContentResolver();
515        try {
516            return Settings.Secure.getInt(cr, Settings.Secure.WIFI_ON);
517        } catch (Settings.SettingNotFoundException e) {
518            Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, WIFI_DISABLED);
519            return WIFI_DISABLED;
520        }
521    }
522
523    private boolean shouldWifiBeEnabled() {
524        if (mAirplaneModeOn.get()) {
525            return mPersistWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE;
526        } else {
527            return mPersistWifiState.get() != WIFI_DISABLED;
528        }
529    }
530
531    private void handleWifiToggled(boolean wifiEnabled) {
532        boolean airplaneEnabled = mAirplaneModeOn.get() && isAirplaneToggleable();
533        if (wifiEnabled) {
534            if (airplaneEnabled) {
535                persistWifiState(WIFI_ENABLED_AIRPLANE_OVERRIDE);
536            } else {
537                persistWifiState(WIFI_ENABLED);
538            }
539        } else {
540            // When wifi state is disabled, we do not care
541            // if airplane mode is on or not. The scenario of
542            // wifi being disabled due to airplane mode being turned on
543            // is handled handleAirplaneModeToggled()
544            persistWifiState(WIFI_DISABLED);
545        }
546    }
547
548    private void handleAirplaneModeToggled(boolean airplaneEnabled) {
549        if (airplaneEnabled) {
550            // Wifi disabled due to airplane on
551            if (mWifiEnabled) {
552                persistWifiState(WIFI_DISABLED_AIRPLANE_ON);
553            }
554        } else {
555            /* On airplane mode disable, restore wifi state if necessary */
556            if (testAndClearWifiSavedState() ||
557                    mPersistWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE) {
558                persistWifiState(WIFI_ENABLED);
559            }
560        }
561    }
562
563    private void persistWifiState(int state) {
564        final ContentResolver cr = mContext.getContentResolver();
565        mPersistWifiState.set(state);
566        Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, state);
567    }
568
569    /**
570     * see {@link android.net.wifi.WifiManager#pingSupplicant()}
571     * @return {@code true} if the operation succeeds, {@code false} otherwise
572     */
573    public boolean pingSupplicant() {
574        enforceAccessPermission();
575        if (mWifiStateMachineChannel != null) {
576            return mWifiStateMachine.syncPingSupplicant(mWifiStateMachineChannel);
577        } else {
578            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
579            return false;
580        }
581    }
582
583    /**
584     * see {@link android.net.wifi.WifiManager#startScan()}
585     */
586    public void startScan(boolean forceActive) {
587        enforceChangePermission();
588        mWifiStateMachine.startScan(forceActive);
589        noteScanStart();
590    }
591
592    private void enforceAccessPermission() {
593        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
594                                                "WifiService");
595    }
596
597    private void enforceChangePermission() {
598        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
599                                                "WifiService");
600
601    }
602
603    private void enforceMulticastChangePermission() {
604        mContext.enforceCallingOrSelfPermission(
605                android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
606                "WifiService");
607    }
608
609    /**
610     * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
611     * @param enable {@code true} to enable, {@code false} to disable.
612     * @return {@code true} if the enable/disable operation was
613     *         started or is already in the queue.
614     */
615    public synchronized boolean setWifiEnabled(boolean enable) {
616        enforceChangePermission();
617        Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid()
618                    + ", uid=" + Binder.getCallingUid());
619        if (DBG) {
620            Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
621        }
622
623        if (enable) {
624            reportStartWorkSource();
625        }
626        mWifiStateMachine.setWifiEnabled(enable);
627
628        /*
629         * Caller might not have WRITE_SECURE_SETTINGS,
630         * only CHANGE_WIFI_STATE is enforced
631         */
632
633        long ident = Binder.clearCallingIdentity();
634        handleWifiToggled(enable);
635        Binder.restoreCallingIdentity(ident);
636
637        if (enable) {
638            if (!mIsReceiverRegistered) {
639                registerForBroadcasts();
640                mIsReceiverRegistered = true;
641            }
642        } else if (mIsReceiverRegistered) {
643            mContext.unregisterReceiver(mReceiver);
644            mIsReceiverRegistered = false;
645        }
646
647        return true;
648    }
649
650    /**
651     * see {@link WifiManager#getWifiState()}
652     * @return One of {@link WifiManager#WIFI_STATE_DISABLED},
653     *         {@link WifiManager#WIFI_STATE_DISABLING},
654     *         {@link WifiManager#WIFI_STATE_ENABLED},
655     *         {@link WifiManager#WIFI_STATE_ENABLING},
656     *         {@link WifiManager#WIFI_STATE_UNKNOWN}
657     */
658    public int getWifiEnabledState() {
659        enforceAccessPermission();
660        return mWifiStateMachine.syncGetWifiState();
661    }
662
663    /**
664     * see {@link android.net.wifi.WifiManager#setWifiApEnabled(WifiConfiguration, boolean)}
665     * @param wifiConfig SSID, security and channel details as
666     *        part of WifiConfiguration
667     * @param enabled true to enable and false to disable
668     */
669    public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
670        enforceChangePermission();
671        mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled);
672    }
673
674    /**
675     * see {@link WifiManager#getWifiApState()}
676     * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
677     *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
678     *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
679     *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
680     *         {@link WifiManager#WIFI_AP_STATE_FAILED}
681     */
682    public int getWifiApEnabledState() {
683        enforceAccessPermission();
684        return mWifiStateMachine.syncGetWifiApState();
685    }
686
687    /**
688     * see {@link WifiManager#getWifiApConfiguration()}
689     * @return soft access point configuration
690     */
691    public WifiConfiguration getWifiApConfiguration() {
692        enforceAccessPermission();
693        return mWifiStateMachine.syncGetWifiApConfiguration();
694    }
695
696    /**
697     * see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)}
698     * @param wifiConfig WifiConfiguration details for soft access point
699     */
700    public void setWifiApConfiguration(WifiConfiguration wifiConfig) {
701        enforceChangePermission();
702        if (wifiConfig == null)
703            return;
704        mWifiStateMachine.setWifiApConfiguration(wifiConfig);
705    }
706
707    /**
708     * see {@link android.net.wifi.WifiManager#disconnect()}
709     */
710    public void disconnect() {
711        enforceChangePermission();
712        mWifiStateMachine.disconnectCommand();
713    }
714
715    /**
716     * see {@link android.net.wifi.WifiManager#reconnect()}
717     */
718    public void reconnect() {
719        enforceChangePermission();
720        mWifiStateMachine.reconnectCommand();
721    }
722
723    /**
724     * see {@link android.net.wifi.WifiManager#reassociate()}
725     */
726    public void reassociate() {
727        enforceChangePermission();
728        mWifiStateMachine.reassociateCommand();
729    }
730
731    /**
732     * see {@link android.net.wifi.WifiManager#getConfiguredNetworks()}
733     * @return the list of configured networks
734     */
735    public List<WifiConfiguration> getConfiguredNetworks() {
736        enforceAccessPermission();
737        if (mWifiStateMachineChannel != null) {
738            return mWifiStateMachine.syncGetConfiguredNetworks(mWifiStateMachineChannel);
739        } else {
740            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
741            return null;
742        }
743    }
744
745    /**
746     * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)}
747     * @return the supplicant-assigned identifier for the new or updated
748     * network if the operation succeeds, or {@code -1} if it fails
749     */
750    public int addOrUpdateNetwork(WifiConfiguration config) {
751        enforceChangePermission();
752        if (mWifiStateMachineChannel != null) {
753            return mWifiStateMachine.syncAddOrUpdateNetwork(mWifiStateMachineChannel, config);
754        } else {
755            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
756            return -1;
757        }
758    }
759
760     /**
761     * See {@link android.net.wifi.WifiManager#removeNetwork(int)}
762     * @param netId the integer that identifies the network configuration
763     * to the supplicant
764     * @return {@code true} if the operation succeeded
765     */
766    public boolean removeNetwork(int netId) {
767        enforceChangePermission();
768        if (mWifiStateMachineChannel != null) {
769            return mWifiStateMachine.syncRemoveNetwork(mWifiStateMachineChannel, netId);
770        } else {
771            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
772            return false;
773        }
774    }
775
776    /**
777     * See {@link android.net.wifi.WifiManager#enableNetwork(int, boolean)}
778     * @param netId the integer that identifies the network configuration
779     * to the supplicant
780     * @param disableOthers if true, disable all other networks.
781     * @return {@code true} if the operation succeeded
782     */
783    public boolean enableNetwork(int netId, boolean disableOthers) {
784        enforceChangePermission();
785        if (mWifiStateMachineChannel != null) {
786            return mWifiStateMachine.syncEnableNetwork(mWifiStateMachineChannel, netId,
787                    disableOthers);
788        } else {
789            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
790            return false;
791        }
792    }
793
794    /**
795     * See {@link android.net.wifi.WifiManager#disableNetwork(int)}
796     * @param netId the integer that identifies the network configuration
797     * to the supplicant
798     * @return {@code true} if the operation succeeded
799     */
800    public boolean disableNetwork(int netId) {
801        enforceChangePermission();
802        if (mWifiStateMachineChannel != null) {
803            return mWifiStateMachine.syncDisableNetwork(mWifiStateMachineChannel, netId);
804        } else {
805            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
806            return false;
807        }
808    }
809
810    /**
811     * See {@link android.net.wifi.WifiManager#getConnectionInfo()}
812     * @return the Wi-Fi information, contained in {@link WifiInfo}.
813     */
814    public WifiInfo getConnectionInfo() {
815        enforceAccessPermission();
816        /*
817         * Make sure we have the latest information, by sending
818         * a status request to the supplicant.
819         */
820        return mWifiStateMachine.syncRequestConnectionInfo();
821    }
822
823    /**
824     * Return the results of the most recent access point scan, in the form of
825     * a list of {@link ScanResult} objects.
826     * @return the list of results
827     */
828    public List<ScanResult> getScanResults() {
829        enforceAccessPermission();
830        return mWifiStateMachine.syncGetScanResultsList();
831    }
832
833    /**
834     * Tell the supplicant to persist the current list of configured networks.
835     * @return {@code true} if the operation succeeded
836     *
837     * TODO: deprecate this
838     */
839    public boolean saveConfiguration() {
840        boolean result = true;
841        enforceChangePermission();
842        if (mWifiStateMachineChannel != null) {
843            return mWifiStateMachine.syncSaveConfig(mWifiStateMachineChannel);
844        } else {
845            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
846            return false;
847        }
848    }
849
850    /**
851     * Set the country code
852     * @param countryCode ISO 3166 country code.
853     * @param persist {@code true} if the setting should be remembered.
854     *
855     * The persist behavior exists so that wifi can fall back to the last
856     * persisted country code on a restart, when the locale information is
857     * not available from telephony.
858     */
859    public void setCountryCode(String countryCode, boolean persist) {
860        Slog.i(TAG, "WifiService trying to set country code to " + countryCode +
861                " with persist set to " + persist);
862        enforceChangePermission();
863        mWifiStateMachine.setCountryCode(countryCode, persist);
864    }
865
866    /**
867     * Set the operational frequency band
868     * @param band One of
869     *     {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
870     *     {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ},
871     *     {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ},
872     * @param persist {@code true} if the setting should be remembered.
873     *
874     */
875    public void setFrequencyBand(int band, boolean persist) {
876        enforceChangePermission();
877        if (!isDualBandSupported()) return;
878        Slog.i(TAG, "WifiService trying to set frequency band to " + band +
879                " with persist set to " + persist);
880        mWifiStateMachine.setFrequencyBand(band, persist);
881    }
882
883
884    /**
885     * Get the operational frequency band
886     */
887    public int getFrequencyBand() {
888        enforceAccessPermission();
889        return mWifiStateMachine.getFrequencyBand();
890    }
891
892    public boolean isDualBandSupported() {
893        //TODO: Should move towards adding a driver API that checks at runtime
894        return mContext.getResources().getBoolean(
895                com.android.internal.R.bool.config_wifi_dual_band_support);
896    }
897
898    /**
899     * Return the DHCP-assigned addresses from the last successful DHCP request,
900     * if any.
901     * @return the DHCP information
902     */
903    public DhcpInfo getDhcpInfo() {
904        enforceAccessPermission();
905        return mWifiStateMachine.syncGetDhcpInfo();
906    }
907
908    /**
909     * see {@link android.net.wifi.WifiManager#startWifi}
910     *
911     */
912    public void startWifi() {
913        enforceChangePermission();
914        /* TODO: may be add permissions for access only to connectivity service
915         * TODO: if a start issued, keep wifi alive until a stop issued irrespective
916         * of WifiLock & device idle status unless wifi enabled status is toggled
917         */
918
919        mWifiStateMachine.setDriverStart(true, mEmergencyCallbackMode);
920        mWifiStateMachine.reconnectCommand();
921    }
922
923    /**
924     * see {@link android.net.wifi.WifiManager#stopWifi}
925     *
926     */
927    public void stopWifi() {
928        enforceChangePermission();
929        /* TODO: may be add permissions for access only to connectivity service
930         * TODO: if a stop is issued, wifi is brought up only by startWifi
931         * unless wifi enabled status is toggled
932         */
933        mWifiStateMachine.setDriverStart(false, mEmergencyCallbackMode);
934    }
935
936
937    /**
938     * see {@link android.net.wifi.WifiManager#addToBlacklist}
939     *
940     */
941    public void addToBlacklist(String bssid) {
942        enforceChangePermission();
943
944        mWifiStateMachine.addToBlacklist(bssid);
945    }
946
947    /**
948     * see {@link android.net.wifi.WifiManager#clearBlacklist}
949     *
950     */
951    public void clearBlacklist() {
952        enforceChangePermission();
953
954        mWifiStateMachine.clearBlacklist();
955    }
956
957    /**
958     * Get a reference to handler. This is used by a client to establish
959     * an AsyncChannel communication with WifiService
960     */
961    public Messenger getWifiServiceMessenger() {
962        enforceAccessPermission();
963        enforceChangePermission();
964        return new Messenger(mAsyncServiceHandler);
965    }
966
967    /** Get a reference to WifiStateMachine handler for AsyncChannel communication */
968    public Messenger getWifiStateMachineMessenger() {
969        enforceAccessPermission();
970        enforceChangePermission();
971        return mWifiStateMachine.getMessenger();
972    }
973
974    /**
975     * Get the IP and proxy configuration file
976     */
977    public String getConfigFile() {
978        enforceAccessPermission();
979        return mWifiStateMachine.getConfigFile();
980    }
981
982    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
983        @Override
984        public void onReceive(Context context, Intent intent) {
985            String action = intent.getAction();
986
987            long idleMillis =
988                Settings.Secure.getLong(mContext.getContentResolver(),
989                                        Settings.Secure.WIFI_IDLE_MS, DEFAULT_IDLE_MS);
990            int stayAwakeConditions =
991                Settings.System.getInt(mContext.getContentResolver(),
992                                       Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
993            if (action.equals(Intent.ACTION_SCREEN_ON)) {
994                if (DBG) {
995                    Slog.d(TAG, "ACTION_SCREEN_ON");
996                }
997                mAlarmManager.cancel(mIdleIntent);
998                mScreenOff = false;
999                evaluateTrafficStatsPolling();
1000                setDeviceIdleAndUpdateWifi(false);
1001            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1002                if (DBG) {
1003                    Slog.d(TAG, "ACTION_SCREEN_OFF");
1004                }
1005                mScreenOff = true;
1006                evaluateTrafficStatsPolling();
1007                /*
1008                 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
1009                 * AND the "stay on while plugged in" setting doesn't match the
1010                 * current power conditions (i.e, not plugged in, plugged in to USB,
1011                 * or plugged in to AC).
1012                 */
1013                if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
1014                    //Delayed shutdown if wifi is connected
1015                    if (mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
1016                        if (DBG) Slog.d(TAG, "setting ACTION_DEVICE_IDLE: " + idleMillis + " ms");
1017                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
1018                                + idleMillis, mIdleIntent);
1019                    } else {
1020                        setDeviceIdleAndUpdateWifi(true);
1021                    }
1022                }
1023            } else if (action.equals(ACTION_DEVICE_IDLE)) {
1024                setDeviceIdleAndUpdateWifi(true);
1025            } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
1026                /*
1027                 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
1028                 * AND we are transitioning from a state in which the device was supposed
1029                 * to stay awake to a state in which it is not supposed to stay awake.
1030                 * If "stay awake" state is not changing, we do nothing, to avoid resetting
1031                 * the already-set timer.
1032                 */
1033                int pluggedType = intent.getIntExtra("plugged", 0);
1034                if (DBG) {
1035                    Slog.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType);
1036                }
1037                if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) &&
1038                        !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) {
1039                    long triggerTime = System.currentTimeMillis() + idleMillis;
1040                    if (DBG) {
1041                        Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
1042                    }
1043                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
1044                }
1045
1046                mPluggedType = pluggedType;
1047            } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
1048                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
1049                        BluetoothAdapter.STATE_DISCONNECTED);
1050                mWifiStateMachine.sendBluetoothAdapterStateChange(state);
1051            } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
1052                mEmergencyCallbackMode = intent.getBooleanExtra("phoneinECMState", false);
1053                updateWifiState();
1054            }
1055        }
1056
1057        /**
1058         * Determines whether the Wi-Fi chipset should stay awake or be put to
1059         * sleep. Looks at the setting for the sleep policy and the current
1060         * conditions.
1061         *
1062         * @see #shouldDeviceStayAwake(int, int)
1063         */
1064        private boolean shouldWifiStayAwake(int stayAwakeConditions, int pluggedType) {
1065            //Never sleep as long as the user has not changed the settings
1066            int wifiSleepPolicy = Settings.System.getInt(mContext.getContentResolver(),
1067                    Settings.System.WIFI_SLEEP_POLICY,
1068                    Settings.System.WIFI_SLEEP_POLICY_NEVER);
1069
1070            if (wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER) {
1071                // Never sleep
1072                return true;
1073            } else if ((wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) &&
1074                    (pluggedType != 0)) {
1075                // Never sleep while plugged, and we're plugged
1076                return true;
1077            } else {
1078                // Default
1079                return shouldDeviceStayAwake(stayAwakeConditions, pluggedType);
1080            }
1081        }
1082
1083        /**
1084         * Determine whether the bit value corresponding to {@code pluggedType} is set in
1085         * the bit string {@code stayAwakeConditions}. Because a {@code pluggedType} value
1086         * of {@code 0} isn't really a plugged type, but rather an indication that the
1087         * device isn't plugged in at all, there is no bit value corresponding to a
1088         * {@code pluggedType} value of {@code 0}. That is why we shift by
1089         * {@code pluggedType - 1} instead of by {@code pluggedType}.
1090         * @param stayAwakeConditions a bit string specifying which "plugged types" should
1091         * keep the device (and hence Wi-Fi) awake.
1092         * @param pluggedType the type of plug (USB, AC, or none) for which the check is
1093         * being made
1094         * @return {@code true} if {@code pluggedType} indicates that the device is
1095         * supposed to stay awake, {@code false} otherwise.
1096         */
1097        private boolean shouldDeviceStayAwake(int stayAwakeConditions, int pluggedType) {
1098            return (stayAwakeConditions & pluggedType) != 0;
1099        }
1100    };
1101
1102    private void setDeviceIdleAndUpdateWifi(boolean deviceIdle) {
1103        mDeviceIdle = deviceIdle;
1104        reportStartWorkSource();
1105        updateWifiState();
1106    }
1107
1108    private synchronized void reportStartWorkSource() {
1109        mTmpWorkSource.clear();
1110        if (mDeviceIdle) {
1111            for (int i=0; i<mLocks.mList.size(); i++) {
1112                mTmpWorkSource.add(mLocks.mList.get(i).mWorkSource);
1113            }
1114        }
1115        mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource);
1116    }
1117
1118    private void updateWifiState() {
1119        boolean lockHeld = mLocks.hasLocks();
1120        int strongestLockMode = WifiManager.WIFI_MODE_FULL;
1121        boolean wifiShouldBeStarted;
1122
1123        if (mEmergencyCallbackMode) {
1124            wifiShouldBeStarted = false;
1125        } else {
1126            wifiShouldBeStarted = !mDeviceIdle || lockHeld;
1127        }
1128
1129        if (lockHeld) {
1130            strongestLockMode = mLocks.getStrongestLockMode();
1131        }
1132        /* If device is not idle, lockmode cannot be scan only */
1133        if (!mDeviceIdle && strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY) {
1134            strongestLockMode = WifiManager.WIFI_MODE_FULL;
1135        }
1136
1137        /* Disable tethering when airplane mode is enabled */
1138        if (mAirplaneModeOn.get()) {
1139            mWifiStateMachine.setWifiApEnabled(null, false);
1140        }
1141
1142        if (shouldWifiBeEnabled()) {
1143            if (wifiShouldBeStarted) {
1144                reportStartWorkSource();
1145                mWifiStateMachine.setWifiEnabled(true);
1146                mWifiStateMachine.setScanOnlyMode(
1147                        strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
1148                mWifiStateMachine.setDriverStart(true, mEmergencyCallbackMode);
1149                mWifiStateMachine.setHighPerfModeEnabled(strongestLockMode
1150                        == WifiManager.WIFI_MODE_FULL_HIGH_PERF);
1151            } else {
1152                mWifiStateMachine.setDriverStart(false, mEmergencyCallbackMode);
1153            }
1154        } else {
1155            mWifiStateMachine.setWifiEnabled(false);
1156        }
1157    }
1158
1159    private void registerForBroadcasts() {
1160        IntentFilter intentFilter = new IntentFilter();
1161        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
1162        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
1163        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
1164        intentFilter.addAction(ACTION_DEVICE_IDLE);
1165        intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
1166        intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
1167        mContext.registerReceiver(mReceiver, intentFilter);
1168    }
1169
1170    private boolean isAirplaneSensitive() {
1171        String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
1172                Settings.System.AIRPLANE_MODE_RADIOS);
1173        return airplaneModeRadios == null
1174            || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
1175    }
1176
1177    private boolean isAirplaneToggleable() {
1178        String toggleableRadios = Settings.System.getString(mContext.getContentResolver(),
1179                Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
1180        return toggleableRadios != null
1181            && toggleableRadios.contains(Settings.System.RADIO_WIFI);
1182    }
1183
1184    /**
1185     * Returns true if Wi-Fi is sensitive to airplane mode, and airplane mode is
1186     * currently on.
1187     * @return {@code true} if airplane mode is on.
1188     */
1189    private boolean isAirplaneModeOn() {
1190        return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
1191                Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1192    }
1193
1194    @Override
1195    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1196        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1197                != PackageManager.PERMISSION_GRANTED) {
1198            pw.println("Permission Denial: can't dump WifiService from from pid="
1199                    + Binder.getCallingPid()
1200                    + ", uid=" + Binder.getCallingUid());
1201            return;
1202        }
1203        pw.println("Wi-Fi is " + mWifiStateMachine.syncGetWifiStateByName());
1204        pw.println("Stay-awake conditions: " +
1205                Settings.System.getInt(mContext.getContentResolver(),
1206                                       Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
1207        pw.println();
1208
1209        pw.println("Internal state:");
1210        pw.println(mWifiStateMachine);
1211        pw.println();
1212        pw.println("Latest scan results:");
1213        List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
1214        if (scanResults != null && scanResults.size() != 0) {
1215            pw.println("  BSSID              Frequency   RSSI  Flags             SSID");
1216            for (ScanResult r : scanResults) {
1217                pw.printf("  %17s  %9d  %5d  %-16s  %s%n",
1218                                         r.BSSID,
1219                                         r.frequency,
1220                                         r.level,
1221                                         r.capabilities,
1222                                         r.SSID == null ? "" : r.SSID);
1223            }
1224        }
1225        pw.println();
1226        pw.println("Locks acquired: " + mFullLocksAcquired + " full, " +
1227                mFullHighPerfLocksAcquired + " full high perf, " +
1228                mScanLocksAcquired + " scan");
1229        pw.println("Locks released: " + mFullLocksReleased + " full, " +
1230                mFullHighPerfLocksReleased + " full high perf, " +
1231                mScanLocksReleased + " scan");
1232        pw.println();
1233        pw.println("Locks held:");
1234        mLocks.dump(pw);
1235
1236        pw.println();
1237        pw.println("WifiWatchdogStateMachine dump");
1238        mWifiWatchdogStateMachine.dump(pw);
1239        pw.println("WifiStateMachine dump");
1240        mWifiStateMachine.dump(fd, pw, args);
1241    }
1242
1243    private class WifiLock extends DeathRecipient {
1244        WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) {
1245            super(lockMode, tag, binder, ws);
1246        }
1247
1248        public void binderDied() {
1249            synchronized (mLocks) {
1250                releaseWifiLockLocked(mBinder);
1251            }
1252        }
1253
1254        public String toString() {
1255            return "WifiLock{" + mTag + " type=" + mMode + " binder=" + mBinder + "}";
1256        }
1257    }
1258
1259    private class LockList {
1260        private List<WifiLock> mList;
1261
1262        private LockList() {
1263            mList = new ArrayList<WifiLock>();
1264        }
1265
1266        private synchronized boolean hasLocks() {
1267            return !mList.isEmpty();
1268        }
1269
1270        private synchronized int getStrongestLockMode() {
1271            if (mList.isEmpty()) {
1272                return WifiManager.WIFI_MODE_FULL;
1273            }
1274
1275            if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) {
1276                return WifiManager.WIFI_MODE_FULL_HIGH_PERF;
1277            }
1278
1279            if (mFullLocksAcquired > mFullLocksReleased) {
1280                return WifiManager.WIFI_MODE_FULL;
1281            }
1282
1283            return WifiManager.WIFI_MODE_SCAN_ONLY;
1284        }
1285
1286        private void addLock(WifiLock lock) {
1287            if (findLockByBinder(lock.mBinder) < 0) {
1288                mList.add(lock);
1289            }
1290        }
1291
1292        private WifiLock removeLock(IBinder binder) {
1293            int index = findLockByBinder(binder);
1294            if (index >= 0) {
1295                WifiLock ret = mList.remove(index);
1296                ret.unlinkDeathRecipient();
1297                return ret;
1298            } else {
1299                return null;
1300            }
1301        }
1302
1303        private int findLockByBinder(IBinder binder) {
1304            int size = mList.size();
1305            for (int i = size - 1; i >= 0; i--)
1306                if (mList.get(i).mBinder == binder)
1307                    return i;
1308            return -1;
1309        }
1310
1311        private void dump(PrintWriter pw) {
1312            for (WifiLock l : mList) {
1313                pw.print("    ");
1314                pw.println(l);
1315            }
1316        }
1317    }
1318
1319    void enforceWakeSourcePermission(int uid, int pid) {
1320        if (uid == android.os.Process.myUid()) {
1321            return;
1322        }
1323        mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
1324                pid, uid, null);
1325    }
1326
1327    public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws) {
1328        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
1329        if (lockMode != WifiManager.WIFI_MODE_FULL &&
1330                lockMode != WifiManager.WIFI_MODE_SCAN_ONLY &&
1331                lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF) {
1332            Slog.e(TAG, "Illegal argument, lockMode= " + lockMode);
1333            if (DBG) throw new IllegalArgumentException("lockMode=" + lockMode);
1334            return false;
1335        }
1336        if (ws != null && ws.size() == 0) {
1337            ws = null;
1338        }
1339        if (ws != null) {
1340            enforceWakeSourcePermission(Binder.getCallingUid(), Binder.getCallingPid());
1341        }
1342        if (ws == null) {
1343            ws = new WorkSource(Binder.getCallingUid());
1344        }
1345        WifiLock wifiLock = new WifiLock(lockMode, tag, binder, ws);
1346        synchronized (mLocks) {
1347            return acquireWifiLockLocked(wifiLock);
1348        }
1349    }
1350
1351    private void noteAcquireWifiLock(WifiLock wifiLock) throws RemoteException {
1352        switch(wifiLock.mMode) {
1353            case WifiManager.WIFI_MODE_FULL:
1354            case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
1355            case WifiManager.WIFI_MODE_SCAN_ONLY:
1356                mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource);
1357                break;
1358        }
1359    }
1360
1361    private void noteReleaseWifiLock(WifiLock wifiLock) throws RemoteException {
1362        switch(wifiLock.mMode) {
1363            case WifiManager.WIFI_MODE_FULL:
1364            case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
1365            case WifiManager.WIFI_MODE_SCAN_ONLY:
1366                mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
1367                break;
1368        }
1369    }
1370
1371    private boolean acquireWifiLockLocked(WifiLock wifiLock) {
1372        if (DBG) Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
1373
1374        mLocks.addLock(wifiLock);
1375
1376        long ident = Binder.clearCallingIdentity();
1377        try {
1378            noteAcquireWifiLock(wifiLock);
1379            switch(wifiLock.mMode) {
1380            case WifiManager.WIFI_MODE_FULL:
1381                ++mFullLocksAcquired;
1382                break;
1383            case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
1384                ++mFullHighPerfLocksAcquired;
1385                break;
1386
1387            case WifiManager.WIFI_MODE_SCAN_ONLY:
1388                ++mScanLocksAcquired;
1389                break;
1390            }
1391
1392            // Be aggressive about adding new locks into the accounted state...
1393            // we want to over-report rather than under-report.
1394            reportStartWorkSource();
1395
1396            updateWifiState();
1397            return true;
1398        } catch (RemoteException e) {
1399            return false;
1400        } finally {
1401            Binder.restoreCallingIdentity(ident);
1402        }
1403    }
1404
1405    public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) {
1406        int uid = Binder.getCallingUid();
1407        int pid = Binder.getCallingPid();
1408        if (ws != null && ws.size() == 0) {
1409            ws = null;
1410        }
1411        if (ws != null) {
1412            enforceWakeSourcePermission(uid, pid);
1413        }
1414        long ident = Binder.clearCallingIdentity();
1415        try {
1416            synchronized (mLocks) {
1417                int index = mLocks.findLockByBinder(lock);
1418                if (index < 0) {
1419                    throw new IllegalArgumentException("Wifi lock not active");
1420                }
1421                WifiLock wl = mLocks.mList.get(index);
1422                noteReleaseWifiLock(wl);
1423                wl.mWorkSource = ws != null ? new WorkSource(ws) : new WorkSource(uid);
1424                noteAcquireWifiLock(wl);
1425            }
1426        } catch (RemoteException e) {
1427        } finally {
1428            Binder.restoreCallingIdentity(ident);
1429        }
1430    }
1431
1432    public boolean releaseWifiLock(IBinder lock) {
1433        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
1434        synchronized (mLocks) {
1435            return releaseWifiLockLocked(lock);
1436        }
1437    }
1438
1439    private boolean releaseWifiLockLocked(IBinder lock) {
1440        boolean hadLock;
1441
1442        WifiLock wifiLock = mLocks.removeLock(lock);
1443
1444        if (DBG) Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock);
1445
1446        hadLock = (wifiLock != null);
1447
1448        long ident = Binder.clearCallingIdentity();
1449        try {
1450            if (hadLock) {
1451                noteReleaseWifiLock(wifiLock);
1452                switch(wifiLock.mMode) {
1453                    case WifiManager.WIFI_MODE_FULL:
1454                        ++mFullLocksReleased;
1455                        break;
1456                    case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
1457                        ++mFullHighPerfLocksReleased;
1458                        break;
1459                    case WifiManager.WIFI_MODE_SCAN_ONLY:
1460                        ++mScanLocksReleased;
1461                        break;
1462                }
1463            }
1464
1465            // TODO - should this only happen if you hadLock?
1466            updateWifiState();
1467
1468        } catch (RemoteException e) {
1469        } finally {
1470            Binder.restoreCallingIdentity(ident);
1471        }
1472
1473        return hadLock;
1474    }
1475
1476    private abstract class DeathRecipient
1477            implements IBinder.DeathRecipient {
1478        String mTag;
1479        int mMode;
1480        IBinder mBinder;
1481        WorkSource mWorkSource;
1482
1483        DeathRecipient(int mode, String tag, IBinder binder, WorkSource ws) {
1484            super();
1485            mTag = tag;
1486            mMode = mode;
1487            mBinder = binder;
1488            mWorkSource = ws;
1489            try {
1490                mBinder.linkToDeath(this, 0);
1491            } catch (RemoteException e) {
1492                binderDied();
1493            }
1494        }
1495
1496        void unlinkDeathRecipient() {
1497            mBinder.unlinkToDeath(this, 0);
1498        }
1499    }
1500
1501    private class Multicaster extends DeathRecipient {
1502        Multicaster(String tag, IBinder binder) {
1503            super(Binder.getCallingUid(), tag, binder, null);
1504        }
1505
1506        public void binderDied() {
1507            Slog.e(TAG, "Multicaster binderDied");
1508            synchronized (mMulticasters) {
1509                int i = mMulticasters.indexOf(this);
1510                if (i != -1) {
1511                    removeMulticasterLocked(i, mMode);
1512                }
1513            }
1514        }
1515
1516        public String toString() {
1517            return "Multicaster{" + mTag + " binder=" + mBinder + "}";
1518        }
1519
1520        public int getUid() {
1521            return mMode;
1522        }
1523    }
1524
1525    public void initializeMulticastFiltering() {
1526        enforceMulticastChangePermission();
1527
1528        synchronized (mMulticasters) {
1529            // if anybody had requested filters be off, leave off
1530            if (mMulticasters.size() != 0) {
1531                return;
1532            } else {
1533                mWifiStateMachine.startFilteringMulticastV4Packets();
1534            }
1535        }
1536    }
1537
1538    public void acquireMulticastLock(IBinder binder, String tag) {
1539        enforceMulticastChangePermission();
1540
1541        synchronized (mMulticasters) {
1542            mMulticastEnabled++;
1543            mMulticasters.add(new Multicaster(tag, binder));
1544            // Note that we could call stopFilteringMulticastV4Packets only when
1545            // our new size == 1 (first call), but this function won't
1546            // be called often and by making the stopPacket call each
1547            // time we're less fragile and self-healing.
1548            mWifiStateMachine.stopFilteringMulticastV4Packets();
1549        }
1550
1551        int uid = Binder.getCallingUid();
1552        Long ident = Binder.clearCallingIdentity();
1553        try {
1554            mBatteryStats.noteWifiMulticastEnabled(uid);
1555        } catch (RemoteException e) {
1556        } finally {
1557            Binder.restoreCallingIdentity(ident);
1558        }
1559    }
1560
1561    public void releaseMulticastLock() {
1562        enforceMulticastChangePermission();
1563
1564        int uid = Binder.getCallingUid();
1565        synchronized (mMulticasters) {
1566            mMulticastDisabled++;
1567            int size = mMulticasters.size();
1568            for (int i = size - 1; i >= 0; i--) {
1569                Multicaster m = mMulticasters.get(i);
1570                if ((m != null) && (m.getUid() == uid)) {
1571                    removeMulticasterLocked(i, uid);
1572                }
1573            }
1574        }
1575    }
1576
1577    private void removeMulticasterLocked(int i, int uid)
1578    {
1579        Multicaster removed = mMulticasters.remove(i);
1580
1581        if (removed != null) {
1582            removed.unlinkDeathRecipient();
1583        }
1584        if (mMulticasters.size() == 0) {
1585            mWifiStateMachine.startFilteringMulticastV4Packets();
1586        }
1587
1588        Long ident = Binder.clearCallingIdentity();
1589        try {
1590            mBatteryStats.noteWifiMulticastDisabled(uid);
1591        } catch (RemoteException e) {
1592        } finally {
1593            Binder.restoreCallingIdentity(ident);
1594        }
1595    }
1596
1597    public boolean isMulticastEnabled() {
1598        enforceAccessPermission();
1599
1600        synchronized (mMulticasters) {
1601            return (mMulticasters.size() > 0);
1602        }
1603    }
1604
1605    /**
1606     * Evaluate if traffic stats polling is needed based on
1607     * connection and screen on status
1608     */
1609    private void evaluateTrafficStatsPolling() {
1610        Message msg;
1611        if (mNetworkInfo.getDetailedState() == DetailedState.CONNECTED && !mScreenOff) {
1612            msg = Message.obtain(mAsyncServiceHandler,
1613                    WifiManager.ENABLE_TRAFFIC_STATS_POLL, 1, 0);
1614        } else {
1615            msg = Message.obtain(mAsyncServiceHandler,
1616                    WifiManager.ENABLE_TRAFFIC_STATS_POLL, 0, 0);
1617        }
1618        msg.sendToTarget();
1619    }
1620
1621    private void notifyOnDataActivity() {
1622        long sent, received;
1623        long preTxPkts = mTxPkts, preRxPkts = mRxPkts;
1624        int dataActivity = WifiManager.DATA_ACTIVITY_NONE;
1625
1626        mTxPkts = TrafficStats.getTxPackets(mInterfaceName);
1627        mRxPkts = TrafficStats.getRxPackets(mInterfaceName);
1628
1629        if (preTxPkts > 0 || preRxPkts > 0) {
1630            sent = mTxPkts - preTxPkts;
1631            received = mRxPkts - preRxPkts;
1632            if (sent > 0) {
1633                dataActivity |= WifiManager.DATA_ACTIVITY_OUT;
1634            }
1635            if (received > 0) {
1636                dataActivity |= WifiManager.DATA_ACTIVITY_IN;
1637            }
1638
1639            if (dataActivity != mDataActivity && !mScreenOff) {
1640                mDataActivity = dataActivity;
1641                for (AsyncChannel client : mClients) {
1642                    client.sendMessage(WifiManager.DATA_ACTIVITY_NOTIFICATION, mDataActivity);
1643                }
1644            }
1645        }
1646    }
1647
1648
1649    private void checkAndSetNotification() {
1650        // If we shouldn't place a notification on available networks, then
1651        // don't bother doing any of the following
1652        if (!mNotificationEnabled) return;
1653
1654        State state = mNetworkInfo.getState();
1655        if ((state == NetworkInfo.State.DISCONNECTED)
1656                || (state == NetworkInfo.State.UNKNOWN)) {
1657            // Look for an open network
1658            List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
1659            if (scanResults != null) {
1660                int numOpenNetworks = 0;
1661                for (int i = scanResults.size() - 1; i >= 0; i--) {
1662                    ScanResult scanResult = scanResults.get(i);
1663
1664                    //A capability of [ESS] represents an open access point
1665                    //that is available for an STA to connect
1666                    if (scanResult.capabilities != null &&
1667                            scanResult.capabilities.equals("[ESS]")) {
1668                        numOpenNetworks++;
1669                    }
1670                }
1671
1672                if (numOpenNetworks > 0) {
1673                    if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
1674                        /*
1675                         * We've scanned continuously at least
1676                         * NUM_SCANS_BEFORE_NOTIFICATION times. The user
1677                         * probably does not have a remembered network in range,
1678                         * since otherwise supplicant would have tried to
1679                         * associate and thus resetting this counter.
1680                         */
1681                        setNotificationVisible(true, numOpenNetworks, false, 0);
1682                    }
1683                    return;
1684                }
1685            }
1686        }
1687
1688        // No open networks in range, remove the notification
1689        setNotificationVisible(false, 0, false, 0);
1690    }
1691
1692    /**
1693     * Clears variables related to tracking whether a notification has been
1694     * shown recently and clears the current notification.
1695     */
1696    private void resetNotification() {
1697        mNotificationRepeatTime = 0;
1698        mNumScansSinceNetworkStateChange = 0;
1699        setNotificationVisible(false, 0, false, 0);
1700    }
1701
1702    /**
1703     * Display or don't display a notification that there are open Wi-Fi networks.
1704     * @param visible {@code true} if notification should be visible, {@code false} otherwise
1705     * @param numNetworks the number networks seen
1706     * @param force {@code true} to force notification to be shown/not-shown,
1707     * even if it is already shown/not-shown.
1708     * @param delay time in milliseconds after which the notification should be made
1709     * visible or invisible.
1710     */
1711    private void setNotificationVisible(boolean visible, int numNetworks, boolean force,
1712            int delay) {
1713
1714        // Since we use auto cancel on the notification, when the
1715        // mNetworksAvailableNotificationShown is true, the notification may
1716        // have actually been canceled.  However, when it is false we know
1717        // for sure that it is not being shown (it will not be shown any other
1718        // place than here)
1719
1720        // If it should be hidden and it is already hidden, then noop
1721        if (!visible && !mNotificationShown && !force) {
1722            return;
1723        }
1724
1725        NotificationManager notificationManager = (NotificationManager) mContext
1726                .getSystemService(Context.NOTIFICATION_SERVICE);
1727
1728        Message message;
1729        if (visible) {
1730
1731            // Not enough time has passed to show the notification again
1732            if (System.currentTimeMillis() < mNotificationRepeatTime) {
1733                return;
1734            }
1735
1736            if (mNotification == null) {
1737                // Cache the Notification object.
1738                mNotification = new Notification();
1739                mNotification.when = 0;
1740                mNotification.icon = ICON_NETWORKS_AVAILABLE;
1741                mNotification.flags = Notification.FLAG_AUTO_CANCEL;
1742                mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
1743                        new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
1744            }
1745
1746            CharSequence title = mContext.getResources().getQuantityText(
1747                    com.android.internal.R.plurals.wifi_available, numNetworks);
1748            CharSequence details = mContext.getResources().getQuantityText(
1749                    com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
1750            mNotification.tickerText = title;
1751            mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
1752
1753            mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
1754
1755            notificationManager.notify(ICON_NETWORKS_AVAILABLE, mNotification);
1756        } else {
1757            notificationManager.cancel(ICON_NETWORKS_AVAILABLE);
1758        }
1759
1760        mNotificationShown = visible;
1761    }
1762
1763    private class NotificationEnabledSettingObserver extends ContentObserver {
1764
1765        public NotificationEnabledSettingObserver(Handler handler) {
1766            super(handler);
1767        }
1768
1769        public void register() {
1770            ContentResolver cr = mContext.getContentResolver();
1771            cr.registerContentObserver(Settings.Secure.getUriFor(
1772                Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
1773            mNotificationEnabled = getValue();
1774        }
1775
1776        @Override
1777        public void onChange(boolean selfChange) {
1778            super.onChange(selfChange);
1779
1780            mNotificationEnabled = getValue();
1781            resetNotification();
1782        }
1783
1784        private boolean getValue() {
1785            return Settings.Secure.getInt(mContext.getContentResolver(),
1786                    Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
1787        }
1788    }
1789
1790
1791}
1792