WifiService.java revision cfce303cbdd59a3883957e4bc96a0476ceeb86ac
1/*
2 * Copyright (C) 2010 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 android.app.AlarmManager;
20import android.app.Notification;
21import android.app.NotificationManager;
22import android.app.PendingIntent;
23import android.bluetooth.BluetoothA2dp;
24import android.bluetooth.BluetoothAdapter;
25import android.bluetooth.BluetoothDevice;
26import android.bluetooth.BluetoothProfile;
27import android.content.BroadcastReceiver;
28import android.content.ContentResolver;
29import android.content.Context;
30import android.content.Intent;
31import android.content.IntentFilter;
32import android.content.pm.PackageManager;
33import android.database.ContentObserver;
34import android.net.wifi.IWifiManager;
35import android.net.wifi.WifiInfo;
36import android.net.wifi.WifiManager;
37import android.net.wifi.WifiStateMachine;
38import android.net.wifi.ScanResult;
39import android.net.wifi.WifiConfiguration;
40import android.net.wifi.SupplicantState;
41import android.net.wifi.WifiConfiguration.KeyMgmt;
42import android.net.ConnectivityManager;
43import android.net.InterfaceConfiguration;
44import android.net.DhcpInfo;
45import android.net.NetworkInfo;
46import android.net.NetworkInfo.State;
47import android.os.Binder;
48import android.os.Handler;
49import android.os.HandlerThread;
50import android.os.IBinder;
51import android.os.INetworkManagementService;
52import android.os.Message;
53import android.os.RemoteException;
54import android.os.ServiceManager;
55import android.os.WorkSource;
56import android.provider.Settings;
57import android.text.TextUtils;
58import android.util.Slog;
59
60import java.util.ArrayList;
61import java.util.List;
62import java.util.Set;
63import java.util.concurrent.atomic.AtomicBoolean;
64import java.io.FileDescriptor;
65import java.io.PrintWriter;
66
67import com.android.internal.app.IBatteryStats;
68import com.android.internal.util.AsyncChannel;
69import com.android.server.am.BatteryStatsService;
70import com.android.internal.R;
71
72/**
73 * WifiService handles remote WiFi operation requests by implementing
74 * the IWifiManager interface.
75 *
76 * @hide
77 */
78//TODO: Clean up multiple locks and implement WifiService
79// as a SM to track soft AP/client/adhoc bring up based
80// on device idle state, airplane mode and boot.
81
82public class WifiService extends IWifiManager.Stub {
83    private static final String TAG = "WifiService";
84    private static final boolean DBG = true;
85
86    private final WifiStateMachine mWifiStateMachine;
87
88    private Context mContext;
89
90    private AlarmManager mAlarmManager;
91    private PendingIntent mIdleIntent;
92    private BluetoothA2dp mBluetoothA2dp;
93    private static final int IDLE_REQUEST = 0;
94    private boolean mScreenOff;
95    private boolean mDeviceIdle;
96    private int mPluggedType;
97
98    // true if the user enabled Wifi while in airplane mode
99    private AtomicBoolean mAirplaneModeOverwridden = new AtomicBoolean(false);
100
101    private final LockList mLocks = new LockList();
102    // some wifi lock statistics
103    private int mFullHighPerfLocksAcquired;
104    private int mFullHighPerfLocksReleased;
105    private int mFullLocksAcquired;
106    private int mFullLocksReleased;
107    private int mScanLocksAcquired;
108    private int mScanLocksReleased;
109
110    private final List<Multicaster> mMulticasters =
111            new ArrayList<Multicaster>();
112    private int mMulticastEnabled;
113    private int mMulticastDisabled;
114
115    private final IBatteryStats mBatteryStats;
116
117    ConnectivityManager mCm;
118    private String[] mWifiRegexs;
119
120    /**
121     * See {@link Settings.Secure#WIFI_IDLE_MS}. This is the default value if a
122     * Settings.Secure value is not present. This timeout value is chosen as
123     * the approximate point at which the battery drain caused by Wi-Fi
124     * being enabled but not active exceeds the battery drain caused by
125     * re-establishing a connection to the mobile data network.
126     */
127    private static final long DEFAULT_IDLE_MS = 15 * 60 * 1000; /* 15 minutes */
128
129    private static final String ACTION_DEVICE_IDLE =
130            "com.android.server.WifiManager.action.DEVICE_IDLE";
131
132    private boolean mIsReceiverRegistered = false;
133
134
135    NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
136
137    // Variables relating to the 'available networks' notification
138    /**
139     * The icon to show in the 'available networks' notification. This will also
140     * be the ID of the Notification given to the NotificationManager.
141     */
142    private static final int ICON_NETWORKS_AVAILABLE =
143            com.android.internal.R.drawable.stat_notify_wifi_in_range;
144    /**
145     * When a notification is shown, we wait this amount before possibly showing it again.
146     */
147    private final long NOTIFICATION_REPEAT_DELAY_MS;
148    /**
149     * Whether the user has set the setting to show the 'available networks' notification.
150     */
151    private boolean mNotificationEnabled;
152    /**
153     * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
154     */
155    private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
156    /**
157     * The {@link System#currentTimeMillis()} must be at least this value for us
158     * to show the notification again.
159     */
160    private long mNotificationRepeatTime;
161    /**
162     * The Notification object given to the NotificationManager.
163     */
164    private Notification mNotification;
165    /**
166     * Whether the notification is being shown, as set by us. That is, if the
167     * user cancels the notification, we will not receive the callback so this
168     * will still be true. We only guarantee if this is false, then the
169     * notification is not showing.
170     */
171    private boolean mNotificationShown;
172    /**
173     * The number of continuous scans that must occur before consider the
174     * supplicant in a scanning state. This allows supplicant to associate with
175     * remembered networks that are in the scan results.
176     */
177    private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
178    /**
179     * The number of scans since the last network state change. When this
180     * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
181     * supplicant to actually be scanning. When the network state changes to
182     * something other than scanning, we reset this to 0.
183     */
184    private int mNumScansSinceNetworkStateChange;
185
186    /**
187     * Asynchronous channel to WifiStateMachine
188     */
189    private AsyncChannel mChannel;
190
191    /**
192     * TODO: Possibly change WifiService into an AsyncService.
193     */
194    private class WifiServiceHandler extends Handler {
195        private AsyncChannel mWshChannel;
196
197        WifiServiceHandler(android.os.Looper looper, Context context) {
198            super(looper);
199            mWshChannel = new AsyncChannel();
200            mWshChannel.connect(context, this, mWifiStateMachine.getHandler());
201        }
202
203        @Override
204        public void handleMessage(Message msg) {
205            switch (msg.what) {
206                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
207                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
208                        mChannel = mWshChannel;
209                    } else {
210                        Slog.d(TAG, "WifiServicehandler.handleMessage could not connect error=" +
211                                msg.arg1);
212                        mChannel = null;
213                    }
214                    break;
215                }
216                default: {
217                    Slog.d(TAG, "WifiServicehandler.handleMessage ignoring msg=" + msg);
218                    break;
219                }
220            }
221        }
222    }
223    WifiServiceHandler mHandler;
224
225    /**
226     * Temporary for computing UIDS that are responsible for starting WIFI.
227     * Protected by mWifiStateTracker lock.
228     */
229    private final WorkSource mTmpWorkSource = new WorkSource();
230
231    WifiService(Context context) {
232        mContext = context;
233        mWifiStateMachine = new WifiStateMachine(mContext);
234        mWifiStateMachine.enableRssiPolling(true);
235        mBatteryStats = BatteryStatsService.getService();
236
237        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
238        Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
239        mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
240
241        HandlerThread wifiThread = new HandlerThread("WifiService");
242        wifiThread.start();
243        mHandler = new WifiServiceHandler(wifiThread.getLooper(), context);
244
245        mContext.registerReceiver(
246                new BroadcastReceiver() {
247                    @Override
248                    public void onReceive(Context context, Intent intent) {
249                        // clear our flag indicating the user has overwridden airplane mode
250                        mAirplaneModeOverwridden.set(false);
251                        // on airplane disable, restore Wifi if the saved state indicates so
252                        if (!isAirplaneModeOn() && testAndClearWifiSavedState()) {
253                            persistWifiEnabled(true);
254                        }
255                        updateWifiState();
256                    }
257                },
258                new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
259
260        mContext.registerReceiver(
261            new BroadcastReceiver() {
262                @Override
263                public void onReceive(Context context, Intent intent) {
264
265                    ArrayList<String> available = intent.getStringArrayListExtra(
266                            ConnectivityManager.EXTRA_AVAILABLE_TETHER);
267                    ArrayList<String> active = intent.getStringArrayListExtra(
268                            ConnectivityManager.EXTRA_ACTIVE_TETHER);
269                    updateTetherState(available, active);
270
271                }
272            },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
273
274        IntentFilter filter = new IntentFilter();
275        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
276        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
277        filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
278
279        mContext.registerReceiver(
280                new BroadcastReceiver() {
281                    @Override
282                    public void onReceive(Context context, Intent intent) {
283                        if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
284                            // reset & clear notification on any wifi state change
285                            resetNotification();
286                        } else if (intent.getAction().equals(
287                                WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
288                            mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
289                                    WifiManager.EXTRA_NETWORK_INFO);
290                            // reset & clear notification on a network connect & disconnect
291                            switch(mNetworkInfo.getDetailedState()) {
292                                case CONNECTED:
293                                case DISCONNECTED:
294                                    resetNotification();
295                                    break;
296                            }
297                        } else if (intent.getAction().equals(
298                                WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
299                            checkAndSetNotification();
300                        }
301                    }
302                }, filter);
303
304        // Setting is in seconds
305        NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
306                Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
307        mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
308        mNotificationEnabledSettingObserver.register();
309    }
310
311    /**
312     * Check if Wi-Fi needs to be enabled and start
313     * if needed
314     *
315     * This function is used only at boot time
316     */
317    public void checkAndStartWifi() {
318        /* Start if Wi-Fi is enabled or the saved state indicates Wi-Fi was on */
319        boolean wifiEnabled = !isAirplaneModeOn()
320                && (getPersistedWifiEnabled() || testAndClearWifiSavedState());
321        Slog.i(TAG, "WifiService starting up with Wi-Fi " +
322                (wifiEnabled ? "enabled" : "disabled"));
323        setWifiEnabled(wifiEnabled);
324    }
325
326    private void updateTetherState(ArrayList<String> available, ArrayList<String> tethered) {
327
328        boolean wifiTethered = false;
329        boolean wifiAvailable = false;
330
331        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
332        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
333
334        if (mCm == null) {
335            mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
336        }
337
338        mWifiRegexs = mCm.getTetherableWifiRegexs();
339
340        for (String intf : available) {
341            for (String regex : mWifiRegexs) {
342                if (intf.matches(regex)) {
343
344                    InterfaceConfiguration ifcg = null;
345                    try {
346                        ifcg = service.getInterfaceConfig(intf);
347                        if (ifcg != null) {
348                            /* IP/netmask: 192.168.43.1/255.255.255.0 */
349                            ifcg.ipAddr = (192 << 24) + (168 << 16) + (43 << 8) + 1;
350                            ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0;
351                            ifcg.interfaceFlags = "[up]";
352
353                            service.setInterfaceConfig(intf, ifcg);
354                        }
355                    } catch (Exception e) {
356                        Slog.e(TAG, "Error configuring interface " + intf + ", :" + e);
357                        setWifiApEnabled(null, false);
358                        return;
359                    }
360
361                    if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
362                        Slog.e(TAG, "Error tethering on " + intf);
363                        setWifiApEnabled(null, false);
364                        return;
365                    }
366                    break;
367                }
368            }
369        }
370    }
371
372    private boolean testAndClearWifiSavedState() {
373        final ContentResolver cr = mContext.getContentResolver();
374        int wifiSavedState = 0;
375        try {
376            wifiSavedState = Settings.Secure.getInt(cr, Settings.Secure.WIFI_SAVED_STATE);
377            if(wifiSavedState == 1)
378                Settings.Secure.putInt(cr, Settings.Secure.WIFI_SAVED_STATE, 0);
379        } catch (Settings.SettingNotFoundException e) {
380            ;
381        }
382        return (wifiSavedState == 1);
383    }
384
385    private boolean getPersistedWifiEnabled() {
386        final ContentResolver cr = mContext.getContentResolver();
387        try {
388            return Settings.Secure.getInt(cr, Settings.Secure.WIFI_ON) == 1;
389        } catch (Settings.SettingNotFoundException e) {
390            Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, 0);
391            return false;
392        }
393    }
394
395    private void persistWifiEnabled(boolean enabled) {
396        final ContentResolver cr = mContext.getContentResolver();
397        Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0);
398    }
399
400    /**
401     * see {@link android.net.wifi.WifiManager#pingSupplicant()}
402     * @return {@code true} if the operation succeeds, {@code false} otherwise
403     */
404    public boolean pingSupplicant() {
405        enforceAccessPermission();
406        if (mChannel != null) {
407            return mWifiStateMachine.syncPingSupplicant(mChannel);
408        } else {
409            Slog.e(TAG, "mChannel is not initialized");
410            return false;
411        }
412    }
413
414    /**
415     * see {@link android.net.wifi.WifiManager#startScan()}
416     */
417    public void startScan(boolean forceActive) {
418        enforceChangePermission();
419        mWifiStateMachine.startScan(forceActive);
420    }
421
422    private void enforceAccessPermission() {
423        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
424                                                "WifiService");
425    }
426
427    private void enforceChangePermission() {
428        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
429                                                "WifiService");
430
431    }
432
433    private void enforceMulticastChangePermission() {
434        mContext.enforceCallingOrSelfPermission(
435                android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
436                "WifiService");
437    }
438
439    /**
440     * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
441     * @param enable {@code true} to enable, {@code false} to disable.
442     * @return {@code true} if the enable/disable operation was
443     *         started or is already in the queue.
444     */
445    public synchronized boolean setWifiEnabled(boolean enable) {
446        enforceChangePermission();
447
448        if (DBG) {
449            Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
450        }
451
452        // set a flag if the user is enabling Wifi while in airplane mode
453        if (enable && isAirplaneModeOn() && isAirplaneToggleable()) {
454            mAirplaneModeOverwridden.set(true);
455        }
456
457        if (enable) {
458            reportStartWorkSource();
459        }
460        mWifiStateMachine.setWifiEnabled(enable);
461
462        /*
463         * Caller might not have WRITE_SECURE_SETTINGS,
464         * only CHANGE_WIFI_STATE is enforced
465         */
466        long ident = Binder.clearCallingIdentity();
467        persistWifiEnabled(enable);
468        Binder.restoreCallingIdentity(ident);
469
470        if (enable) {
471            if (!mIsReceiverRegistered) {
472                registerForBroadcasts();
473                mIsReceiverRegistered = true;
474            }
475        } else if (mIsReceiverRegistered){
476            mContext.unregisterReceiver(mReceiver);
477            mIsReceiverRegistered = false;
478        }
479
480        return true;
481    }
482
483    /**
484     * see {@link WifiManager#getWifiState()}
485     * @return One of {@link WifiManager#WIFI_STATE_DISABLED},
486     *         {@link WifiManager#WIFI_STATE_DISABLING},
487     *         {@link WifiManager#WIFI_STATE_ENABLED},
488     *         {@link WifiManager#WIFI_STATE_ENABLING},
489     *         {@link WifiManager#WIFI_STATE_UNKNOWN}
490     */
491    public int getWifiEnabledState() {
492        enforceAccessPermission();
493        return mWifiStateMachine.syncGetWifiState();
494    }
495
496    /**
497     * see {@link android.net.wifi.WifiManager#setWifiApEnabled(WifiConfiguration, boolean)}
498     * @param wifiConfig SSID, security and channel details as
499     *        part of WifiConfiguration
500     * @param enabled true to enable and false to disable
501     * @return {@code true} if the start operation was
502     *         started or is already in the queue.
503     */
504    public synchronized boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
505        enforceChangePermission();
506
507        if (enabled) {
508            /* Use default config if there is no existing config */
509            if (wifiConfig == null && ((wifiConfig = getWifiApConfiguration()) == null)) {
510                wifiConfig = new WifiConfiguration();
511                wifiConfig.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default);
512                wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE);
513            }
514            /*
515             * Caller might not have WRITE_SECURE_SETTINGS,
516             * only CHANGE_WIFI_STATE is enforced
517             */
518            long ident = Binder.clearCallingIdentity();
519            setWifiApConfiguration(wifiConfig);
520            Binder.restoreCallingIdentity(ident);
521        }
522
523        mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled);
524
525        return true;
526    }
527
528    /**
529     * see {@link WifiManager#getWifiApState()}
530     * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
531     *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
532     *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
533     *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
534     *         {@link WifiManager#WIFI_AP_STATE_FAILED}
535     */
536    public int getWifiApEnabledState() {
537        enforceAccessPermission();
538        return mWifiStateMachine.syncGetWifiApState();
539    }
540
541    /**
542     * see {@link WifiManager#getWifiApConfiguration()}
543     * @return soft access point configuration
544     */
545    public synchronized WifiConfiguration getWifiApConfiguration() {
546        final ContentResolver cr = mContext.getContentResolver();
547        WifiConfiguration wifiConfig = new WifiConfiguration();
548        int authType;
549        try {
550            wifiConfig.SSID = Settings.Secure.getString(cr, Settings.Secure.WIFI_AP_SSID);
551            if (wifiConfig.SSID == null)
552                return null;
553            authType = Settings.Secure.getInt(cr, Settings.Secure.WIFI_AP_SECURITY);
554            wifiConfig.allowedKeyManagement.set(authType);
555            wifiConfig.preSharedKey = Settings.Secure.getString(cr, Settings.Secure.WIFI_AP_PASSWD);
556            return wifiConfig;
557        } catch (Settings.SettingNotFoundException e) {
558            Slog.e(TAG,"AP settings not found, returning");
559            return null;
560        }
561    }
562
563    /**
564     * see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)}
565     * @param wifiConfig WifiConfiguration details for soft access point
566     */
567    public synchronized void setWifiApConfiguration(WifiConfiguration wifiConfig) {
568        enforceChangePermission();
569        final ContentResolver cr = mContext.getContentResolver();
570        boolean isWpa;
571        if (wifiConfig == null)
572            return;
573        Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_SSID, wifiConfig.SSID);
574        isWpa = wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK);
575        Settings.Secure.putInt(cr,
576                               Settings.Secure.WIFI_AP_SECURITY,
577                               isWpa ? KeyMgmt.WPA_PSK : KeyMgmt.NONE);
578        if (isWpa)
579            Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_PASSWD, wifiConfig.preSharedKey);
580    }
581
582    /**
583     * see {@link android.net.wifi.WifiManager#disconnect()}
584     */
585    public void disconnect() {
586        enforceChangePermission();
587        mWifiStateMachine.disconnectCommand();
588    }
589
590    /**
591     * see {@link android.net.wifi.WifiManager#reconnect()}
592     */
593    public void reconnect() {
594        enforceChangePermission();
595        mWifiStateMachine.reconnectCommand();
596    }
597
598    /**
599     * see {@link android.net.wifi.WifiManager#reassociate()}
600     */
601    public void reassociate() {
602        enforceChangePermission();
603        mWifiStateMachine.reassociateCommand();
604    }
605
606    /**
607     * see {@link android.net.wifi.WifiManager#getConfiguredNetworks()}
608     * @return the list of configured networks
609     */
610    public List<WifiConfiguration> getConfiguredNetworks() {
611        enforceAccessPermission();
612        return mWifiStateMachine.syncGetConfiguredNetworks();
613    }
614
615    /**
616     * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)}
617     * @return the supplicant-assigned identifier for the new or updated
618     * network if the operation succeeds, or {@code -1} if it fails
619     */
620    public int addOrUpdateNetwork(WifiConfiguration config) {
621        enforceChangePermission();
622        if (mChannel != null) {
623            return mWifiStateMachine.syncAddOrUpdateNetwork(mChannel, config);
624        } else {
625            Slog.e(TAG, "mChannel is not initialized");
626            return -1;
627        }
628    }
629
630     /**
631     * See {@link android.net.wifi.WifiManager#removeNetwork(int)}
632     * @param netId the integer that identifies the network configuration
633     * to the supplicant
634     * @return {@code true} if the operation succeeded
635     */
636    public boolean removeNetwork(int netId) {
637        enforceChangePermission();
638        if (mChannel != null) {
639            return mWifiStateMachine.syncRemoveNetwork(mChannel, netId);
640        } else {
641            Slog.e(TAG, "mChannel is not initialized");
642            return false;
643        }
644    }
645
646    /**
647     * See {@link android.net.wifi.WifiManager#enableNetwork(int, boolean)}
648     * @param netId the integer that identifies the network configuration
649     * to the supplicant
650     * @param disableOthers if true, disable all other networks.
651     * @return {@code true} if the operation succeeded
652     */
653    public boolean enableNetwork(int netId, boolean disableOthers) {
654        enforceChangePermission();
655        if (mChannel != null) {
656            return mWifiStateMachine.syncEnableNetwork(mChannel, netId, disableOthers);
657        } else {
658            Slog.e(TAG, "mChannel is not initialized");
659            return false;
660        }
661    }
662
663    /**
664     * See {@link android.net.wifi.WifiManager#disableNetwork(int)}
665     * @param netId the integer that identifies the network configuration
666     * to the supplicant
667     * @return {@code true} if the operation succeeded
668     */
669    public boolean disableNetwork(int netId) {
670        enforceChangePermission();
671        if (mChannel != null) {
672            return mWifiStateMachine.syncDisableNetwork(mChannel, netId);
673        } else {
674            Slog.e(TAG, "mChannel is not initialized");
675            return false;
676        }
677    }
678
679    /**
680     * See {@link android.net.wifi.WifiManager#getConnectionInfo()}
681     * @return the Wi-Fi information, contained in {@link WifiInfo}.
682     */
683    public WifiInfo getConnectionInfo() {
684        enforceAccessPermission();
685        /*
686         * Make sure we have the latest information, by sending
687         * a status request to the supplicant.
688         */
689        return mWifiStateMachine.syncRequestConnectionInfo();
690    }
691
692    /**
693     * Return the results of the most recent access point scan, in the form of
694     * a list of {@link ScanResult} objects.
695     * @return the list of results
696     */
697    public List<ScanResult> getScanResults() {
698        enforceAccessPermission();
699        return mWifiStateMachine.syncGetScanResultsList();
700    }
701
702    /**
703     * Tell the supplicant to persist the current list of configured networks.
704     * @return {@code true} if the operation succeeded
705     *
706     * TODO: deprecate this
707     */
708    public boolean saveConfiguration() {
709        boolean result = true;
710        enforceChangePermission();
711        if (mChannel != null) {
712            return mWifiStateMachine.syncSaveConfig(mChannel);
713        } else {
714            Slog.e(TAG, "mChannel is not initialized");
715            return false;
716        }
717    }
718
719    /**
720     * Set the country code
721     * @param countryCode ISO 3166 country code.
722     * @param persist {@code true} if the setting should be remembered.
723     *
724     * The persist behavior exists so that wifi can fall back to the last
725     * persisted country code on a restart, when the locale information is
726     * not available from telephony.
727     */
728    public void setCountryCode(String countryCode, boolean persist) {
729        Slog.i(TAG, "WifiService trying to set country code to " + countryCode +
730                " with persist set to " + persist);
731        enforceChangePermission();
732        mWifiStateMachine.setCountryCode(countryCode, persist);
733    }
734
735    /**
736     * Set the operational frequency band
737     * @param band One of
738     *     {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
739     *     {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ},
740     *     {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ},
741     * @param persist {@code true} if the setting should be remembered.
742     *
743     */
744    public void setFrequencyBand(int band, boolean persist) {
745        enforceChangePermission();
746        if (!isDualBandSupported()) return;
747        Slog.i(TAG, "WifiService trying to set frequency band to " + band +
748                " with persist set to " + persist);
749        mWifiStateMachine.setFrequencyBand(band, persist);
750    }
751
752
753    /**
754     * Get the operational frequency band
755     */
756    public int getFrequencyBand() {
757        enforceAccessPermission();
758        return mWifiStateMachine.getFrequencyBand();
759    }
760
761    public boolean isDualBandSupported() {
762        //TODO: Should move towards adding a driver API that checks at runtime
763        return mContext.getResources().getBoolean(
764                com.android.internal.R.bool.config_wifi_dual_band_support);
765    }
766
767    /**
768     * Return the DHCP-assigned addresses from the last successful DHCP request,
769     * if any.
770     * @return the DHCP information
771     */
772    public DhcpInfo getDhcpInfo() {
773        enforceAccessPermission();
774        return mWifiStateMachine.syncGetDhcpInfo();
775    }
776
777    /**
778     * see {@link android.net.wifi.WifiManager#startWifi}
779     *
780     */
781    public void startWifi() {
782        enforceChangePermission();
783        /* TODO: may be add permissions for access only to connectivity service
784         * TODO: if a start issued, keep wifi alive until a stop issued irrespective
785         * of WifiLock & device idle status unless wifi enabled status is toggled
786         */
787
788        mWifiStateMachine.setDriverStart(true);
789        mWifiStateMachine.reconnectCommand();
790    }
791
792    /**
793     * see {@link android.net.wifi.WifiManager#stopWifi}
794     *
795     */
796    public void stopWifi() {
797        enforceChangePermission();
798        /* TODO: may be add permissions for access only to connectivity service
799         * TODO: if a stop is issued, wifi is brought up only by startWifi
800         * unless wifi enabled status is toggled
801         */
802        mWifiStateMachine.setDriverStart(false);
803    }
804
805
806    /**
807     * see {@link android.net.wifi.WifiManager#addToBlacklist}
808     *
809     */
810    public void addToBlacklist(String bssid) {
811        enforceChangePermission();
812
813        mWifiStateMachine.addToBlacklist(bssid);
814    }
815
816    /**
817     * see {@link android.net.wifi.WifiManager#clearBlacklist}
818     *
819     */
820    public void clearBlacklist() {
821        enforceChangePermission();
822
823        mWifiStateMachine.clearBlacklist();
824    }
825
826    public void connectNetworkWithId(int networkId) {
827        enforceChangePermission();
828        mWifiStateMachine.connectNetwork(networkId);
829    }
830
831    public void connectNetworkWithConfig(WifiConfiguration config) {
832        enforceChangePermission();
833        mWifiStateMachine.connectNetwork(config);
834    }
835
836    public void saveNetwork(WifiConfiguration config) {
837        enforceChangePermission();
838        mWifiStateMachine.saveNetwork(config);
839    }
840
841    public void forgetNetwork(int netId) {
842        enforceChangePermission();
843        mWifiStateMachine.forgetNetwork(netId);
844    }
845
846    public void startWpsPbc(String bssid) {
847        enforceChangePermission();
848        mWifiStateMachine.startWpsPbc(bssid);
849    }
850
851    public void startWpsWithPinFromAccessPoint(String bssid, int apPin) {
852        enforceChangePermission();
853        mWifiStateMachine.startWpsWithPinFromAccessPoint(bssid, apPin);
854    }
855
856    public int startWpsWithPinFromDevice(String bssid) {
857        enforceChangePermission();
858        if (mChannel != null) {
859            return mWifiStateMachine.syncStartWpsWithPinFromDevice(mChannel, bssid);
860        } else {
861            Slog.e(TAG, "mChannel is not initialized");
862            return -1;
863        }
864    }
865
866    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
867        @Override
868        public void onReceive(Context context, Intent intent) {
869            String action = intent.getAction();
870
871            long idleMillis =
872                Settings.Secure.getLong(mContext.getContentResolver(),
873                                        Settings.Secure.WIFI_IDLE_MS, DEFAULT_IDLE_MS);
874            int stayAwakeConditions =
875                Settings.System.getInt(mContext.getContentResolver(),
876                                       Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
877            if (action.equals(Intent.ACTION_SCREEN_ON)) {
878                if (DBG) {
879                    Slog.d(TAG, "ACTION_SCREEN_ON");
880                }
881                mAlarmManager.cancel(mIdleIntent);
882                mDeviceIdle = false;
883                mScreenOff = false;
884                // Once the screen is on, we are not keeping WIFI running
885                // because of any locks so clear that tracking immediately.
886                reportStartWorkSource();
887                mWifiStateMachine.enableRssiPolling(true);
888                updateWifiState();
889            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
890                if (DBG) {
891                    Slog.d(TAG, "ACTION_SCREEN_OFF");
892                }
893                mScreenOff = true;
894                mWifiStateMachine.enableRssiPolling(false);
895                /*
896                 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
897                 * AND the "stay on while plugged in" setting doesn't match the
898                 * current power conditions (i.e, not plugged in, plugged in to USB,
899                 * or plugged in to AC).
900                 */
901                if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
902                    WifiInfo info = mWifiStateMachine.syncRequestConnectionInfo();
903                    if (info.getSupplicantState() != SupplicantState.COMPLETED) {
904                        // we used to go to sleep immediately, but this caused some race conditions
905                        // we don't have time to track down for this release.  Delay instead,
906                        // but not as long as we would if connected (below)
907                        // TODO - fix the race conditions and switch back to the immediate turn-off
908                        long triggerTime = System.currentTimeMillis() + (2*60*1000); // 2 min
909                        if (DBG) {
910                            Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for 120,000 ms");
911                        }
912                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
913                        //  // do not keep Wifi awake when screen is off if Wifi is not associated
914                        //  mDeviceIdle = true;
915                        //  updateWifiState();
916                    } else {
917                        long triggerTime = System.currentTimeMillis() + idleMillis;
918                        if (DBG) {
919                            Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis
920                                    + "ms");
921                        }
922                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
923                    }
924                }
925            } else if (action.equals(ACTION_DEVICE_IDLE)) {
926                if (DBG) {
927                    Slog.d(TAG, "got ACTION_DEVICE_IDLE");
928                }
929                mDeviceIdle = true;
930                reportStartWorkSource();
931                updateWifiState();
932            } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
933                /*
934                 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
935                 * AND we are transitioning from a state in which the device was supposed
936                 * to stay awake to a state in which it is not supposed to stay awake.
937                 * If "stay awake" state is not changing, we do nothing, to avoid resetting
938                 * the already-set timer.
939                 */
940                int pluggedType = intent.getIntExtra("plugged", 0);
941                if (DBG) {
942                    Slog.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType);
943                }
944                if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) &&
945                        !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) {
946                    long triggerTime = System.currentTimeMillis() + idleMillis;
947                    if (DBG) {
948                        Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
949                    }
950                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
951                }
952                mPluggedType = pluggedType;
953            } else if (action.equals(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED)) {
954                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
955                                               BluetoothA2dp.STATE_NOT_PLAYING);
956                mWifiStateMachine.setBluetoothScanMode(state == BluetoothA2dp.STATE_PLAYING);
957            }
958        }
959
960        /**
961         * Determines whether the Wi-Fi chipset should stay awake or be put to
962         * sleep. Looks at the setting for the sleep policy and the current
963         * conditions.
964         *
965         * @see #shouldDeviceStayAwake(int, int)
966         */
967        private boolean shouldWifiStayAwake(int stayAwakeConditions, int pluggedType) {
968            int wifiSleepPolicy = Settings.System.getInt(mContext.getContentResolver(),
969                    Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_DEFAULT);
970
971            if (wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER) {
972                // Never sleep
973                return true;
974            } else if ((wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) &&
975                    (pluggedType != 0)) {
976                // Never sleep while plugged, and we're plugged
977                return true;
978            } else {
979                // Default
980                return shouldDeviceStayAwake(stayAwakeConditions, pluggedType);
981            }
982        }
983
984        /**
985         * Determine whether the bit value corresponding to {@code pluggedType} is set in
986         * the bit string {@code stayAwakeConditions}. Because a {@code pluggedType} value
987         * of {@code 0} isn't really a plugged type, but rather an indication that the
988         * device isn't plugged in at all, there is no bit value corresponding to a
989         * {@code pluggedType} value of {@code 0}. That is why we shift by
990         * {@code pluggedType - 1} instead of by {@code pluggedType}.
991         * @param stayAwakeConditions a bit string specifying which "plugged types" should
992         * keep the device (and hence Wi-Fi) awake.
993         * @param pluggedType the type of plug (USB, AC, or none) for which the check is
994         * being made
995         * @return {@code true} if {@code pluggedType} indicates that the device is
996         * supposed to stay awake, {@code false} otherwise.
997         */
998        private boolean shouldDeviceStayAwake(int stayAwakeConditions, int pluggedType) {
999            return (stayAwakeConditions & pluggedType) != 0;
1000        }
1001    };
1002
1003    private synchronized void reportStartWorkSource() {
1004        mTmpWorkSource.clear();
1005        if (mDeviceIdle) {
1006            for (int i=0; i<mLocks.mList.size(); i++) {
1007                mTmpWorkSource.add(mLocks.mList.get(i).mWorkSource);
1008            }
1009        }
1010        mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource);
1011    }
1012
1013    private void updateWifiState() {
1014        boolean wifiEnabled = getPersistedWifiEnabled();
1015        boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden.get();
1016        boolean lockHeld = mLocks.hasLocks();
1017        int strongestLockMode = WifiManager.WIFI_MODE_FULL;
1018        boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
1019        boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
1020
1021        if (lockHeld) {
1022            strongestLockMode = mLocks.getStrongestLockMode();
1023        }
1024        /* If device is not idle, lockmode cannot be scan only */
1025        if (!mDeviceIdle && strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY) {
1026            strongestLockMode = WifiManager.WIFI_MODE_FULL;
1027        }
1028
1029        /* Disable tethering when airplane mode is enabled */
1030        if (airplaneMode) {
1031            mWifiStateMachine.setWifiApEnabled(null, false);
1032        }
1033
1034        if (wifiShouldBeEnabled) {
1035            if (wifiShouldBeStarted) {
1036                reportStartWorkSource();
1037                mWifiStateMachine.setWifiEnabled(true);
1038                mWifiStateMachine.setScanOnlyMode(
1039                        strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
1040                mWifiStateMachine.setDriverStart(true);
1041                mWifiStateMachine.setHighPerfModeEnabled(strongestLockMode
1042                        == WifiManager.WIFI_MODE_FULL_HIGH_PERF);
1043            } else {
1044                mWifiStateMachine.requestCmWakeLock();
1045                mWifiStateMachine.setDriverStart(false);
1046            }
1047        } else {
1048            mWifiStateMachine.setWifiEnabled(false);
1049        }
1050    }
1051
1052    private void registerForBroadcasts() {
1053        IntentFilter intentFilter = new IntentFilter();
1054        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
1055        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
1056        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
1057        intentFilter.addAction(ACTION_DEVICE_IDLE);
1058        intentFilter.addAction(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED);
1059        mContext.registerReceiver(mReceiver, intentFilter);
1060    }
1061
1062    private boolean isAirplaneSensitive() {
1063        String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
1064                Settings.System.AIRPLANE_MODE_RADIOS);
1065        return airplaneModeRadios == null
1066            || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
1067    }
1068
1069    private boolean isAirplaneToggleable() {
1070        String toggleableRadios = Settings.System.getString(mContext.getContentResolver(),
1071                Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
1072        return toggleableRadios != null
1073            && toggleableRadios.contains(Settings.System.RADIO_WIFI);
1074    }
1075
1076    /**
1077     * Returns true if Wi-Fi is sensitive to airplane mode, and airplane mode is
1078     * currently on.
1079     * @return {@code true} if airplane mode is on.
1080     */
1081    private boolean isAirplaneModeOn() {
1082        return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
1083                Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1084    }
1085
1086    @Override
1087    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1088        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1089                != PackageManager.PERMISSION_GRANTED) {
1090            pw.println("Permission Denial: can't dump WifiService from from pid="
1091                    + Binder.getCallingPid()
1092                    + ", uid=" + Binder.getCallingUid());
1093            return;
1094        }
1095        pw.println("Wi-Fi is " + mWifiStateMachine.syncGetWifiStateByName());
1096        pw.println("Stay-awake conditions: " +
1097                Settings.System.getInt(mContext.getContentResolver(),
1098                                       Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
1099        pw.println();
1100
1101        pw.println("Internal state:");
1102        pw.println(mWifiStateMachine);
1103        pw.println();
1104        pw.println("Latest scan results:");
1105        List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
1106        if (scanResults != null && scanResults.size() != 0) {
1107            pw.println("  BSSID              Frequency   RSSI  Flags             SSID");
1108            for (ScanResult r : scanResults) {
1109                pw.printf("  %17s  %9d  %5d  %-16s  %s%n",
1110                                         r.BSSID,
1111                                         r.frequency,
1112                                         r.level,
1113                                         r.capabilities,
1114                                         r.SSID == null ? "" : r.SSID);
1115            }
1116        }
1117        pw.println();
1118        pw.println("Locks acquired: " + mFullLocksAcquired + " full, " +
1119                mFullHighPerfLocksAcquired + " full high perf, " +
1120                mScanLocksAcquired + " scan");
1121        pw.println("Locks released: " + mFullLocksReleased + " full, " +
1122                mFullHighPerfLocksReleased + " full high perf, " +
1123                mScanLocksReleased + " scan");
1124        pw.println();
1125        pw.println("Locks held:");
1126        mLocks.dump(pw);
1127    }
1128
1129    private class WifiLock extends DeathRecipient {
1130        WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) {
1131            super(lockMode, tag, binder, ws);
1132        }
1133
1134        public void binderDied() {
1135            synchronized (mLocks) {
1136                releaseWifiLockLocked(mBinder);
1137            }
1138        }
1139
1140        public String toString() {
1141            return "WifiLock{" + mTag + " type=" + mMode + " binder=" + mBinder + "}";
1142        }
1143    }
1144
1145    private class LockList {
1146        private List<WifiLock> mList;
1147
1148        private LockList() {
1149            mList = new ArrayList<WifiLock>();
1150        }
1151
1152        private synchronized boolean hasLocks() {
1153            return !mList.isEmpty();
1154        }
1155
1156        private synchronized int getStrongestLockMode() {
1157            if (mList.isEmpty()) {
1158                return WifiManager.WIFI_MODE_FULL;
1159            }
1160
1161            if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) {
1162                return WifiManager.WIFI_MODE_FULL_HIGH_PERF;
1163            }
1164
1165            if (mFullLocksAcquired > mFullLocksReleased) {
1166                return WifiManager.WIFI_MODE_FULL;
1167            }
1168
1169            return WifiManager.WIFI_MODE_SCAN_ONLY;
1170        }
1171
1172        private void addLock(WifiLock lock) {
1173            if (findLockByBinder(lock.mBinder) < 0) {
1174                mList.add(lock);
1175            }
1176        }
1177
1178        private WifiLock removeLock(IBinder binder) {
1179            int index = findLockByBinder(binder);
1180            if (index >= 0) {
1181                WifiLock ret = mList.remove(index);
1182                ret.unlinkDeathRecipient();
1183                return ret;
1184            } else {
1185                return null;
1186            }
1187        }
1188
1189        private int findLockByBinder(IBinder binder) {
1190            int size = mList.size();
1191            for (int i = size - 1; i >= 0; i--)
1192                if (mList.get(i).mBinder == binder)
1193                    return i;
1194            return -1;
1195        }
1196
1197        private void dump(PrintWriter pw) {
1198            for (WifiLock l : mList) {
1199                pw.print("    ");
1200                pw.println(l);
1201            }
1202        }
1203    }
1204
1205    void enforceWakeSourcePermission(int uid, int pid) {
1206        if (uid == android.os.Process.myUid()) {
1207            return;
1208        }
1209        mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
1210                pid, uid, null);
1211    }
1212
1213    public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws) {
1214        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
1215        if (lockMode != WifiManager.WIFI_MODE_FULL &&
1216                lockMode != WifiManager.WIFI_MODE_SCAN_ONLY &&
1217                lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF) {
1218            Slog.e(TAG, "Illegal argument, lockMode= " + lockMode);
1219            if (DBG) throw new IllegalArgumentException("lockMode=" + lockMode);
1220            return false;
1221        }
1222        if (ws != null && ws.size() == 0) {
1223            ws = null;
1224        }
1225        if (ws != null) {
1226            enforceWakeSourcePermission(Binder.getCallingUid(), Binder.getCallingPid());
1227        }
1228        if (ws == null) {
1229            ws = new WorkSource(Binder.getCallingUid());
1230        }
1231        WifiLock wifiLock = new WifiLock(lockMode, tag, binder, ws);
1232        synchronized (mLocks) {
1233            return acquireWifiLockLocked(wifiLock);
1234        }
1235    }
1236
1237    private void noteAcquireWifiLock(WifiLock wifiLock) throws RemoteException {
1238        switch(wifiLock.mMode) {
1239            case WifiManager.WIFI_MODE_FULL:
1240            case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
1241                mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource);
1242                break;
1243            case WifiManager.WIFI_MODE_SCAN_ONLY:
1244                mBatteryStats.noteScanWifiLockAcquiredFromSource(wifiLock.mWorkSource);
1245                break;
1246        }
1247    }
1248
1249    private void noteReleaseWifiLock(WifiLock wifiLock) throws RemoteException {
1250        switch(wifiLock.mMode) {
1251            case WifiManager.WIFI_MODE_FULL:
1252            case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
1253                mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
1254                break;
1255            case WifiManager.WIFI_MODE_SCAN_ONLY:
1256                mBatteryStats.noteScanWifiLockReleasedFromSource(wifiLock.mWorkSource);
1257                break;
1258        }
1259    }
1260
1261    private boolean acquireWifiLockLocked(WifiLock wifiLock) {
1262        if (DBG) Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
1263
1264        mLocks.addLock(wifiLock);
1265
1266        long ident = Binder.clearCallingIdentity();
1267        try {
1268            noteAcquireWifiLock(wifiLock);
1269            switch(wifiLock.mMode) {
1270            case WifiManager.WIFI_MODE_FULL:
1271                ++mFullLocksAcquired;
1272                break;
1273            case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
1274                ++mFullHighPerfLocksAcquired;
1275                break;
1276
1277            case WifiManager.WIFI_MODE_SCAN_ONLY:
1278                ++mScanLocksAcquired;
1279                break;
1280            }
1281
1282            // Be aggressive about adding new locks into the accounted state...
1283            // we want to over-report rather than under-report.
1284            reportStartWorkSource();
1285
1286            updateWifiState();
1287            return true;
1288        } catch (RemoteException e) {
1289            return false;
1290        } finally {
1291            Binder.restoreCallingIdentity(ident);
1292        }
1293    }
1294
1295    public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) {
1296        int uid = Binder.getCallingUid();
1297        int pid = Binder.getCallingPid();
1298        if (ws != null && ws.size() == 0) {
1299            ws = null;
1300        }
1301        if (ws != null) {
1302            enforceWakeSourcePermission(uid, pid);
1303        }
1304        long ident = Binder.clearCallingIdentity();
1305        try {
1306            synchronized (mLocks) {
1307                int index = mLocks.findLockByBinder(lock);
1308                if (index < 0) {
1309                    throw new IllegalArgumentException("Wifi lock not active");
1310                }
1311                WifiLock wl = mLocks.mList.get(index);
1312                noteReleaseWifiLock(wl);
1313                wl.mWorkSource = ws != null ? new WorkSource(ws) : new WorkSource(uid);
1314                noteAcquireWifiLock(wl);
1315            }
1316        } catch (RemoteException e) {
1317        } finally {
1318            Binder.restoreCallingIdentity(ident);
1319        }
1320    }
1321
1322    public boolean releaseWifiLock(IBinder lock) {
1323        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
1324        synchronized (mLocks) {
1325            return releaseWifiLockLocked(lock);
1326        }
1327    }
1328
1329    private boolean releaseWifiLockLocked(IBinder lock) {
1330        boolean hadLock;
1331
1332        WifiLock wifiLock = mLocks.removeLock(lock);
1333
1334        if (DBG) Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock);
1335
1336        hadLock = (wifiLock != null);
1337
1338        long ident = Binder.clearCallingIdentity();
1339        try {
1340            if (hadLock) {
1341                noteAcquireWifiLock(wifiLock);
1342                switch(wifiLock.mMode) {
1343                    case WifiManager.WIFI_MODE_FULL:
1344                        ++mFullLocksReleased;
1345                        break;
1346                    case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
1347                        ++mFullHighPerfLocksReleased;
1348                        break;
1349                    case WifiManager.WIFI_MODE_SCAN_ONLY:
1350                        ++mScanLocksReleased;
1351                        break;
1352                }
1353            }
1354
1355            // TODO - should this only happen if you hadLock?
1356            updateWifiState();
1357
1358        } catch (RemoteException e) {
1359        } finally {
1360            Binder.restoreCallingIdentity(ident);
1361        }
1362
1363        return hadLock;
1364    }
1365
1366    private abstract class DeathRecipient
1367            implements IBinder.DeathRecipient {
1368        String mTag;
1369        int mMode;
1370        IBinder mBinder;
1371        WorkSource mWorkSource;
1372
1373        DeathRecipient(int mode, String tag, IBinder binder, WorkSource ws) {
1374            super();
1375            mTag = tag;
1376            mMode = mode;
1377            mBinder = binder;
1378            mWorkSource = ws;
1379            try {
1380                mBinder.linkToDeath(this, 0);
1381            } catch (RemoteException e) {
1382                binderDied();
1383            }
1384        }
1385
1386        void unlinkDeathRecipient() {
1387            mBinder.unlinkToDeath(this, 0);
1388        }
1389    }
1390
1391    private class Multicaster extends DeathRecipient {
1392        Multicaster(String tag, IBinder binder) {
1393            super(Binder.getCallingUid(), tag, binder, null);
1394        }
1395
1396        public void binderDied() {
1397            Slog.e(TAG, "Multicaster binderDied");
1398            synchronized (mMulticasters) {
1399                int i = mMulticasters.indexOf(this);
1400                if (i != -1) {
1401                    removeMulticasterLocked(i, mMode);
1402                }
1403            }
1404        }
1405
1406        public String toString() {
1407            return "Multicaster{" + mTag + " binder=" + mBinder + "}";
1408        }
1409
1410        public int getUid() {
1411            return mMode;
1412        }
1413    }
1414
1415    public void initializeMulticastFiltering() {
1416        enforceMulticastChangePermission();
1417
1418        synchronized (mMulticasters) {
1419            // if anybody had requested filters be off, leave off
1420            if (mMulticasters.size() != 0) {
1421                return;
1422            } else {
1423                mWifiStateMachine.startPacketFiltering();
1424            }
1425        }
1426    }
1427
1428    public void acquireMulticastLock(IBinder binder, String tag) {
1429        enforceMulticastChangePermission();
1430
1431        synchronized (mMulticasters) {
1432            mMulticastEnabled++;
1433            mMulticasters.add(new Multicaster(tag, binder));
1434            // Note that we could call stopPacketFiltering only when
1435            // our new size == 1 (first call), but this function won't
1436            // be called often and by making the stopPacket call each
1437            // time we're less fragile and self-healing.
1438            mWifiStateMachine.stopPacketFiltering();
1439        }
1440
1441        int uid = Binder.getCallingUid();
1442        Long ident = Binder.clearCallingIdentity();
1443        try {
1444            mBatteryStats.noteWifiMulticastEnabled(uid);
1445        } catch (RemoteException e) {
1446        } finally {
1447            Binder.restoreCallingIdentity(ident);
1448        }
1449    }
1450
1451    public void releaseMulticastLock() {
1452        enforceMulticastChangePermission();
1453
1454        int uid = Binder.getCallingUid();
1455        synchronized (mMulticasters) {
1456            mMulticastDisabled++;
1457            int size = mMulticasters.size();
1458            for (int i = size - 1; i >= 0; i--) {
1459                Multicaster m = mMulticasters.get(i);
1460                if ((m != null) && (m.getUid() == uid)) {
1461                    removeMulticasterLocked(i, uid);
1462                }
1463            }
1464        }
1465    }
1466
1467    private void removeMulticasterLocked(int i, int uid)
1468    {
1469        Multicaster removed = mMulticasters.remove(i);
1470
1471        if (removed != null) {
1472            removed.unlinkDeathRecipient();
1473        }
1474        if (mMulticasters.size() == 0) {
1475            mWifiStateMachine.startPacketFiltering();
1476        }
1477
1478        Long ident = Binder.clearCallingIdentity();
1479        try {
1480            mBatteryStats.noteWifiMulticastDisabled(uid);
1481        } catch (RemoteException e) {
1482        } finally {
1483            Binder.restoreCallingIdentity(ident);
1484        }
1485    }
1486
1487    public boolean isMulticastEnabled() {
1488        enforceAccessPermission();
1489
1490        synchronized (mMulticasters) {
1491            return (mMulticasters.size() > 0);
1492        }
1493    }
1494
1495    private void checkAndSetNotification() {
1496        // If we shouldn't place a notification on available networks, then
1497        // don't bother doing any of the following
1498        if (!mNotificationEnabled) return;
1499
1500        State state = mNetworkInfo.getState();
1501        if ((state == NetworkInfo.State.DISCONNECTED)
1502                || (state == NetworkInfo.State.UNKNOWN)) {
1503            // Look for an open network
1504            List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
1505            if (scanResults != null) {
1506                int numOpenNetworks = 0;
1507                for (int i = scanResults.size() - 1; i >= 0; i--) {
1508                    ScanResult scanResult = scanResults.get(i);
1509
1510                    if (TextUtils.isEmpty(scanResult.capabilities)) {
1511                        numOpenNetworks++;
1512                    }
1513                }
1514
1515                if (numOpenNetworks > 0) {
1516                    if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
1517                        /*
1518                         * We've scanned continuously at least
1519                         * NUM_SCANS_BEFORE_NOTIFICATION times. The user
1520                         * probably does not have a remembered network in range,
1521                         * since otherwise supplicant would have tried to
1522                         * associate and thus resetting this counter.
1523                         */
1524                        setNotificationVisible(true, numOpenNetworks, false, 0);
1525                    }
1526                    return;
1527                }
1528            }
1529        }
1530
1531        // No open networks in range, remove the notification
1532        setNotificationVisible(false, 0, false, 0);
1533    }
1534
1535    /**
1536     * Clears variables related to tracking whether a notification has been
1537     * shown recently and clears the current notification.
1538     */
1539    private void resetNotification() {
1540        mNotificationRepeatTime = 0;
1541        mNumScansSinceNetworkStateChange = 0;
1542        setNotificationVisible(false, 0, false, 0);
1543    }
1544
1545    /**
1546     * Display or don't display a notification that there are open Wi-Fi networks.
1547     * @param visible {@code true} if notification should be visible, {@code false} otherwise
1548     * @param numNetworks the number networks seen
1549     * @param force {@code true} to force notification to be shown/not-shown,
1550     * even if it is already shown/not-shown.
1551     * @param delay time in milliseconds after which the notification should be made
1552     * visible or invisible.
1553     */
1554    private void setNotificationVisible(boolean visible, int numNetworks, boolean force,
1555            int delay) {
1556
1557        // Since we use auto cancel on the notification, when the
1558        // mNetworksAvailableNotificationShown is true, the notification may
1559        // have actually been canceled.  However, when it is false we know
1560        // for sure that it is not being shown (it will not be shown any other
1561        // place than here)
1562
1563        // If it should be hidden and it is already hidden, then noop
1564        if (!visible && !mNotificationShown && !force) {
1565            return;
1566        }
1567
1568        NotificationManager notificationManager = (NotificationManager) mContext
1569                .getSystemService(Context.NOTIFICATION_SERVICE);
1570
1571        Message message;
1572        if (visible) {
1573
1574            // Not enough time has passed to show the notification again
1575            if (System.currentTimeMillis() < mNotificationRepeatTime) {
1576                return;
1577            }
1578
1579            if (mNotification == null) {
1580                // Cache the Notification object.
1581                mNotification = new Notification();
1582                mNotification.when = 0;
1583                mNotification.icon = ICON_NETWORKS_AVAILABLE;
1584                mNotification.flags = Notification.FLAG_AUTO_CANCEL;
1585                mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
1586                        new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
1587            }
1588
1589            CharSequence title = mContext.getResources().getQuantityText(
1590                    com.android.internal.R.plurals.wifi_available, numNetworks);
1591            CharSequence details = mContext.getResources().getQuantityText(
1592                    com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
1593            mNotification.tickerText = title;
1594            mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
1595
1596            mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
1597
1598            notificationManager.notify(ICON_NETWORKS_AVAILABLE, mNotification);
1599        } else {
1600            notificationManager.cancel(ICON_NETWORKS_AVAILABLE);
1601        }
1602
1603        mNotificationShown = visible;
1604    }
1605
1606    private class NotificationEnabledSettingObserver extends ContentObserver {
1607
1608        public NotificationEnabledSettingObserver(Handler handler) {
1609            super(handler);
1610        }
1611
1612        public void register() {
1613            ContentResolver cr = mContext.getContentResolver();
1614            cr.registerContentObserver(Settings.Secure.getUriFor(
1615                Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
1616            mNotificationEnabled = getValue();
1617        }
1618
1619        @Override
1620        public void onChange(boolean selfChange) {
1621            super.onChange(selfChange);
1622
1623            mNotificationEnabled = getValue();
1624            resetNotification();
1625        }
1626
1627        private boolean getValue() {
1628            return Settings.Secure.getInt(mContext.getContentResolver(),
1629                    Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
1630        }
1631    }
1632
1633
1634}
1635