WifiService.java revision a5ec95cdb1a7d2024249277dff1f99d0046c9b56
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(boolean forceActive) {
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(forceActive);
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                    // bssid is the hash key
1367                    scanResult = mScanResultCache.get(bssid);
1368                    if (scanResult != null) {
1369                        scanResult.level = level;
1370                    } else {
1371                        /*
1372                         * The formatting of the results returned by
1373                         * wpa_supplicant is intended to make the fields
1374                         * line up nicely when printed,
1375                         * not to make them easy to parse. So we have to
1376                         * apply some heuristics to figure out which field
1377                         * is the SSID and which field is the flags.
1378                         */
1379                        String ssid;
1380                        String flags;
1381                        if (result.length == 4) {
1382                            if (result[3].charAt(0) == '[') {
1383                                flags = result[3];
1384                                ssid = "";
1385                            } else {
1386                                flags = "";
1387                                ssid = result[3];
1388                            }
1389                        } else if (result.length == 5) {
1390                            flags = result[3];
1391                            ssid = result[4];
1392                        } else {
1393                            // Here, we must have 3 fields: no flags and ssid
1394                            // set
1395                            flags = "";
1396                            ssid = "";
1397                        }
1398
1399                        // Do not add scan results that have no SSID set
1400                        if (0 < ssid.trim().length()) {
1401                            scanResult =
1402                                new ScanResult(
1403                                    ssid, bssid, flags, level, frequency);
1404                            mScanResultCache.put(bssid, scanResult);
1405                        }
1406                    }
1407                } else {
1408                    Log.w(TAG, "Misformatted scan result text with " +
1409                          result.length + " fields: " + line);
1410                }
1411            }
1412        }
1413
1414        return scanResult;
1415    }
1416
1417    /**
1418     * Parse the "flags" field passed back in a scan result by wpa_supplicant,
1419     * and construct a {@code WifiConfiguration} that describes the encryption,
1420     * key management, and authenticaion capabilities of the access point.
1421     * @param flags the string returned by wpa_supplicant
1422     * @return the {@link WifiConfiguration} object, filled in
1423     */
1424    WifiConfiguration parseScanFlags(String flags) {
1425        WifiConfiguration config = new WifiConfiguration();
1426
1427        if (flags.length() == 0) {
1428            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
1429        }
1430        // ... to be implemented
1431        return config;
1432    }
1433
1434    /**
1435     * Tell the supplicant to persist the current list of configured networks.
1436     * @return {@code true} if the operation succeeded
1437     */
1438    public boolean saveConfiguration() {
1439        boolean result;
1440        enforceChangePermission();
1441        synchronized (mWifiStateTracker) {
1442            result = WifiNative.saveConfigCommand();
1443            if (result && mNeedReconfig) {
1444                mNeedReconfig = false;
1445                result = WifiNative.reloadConfigCommand();
1446
1447                if (result) {
1448                    Intent intent = new Intent(WifiManager.NETWORK_IDS_CHANGED_ACTION);
1449                    mContext.sendBroadcast(intent);
1450                }
1451            }
1452        }
1453        // Inform the backup manager about a data change
1454        IBackupManager ibm = IBackupManager.Stub.asInterface(
1455                ServiceManager.getService(Context.BACKUP_SERVICE));
1456        if (ibm != null) {
1457            try {
1458                ibm.dataChanged("com.android.providers.settings");
1459            } catch (Exception e) {
1460                // Try again later
1461            }
1462        }
1463        return result;
1464    }
1465
1466    /**
1467     * Set the number of radio frequency channels that are allowed to be used
1468     * in the current regulatory domain. This method should be used only
1469     * if the correct number of channels cannot be determined automatically
1470     * for some reason. If the operation is successful, the new value is
1471     * persisted as a Secure setting.
1472     * @param numChannels the number of allowed channels. Must be greater than 0
1473     * and less than or equal to 16.
1474     * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
1475     * {@code numChannels} is outside the valid range.
1476     */
1477    public boolean setNumAllowedChannels(int numChannels) {
1478        enforceChangePermission();
1479        /*
1480         * Validate the argument. We'd like to let the Wi-Fi driver do this,
1481         * but if Wi-Fi isn't currently enabled, that's not possible, and
1482         * we want to persist the setting anyway,so that it will take
1483         * effect when Wi-Fi does become enabled.
1484         */
1485        boolean found = false;
1486        for (int validChan : sValidRegulatoryChannelCounts) {
1487            if (validChan == numChannels) {
1488                found = true;
1489                break;
1490            }
1491        }
1492        if (!found) {
1493            return false;
1494        }
1495
1496        Settings.Secure.putInt(mContext.getContentResolver(),
1497                               Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
1498                               numChannels);
1499        mWifiStateTracker.setNumAllowedChannels(numChannels);
1500        return true;
1501    }
1502
1503    /**
1504     * Return the number of frequency channels that are allowed
1505     * to be used in the current regulatory domain.
1506     * @return the number of allowed channels, or {@code -1} if an error occurs
1507     */
1508    public int getNumAllowedChannels() {
1509        int numChannels;
1510
1511        enforceAccessPermission();
1512        synchronized (mWifiStateTracker) {
1513            /*
1514             * If we can't get the value from the driver (e.g., because
1515             * Wi-Fi is not currently enabled), get the value from
1516             * Settings.
1517             */
1518            numChannels = WifiNative.getNumAllowedChannelsCommand();
1519            if (numChannels < 0) {
1520                numChannels = Settings.Secure.getInt(mContext.getContentResolver(),
1521                                                     Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
1522                                                     -1);
1523            }
1524        }
1525        return numChannels;
1526    }
1527
1528    /**
1529     * Return the list of valid values for the number of allowed radio channels
1530     * for various regulatory domains.
1531     * @return the list of channel counts
1532     */
1533    public int[] getValidChannelCounts() {
1534        enforceAccessPermission();
1535        return sValidRegulatoryChannelCounts;
1536    }
1537
1538    /**
1539     * Return the DHCP-assigned addresses from the last successful DHCP request,
1540     * if any.
1541     * @return the DHCP information
1542     */
1543    public DhcpInfo getDhcpInfo() {
1544        enforceAccessPermission();
1545        return mWifiStateTracker.getDhcpInfo();
1546    }
1547
1548    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1549        @Override
1550        public void onReceive(Context context, Intent intent) {
1551            String action = intent.getAction();
1552
1553            long idleMillis = Settings.Gservices.getLong(mContext.getContentResolver(),
1554                                                  Settings.Gservices.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS);
1555            int stayAwakeConditions =
1556                    Settings.System.getInt(mContext.getContentResolver(),
1557                                           Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
1558            if (action.equals(Intent.ACTION_SCREEN_ON)) {
1559                Log.d(TAG, "ACTION_SCREEN_ON");
1560                mAlarmManager.cancel(mIdleIntent);
1561                mDeviceIdle = false;
1562                mScreenOff = false;
1563            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1564                Log.d(TAG, "ACTION_SCREEN_OFF");
1565                mScreenOff = true;
1566                /*
1567                 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
1568                 * AND the "stay on while plugged in" setting doesn't match the
1569                 * current power conditions (i.e, not plugged in, plugged in to USB,
1570                 * or plugged in to AC).
1571                 */
1572                if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
1573                    WifiInfo info = mWifiStateTracker.requestConnectionInfo();
1574                    if (info.getSupplicantState() != SupplicantState.COMPLETED) {
1575                        // do not keep Wifi awake when screen is off if Wifi is not associated
1576                        mDeviceIdle = true;
1577                        updateWifiState();
1578                    } else {
1579                        long triggerTime = System.currentTimeMillis() + idleMillis;
1580                        Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
1581                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
1582                    }
1583                }
1584                /* we can return now -- there's nothing to do until we get the idle intent back */
1585                return;
1586            } else if (action.equals(ACTION_DEVICE_IDLE)) {
1587                Log.d(TAG, "got ACTION_DEVICE_IDLE");
1588                mDeviceIdle = true;
1589            } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
1590                /*
1591                 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
1592                 * AND we are transitioning from a state in which the device was supposed
1593                 * to stay awake to a state in which it is not supposed to stay awake.
1594                 * If "stay awake" state is not changing, we do nothing, to avoid resetting
1595                 * the already-set timer.
1596                 */
1597                int pluggedType = intent.getIntExtra("plugged", 0);
1598                Log.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType);
1599                if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) &&
1600                        !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) {
1601                    long triggerTime = System.currentTimeMillis() + idleMillis;
1602                    Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
1603                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
1604                    mPluggedType = pluggedType;
1605                    return;
1606                }
1607                mPluggedType = pluggedType;
1608            } else if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) {
1609                boolean isBluetoothPlaying =
1610                        intent.getIntExtra(
1611                                BluetoothA2dp.SINK_STATE,
1612                                BluetoothA2dp.STATE_DISCONNECTED) == BluetoothA2dp.STATE_PLAYING;
1613                mWifiStateTracker.setBluetoothScanMode(isBluetoothPlaying);
1614            } else {
1615                return;
1616            }
1617
1618            updateWifiState();
1619        }
1620
1621        /**
1622         * Determines whether the Wi-Fi chipset should stay awake or be put to
1623         * sleep. Looks at the setting for the sleep policy and the current
1624         * conditions.
1625         *
1626         * @see #shouldDeviceStayAwake(int, int)
1627         */
1628        private boolean shouldWifiStayAwake(int stayAwakeConditions, int pluggedType) {
1629            int wifiSleepPolicy = Settings.System.getInt(mContext.getContentResolver(),
1630                    Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_DEFAULT);
1631
1632            if (wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER) {
1633                // Never sleep
1634                return true;
1635            } else if ((wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) &&
1636                    (pluggedType != 0)) {
1637                // Never sleep while plugged, and we're plugged
1638                return true;
1639            } else {
1640                // Default
1641                return shouldDeviceStayAwake(stayAwakeConditions, pluggedType);
1642            }
1643        }
1644
1645        /**
1646         * Determine whether the bit value corresponding to {@code pluggedType} is set in
1647         * the bit string {@code stayAwakeConditions}. Because a {@code pluggedType} value
1648         * of {@code 0} isn't really a plugged type, but rather an indication that the
1649         * device isn't plugged in at all, there is no bit value corresponding to a
1650         * {@code pluggedType} value of {@code 0}. That is why we shift by
1651         * {@code pluggedType&nbsp;&#8212;&nbsp;1} instead of by {@code pluggedType}.
1652         * @param stayAwakeConditions a bit string specifying which "plugged types" should
1653         * keep the device (and hence Wi-Fi) awake.
1654         * @param pluggedType the type of plug (USB, AC, or none) for which the check is
1655         * being made
1656         * @return {@code true} if {@code pluggedType} indicates that the device is
1657         * supposed to stay awake, {@code false} otherwise.
1658         */
1659        private boolean shouldDeviceStayAwake(int stayAwakeConditions, int pluggedType) {
1660            return (stayAwakeConditions & pluggedType) != 0;
1661        }
1662    };
1663
1664    private void sendEnableMessage(boolean enable, boolean persist, int uid) {
1665        Message msg = Message.obtain(mWifiHandler,
1666                                     (enable ? MESSAGE_ENABLE_WIFI : MESSAGE_DISABLE_WIFI),
1667                                     (persist ? 1 : 0), uid);
1668        msg.sendToTarget();
1669    }
1670
1671    private void sendStartMessage(boolean scanOnlyMode) {
1672        Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget();
1673    }
1674
1675    private void updateWifiState() {
1676        boolean wifiEnabled = getPersistedWifiEnabled();
1677        boolean airplaneMode = isAirplaneModeOn();
1678        boolean lockHeld = mLocks.hasLocks();
1679        int strongestLockMode;
1680        boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
1681        boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
1682        if (mDeviceIdle && lockHeld) {
1683            strongestLockMode = mLocks.getStrongestLockMode();
1684        } else {
1685            strongestLockMode = WifiManager.WIFI_MODE_FULL;
1686        }
1687
1688        synchronized (mWifiHandler) {
1689            if (mWifiState == WIFI_STATE_ENABLING && !airplaneMode) {
1690                return;
1691            }
1692            if (wifiShouldBeEnabled) {
1693                if (wifiShouldBeStarted) {
1694                    sWakeLock.acquire();
1695                    sendEnableMessage(true, false, mLastEnableUid);
1696                    sWakeLock.acquire();
1697                    sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
1698                } else {
1699                    int wakeLockTimeout =
1700                            Settings.Secure.getInt(
1701                                    mContext.getContentResolver(),
1702                                    Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
1703                                    DEFAULT_WAKELOCK_TIMEOUT);
1704                    /*
1705                     * The following wakelock is held in order to ensure
1706                     * that the connectivity manager has time to fail over
1707                     * to the mobile data network. The connectivity manager
1708                     * releases it once mobile data connectivity has been
1709                     * established. If connectivity cannot be established,
1710                     * the wakelock is released after wakeLockTimeout
1711                     * milliseconds have elapsed.
1712                     */
1713                    sDriverStopWakeLock.acquire();
1714                    mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI);
1715                    mWifiHandler.sendEmptyMessageDelayed(MESSAGE_RELEASE_WAKELOCK, wakeLockTimeout);
1716                }
1717            } else {
1718                sWakeLock.acquire();
1719                sendEnableMessage(false, false, mLastEnableUid);
1720            }
1721        }
1722    }
1723
1724    private void registerForBroadcasts() {
1725        IntentFilter intentFilter = new IntentFilter();
1726        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
1727        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
1728        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
1729        intentFilter.addAction(ACTION_DEVICE_IDLE);
1730        intentFilter.addAction(BluetoothA2dp.SINK_STATE_CHANGED_ACTION);
1731        mContext.registerReceiver(mReceiver, intentFilter);
1732    }
1733
1734    private boolean isAirplaneSensitive() {
1735        String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
1736                Settings.System.AIRPLANE_MODE_RADIOS);
1737        return airplaneModeRadios == null
1738            || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
1739    }
1740
1741    /**
1742     * Returns true if Wi-Fi is sensitive to airplane mode, and airplane mode is
1743     * currently on.
1744     * @return {@code true} if airplane mode is on.
1745     */
1746    private boolean isAirplaneModeOn() {
1747        return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
1748                Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1749    }
1750
1751    /**
1752     * Handler that allows posting to the WifiThread.
1753     */
1754    private class WifiHandler extends Handler {
1755        public WifiHandler(Looper looper) {
1756            super(looper);
1757        }
1758
1759        @Override
1760        public void handleMessage(Message msg) {
1761            switch (msg.what) {
1762
1763                case MESSAGE_ENABLE_WIFI:
1764                    setWifiEnabledBlocking(true, msg.arg1 == 1, msg.arg2);
1765                    sWakeLock.release();
1766                    break;
1767
1768                case MESSAGE_START_WIFI:
1769                    mWifiStateTracker.setScanOnlyMode(msg.arg1 != 0);
1770                    mWifiStateTracker.restart();
1771                    sWakeLock.release();
1772                    break;
1773
1774                case MESSAGE_DISABLE_WIFI:
1775                    // a non-zero msg.arg1 value means the "enabled" setting
1776                    // should be persisted
1777                    setWifiEnabledBlocking(false, msg.arg1 == 1, msg.arg2);
1778                    sWakeLock.release();
1779                    break;
1780
1781                case MESSAGE_STOP_WIFI:
1782                    mWifiStateTracker.disconnectAndStop();
1783                    // don't release wakelock
1784                    break;
1785
1786                case MESSAGE_RELEASE_WAKELOCK:
1787                    synchronized (sDriverStopWakeLock) {
1788                        if (sDriverStopWakeLock.isHeld()) {
1789                            sDriverStopWakeLock.release();
1790                        }
1791                    }
1792                    break;
1793            }
1794        }
1795    }
1796
1797    @Override
1798    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1799        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1800                != PackageManager.PERMISSION_GRANTED) {
1801            pw.println("Permission Denial: can't dump WifiService from from pid="
1802                    + Binder.getCallingPid()
1803                    + ", uid=" + Binder.getCallingUid());
1804            return;
1805        }
1806        pw.println("Wi-Fi is " + stateName(mWifiState));
1807        pw.println("Stay-awake conditions: " +
1808                Settings.System.getInt(mContext.getContentResolver(),
1809                                       Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
1810        pw.println();
1811
1812        pw.println("Internal state:");
1813        pw.println(mWifiStateTracker);
1814        pw.println();
1815        pw.println("Latest scan results:");
1816        List<ScanResult> scanResults = mWifiStateTracker.getScanResultsList();
1817        if (scanResults != null && scanResults.size() != 0) {
1818            pw.println("  BSSID              Frequency   RSSI  Flags             SSID");
1819            for (ScanResult r : scanResults) {
1820                pw.printf("  %17s  %9d  %5d  %-16s  %s%n",
1821                                         r.BSSID,
1822                                         r.frequency,
1823                                         r.level,
1824                                         r.capabilities,
1825                                         r.SSID == null ? "" : r.SSID);
1826            }
1827        }
1828        pw.println();
1829        pw.println("Locks acquired: " + mFullLocksAcquired + " full, " +
1830                mScanLocksAcquired + " scan");
1831        pw.println("Locks released: " + mFullLocksReleased + " full, " +
1832                mScanLocksReleased + " scan");
1833        pw.println();
1834        pw.println("Locks held:");
1835        mLocks.dump(pw);
1836    }
1837
1838    private static String stateName(int wifiState) {
1839        switch (wifiState) {
1840            case WIFI_STATE_DISABLING:
1841                return "disabling";
1842            case WIFI_STATE_DISABLED:
1843                return "disabled";
1844            case WIFI_STATE_ENABLING:
1845                return "enabling";
1846            case WIFI_STATE_ENABLED:
1847                return "enabled";
1848            case WIFI_STATE_UNKNOWN:
1849                return "unknown state";
1850            default:
1851                return "[invalid state]";
1852        }
1853    }
1854
1855    private class WifiLock extends DeathRecipient {
1856        WifiLock(int lockMode, String tag, IBinder binder) {
1857            super(lockMode, tag, binder);
1858        }
1859
1860        public void binderDied() {
1861            synchronized (mLocks) {
1862                releaseWifiLockLocked(mBinder);
1863            }
1864        }
1865
1866        public String toString() {
1867            return "WifiLock{" + mTag + " type=" + mMode + " binder=" + mBinder + "}";
1868        }
1869    }
1870
1871    private class LockList {
1872        private List<WifiLock> mList;
1873
1874        private LockList() {
1875            mList = new ArrayList<WifiLock>();
1876        }
1877
1878        private synchronized boolean hasLocks() {
1879            return !mList.isEmpty();
1880        }
1881
1882        private synchronized int getStrongestLockMode() {
1883            if (mList.isEmpty()) {
1884                return WifiManager.WIFI_MODE_FULL;
1885            }
1886            for (WifiLock l : mList) {
1887                if (l.mMode == WifiManager.WIFI_MODE_FULL) {
1888                    return WifiManager.WIFI_MODE_FULL;
1889                }
1890            }
1891            return WifiManager.WIFI_MODE_SCAN_ONLY;
1892        }
1893
1894        private void addLock(WifiLock lock) {
1895            if (findLockByBinder(lock.mBinder) < 0) {
1896                mList.add(lock);
1897            }
1898        }
1899
1900        private WifiLock removeLock(IBinder binder) {
1901            int index = findLockByBinder(binder);
1902            if (index >= 0) {
1903                WifiLock ret = mList.remove(index);
1904                ret.unlinkDeathRecipient();
1905                return ret;
1906            } else {
1907                return null;
1908            }
1909        }
1910
1911        private int findLockByBinder(IBinder binder) {
1912            int size = mList.size();
1913            for (int i = size - 1; i >= 0; i--)
1914                if (mList.get(i).mBinder == binder)
1915                    return i;
1916            return -1;
1917        }
1918
1919        private void dump(PrintWriter pw) {
1920            for (WifiLock l : mList) {
1921                pw.print("    ");
1922                pw.println(l);
1923            }
1924        }
1925    }
1926
1927    public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) {
1928        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
1929        if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) {
1930            return false;
1931        }
1932        WifiLock wifiLock = new WifiLock(lockMode, tag, binder);
1933        synchronized (mLocks) {
1934            return acquireWifiLockLocked(wifiLock);
1935        }
1936    }
1937
1938    private boolean acquireWifiLockLocked(WifiLock wifiLock) {
1939        mLocks.addLock(wifiLock);
1940
1941        int uid = Binder.getCallingUid();
1942        long ident = Binder.clearCallingIdentity();
1943        try {
1944            switch(wifiLock.mMode) {
1945            case WifiManager.WIFI_MODE_FULL:
1946                ++mFullLocksAcquired;
1947                mBatteryStats.noteFullWifiLockAcquired(uid);
1948                break;
1949            case WifiManager.WIFI_MODE_SCAN_ONLY:
1950                ++mScanLocksAcquired;
1951                mBatteryStats.noteScanWifiLockAcquired(uid);
1952                break;
1953            }
1954        } catch (RemoteException e) {
1955        } finally {
1956            Binder.restoreCallingIdentity(ident);
1957        }
1958
1959        updateWifiState();
1960        return true;
1961    }
1962
1963    public boolean releaseWifiLock(IBinder lock) {
1964        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
1965        synchronized (mLocks) {
1966            return releaseWifiLockLocked(lock);
1967        }
1968    }
1969
1970    private boolean releaseWifiLockLocked(IBinder lock) {
1971        boolean hadLock;
1972
1973        WifiLock wifiLock = mLocks.removeLock(lock);
1974        hadLock = (wifiLock != null);
1975
1976        if (hadLock) {
1977            int uid = Binder.getCallingUid();
1978            long ident = Binder.clearCallingIdentity();
1979            try {
1980                switch(wifiLock.mMode) {
1981                    case WifiManager.WIFI_MODE_FULL:
1982                        ++mFullLocksReleased;
1983                        mBatteryStats.noteFullWifiLockReleased(uid);
1984                        break;
1985                    case WifiManager.WIFI_MODE_SCAN_ONLY:
1986                        ++mScanLocksReleased;
1987                        mBatteryStats.noteScanWifiLockReleased(uid);
1988                        break;
1989                }
1990            } catch (RemoteException e) {
1991            } finally {
1992                Binder.restoreCallingIdentity(ident);
1993            }
1994        }
1995
1996        updateWifiState();
1997        return hadLock;
1998    }
1999
2000    private abstract class DeathRecipient
2001            implements IBinder.DeathRecipient {
2002        String mTag;
2003        int mMode;
2004        IBinder mBinder;
2005
2006        DeathRecipient(int mode, String tag, IBinder binder) {
2007            super();
2008            mTag = tag;
2009            mMode = mode;
2010            mBinder = binder;
2011            try {
2012                mBinder.linkToDeath(this, 0);
2013            } catch (RemoteException e) {
2014                binderDied();
2015            }
2016        }
2017
2018        void unlinkDeathRecipient() {
2019            mBinder.unlinkToDeath(this, 0);
2020        }
2021    }
2022
2023    private class Multicaster extends DeathRecipient {
2024        Multicaster(String tag, IBinder binder) {
2025            super(Binder.getCallingUid(), tag, binder);
2026        }
2027
2028        public void binderDied() {
2029            Log.e(TAG, "Multicaster binderDied");
2030            synchronized (mMulticasters) {
2031                int i = mMulticasters.indexOf(this);
2032                if (i != -1) {
2033                    removeMulticasterLocked(i, mMode);
2034                }
2035            }
2036        }
2037
2038        public String toString() {
2039            return "Multicaster{" + mTag + " binder=" + mBinder + "}";
2040        }
2041
2042        public int getUid() {
2043            return mMode;
2044        }
2045    }
2046
2047    public void acquireMulticastLock(IBinder binder, String tag) {
2048        enforceMulticastChangePermission();
2049
2050        synchronized (mMulticasters) {
2051            mMulticastEnabled++;
2052            mMulticasters.add(new Multicaster(tag, binder));
2053            // Note that we could call stopPacketFiltering only when
2054            // our new size == 1 (first call), but this function won't
2055            // be called often and by making the stopPacket call each
2056            // time we're less fragile and self-healing.
2057            WifiNative.stopPacketFiltering();
2058        }
2059
2060        int uid = Binder.getCallingUid();
2061        Long ident = Binder.clearCallingIdentity();
2062        try {
2063            mBatteryStats.noteWifiMulticastEnabled(uid);
2064        } catch (RemoteException e) {
2065        } finally {
2066            Binder.restoreCallingIdentity(ident);
2067        }
2068    }
2069
2070    public void releaseMulticastLock() {
2071        enforceMulticastChangePermission();
2072
2073        int uid = Binder.getCallingUid();
2074        synchronized (mMulticasters) {
2075            mMulticastDisabled++;
2076            int size = mMulticasters.size();
2077            for (int i = size - 1; i >= 0; i--) {
2078                Multicaster m = mMulticasters.get(i);
2079                if ((m != null) && (m.getUid() == uid)) {
2080                    removeMulticasterLocked(i, uid);
2081                }
2082            }
2083        }
2084    }
2085
2086    private void removeMulticasterLocked(int i, int uid)
2087    {
2088        Multicaster removed = mMulticasters.remove(i);
2089        if (removed != null) {
2090            removed.unlinkDeathRecipient();
2091        }
2092        if (mMulticasters.size() == 0) {
2093            WifiNative.startPacketFiltering();
2094        }
2095
2096        Long ident = Binder.clearCallingIdentity();
2097        try {
2098            mBatteryStats.noteWifiMulticastDisabled(uid);
2099        } catch (RemoteException e) {
2100        } finally {
2101            Binder.restoreCallingIdentity(ident);
2102        }
2103    }
2104
2105    public boolean isMulticastEnabled() {
2106        enforceAccessPermission();
2107
2108        synchronized (mMulticasters) {
2109            return (mMulticasters.size() > 0);
2110        }
2111    }
2112}
2113