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