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