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