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