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