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