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