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