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