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