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