WifiService.java revision 0900f3657664d9046e6723825fd32b244eef2b6c
1/*
2 * Copyright (C) 2008 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 static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
20import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
21import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
22import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
23import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
24
25import android.app.AlarmManager;
26import android.app.PendingIntent;
27import android.bluetooth.BluetoothA2dp;
28import android.content.BroadcastReceiver;
29import android.content.ContentResolver;
30import android.content.Context;
31import android.content.Intent;
32import android.content.IntentFilter;
33import android.content.pm.PackageManager;
34import android.net.wifi.IWifiManager;
35import android.net.wifi.WifiInfo;
36import android.net.wifi.WifiManager;
37import android.net.wifi.WifiNative;
38import android.net.wifi.WifiStateTracker;
39import android.net.wifi.ScanResult;
40import android.net.wifi.WifiConfiguration;
41import android.net.wifi.SupplicantState;
42import android.net.NetworkStateTracker;
43import android.net.DhcpInfo;
44import android.net.NetworkUtils;
45import android.os.Binder;
46import android.os.Handler;
47import android.os.HandlerThread;
48import android.os.IBinder;
49import android.os.Looper;
50import android.os.Message;
51import android.os.PowerManager;
52import android.os.Process;
53import android.os.RemoteException;
54import android.os.ServiceManager;
55import android.provider.Settings;
56import android.util.Log;
57import android.text.TextUtils;
58
59import java.util.ArrayList;
60import java.util.BitSet;
61import java.util.HashMap;
62import java.util.LinkedHashMap;
63import java.util.List;
64import java.util.Map;
65import java.util.regex.Pattern;
66import java.io.FileDescriptor;
67import java.io.PrintWriter;
68
69import com.android.internal.app.IBatteryStats;
70import android.backup.IBackupManager;
71import com.android.server.am.BatteryStatsService;
72
73/**
74 * WifiService handles remote WiFi operation requests by implementing
75 * the IWifiManager interface. It also creates a WifiMonitor to listen
76 * for Wifi-related events.
77 *
78 * @hide
79 */
80public class WifiService extends IWifiManager.Stub {
81    private static final String TAG = "WifiService";
82    private static final boolean DBG = false;
83    private static final Pattern scanResultPattern = Pattern.compile("\t+");
84    private final WifiStateTracker mWifiStateTracker;
85
86    private Context mContext;
87    private int mWifiState;
88
89    private AlarmManager mAlarmManager;
90    private PendingIntent mIdleIntent;
91    private static final int IDLE_REQUEST = 0;
92    private boolean mScreenOff;
93    private boolean mDeviceIdle;
94    private int mPluggedType;
95
96    private final LockList mLocks = new LockList();
97    // some wifi lock statistics
98    private int mFullLocksAcquired;
99    private int mFullLocksReleased;
100    private int mScanLocksAcquired;
101    private int mScanLocksReleased;
102
103    private final List<Multicaster> mMulticasters =
104            new ArrayList<Multicaster>();
105    private int mMulticastEnabled;
106    private int mMulticastDisabled;
107
108    private final IBatteryStats mBatteryStats;
109
110    /**
111     * See {@link Settings.Gservices#WIFI_IDLE_MS}. This is the default value if a
112     * Settings.Gservices value is not present. This timeout value is chosen as
113     * the approximate point at which the battery drain caused by Wi-Fi
114     * being enabled but not active exceeds the battery drain caused by
115     * re-establishing a connection to the mobile data network.
116     */
117    private static final long DEFAULT_IDLE_MILLIS = 15 * 60 * 1000; /* 15 minutes */
118
119    private static final String WAKELOCK_TAG = "WifiService";
120
121    /**
122     * The maximum amount of time to hold the wake lock after a disconnect
123     * caused by stopping the driver. Establishing an EDGE connection has been
124     * observed to take about 5 seconds under normal circumstances. This
125     * provides a bit of extra margin.
126     * <p>
127     * See {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS}.
128     * This is the default value if a Settings.Secure value is not present.
129     */
130    private static final int DEFAULT_WAKELOCK_TIMEOUT = 8000;
131
132    // Wake lock used by driver-stop operation
133    private static PowerManager.WakeLock sDriverStopWakeLock;
134    // Wake lock used by other operations
135    private static PowerManager.WakeLock sWakeLock;
136
137    private static final int MESSAGE_ENABLE_WIFI      = 0;
138    private static final int MESSAGE_DISABLE_WIFI     = 1;
139    private static final int MESSAGE_STOP_WIFI        = 2;
140    private static final int MESSAGE_START_WIFI       = 3;
141    private static final int MESSAGE_RELEASE_WAKELOCK = 4;
142
143    private final  WifiHandler mWifiHandler;
144
145    /*
146     * Cache of scan results objects (size is somewhat arbitrary)
147     */
148    private static final int SCAN_RESULT_CACHE_SIZE = 80;
149    private final LinkedHashMap<String, ScanResult> mScanResultCache;
150
151    /*
152     * Character buffer used to parse scan results (optimization)
153     */
154    private static final int SCAN_RESULT_BUFFER_SIZE = 512;
155    private boolean mNeedReconfig;
156
157    /*
158     * Last UID that asked to enable WIFI.
159     */
160    private int mLastEnableUid = Process.myUid();
161
162    /**
163     * Number of allowed radio frequency channels in various regulatory domains.
164     * This list is sufficient for 802.11b/g networks (2.4GHz range).
165     */
166    private static int[] sValidRegulatoryChannelCounts = new int[] {11, 13, 14};
167
168    private static final String ACTION_DEVICE_IDLE =
169            "com.android.server.WifiManager.action.DEVICE_IDLE";
170
171    WifiService(Context context, WifiStateTracker tracker) {
172        mContext = context;
173        mWifiStateTracker = tracker;
174        mBatteryStats = BatteryStatsService.getService();
175
176        mScanResultCache = new LinkedHashMap<String, ScanResult>(
177            SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
178                /*
179                 * Limit the cache size by SCAN_RESULT_CACHE_SIZE
180                 * elements
181                 */
182                public boolean removeEldestEntry(Map.Entry eldest) {
183                    return SCAN_RESULT_CACHE_SIZE < this.size();
184                }
185            };
186
187        HandlerThread wifiThread = new HandlerThread("WifiService");
188        wifiThread.start();
189        mWifiHandler = new WifiHandler(wifiThread.getLooper());
190
191        mWifiState = WIFI_STATE_DISABLED;
192        boolean wifiEnabled = getPersistedWifiEnabled();
193
194        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
195        Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
196        mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
197
198        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
199        sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
200        sDriverStopWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
201        mWifiStateTracker.setReleaseWakeLockCallback(
202                new Runnable() {
203                    public void run() {
204                        mWifiHandler.removeMessages(MESSAGE_RELEASE_WAKELOCK);
205                        synchronized (sDriverStopWakeLock) {
206                            if (sDriverStopWakeLock.isHeld()) {
207                                sDriverStopWakeLock.release();
208                            }
209                        }
210                    }
211                }
212        );
213
214        Log.i(TAG, "WifiService starting up with Wi-Fi " +
215                (wifiEnabled ? "enabled" : "disabled"));
216
217        mContext.registerReceiver(
218                new BroadcastReceiver() {
219                    @Override
220                    public void onReceive(Context context, Intent intent) {
221                        updateWifiState();
222                    }
223                },
224                new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
225
226        setWifiEnabledBlocking(wifiEnabled, false, Process.myUid());
227    }
228
229    private boolean getPersistedWifiEnabled() {
230        final ContentResolver cr = mContext.getContentResolver();
231        try {
232            return Settings.Secure.getInt(cr, Settings.Secure.WIFI_ON) == 1;
233        } catch (Settings.SettingNotFoundException e) {
234            Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, 0);
235            return false;
236        }
237    }
238
239    private void persistWifiEnabled(boolean enabled) {
240        final ContentResolver cr = mContext.getContentResolver();
241        Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0);
242    }
243
244    NetworkStateTracker getNetworkStateTracker() {
245        return mWifiStateTracker;
246    }
247
248    /**
249     * see {@link android.net.wifi.WifiManager#pingSupplicant()}
250     * @return {@code true} if the operation succeeds
251     */
252    public boolean pingSupplicant() {
253        enforceChangePermission();
254        synchronized (mWifiStateTracker) {
255            return WifiNative.pingCommand();
256        }
257    }
258
259    /**
260     * see {@link android.net.wifi.WifiManager#startScan()}
261     * @return {@code true} if the operation succeeds
262     */
263    public boolean startScan(boolean forceActive) {
264        enforceChangePermission();
265        synchronized (mWifiStateTracker) {
266            switch (mWifiStateTracker.getSupplicantState()) {
267                case DISCONNECTED:
268                case INACTIVE:
269                case SCANNING:
270                case DORMANT:
271                    break;
272                default:
273                    WifiNative.setScanResultHandlingCommand(
274                            WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);
275                    break;
276            }
277            return WifiNative.scanCommand(forceActive);
278        }
279    }
280
281    /**
282     * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
283     * @param enable {@code true} to enable, {@code false} to disable.
284     * @return {@code true} if the enable/disable operation was
285     *         started or is already in the queue.
286     */
287    public boolean setWifiEnabled(boolean enable) {
288        enforceChangePermission();
289        if (mWifiHandler == null) return false;
290
291        synchronized (mWifiHandler) {
292            sWakeLock.acquire();
293            mLastEnableUid = Binder.getCallingUid();
294            sendEnableMessage(enable, true, Binder.getCallingUid());
295        }
296
297        return true;
298    }
299
300    /**
301     * Enables/disables Wi-Fi synchronously.
302     * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off.
303     * @param persist {@code true} if the setting should be persisted.
304     * @param uid The UID of the process making the request.
305     * @return {@code true} if the operation succeeds (or if the existing state
306     *         is the same as the requested state)
307     */
308    private boolean setWifiEnabledBlocking(boolean enable, boolean persist, int uid) {
309        final int eventualWifiState = enable ? WIFI_STATE_ENABLED : WIFI_STATE_DISABLED;
310
311        if (mWifiState == eventualWifiState) {
312            return true;
313        }
314        if (enable && isAirplaneModeOn()) {
315            return false;
316        }
317
318        setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING, uid);
319
320        if (enable) {
321            if (!WifiNative.loadDriver()) {
322                Log.e(TAG, "Failed to load Wi-Fi driver.");
323                setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
324                return false;
325            }
326            if (!WifiNative.startSupplicant()) {
327                WifiNative.unloadDriver();
328                Log.e(TAG, "Failed to start supplicant daemon.");
329                setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
330                return false;
331            }
332            registerForBroadcasts();
333            mWifiStateTracker.startEventLoop();
334        } else {
335
336            mContext.unregisterReceiver(mReceiver);
337           // Remove notification (it will no-op if it isn't visible)
338            mWifiStateTracker.setNotificationVisible(false, 0, false, 0);
339
340            boolean failedToStopSupplicantOrUnloadDriver = false;
341            if (!WifiNative.stopSupplicant()) {
342                Log.e(TAG, "Failed to stop supplicant daemon.");
343                setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
344                failedToStopSupplicantOrUnloadDriver = true;
345            }
346
347            // We must reset the interface before we unload the driver
348            mWifiStateTracker.resetInterface(false);
349
350            if (!WifiNative.unloadDriver()) {
351                Log.e(TAG, "Failed to unload Wi-Fi driver.");
352                if (!failedToStopSupplicantOrUnloadDriver) {
353                    setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
354                    failedToStopSupplicantOrUnloadDriver = true;
355                }
356            }
357            if (failedToStopSupplicantOrUnloadDriver) {
358                return false;
359            }
360        }
361
362        // Success!
363
364        if (persist) {
365            persistWifiEnabled(enable);
366        }
367        setWifiEnabledState(eventualWifiState, uid);
368
369        /*
370         * Initialize the number of allowed radio channels if Wi-Fi is being turned on.
371         */
372        if (enable) {
373            mWifiStateTracker.setNumAllowedChannels();
374        }
375
376        return true;
377    }
378
379    private void setWifiEnabledState(int wifiState, int uid) {
380        final int previousWifiState = mWifiState;
381
382        long ident = Binder.clearCallingIdentity();
383        try {
384            if (wifiState == WIFI_STATE_ENABLED) {
385                mBatteryStats.noteWifiOn(uid);
386            } else if (wifiState == WIFI_STATE_DISABLED) {
387                mBatteryStats.noteWifiOff(uid);
388            }
389        } catch (RemoteException e) {
390        } finally {
391            Binder.restoreCallingIdentity(ident);
392        }
393
394        // Update state
395        mWifiState = wifiState;
396
397        // Broadcast
398        final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
399        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
400        intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
401        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
402        mContext.sendStickyBroadcast(intent);
403    }
404
405    private void enforceAccessPermission() {
406        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
407                                                "WifiService");
408    }
409
410    private void enforceChangePermission() {
411        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
412                                                "WifiService");
413
414    }
415
416    private void enforceMulticastChangePermission() {
417        mContext.enforceCallingOrSelfPermission(
418                android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
419                "WifiService");
420    }
421
422    /**
423     * see {@link WifiManager#getWifiState()}
424     * @return One of {@link WifiManager#WIFI_STATE_DISABLED},
425     *         {@link WifiManager#WIFI_STATE_DISABLING},
426     *         {@link WifiManager#WIFI_STATE_ENABLED},
427     *         {@link WifiManager#WIFI_STATE_ENABLING},
428     *         {@link WifiManager#WIFI_STATE_UNKNOWN}
429     */
430    public int getWifiEnabledState() {
431        enforceAccessPermission();
432        return mWifiState;
433    }
434
435    /**
436     * see {@link android.net.wifi.WifiManager#disconnect()}
437     * @return {@code true} if the operation succeeds
438     */
439    public boolean disconnect() {
440        enforceChangePermission();
441        synchronized (mWifiStateTracker) {
442            return WifiNative.disconnectCommand();
443        }
444    }
445
446    /**
447     * see {@link android.net.wifi.WifiManager#reconnect()}
448     * @return {@code true} if the operation succeeds
449     */
450    public boolean reconnect() {
451        enforceChangePermission();
452        synchronized (mWifiStateTracker) {
453            return WifiNative.reconnectCommand();
454        }
455    }
456
457    /**
458     * see {@link android.net.wifi.WifiManager#reassociate()}
459     * @return {@code true} if the operation succeeds
460     */
461    public boolean reassociate() {
462        enforceChangePermission();
463        synchronized (mWifiStateTracker) {
464            return WifiNative.reassociateCommand();
465        }
466    }
467
468    /**
469     * see {@link android.net.wifi.WifiManager#getConfiguredNetworks()}
470     * @return the list of configured networks
471     */
472    public List<WifiConfiguration> getConfiguredNetworks() {
473        enforceAccessPermission();
474        String listStr;
475        /*
476         * We don't cache the list, because we want to allow
477         * for the possibility that the configuration file
478         * has been modified through some external means,
479         * such as the wpa_cli command line program.
480         */
481        synchronized (mWifiStateTracker) {
482            listStr = WifiNative.listNetworksCommand();
483        }
484        List<WifiConfiguration> networks =
485            new ArrayList<WifiConfiguration>();
486        if (listStr == null)
487            return networks;
488
489        String[] lines = listStr.split("\n");
490        // Skip the first line, which is a header
491       for (int i = 1; i < lines.length; i++) {
492           String[] result = lines[i].split("\t");
493           // network-id | ssid | bssid | flags
494           WifiConfiguration config = new WifiConfiguration();
495           try {
496               config.networkId = Integer.parseInt(result[0]);
497           } catch(NumberFormatException e) {
498               continue;
499           }
500           if (result.length > 3) {
501               if (result[3].indexOf("[CURRENT]") != -1)
502                   config.status = WifiConfiguration.Status.CURRENT;
503               else if (result[3].indexOf("[DISABLED]") != -1)
504                   config.status = WifiConfiguration.Status.DISABLED;
505               else
506                   config.status = WifiConfiguration.Status.ENABLED;
507           } else
508               config.status = WifiConfiguration.Status.ENABLED;
509           synchronized (mWifiStateTracker) {
510               readNetworkVariables(config);
511           }
512           networks.add(config);
513       }
514
515        return networks;
516    }
517
518    /**
519     * Read the variables from the supplicant daemon that are needed to
520     * fill in the WifiConfiguration object.
521     * <p/>
522     * The caller must hold the synchronization monitor.
523     * @param config the {@link WifiConfiguration} object to be filled in.
524     */
525    private static void readNetworkVariables(WifiConfiguration config) {
526
527        int netId = config.networkId;
528        if (netId < 0)
529            return;
530
531        /*
532         * TODO: maybe should have a native method that takes an array of
533         * variable names and returns an array of values. But we'd still
534         * be doing a round trip to the supplicant daemon for each variable.
535         */
536        String value;
537
538        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
539        if (!TextUtils.isEmpty(value)) {
540            config.SSID = value;
541        } else {
542            config.SSID = null;
543        }
544
545        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName);
546        if (!TextUtils.isEmpty(value)) {
547            config.BSSID = value;
548        } else {
549            config.BSSID = null;
550        }
551
552        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
553        config.priority = -1;
554        if (!TextUtils.isEmpty(value)) {
555            try {
556                config.priority = Integer.parseInt(value);
557            } catch (NumberFormatException ignore) {
558            }
559        }
560
561        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName);
562        config.hiddenSSID = false;
563        if (!TextUtils.isEmpty(value)) {
564            try {
565                config.hiddenSSID = Integer.parseInt(value) != 0;
566            } catch (NumberFormatException ignore) {
567            }
568        }
569
570        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName);
571        config.wepTxKeyIndex = -1;
572        if (!TextUtils.isEmpty(value)) {
573            try {
574                config.wepTxKeyIndex = Integer.parseInt(value);
575            } catch (NumberFormatException ignore) {
576            }
577        }
578
579        /*
580         * Get up to 4 WEP keys. Note that the actual keys are not passed back,
581         * just a "*" if the key is set, or the null string otherwise.
582         */
583        for (int i = 0; i < 4; i++) {
584            value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepKeyVarNames[i]);
585            if (!TextUtils.isEmpty(value)) {
586                config.wepKeys[i] = value;
587            } else {
588                config.wepKeys[i] = null;
589            }
590        }
591
592        /*
593         * Get the private shared key. Note that the actual keys are not passed back,
594         * just a "*" if the key is set, or the null string otherwise.
595         */
596        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName);
597        if (!TextUtils.isEmpty(value)) {
598            config.preSharedKey = value;
599        } else {
600            config.preSharedKey = null;
601        }
602
603        value = WifiNative.getNetworkVariableCommand(config.networkId,
604                WifiConfiguration.Protocol.varName);
605        if (!TextUtils.isEmpty(value)) {
606            String vals[] = value.split(" ");
607            for (String val : vals) {
608                int index =
609                    lookupString(val, WifiConfiguration.Protocol.strings);
610                if (0 <= index) {
611                    config.allowedProtocols.set(index);
612                }
613            }
614        }
615
616        value = WifiNative.getNetworkVariableCommand(config.networkId,
617                WifiConfiguration.KeyMgmt.varName);
618        if (!TextUtils.isEmpty(value)) {
619            String vals[] = value.split(" ");
620            for (String val : vals) {
621                int index =
622                    lookupString(val, WifiConfiguration.KeyMgmt.strings);
623                if (0 <= index) {
624                    config.allowedKeyManagement.set(index);
625                }
626            }
627        }
628
629        value = WifiNative.getNetworkVariableCommand(config.networkId,
630                WifiConfiguration.AuthAlgorithm.varName);
631        if (!TextUtils.isEmpty(value)) {
632            String vals[] = value.split(" ");
633            for (String val : vals) {
634                int index =
635                    lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
636                if (0 <= index) {
637                    config.allowedAuthAlgorithms.set(index);
638                }
639            }
640        }
641
642        value = WifiNative.getNetworkVariableCommand(config.networkId,
643                WifiConfiguration.PairwiseCipher.varName);
644        if (!TextUtils.isEmpty(value)) {
645            String vals[] = value.split(" ");
646            for (String val : vals) {
647                int index =
648                    lookupString(val, WifiConfiguration.PairwiseCipher.strings);
649                if (0 <= index) {
650                    config.allowedPairwiseCiphers.set(index);
651                }
652            }
653        }
654
655        value = WifiNative.getNetworkVariableCommand(config.networkId,
656                WifiConfiguration.GroupCipher.varName);
657        if (!TextUtils.isEmpty(value)) {
658            String vals[] = value.split(" ");
659            for (String val : vals) {
660                int index =
661                    lookupString(val, WifiConfiguration.GroupCipher.strings);
662                if (0 <= index) {
663                    config.allowedGroupCiphers.set(index);
664                }
665            }
666        }
667    }
668
669    /**
670     * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)}
671     * @return the supplicant-assigned identifier for the new or updated
672     * network if the operation succeeds, or {@code -1} if it fails
673     */
674    public synchronized int addOrUpdateNetwork(WifiConfiguration config) {
675        enforceChangePermission();
676        /*
677         * If the supplied networkId is -1, we create a new empty
678         * network configuration. Otherwise, the networkId should
679         * refer to an existing configuration.
680         */
681        int netId = config.networkId;
682        boolean newNetwork = netId == -1;
683        boolean doReconfig;
684        int currentPriority;
685        // networkId of -1 means we want to create a new network
686        if (newNetwork) {
687            netId = WifiNative.addNetworkCommand();
688            if (netId < 0) {
689                if (DBG) {
690                    Log.d(TAG, "Failed to add a network!");
691                }
692                return -1;
693            }
694            doReconfig = true;
695        } else {
696            String priorityVal = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
697            currentPriority = -1;
698            if (!TextUtils.isEmpty(priorityVal)) {
699                try {
700                    currentPriority = Integer.parseInt(priorityVal);
701                } catch (NumberFormatException ignore) {
702                }
703            }
704            doReconfig = currentPriority != config.priority;
705        }
706        mNeedReconfig = mNeedReconfig || doReconfig;
707
708        setVariables: {
709            /*
710             * Note that if a networkId for a non-existent network
711             * was supplied, then the first setNetworkVariableCommand()
712             * will fail, so we don't bother to make a separate check
713             * for the validity of the ID up front.
714             */
715
716            if (config.SSID != null &&
717                !WifiNative.setNetworkVariableCommand(
718                    netId,
719                    WifiConfiguration.ssidVarName,
720                    config.SSID)) {
721                if (DBG) {
722                    Log.d(TAG, "failed to set SSID: "+config.SSID);
723                }
724                break setVariables;
725            }
726
727            if (config.BSSID != null &&
728                !WifiNative.setNetworkVariableCommand(
729                    netId,
730                    WifiConfiguration.bssidVarName,
731                    config.BSSID)) {
732                if (DBG) {
733                    Log.d(TAG, "failed to set BSSID: "+config.BSSID);
734                }
735                break setVariables;
736            }
737
738            String allowedKeyManagementString =
739                makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
740            if (config.allowedKeyManagement.cardinality() != 0 &&
741                !WifiNative.setNetworkVariableCommand(
742                    netId,
743                    WifiConfiguration.KeyMgmt.varName,
744                    allowedKeyManagementString)) {
745                if (DBG) {
746                    Log.d(TAG, "failed to set key_mgmt: "+
747                          allowedKeyManagementString);
748                }
749                break setVariables;
750            }
751
752            String allowedProtocolsString =
753                makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
754            if (config.allowedProtocols.cardinality() != 0 &&
755                !WifiNative.setNetworkVariableCommand(
756                    netId,
757                    WifiConfiguration.Protocol.varName,
758                    allowedProtocolsString)) {
759                if (DBG) {
760                    Log.d(TAG, "failed to set proto: "+
761                          allowedProtocolsString);
762                }
763                break setVariables;
764            }
765
766            String allowedAuthAlgorithmsString =
767                makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
768            if (config.allowedAuthAlgorithms.cardinality() != 0 &&
769                !WifiNative.setNetworkVariableCommand(
770                    netId,
771                    WifiConfiguration.AuthAlgorithm.varName,
772                    allowedAuthAlgorithmsString)) {
773                if (DBG) {
774                    Log.d(TAG, "failed to set auth_alg: "+
775                          allowedAuthAlgorithmsString);
776                }
777                break setVariables;
778            }
779
780            String allowedPairwiseCiphersString =
781                makeString(config.allowedPairwiseCiphers, WifiConfiguration.PairwiseCipher.strings);
782            if (config.allowedPairwiseCiphers.cardinality() != 0 &&
783                !WifiNative.setNetworkVariableCommand(
784                    netId,
785                    WifiConfiguration.PairwiseCipher.varName,
786                    allowedPairwiseCiphersString)) {
787                if (DBG) {
788                    Log.d(TAG, "failed to set pairwise: "+
789                          allowedPairwiseCiphersString);
790                }
791                break setVariables;
792            }
793
794            String allowedGroupCiphersString =
795                makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
796            if (config.allowedGroupCiphers.cardinality() != 0 &&
797                !WifiNative.setNetworkVariableCommand(
798                    netId,
799                    WifiConfiguration.GroupCipher.varName,
800                    allowedGroupCiphersString)) {
801                if (DBG) {
802                    Log.d(TAG, "failed to set group: "+
803                          allowedGroupCiphersString);
804                }
805                break setVariables;
806            }
807
808            // Prevent client screw-up by passing in a WifiConfiguration we gave it
809            // by preventing "*" as a key.
810            if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
811                !WifiNative.setNetworkVariableCommand(
812                    netId,
813                    WifiConfiguration.pskVarName,
814                    config.preSharedKey)) {
815                if (DBG) {
816                    Log.d(TAG, "failed to set psk: "+config.preSharedKey);
817                }
818                break setVariables;
819            }
820
821            boolean hasSetKey = false;
822            if (config.wepKeys != null) {
823                for (int i = 0; i < config.wepKeys.length; i++) {
824                    // Prevent client screw-up by passing in a WifiConfiguration we gave it
825                    // by preventing "*" as a key.
826                    if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
827                        if (!WifiNative.setNetworkVariableCommand(
828                                netId,
829                                WifiConfiguration.wepKeyVarNames[i],
830                                config.wepKeys[i])) {
831                            if (DBG) {
832                                Log.d(TAG,
833                                      "failed to set wep_key"+i+": " +
834                                      config.wepKeys[i]);
835                            }
836                            break setVariables;
837                        }
838                        hasSetKey = true;
839                    }
840                }
841            }
842
843            if (hasSetKey) {
844                if (!WifiNative.setNetworkVariableCommand(
845                        netId,
846                        WifiConfiguration.wepTxKeyIdxVarName,
847                        Integer.toString(config.wepTxKeyIndex))) {
848                    if (DBG) {
849                        Log.d(TAG,
850                              "failed to set wep_tx_keyidx: "+
851                              config.wepTxKeyIndex);
852                    }
853                    break setVariables;
854                }
855            }
856
857            if (!WifiNative.setNetworkVariableCommand(
858                    netId,
859                    WifiConfiguration.priorityVarName,
860                    Integer.toString(config.priority))) {
861                if (DBG) {
862                    Log.d(TAG, config.SSID + ": failed to set priority: "
863                          +config.priority);
864                }
865                break setVariables;
866            }
867
868            if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand(
869                    netId,
870                    WifiConfiguration.hiddenSSIDVarName,
871                    Integer.toString(config.hiddenSSID ? 1 : 0))) {
872                if (DBG) {
873                    Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+
874                          config.hiddenSSID);
875                }
876                break setVariables;
877            }
878
879            if ((config.eap != null) && !WifiNative.setNetworkVariableCommand(
880                    netId,
881                    WifiConfiguration.eapVarName,
882                    config.eap)) {
883                if (DBG) {
884                    Log.d(TAG, config.SSID + ": failed to set eap: "+
885                          config.eap);
886                }
887                break setVariables;
888            }
889
890            if ((config.identity != null) && !WifiNative.setNetworkVariableCommand(
891                    netId,
892                    WifiConfiguration.identityVarName,
893                    config.identity)) {
894                if (DBG) {
895                    Log.d(TAG, config.SSID + ": failed to set identity: "+
896                          config.identity);
897                }
898                break setVariables;
899            }
900
901            if ((config.anonymousIdentity != null) && !WifiNative.setNetworkVariableCommand(
902                    netId,
903                    WifiConfiguration.anonymousIdentityVarName,
904                    config.anonymousIdentity)) {
905                if (DBG) {
906                    Log.d(TAG, config.SSID + ": failed to set anonymousIdentity: "+
907                          config.anonymousIdentity);
908                }
909                break setVariables;
910            }
911
912            if ((config.password != null) && !WifiNative.setNetworkVariableCommand(
913                    netId,
914                    WifiConfiguration.passwordVarName,
915                    config.password)) {
916                if (DBG) {
917                    Log.d(TAG, config.SSID + ": failed to set password: "+
918                          config.password);
919                }
920                break setVariables;
921            }
922
923            if ((config.clientCert != null) && !WifiNative.setNetworkVariableCommand(
924                    netId,
925                    WifiConfiguration.clientCertVarName,
926                    config.clientCert)) {
927                if (DBG) {
928                    Log.d(TAG, config.SSID + ": failed to set clientCert: "+
929                          config.clientCert);
930                }
931                break setVariables;
932            }
933
934            if ((config.caCert != null) && !WifiNative.setNetworkVariableCommand(
935                    netId,
936                    WifiConfiguration.caCertVarName,
937                    config.caCert)) {
938                if (DBG) {
939                    Log.d(TAG, config.SSID + ": failed to set caCert: "+
940                          config.caCert);
941                }
942                break setVariables;
943            }
944
945            if ((config.privateKey != null) && !WifiNative.setNetworkVariableCommand(
946                    netId,
947                    WifiConfiguration.privateKeyVarName,
948                    config.privateKey)) {
949                if (DBG) {
950                    Log.d(TAG, config.SSID + ": failed to set privateKey: "+
951                          config.privateKey);
952                }
953                break setVariables;
954            }
955
956            if ((config.privateKeyPasswd != null) && !WifiNative.setNetworkVariableCommand(
957                    netId,
958                    WifiConfiguration.privateKeyPasswdVarName,
959                    config.privateKeyPasswd)) {
960                if (DBG) {
961                    Log.d(TAG, config.SSID + ": failed to set privateKeyPasswd: "+
962                          config.privateKeyPasswd);
963                }
964                break setVariables;
965            }
966
967            return netId;
968        }
969
970        /*
971         * For an update, if one of the setNetworkVariable operations fails,
972         * we might want to roll back all the changes already made. But the
973         * chances are that if anything is going to go wrong, it'll happen
974         * the first time we try to set one of the variables.
975         */
976        if (newNetwork) {
977            removeNetwork(netId);
978            if (DBG) {
979                Log.d(TAG,
980                      "Failed to set a network variable, removed network: "
981                      + netId);
982            }
983        }
984        return -1;
985    }
986
987    private static String makeString(BitSet set, String[] strings) {
988        StringBuffer buf = new StringBuffer();
989        int nextSetBit = -1;
990
991        /* Make sure all set bits are in [0, strings.length) to avoid
992         * going out of bounds on strings.  (Shouldn't happen, but...) */
993        set = set.get(0, strings.length);
994
995        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
996            buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
997        }
998
999        // remove trailing space
1000        if (set.cardinality() > 0) {
1001            buf.setLength(buf.length() - 1);
1002        }
1003
1004        return buf.toString();
1005    }
1006
1007    private static int lookupString(String string, String[] strings) {
1008        int size = strings.length;
1009
1010        string = string.replace('-', '_');
1011
1012        for (int i = 0; i < size; i++)
1013            if (string.equals(strings[i]))
1014                return i;
1015
1016        if (DBG) {
1017            // if we ever get here, we should probably add the
1018            // value to WifiConfiguration to reflect that it's
1019            // supported by the WPA supplicant
1020            Log.w(TAG, "Failed to look-up a string: " + string);
1021        }
1022
1023        return -1;
1024    }
1025
1026    /**
1027     * See {@link android.net.wifi.WifiManager#removeNetwork(int)}
1028     * @param netId the integer that identifies the network configuration
1029     * to the supplicant
1030     * @return {@code true} if the operation succeeded
1031     */
1032    public boolean removeNetwork(int netId) {
1033        enforceChangePermission();
1034
1035        return mWifiStateTracker.removeNetwork(netId);
1036    }
1037
1038    /**
1039     * See {@link android.net.wifi.WifiManager#enableNetwork(int, boolean)}
1040     * @param netId the integer that identifies the network configuration
1041     * to the supplicant
1042     * @param disableOthers if true, disable all other networks.
1043     * @return {@code true} if the operation succeeded
1044     */
1045    public boolean enableNetwork(int netId, boolean disableOthers) {
1046        enforceChangePermission();
1047
1048        synchronized (mWifiStateTracker) {
1049            String ifname = mWifiStateTracker.getInterfaceName();
1050            NetworkUtils.enableInterface(ifname);
1051            boolean result = WifiNative.enableNetworkCommand(netId, disableOthers);
1052            if (!result) {
1053                NetworkUtils.disableInterface(ifname);
1054            }
1055            return result;
1056        }
1057    }
1058
1059    /**
1060     * See {@link android.net.wifi.WifiManager#disableNetwork(int)}
1061     * @param netId the integer that identifies the network configuration
1062     * to the supplicant
1063     * @return {@code true} if the operation succeeded
1064     */
1065    public boolean disableNetwork(int netId) {
1066        enforceChangePermission();
1067
1068        synchronized (mWifiStateTracker) {
1069            return WifiNative.disableNetworkCommand(netId);
1070        }
1071    }
1072
1073    /**
1074     * See {@link android.net.wifi.WifiManager#getConnectionInfo()}
1075     * @return the Wi-Fi information, contained in {@link WifiInfo}.
1076     */
1077    public WifiInfo getConnectionInfo() {
1078        enforceAccessPermission();
1079        /*
1080         * Make sure we have the latest information, by sending
1081         * a status request to the supplicant.
1082         */
1083        return mWifiStateTracker.requestConnectionInfo();
1084    }
1085
1086    /**
1087     * Return the results of the most recent access point scan, in the form of
1088     * a list of {@link ScanResult} objects.
1089     * @return the list of results
1090     */
1091    public List<ScanResult> getScanResults() {
1092        enforceAccessPermission();
1093        String reply;
1094        synchronized (mWifiStateTracker) {
1095            reply = WifiNative.scanResultsCommand();
1096        }
1097        if (reply == null) {
1098            return null;
1099        }
1100
1101        List<ScanResult> scanList = new ArrayList<ScanResult>();
1102
1103        int lineCount = 0;
1104
1105        int replyLen = reply.length();
1106        // Parse the result string, keeping in mind that the last line does
1107        // not end with a newline.
1108        for (int lineBeg = 0, lineEnd = 0; lineEnd <= replyLen; ++lineEnd) {
1109            if (lineEnd == replyLen || reply.charAt(lineEnd) == '\n') {
1110                ++lineCount;
1111                /*
1112                 * Skip the first line, which is a header
1113                 */
1114                if (lineCount == 1) {
1115                    lineBeg = lineEnd + 1;
1116                    continue;
1117                }
1118                if (lineEnd > lineBeg) {
1119                    String line = reply.substring(lineBeg, lineEnd);
1120                    ScanResult scanResult = parseScanResult(line);
1121                    if (scanResult != null) {
1122                        scanList.add(scanResult);
1123                    } else if (DBG) {
1124                        Log.w(TAG, "misformatted scan result for: " + line);
1125                    }
1126                }
1127                lineBeg = lineEnd + 1;
1128            }
1129        }
1130        mWifiStateTracker.setScanResultsList(scanList);
1131        return scanList;
1132    }
1133
1134    /**
1135     * Parse the scan result line passed to us by wpa_supplicant (helper).
1136     * @param line the line to parse
1137     * @return the {@link ScanResult} object
1138     */
1139    private ScanResult parseScanResult(String line) {
1140        ScanResult scanResult = null;
1141        if (line != null) {
1142            /*
1143             * Cache implementation (LinkedHashMap) is not synchronized, thus,
1144             * must synchronized here!
1145             */
1146            synchronized (mScanResultCache) {
1147                String[] result = scanResultPattern.split(line);
1148                if (3 <= result.length && result.length <= 5) {
1149                    String bssid = result[0];
1150                    // bssid | frequency | level | flags | ssid
1151                    int frequency;
1152                    int level;
1153                    try {
1154                        frequency = Integer.parseInt(result[1]);
1155                        level = Integer.parseInt(result[2]);
1156                        /* some implementations avoid negative values by adding 256
1157                         * so we need to adjust for that here.
1158                         */
1159                        if (level > 0) level -= 256;
1160                    } catch (NumberFormatException e) {
1161                        frequency = 0;
1162                        level = 0;
1163                    }
1164
1165                    /*
1166                     * The formatting of the results returned by
1167                     * wpa_supplicant is intended to make the fields
1168                     * line up nicely when printed,
1169                     * not to make them easy to parse. So we have to
1170                     * apply some heuristics to figure out which field
1171                     * is the SSID and which field is the flags.
1172                     */
1173                    String ssid;
1174                    String flags;
1175                    if (result.length == 4) {
1176                        if (result[3].charAt(0) == '[') {
1177                            flags = result[3];
1178                            ssid = "";
1179                        } else {
1180                            flags = "";
1181                            ssid = result[3];
1182                        }
1183                    } else if (result.length == 5) {
1184                        flags = result[3];
1185                        ssid = result[4];
1186                    } else {
1187                        // Here, we must have 3 fields: no flags and ssid
1188                        // set
1189                        flags = "";
1190                        ssid = "";
1191                    }
1192
1193                    // bssid is the hash key
1194                    scanResult = mScanResultCache.get(bssid);
1195                    if (scanResult != null) {
1196                        scanResult.level = level;
1197                        scanResult.SSID = ssid;
1198                        scanResult.capabilities = flags;
1199                        scanResult.frequency = frequency;
1200                    } else {
1201                        // Do not add scan results that have no SSID set
1202                        if (0 < ssid.trim().length()) {
1203                            scanResult =
1204                                new ScanResult(
1205                                    ssid, bssid, flags, level, frequency);
1206                            mScanResultCache.put(bssid, scanResult);
1207                        }
1208                    }
1209                } else {
1210                    Log.w(TAG, "Misformatted scan result text with " +
1211                          result.length + " fields: " + line);
1212                }
1213            }
1214        }
1215
1216        return scanResult;
1217    }
1218
1219    /**
1220     * Parse the "flags" field passed back in a scan result by wpa_supplicant,
1221     * and construct a {@code WifiConfiguration} that describes the encryption,
1222     * key management, and authenticaion capabilities of the access point.
1223     * @param flags the string returned by wpa_supplicant
1224     * @return the {@link WifiConfiguration} object, filled in
1225     */
1226    WifiConfiguration parseScanFlags(String flags) {
1227        WifiConfiguration config = new WifiConfiguration();
1228
1229        if (flags.length() == 0) {
1230            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
1231        }
1232        // ... to be implemented
1233        return config;
1234    }
1235
1236    /**
1237     * Tell the supplicant to persist the current list of configured networks.
1238     * @return {@code true} if the operation succeeded
1239     */
1240    public boolean saveConfiguration() {
1241        boolean result;
1242        enforceChangePermission();
1243        synchronized (mWifiStateTracker) {
1244            result = WifiNative.saveConfigCommand();
1245            if (result && mNeedReconfig) {
1246                mNeedReconfig = false;
1247                result = WifiNative.reloadConfigCommand();
1248
1249                if (result) {
1250                    Intent intent = new Intent(WifiManager.NETWORK_IDS_CHANGED_ACTION);
1251                    mContext.sendBroadcast(intent);
1252                }
1253            }
1254        }
1255        // Inform the backup manager about a data change
1256        IBackupManager ibm = IBackupManager.Stub.asInterface(
1257                ServiceManager.getService(Context.BACKUP_SERVICE));
1258        if (ibm != null) {
1259            try {
1260                ibm.dataChanged("com.android.providers.settings");
1261            } catch (Exception e) {
1262                // Try again later
1263            }
1264        }
1265        return result;
1266    }
1267
1268    /**
1269     * Set the number of radio frequency channels that are allowed to be used
1270     * in the current regulatory domain. This method should be used only
1271     * if the correct number of channels cannot be determined automatically
1272     * for some reason. If the operation is successful, the new value may be
1273     * persisted as a Secure setting.
1274     * @param numChannels the number of allowed channels. Must be greater than 0
1275     * and less than or equal to 16.
1276     * @param persist {@code true} if the setting should be remembered.
1277     * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
1278     * {@code numChannels} is outside the valid range.
1279     */
1280    public boolean setNumAllowedChannels(int numChannels, boolean persist) {
1281        Log.i(TAG, "WifiService trying to setNumAllowed to "+numChannels+
1282                " with persist set to "+persist);
1283        enforceChangePermission();
1284        /*
1285         * Validate the argument. We'd like to let the Wi-Fi driver do this,
1286         * but if Wi-Fi isn't currently enabled, that's not possible, and
1287         * we want to persist the setting anyway,so that it will take
1288         * effect when Wi-Fi does become enabled.
1289         */
1290        boolean found = false;
1291        for (int validChan : sValidRegulatoryChannelCounts) {
1292            if (validChan == numChannels) {
1293                found = true;
1294                break;
1295            }
1296        }
1297        if (!found) {
1298            return false;
1299        }
1300
1301        if (persist) {
1302            Settings.Secure.putInt(mContext.getContentResolver(),
1303                                   Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
1304                                   numChannels);
1305        }
1306        mWifiStateTracker.setNumAllowedChannels(numChannels);
1307        return true;
1308    }
1309
1310    /**
1311     * Return the number of frequency channels that are allowed
1312     * to be used in the current regulatory domain.
1313     * @return the number of allowed channels, or {@code -1} if an error occurs
1314     */
1315    public int getNumAllowedChannels() {
1316        int numChannels;
1317
1318        enforceAccessPermission();
1319        synchronized (mWifiStateTracker) {
1320            /*
1321             * If we can't get the value from the driver (e.g., because
1322             * Wi-Fi is not currently enabled), get the value from
1323             * Settings.
1324             */
1325            numChannels = WifiNative.getNumAllowedChannelsCommand();
1326            if (numChannels < 0) {
1327                numChannels = Settings.Secure.getInt(mContext.getContentResolver(),
1328                                                     Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
1329                                                     -1);
1330            }
1331        }
1332        return numChannels;
1333    }
1334
1335    /**
1336     * Return the list of valid values for the number of allowed radio channels
1337     * for various regulatory domains.
1338     * @return the list of channel counts
1339     */
1340    public int[] getValidChannelCounts() {
1341        enforceAccessPermission();
1342        return sValidRegulatoryChannelCounts;
1343    }
1344
1345    /**
1346     * Return the DHCP-assigned addresses from the last successful DHCP request,
1347     * if any.
1348     * @return the DHCP information
1349     */
1350    public DhcpInfo getDhcpInfo() {
1351        enforceAccessPermission();
1352        return mWifiStateTracker.getDhcpInfo();
1353    }
1354
1355    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1356        @Override
1357        public void onReceive(Context context, Intent intent) {
1358            String action = intent.getAction();
1359
1360            long idleMillis = Settings.Gservices.getLong(mContext.getContentResolver(),
1361                                                  Settings.Gservices.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS);
1362            int stayAwakeConditions =
1363                    Settings.System.getInt(mContext.getContentResolver(),
1364                                           Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
1365            if (action.equals(Intent.ACTION_SCREEN_ON)) {
1366                Log.d(TAG, "ACTION_SCREEN_ON");
1367                mAlarmManager.cancel(mIdleIntent);
1368                mDeviceIdle = false;
1369                mScreenOff = false;
1370            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1371                Log.d(TAG, "ACTION_SCREEN_OFF");
1372                mScreenOff = true;
1373                /*
1374                 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
1375                 * AND the "stay on while plugged in" setting doesn't match the
1376                 * current power conditions (i.e, not plugged in, plugged in to USB,
1377                 * or plugged in to AC).
1378                 */
1379                if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
1380                    WifiInfo info = mWifiStateTracker.requestConnectionInfo();
1381                    if (info.getSupplicantState() != SupplicantState.COMPLETED) {
1382                        // do not keep Wifi awake when screen is off if Wifi is not associated
1383                        mDeviceIdle = true;
1384                        updateWifiState();
1385                    } else {
1386                        long triggerTime = System.currentTimeMillis() + idleMillis;
1387                        Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
1388                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
1389                    }
1390                }
1391                /* we can return now -- there's nothing to do until we get the idle intent back */
1392                return;
1393            } else if (action.equals(ACTION_DEVICE_IDLE)) {
1394                Log.d(TAG, "got ACTION_DEVICE_IDLE");
1395                mDeviceIdle = true;
1396            } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
1397                /*
1398                 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
1399                 * AND we are transitioning from a state in which the device was supposed
1400                 * to stay awake to a state in which it is not supposed to stay awake.
1401                 * If "stay awake" state is not changing, we do nothing, to avoid resetting
1402                 * the already-set timer.
1403                 */
1404                int pluggedType = intent.getIntExtra("plugged", 0);
1405                Log.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType);
1406                if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) &&
1407                        !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) {
1408                    long triggerTime = System.currentTimeMillis() + idleMillis;
1409                    Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
1410                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
1411                    mPluggedType = pluggedType;
1412                    return;
1413                }
1414                mPluggedType = pluggedType;
1415            } else if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) {
1416                boolean isBluetoothPlaying =
1417                        intent.getIntExtra(
1418                                BluetoothA2dp.SINK_STATE,
1419                                BluetoothA2dp.STATE_DISCONNECTED) == BluetoothA2dp.STATE_PLAYING;
1420                mWifiStateTracker.setBluetoothScanMode(isBluetoothPlaying);
1421            } else {
1422                return;
1423            }
1424
1425            updateWifiState();
1426        }
1427
1428        /**
1429         * Determines whether the Wi-Fi chipset should stay awake or be put to
1430         * sleep. Looks at the setting for the sleep policy and the current
1431         * conditions.
1432         *
1433         * @see #shouldDeviceStayAwake(int, int)
1434         */
1435        private boolean shouldWifiStayAwake(int stayAwakeConditions, int pluggedType) {
1436            int wifiSleepPolicy = Settings.System.getInt(mContext.getContentResolver(),
1437                    Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_DEFAULT);
1438
1439            if (wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER) {
1440                // Never sleep
1441                return true;
1442            } else if ((wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) &&
1443                    (pluggedType != 0)) {
1444                // Never sleep while plugged, and we're plugged
1445                return true;
1446            } else {
1447                // Default
1448                return shouldDeviceStayAwake(stayAwakeConditions, pluggedType);
1449            }
1450        }
1451
1452        /**
1453         * Determine whether the bit value corresponding to {@code pluggedType} is set in
1454         * the bit string {@code stayAwakeConditions}. Because a {@code pluggedType} value
1455         * of {@code 0} isn't really a plugged type, but rather an indication that the
1456         * device isn't plugged in at all, there is no bit value corresponding to a
1457         * {@code pluggedType} value of {@code 0}. That is why we shift by
1458         * {@code pluggedType&nbsp;&#8212;&nbsp;1} instead of by {@code pluggedType}.
1459         * @param stayAwakeConditions a bit string specifying which "plugged types" should
1460         * keep the device (and hence Wi-Fi) awake.
1461         * @param pluggedType the type of plug (USB, AC, or none) for which the check is
1462         * being made
1463         * @return {@code true} if {@code pluggedType} indicates that the device is
1464         * supposed to stay awake, {@code false} otherwise.
1465         */
1466        private boolean shouldDeviceStayAwake(int stayAwakeConditions, int pluggedType) {
1467            return (stayAwakeConditions & pluggedType) != 0;
1468        }
1469    };
1470
1471    private void sendEnableMessage(boolean enable, boolean persist, int uid) {
1472        Message msg = Message.obtain(mWifiHandler,
1473                                     (enable ? MESSAGE_ENABLE_WIFI : MESSAGE_DISABLE_WIFI),
1474                                     (persist ? 1 : 0), uid);
1475        msg.sendToTarget();
1476    }
1477
1478    private void sendStartMessage(boolean scanOnlyMode) {
1479        Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget();
1480    }
1481
1482    private void updateWifiState() {
1483        boolean wifiEnabled = getPersistedWifiEnabled();
1484        boolean airplaneMode = isAirplaneModeOn();
1485        boolean lockHeld = mLocks.hasLocks();
1486        int strongestLockMode;
1487        boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
1488        boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
1489        if (mDeviceIdle && lockHeld) {
1490            strongestLockMode = mLocks.getStrongestLockMode();
1491        } else {
1492            strongestLockMode = WifiManager.WIFI_MODE_FULL;
1493        }
1494
1495        synchronized (mWifiHandler) {
1496            if (mWifiState == WIFI_STATE_ENABLING && !airplaneMode) {
1497                return;
1498            }
1499            if (wifiShouldBeEnabled) {
1500                if (wifiShouldBeStarted) {
1501                    sWakeLock.acquire();
1502                    sendEnableMessage(true, false, mLastEnableUid);
1503                    sWakeLock.acquire();
1504                    sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
1505                } else {
1506                    int wakeLockTimeout =
1507                            Settings.Secure.getInt(
1508                                    mContext.getContentResolver(),
1509                                    Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
1510                                    DEFAULT_WAKELOCK_TIMEOUT);
1511                    /*
1512                     * The following wakelock is held in order to ensure
1513                     * that the connectivity manager has time to fail over
1514                     * to the mobile data network. The connectivity manager
1515                     * releases it once mobile data connectivity has been
1516                     * established. If connectivity cannot be established,
1517                     * the wakelock is released after wakeLockTimeout
1518                     * milliseconds have elapsed.
1519                     */
1520                    sDriverStopWakeLock.acquire();
1521                    mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI);
1522                    mWifiHandler.sendEmptyMessageDelayed(MESSAGE_RELEASE_WAKELOCK, wakeLockTimeout);
1523                }
1524            } else {
1525                sWakeLock.acquire();
1526                sendEnableMessage(false, false, mLastEnableUid);
1527            }
1528        }
1529    }
1530
1531    private void registerForBroadcasts() {
1532        IntentFilter intentFilter = new IntentFilter();
1533        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
1534        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
1535        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
1536        intentFilter.addAction(ACTION_DEVICE_IDLE);
1537        intentFilter.addAction(BluetoothA2dp.SINK_STATE_CHANGED_ACTION);
1538        mContext.registerReceiver(mReceiver, intentFilter);
1539    }
1540
1541    private boolean isAirplaneSensitive() {
1542        String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
1543                Settings.System.AIRPLANE_MODE_RADIOS);
1544        return airplaneModeRadios == null
1545            || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
1546    }
1547
1548    /**
1549     * Returns true if Wi-Fi is sensitive to airplane mode, and airplane mode is
1550     * currently on.
1551     * @return {@code true} if airplane mode is on.
1552     */
1553    private boolean isAirplaneModeOn() {
1554        return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
1555                Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1556    }
1557
1558    /**
1559     * Handler that allows posting to the WifiThread.
1560     */
1561    private class WifiHandler extends Handler {
1562        public WifiHandler(Looper looper) {
1563            super(looper);
1564        }
1565
1566        @Override
1567        public void handleMessage(Message msg) {
1568            switch (msg.what) {
1569
1570                case MESSAGE_ENABLE_WIFI:
1571                    setWifiEnabledBlocking(true, msg.arg1 == 1, msg.arg2);
1572                    sWakeLock.release();
1573                    break;
1574
1575                case MESSAGE_START_WIFI:
1576                    mWifiStateTracker.setScanOnlyMode(msg.arg1 != 0);
1577                    mWifiStateTracker.restart();
1578                    sWakeLock.release();
1579                    break;
1580
1581                case MESSAGE_DISABLE_WIFI:
1582                    // a non-zero msg.arg1 value means the "enabled" setting
1583                    // should be persisted
1584                    setWifiEnabledBlocking(false, msg.arg1 == 1, msg.arg2);
1585                    sWakeLock.release();
1586                    break;
1587
1588                case MESSAGE_STOP_WIFI:
1589                    mWifiStateTracker.disconnectAndStop();
1590                    // don't release wakelock
1591                    break;
1592
1593                case MESSAGE_RELEASE_WAKELOCK:
1594                    synchronized (sDriverStopWakeLock) {
1595                        if (sDriverStopWakeLock.isHeld()) {
1596                            sDriverStopWakeLock.release();
1597                        }
1598                    }
1599                    break;
1600            }
1601        }
1602    }
1603
1604    @Override
1605    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1606        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1607                != PackageManager.PERMISSION_GRANTED) {
1608            pw.println("Permission Denial: can't dump WifiService from from pid="
1609                    + Binder.getCallingPid()
1610                    + ", uid=" + Binder.getCallingUid());
1611            return;
1612        }
1613        pw.println("Wi-Fi is " + stateName(mWifiState));
1614        pw.println("Stay-awake conditions: " +
1615                Settings.System.getInt(mContext.getContentResolver(),
1616                                       Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
1617        pw.println();
1618
1619        pw.println("Internal state:");
1620        pw.println(mWifiStateTracker);
1621        pw.println();
1622        pw.println("Latest scan results:");
1623        List<ScanResult> scanResults = mWifiStateTracker.getScanResultsList();
1624        if (scanResults != null && scanResults.size() != 0) {
1625            pw.println("  BSSID              Frequency   RSSI  Flags             SSID");
1626            for (ScanResult r : scanResults) {
1627                pw.printf("  %17s  %9d  %5d  %-16s  %s%n",
1628                                         r.BSSID,
1629                                         r.frequency,
1630                                         r.level,
1631                                         r.capabilities,
1632                                         r.SSID == null ? "" : r.SSID);
1633            }
1634        }
1635        pw.println();
1636        pw.println("Locks acquired: " + mFullLocksAcquired + " full, " +
1637                mScanLocksAcquired + " scan");
1638        pw.println("Locks released: " + mFullLocksReleased + " full, " +
1639                mScanLocksReleased + " scan");
1640        pw.println();
1641        pw.println("Locks held:");
1642        mLocks.dump(pw);
1643    }
1644
1645    private static String stateName(int wifiState) {
1646        switch (wifiState) {
1647            case WIFI_STATE_DISABLING:
1648                return "disabling";
1649            case WIFI_STATE_DISABLED:
1650                return "disabled";
1651            case WIFI_STATE_ENABLING:
1652                return "enabling";
1653            case WIFI_STATE_ENABLED:
1654                return "enabled";
1655            case WIFI_STATE_UNKNOWN:
1656                return "unknown state";
1657            default:
1658                return "[invalid state]";
1659        }
1660    }
1661
1662    private class WifiLock extends DeathRecipient {
1663        WifiLock(int lockMode, String tag, IBinder binder) {
1664            super(lockMode, tag, binder);
1665        }
1666
1667        public void binderDied() {
1668            synchronized (mLocks) {
1669                releaseWifiLockLocked(mBinder);
1670            }
1671        }
1672
1673        public String toString() {
1674            return "WifiLock{" + mTag + " type=" + mMode + " binder=" + mBinder + "}";
1675        }
1676    }
1677
1678    private class LockList {
1679        private List<WifiLock> mList;
1680
1681        private LockList() {
1682            mList = new ArrayList<WifiLock>();
1683        }
1684
1685        private synchronized boolean hasLocks() {
1686            return !mList.isEmpty();
1687        }
1688
1689        private synchronized int getStrongestLockMode() {
1690            if (mList.isEmpty()) {
1691                return WifiManager.WIFI_MODE_FULL;
1692            }
1693            for (WifiLock l : mList) {
1694                if (l.mMode == WifiManager.WIFI_MODE_FULL) {
1695                    return WifiManager.WIFI_MODE_FULL;
1696                }
1697            }
1698            return WifiManager.WIFI_MODE_SCAN_ONLY;
1699        }
1700
1701        private void addLock(WifiLock lock) {
1702            if (findLockByBinder(lock.mBinder) < 0) {
1703                mList.add(lock);
1704            }
1705        }
1706
1707        private WifiLock removeLock(IBinder binder) {
1708            int index = findLockByBinder(binder);
1709            if (index >= 0) {
1710                WifiLock ret = mList.remove(index);
1711                ret.unlinkDeathRecipient();
1712                return ret;
1713            } else {
1714                return null;
1715            }
1716        }
1717
1718        private int findLockByBinder(IBinder binder) {
1719            int size = mList.size();
1720            for (int i = size - 1; i >= 0; i--)
1721                if (mList.get(i).mBinder == binder)
1722                    return i;
1723            return -1;
1724        }
1725
1726        private void dump(PrintWriter pw) {
1727            for (WifiLock l : mList) {
1728                pw.print("    ");
1729                pw.println(l);
1730            }
1731        }
1732    }
1733
1734    public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) {
1735        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
1736        if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) {
1737            return false;
1738        }
1739        WifiLock wifiLock = new WifiLock(lockMode, tag, binder);
1740        synchronized (mLocks) {
1741            return acquireWifiLockLocked(wifiLock);
1742        }
1743    }
1744
1745    private boolean acquireWifiLockLocked(WifiLock wifiLock) {
1746        mLocks.addLock(wifiLock);
1747
1748        int uid = Binder.getCallingUid();
1749        long ident = Binder.clearCallingIdentity();
1750        try {
1751            switch(wifiLock.mMode) {
1752            case WifiManager.WIFI_MODE_FULL:
1753                ++mFullLocksAcquired;
1754                mBatteryStats.noteFullWifiLockAcquired(uid);
1755                break;
1756            case WifiManager.WIFI_MODE_SCAN_ONLY:
1757                ++mScanLocksAcquired;
1758                mBatteryStats.noteScanWifiLockAcquired(uid);
1759                break;
1760            }
1761        } catch (RemoteException e) {
1762        } finally {
1763            Binder.restoreCallingIdentity(ident);
1764        }
1765
1766        updateWifiState();
1767        return true;
1768    }
1769
1770    public boolean releaseWifiLock(IBinder lock) {
1771        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
1772        synchronized (mLocks) {
1773            return releaseWifiLockLocked(lock);
1774        }
1775    }
1776
1777    private boolean releaseWifiLockLocked(IBinder lock) {
1778        boolean hadLock;
1779
1780        WifiLock wifiLock = mLocks.removeLock(lock);
1781        hadLock = (wifiLock != null);
1782
1783        if (hadLock) {
1784            int uid = Binder.getCallingUid();
1785            long ident = Binder.clearCallingIdentity();
1786            try {
1787                switch(wifiLock.mMode) {
1788                    case WifiManager.WIFI_MODE_FULL:
1789                        ++mFullLocksReleased;
1790                        mBatteryStats.noteFullWifiLockReleased(uid);
1791                        break;
1792                    case WifiManager.WIFI_MODE_SCAN_ONLY:
1793                        ++mScanLocksReleased;
1794                        mBatteryStats.noteScanWifiLockReleased(uid);
1795                        break;
1796                }
1797            } catch (RemoteException e) {
1798            } finally {
1799                Binder.restoreCallingIdentity(ident);
1800            }
1801        }
1802
1803        updateWifiState();
1804        return hadLock;
1805    }
1806
1807    private abstract class DeathRecipient
1808            implements IBinder.DeathRecipient {
1809        String mTag;
1810        int mMode;
1811        IBinder mBinder;
1812
1813        DeathRecipient(int mode, String tag, IBinder binder) {
1814            super();
1815            mTag = tag;
1816            mMode = mode;
1817            mBinder = binder;
1818            try {
1819                mBinder.linkToDeath(this, 0);
1820            } catch (RemoteException e) {
1821                binderDied();
1822            }
1823        }
1824
1825        void unlinkDeathRecipient() {
1826            mBinder.unlinkToDeath(this, 0);
1827        }
1828    }
1829
1830    private class Multicaster extends DeathRecipient {
1831        Multicaster(String tag, IBinder binder) {
1832            super(Binder.getCallingUid(), tag, binder);
1833        }
1834
1835        public void binderDied() {
1836            Log.e(TAG, "Multicaster binderDied");
1837            synchronized (mMulticasters) {
1838                int i = mMulticasters.indexOf(this);
1839                if (i != -1) {
1840                    removeMulticasterLocked(i, mMode);
1841                }
1842            }
1843        }
1844
1845        public String toString() {
1846            return "Multicaster{" + mTag + " binder=" + mBinder + "}";
1847        }
1848
1849        public int getUid() {
1850            return mMode;
1851        }
1852    }
1853
1854    public void acquireMulticastLock(IBinder binder, String tag) {
1855        enforceMulticastChangePermission();
1856
1857        synchronized (mMulticasters) {
1858            mMulticastEnabled++;
1859            mMulticasters.add(new Multicaster(tag, binder));
1860            // Note that we could call stopPacketFiltering only when
1861            // our new size == 1 (first call), but this function won't
1862            // be called often and by making the stopPacket call each
1863            // time we're less fragile and self-healing.
1864            WifiNative.stopPacketFiltering();
1865        }
1866
1867        int uid = Binder.getCallingUid();
1868        Long ident = Binder.clearCallingIdentity();
1869        try {
1870            mBatteryStats.noteWifiMulticastEnabled(uid);
1871        } catch (RemoteException e) {
1872        } finally {
1873            Binder.restoreCallingIdentity(ident);
1874        }
1875    }
1876
1877    public void releaseMulticastLock() {
1878        enforceMulticastChangePermission();
1879
1880        int uid = Binder.getCallingUid();
1881        synchronized (mMulticasters) {
1882            mMulticastDisabled++;
1883            int size = mMulticasters.size();
1884            for (int i = size - 1; i >= 0; i--) {
1885                Multicaster m = mMulticasters.get(i);
1886                if ((m != null) && (m.getUid() == uid)) {
1887                    removeMulticasterLocked(i, uid);
1888                }
1889            }
1890        }
1891    }
1892
1893    private void removeMulticasterLocked(int i, int uid)
1894    {
1895        Multicaster removed = mMulticasters.remove(i);
1896        if (removed != null) {
1897            removed.unlinkDeathRecipient();
1898        }
1899        if (mMulticasters.size() == 0) {
1900            WifiNative.startPacketFiltering();
1901        }
1902
1903        Long ident = Binder.clearCallingIdentity();
1904        try {
1905            mBatteryStats.noteWifiMulticastDisabled(uid);
1906        } catch (RemoteException e) {
1907        } finally {
1908            Binder.restoreCallingIdentity(ident);
1909        }
1910    }
1911
1912    public boolean isMulticastEnabled() {
1913        enforceAccessPermission();
1914
1915        synchronized (mMulticasters) {
1916            return (mMulticasters.size() > 0);
1917        }
1918    }
1919}
1920