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