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