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