WifiServiceImpl.java revision fd5470391e5342daa38d00b68ccbccfeacbe6d33
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.wifi;
18
19import android.app.ActivityManager;
20import android.app.AppOpsManager;
21import android.bluetooth.BluetoothAdapter;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.pm.PackageManager;
27import android.content.pm.UserInfo;
28import android.database.ContentObserver;
29import android.net.DhcpInfo;
30import android.net.DhcpResults;
31import android.net.LinkAddress;
32import android.net.NetworkUtils;
33import android.net.RouteInfo;
34import android.net.wifi.IWifiManager;
35import android.net.wifi.ScanResult;
36import android.net.wifi.BatchedScanResult;
37import android.net.wifi.BatchedScanSettings;
38import android.net.wifi.ScanSettings;
39import android.net.wifi.WifiChannel;
40import android.net.wifi.WifiConfiguration;
41import android.net.wifi.WifiConfiguration.ProxySettings;
42import android.net.wifi.WifiInfo;
43import android.net.wifi.WifiManager;
44import android.os.Binder;
45import android.os.Handler;
46import android.os.Messenger;
47import android.os.HandlerThread;
48import android.os.IBinder;
49import android.os.Message;
50import android.os.RemoteException;
51import android.os.SystemProperties;
52import android.os.UserHandle;
53import android.os.UserManager;
54import android.os.WorkSource;
55import android.os.AsyncTask;
56import android.provider.Settings;
57import android.util.Slog;
58
59import java.io.FileNotFoundException;
60import java.io.BufferedReader;
61import java.io.FileDescriptor;
62import java.io.FileReader;
63import java.io.IOException;
64import java.io.PrintWriter;
65import java.lang.Override;
66import java.net.InetAddress;
67import java.net.Inet4Address;
68import java.util.ArrayList;
69import java.util.List;
70
71import com.android.internal.R;
72import com.android.internal.app.IBatteryStats;
73import com.android.internal.telephony.TelephonyIntents;
74import com.android.internal.util.AsyncChannel;
75import com.android.server.am.BatteryStatsService;
76
77import static com.android.server.wifi.WifiController.CMD_AIRPLANE_TOGGLED;
78import static com.android.server.wifi.WifiController.CMD_BATTERY_CHANGED;
79import static com.android.server.wifi.WifiController.CMD_EMERGENCY_MODE_CHANGED;
80import static com.android.server.wifi.WifiController.CMD_LOCKS_CHANGED;
81import static com.android.server.wifi.WifiController.CMD_SCAN_ALWAYS_MODE_CHANGED;
82import static com.android.server.wifi.WifiController.CMD_SCREEN_OFF;
83import static com.android.server.wifi.WifiController.CMD_SCREEN_ON;
84import static com.android.server.wifi.WifiController.CMD_SET_AP;
85import static com.android.server.wifi.WifiController.CMD_USER_PRESENT;
86import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED;
87/**
88 * WifiService handles remote WiFi operation requests by implementing
89 * the IWifiManager interface.
90 *
91 * @hide
92 */
93public final class WifiServiceImpl extends IWifiManager.Stub {
94    private static final String TAG = "WifiService";
95    private static final boolean DBG = false;
96
97    final WifiStateMachine mWifiStateMachine;
98
99    private final Context mContext;
100
101    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    private final AppOpsManager mAppOps;
117
118    private String mInterfaceName;
119
120    /* Tracks the open wi-fi network notification */
121    private WifiNotificationController mNotificationController;
122    /* Polls traffic stats and notifies clients */
123    private WifiTrafficPoller mTrafficPoller;
124    /* Tracks the persisted states for wi-fi & airplane mode */
125    final WifiSettingsStore mSettingsStore;
126
127    final boolean mBatchedScanSupported;
128
129    /**
130     * Asynchronous channel to WifiStateMachine
131     */
132    private AsyncChannel mWifiStateMachineChannel;
133
134    /**
135     * Handles client connections
136     */
137    private class ClientHandler extends Handler {
138
139        ClientHandler(android.os.Looper looper) {
140            super(looper);
141        }
142
143        @Override
144        public void handleMessage(Message msg) {
145            switch (msg.what) {
146                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
147                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
148                        if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
149                        // We track the clients by the Messenger
150                        // since it is expected to be always available
151                        mTrafficPoller.addClient(msg.replyTo);
152                    } else {
153                        Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
154                    }
155                    break;
156                }
157                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
158                    if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
159                        if (DBG) Slog.d(TAG, "Send failed, client connection lost");
160                    } else {
161                        if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
162                    }
163                    mTrafficPoller.removeClient(msg.replyTo);
164                    break;
165                }
166                case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
167                    AsyncChannel ac = new AsyncChannel();
168                    ac.connect(mContext, this, msg.replyTo);
169                    break;
170                }
171                /* Client commands are forwarded to state machine */
172                case WifiManager.CONNECT_NETWORK:
173                case WifiManager.SAVE_NETWORK: {
174                    WifiConfiguration config = (WifiConfiguration) msg.obj;
175                    int networkId = msg.arg1;
176                    if (config != null && config.isValid()) {
177                        // This is restricted because there is no UI for the user to
178                        // monitor/control PAC.
179                        if (config.proxySettings != ProxySettings.PAC) {
180                            if (DBG) Slog.d(TAG, "Connect with config" + config);
181                            mWifiStateMachine.sendMessage(Message.obtain(msg));
182                        } else {
183                            Slog.e(TAG,  "ClientHandler.handleMessage cannot process msg with PAC");
184                            if (msg.what == WifiManager.CONNECT_NETWORK) {
185                                replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED);
186                            } else {
187                                replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED);
188                            }
189                        }
190                    } else if (config == null
191                            && networkId != WifiConfiguration.INVALID_NETWORK_ID) {
192                        if (DBG) Slog.d(TAG, "Connect with networkId" + networkId);
193                        mWifiStateMachine.sendMessage(Message.obtain(msg));
194                    } else {
195                        Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);
196                        if (msg.what == WifiManager.CONNECT_NETWORK) {
197                            replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED);
198                        } else {
199                            replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED);
200                        }
201                    }
202                    break;
203                }
204                case WifiManager.FORGET_NETWORK:
205                case WifiManager.START_WPS:
206                case WifiManager.CANCEL_WPS:
207                case WifiManager.DISABLE_NETWORK:
208                case WifiManager.RSSI_PKTCNT_FETCH: {
209                    mWifiStateMachine.sendMessage(Message.obtain(msg));
210                    break;
211                }
212                default: {
213                    Slog.d(TAG, "ClientHandler.handleMessage ignoring msg=" + msg);
214                    break;
215                }
216            }
217        }
218
219        private void replyFailed(Message msg, int what) {
220            Message reply = msg.obtain();
221            reply.what = what;
222            reply.arg1 = WifiManager.INVALID_ARGS;
223            try {
224                msg.replyTo.send(reply);
225            } catch (RemoteException e) {
226                // There's not much we can do if reply can't be sent!
227            }
228        }
229    }
230    private ClientHandler mClientHandler;
231
232    /**
233     * Handles interaction with WifiStateMachine
234     */
235    private class WifiStateMachineHandler extends Handler {
236        private AsyncChannel mWsmChannel;
237
238        WifiStateMachineHandler(android.os.Looper looper) {
239            super(looper);
240            mWsmChannel = new AsyncChannel();
241            mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
242        }
243
244        @Override
245        public void handleMessage(Message msg) {
246            switch (msg.what) {
247                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
248                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
249                        mWifiStateMachineChannel = mWsmChannel;
250                    } else {
251                        Slog.e(TAG, "WifiStateMachine connection failure, error=" + msg.arg1);
252                        mWifiStateMachineChannel = null;
253                    }
254                    break;
255                }
256                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
257                    Slog.e(TAG, "WifiStateMachine channel lost, msg.arg1 =" + msg.arg1);
258                    mWifiStateMachineChannel = null;
259                    //Re-establish connection to state machine
260                    mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
261                    break;
262                }
263                default: {
264                    Slog.d(TAG, "WifiStateMachineHandler.handleMessage ignoring msg=" + msg);
265                    break;
266                }
267            }
268        }
269    }
270
271    WifiStateMachineHandler mWifiStateMachineHandler;
272
273    private WifiWatchdogStateMachine mWifiWatchdogStateMachine;
274
275    private WifiController mWifiController;
276
277    public WifiServiceImpl(Context context) {
278        mContext = context;
279
280        mInterfaceName =  SystemProperties.get("wifi.interface", "wlan0");
281
282        mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName);
283        mWifiStateMachine.enableRssiPolling(true);
284        mBatteryStats = BatteryStatsService.getService();
285        mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
286
287        mNotificationController = new WifiNotificationController(mContext, mWifiStateMachine);
288        mTrafficPoller = new WifiTrafficPoller(mContext, mInterfaceName);
289        mSettingsStore = new WifiSettingsStore(mContext);
290
291        HandlerThread wifiThread = new HandlerThread("WifiService");
292        wifiThread.start();
293        mClientHandler = new ClientHandler(wifiThread.getLooper());
294        mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper());
295        mWifiController = new WifiController(mContext, this, wifiThread.getLooper());
296
297        mBatchedScanSupported = mContext.getResources().getBoolean(
298                R.bool.config_wifi_batched_scan_supported);
299    }
300
301
302    /**
303     * Check if Wi-Fi needs to be enabled and start
304     * if needed
305     *
306     * This function is used only at boot time
307     */
308    public void checkAndStartWifi() {
309        /* Check if wi-fi needs to be enabled */
310        boolean wifiEnabled = mSettingsStore.isWifiToggleEnabled();
311        Slog.i(TAG, "WifiService starting up with Wi-Fi " +
312                (wifiEnabled ? "enabled" : "disabled"));
313
314        registerForScanModeChange();
315        mContext.registerReceiver(
316                new BroadcastReceiver() {
317                    @Override
318                    public void onReceive(Context context, Intent intent) {
319                        if (mSettingsStore.handleAirplaneModeToggled()) {
320                            mWifiController.sendMessage(CMD_AIRPLANE_TOGGLED);
321                        }
322                    }
323                },
324                new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
325
326        // Adding optimizations of only receiving broadcasts when wifi is enabled
327        // can result in race conditions when apps toggle wifi in the background
328        // without active user involvement. Always receive broadcasts.
329        registerForBroadcasts();
330
331        mWifiController.start();
332
333        // If we are already disabled (could be due to airplane mode), avoid changing persist
334        // state here
335        if (wifiEnabled) setWifiEnabled(wifiEnabled);
336
337        mWifiWatchdogStateMachine = WifiWatchdogStateMachine.
338               makeWifiWatchdogStateMachine(mContext);
339    }
340
341    /**
342     * see {@link android.net.wifi.WifiManager#pingSupplicant()}
343     * @return {@code true} if the operation succeeds, {@code false} otherwise
344     */
345    public boolean pingSupplicant() {
346        enforceAccessPermission();
347        if (mWifiStateMachineChannel != null) {
348            return mWifiStateMachine.syncPingSupplicant(mWifiStateMachineChannel);
349        } else {
350            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
351            return false;
352        }
353    }
354
355    /**
356     * see {@link android.net.wifi.WifiManager#getChannelList}
357     */
358    public List<WifiChannel> getChannelList() {
359        enforceAccessPermission();
360        if (mWifiStateMachineChannel != null) {
361            return mWifiStateMachine.syncGetChannelList(mWifiStateMachineChannel);
362        } else {
363            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
364            return null;
365        }
366    }
367
368    /**
369     * see {@link android.net.wifi.WifiManager#startScan}
370     * and {@link android.net.wifi.WifiManager#startCustomizedScan}
371     *
372     * @param settings If null, use default parameter, i.e. full scan.
373     * @param workSource If null, all blame is given to the calling uid.
374     */
375    public void startScan(ScanSettings settings, WorkSource workSource) {
376        enforceChangePermission();
377        if (settings != null) {
378            // TODO: should be removed once the startCustomizedScan API is opened up
379            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.LOCATION_HARDWARE,
380                    "LocationHardware");
381            settings = new ScanSettings(settings);
382            if (!settings.isValid()) {
383                Slog.e(TAG, "invalid scan setting");
384                return;
385            }
386        }
387        if (workSource != null) {
388            enforceWorkSourcePermission();
389            // WifiManager currently doesn't use names, so need to clear names out of the
390            // supplied WorkSource to allow future WorkSource combining.
391            workSource.clearNames();
392        }
393        mWifiStateMachine.startScan(Binder.getCallingUid(), settings, workSource);
394    }
395
396    private class BatchedScanRequest extends DeathRecipient {
397        final BatchedScanSettings settings;
398        final int uid;
399        final int pid;
400        final WorkSource workSource;
401
402        BatchedScanRequest(BatchedScanSettings settings, IBinder binder, WorkSource ws) {
403            super(0, null, binder, null);
404            this.settings = settings;
405            this.uid = getCallingUid();
406            this.pid = getCallingPid();
407            workSource = ws;
408        }
409        public void binderDied() {
410            stopBatchedScan(settings, uid, pid);
411        }
412        public String toString() {
413            return "BatchedScanRequest{settings=" + settings + ", binder=" + mBinder + "}";
414        }
415
416        public boolean isSameApp(int uid, int pid) {
417            return (this.uid == uid && this.pid == pid);
418        }
419    }
420
421    private final List<BatchedScanRequest> mBatchedScanners = new ArrayList<BatchedScanRequest>();
422
423    public boolean isBatchedScanSupported() {
424        return mBatchedScanSupported;
425    }
426
427    public void pollBatchedScan() {
428        enforceChangePermission();
429        if (mBatchedScanSupported == false) return;
430        mWifiStateMachine.requestBatchedScanPoll();
431    }
432
433    public String getWpsNfcConfigurationToken(int netId) {
434        enforceChangePermission();
435        return mWifiStateMachine.syncGetWpsNfcConfigurationToken(netId);
436    }
437
438    /**
439     * see {@link android.net.wifi.WifiManager#requestBatchedScan()}
440     */
441    public boolean requestBatchedScan(BatchedScanSettings requested, IBinder binder,
442            WorkSource workSource) {
443        enforceChangePermission();
444        if (workSource != null) {
445            enforceWorkSourcePermission();
446            // WifiManager currently doesn't use names, so need to clear names out of the
447            // supplied WorkSource to allow future WorkSource combining.
448            workSource.clearNames();
449        }
450        if (mBatchedScanSupported == false) return false;
451        requested = new BatchedScanSettings(requested);
452        if (requested.isInvalid()) return false;
453        BatchedScanRequest r = new BatchedScanRequest(requested, binder, workSource);
454        synchronized(mBatchedScanners) {
455            mBatchedScanners.add(r);
456            resolveBatchedScannersLocked();
457        }
458        return true;
459    }
460
461    public List<BatchedScanResult> getBatchedScanResults(String callingPackage) {
462        enforceAccessPermission();
463        if (mBatchedScanSupported == false) return new ArrayList<BatchedScanResult>();
464        int uid = Binder.getCallingUid();
465        long ident = Binder.clearCallingIdentity();
466        try {
467            if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage)
468                    != AppOpsManager.MODE_ALLOWED) {
469                return new ArrayList<BatchedScanResult>();
470            }
471            if (!isCurrentOrRelatedUser()) {
472                return new ArrayList<BatchedScanResult>();
473            }
474            return mWifiStateMachine.syncGetBatchedScanResultsList();
475        } finally {
476            Binder.restoreCallingIdentity(ident);
477        }
478    }
479
480    public void stopBatchedScan(BatchedScanSettings settings) {
481        enforceChangePermission();
482        if (mBatchedScanSupported == false) return;
483        stopBatchedScan(settings, getCallingUid(), getCallingPid());
484    }
485
486    private void stopBatchedScan(BatchedScanSettings settings, int uid, int pid) {
487        ArrayList<BatchedScanRequest> found = new ArrayList<BatchedScanRequest>();
488        synchronized(mBatchedScanners) {
489            for (BatchedScanRequest r : mBatchedScanners) {
490                if (r.isSameApp(uid, pid) && (settings == null || settings.equals(r.settings))) {
491                    found.add(r);
492                    if (settings != null) break;
493                }
494            }
495            for (BatchedScanRequest r : found) {
496                mBatchedScanners.remove(r);
497            }
498            if (found.size() != 0) {
499                resolveBatchedScannersLocked();
500            }
501        }
502    }
503
504    private void resolveBatchedScannersLocked() {
505        BatchedScanSettings setting = new BatchedScanSettings();
506        WorkSource responsibleWorkSource = null;
507        int responsibleUid = 0;
508        double responsibleCsph = 0; // Channel Scans Per Hour
509
510        if (mBatchedScanners.size() == 0) {
511            mWifiStateMachine.setBatchedScanSettings(null, 0, 0, null);
512            return;
513        }
514        for (BatchedScanRequest r : mBatchedScanners) {
515            BatchedScanSettings s = r.settings;
516
517            // evaluate responsibility
518            int currentChannelCount;
519            int currentScanInterval;
520            double currentCsph;
521
522            if (s.channelSet == null || s.channelSet.isEmpty()) {
523                // all channels - 11 B and 9 A channels roughly.
524                currentChannelCount = 9 + 11;
525            } else {
526                currentChannelCount = s.channelSet.size();
527                // these are rough est - no real need to correct for reg-domain;
528                if (s.channelSet.contains("A")) currentChannelCount += (9 - 1);
529                if (s.channelSet.contains("B")) currentChannelCount += (11 - 1);
530
531            }
532            if (s.scanIntervalSec == BatchedScanSettings.UNSPECIFIED) {
533                currentScanInterval = BatchedScanSettings.DEFAULT_INTERVAL_SEC;
534            } else {
535                currentScanInterval = s.scanIntervalSec;
536            }
537            currentCsph = 60 * 60 * currentChannelCount / currentScanInterval;
538
539            if (currentCsph > responsibleCsph) {
540                responsibleUid = r.uid;
541                responsibleWorkSource = r.workSource;
542                responsibleCsph = currentCsph;
543            }
544
545            if (s.maxScansPerBatch != BatchedScanSettings.UNSPECIFIED &&
546                    s.maxScansPerBatch < setting.maxScansPerBatch) {
547                setting.maxScansPerBatch = s.maxScansPerBatch;
548            }
549            if (s.maxApPerScan != BatchedScanSettings.UNSPECIFIED &&
550                    (setting.maxApPerScan == BatchedScanSettings.UNSPECIFIED ||
551                    s.maxApPerScan > setting.maxApPerScan)) {
552                setting.maxApPerScan = s.maxApPerScan;
553            }
554            if (s.scanIntervalSec != BatchedScanSettings.UNSPECIFIED &&
555                    s.scanIntervalSec < setting.scanIntervalSec) {
556                setting.scanIntervalSec = s.scanIntervalSec;
557            }
558            if (s.maxApForDistance != BatchedScanSettings.UNSPECIFIED &&
559                    (setting.maxApForDistance == BatchedScanSettings.UNSPECIFIED ||
560                    s.maxApForDistance > setting.maxApForDistance)) {
561                setting.maxApForDistance = s.maxApForDistance;
562            }
563            if (s.channelSet != null && s.channelSet.size() != 0) {
564                if (setting.channelSet == null || setting.channelSet.size() != 0) {
565                    if (setting.channelSet == null) setting.channelSet = new ArrayList<String>();
566                    for (String i : s.channelSet) {
567                        if (setting.channelSet.contains(i) == false) setting.channelSet.add(i);
568                    }
569                } // else, ignore the constraint - we already use all channels
570            } else {
571                if (setting.channelSet == null || setting.channelSet.size() != 0) {
572                    setting.channelSet = new ArrayList<String>();
573                }
574            }
575        }
576
577        setting.constrain();
578        mWifiStateMachine.setBatchedScanSettings(setting, responsibleUid, (int)responsibleCsph,
579                responsibleWorkSource);
580    }
581
582    private void enforceAccessPermission() {
583        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
584                                                "WifiService");
585    }
586
587    private void enforceChangePermission() {
588        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
589                                                "WifiService");
590
591    }
592
593    private void enforceWorkSourcePermission() {
594        mContext.enforceCallingPermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
595                                                "WifiService");
596
597    }
598
599    private void enforceMulticastChangePermission() {
600        mContext.enforceCallingOrSelfPermission(
601                android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
602                "WifiService");
603    }
604
605    private void enforceConnectivityInternalPermission() {
606        mContext.enforceCallingOrSelfPermission(
607                android.Manifest.permission.CONNECTIVITY_INTERNAL,
608                "ConnectivityService");
609    }
610
611    /**
612     * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
613     * @param enable {@code true} to enable, {@code false} to disable.
614     * @return {@code true} if the enable/disable operation was
615     *         started or is already in the queue.
616     */
617    public synchronized boolean setWifiEnabled(boolean enable) {
618        enforceChangePermission();
619        Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid()
620                    + ", uid=" + Binder.getCallingUid());
621        if (DBG) {
622            Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
623        }
624
625        /*
626        * Caller might not have WRITE_SECURE_SETTINGS,
627        * only CHANGE_WIFI_STATE is enforced
628        */
629
630        long ident = Binder.clearCallingIdentity();
631        try {
632            if (! mSettingsStore.handleWifiToggled(enable)) {
633                // Nothing to do if wifi cannot be toggled
634                return true;
635            }
636        } finally {
637            Binder.restoreCallingIdentity(ident);
638        }
639
640        mWifiController.sendMessage(CMD_WIFI_TOGGLED);
641        return true;
642    }
643
644    /**
645     * see {@link WifiManager#getWifiState()}
646     * @return One of {@link WifiManager#WIFI_STATE_DISABLED},
647     *         {@link WifiManager#WIFI_STATE_DISABLING},
648     *         {@link WifiManager#WIFI_STATE_ENABLED},
649     *         {@link WifiManager#WIFI_STATE_ENABLING},
650     *         {@link WifiManager#WIFI_STATE_UNKNOWN}
651     */
652    public int getWifiEnabledState() {
653        enforceAccessPermission();
654        return mWifiStateMachine.syncGetWifiState();
655    }
656
657    /**
658     * see {@link android.net.wifi.WifiManager#setWifiApEnabled(WifiConfiguration, boolean)}
659     * @param wifiConfig SSID, security and channel details as
660     *        part of WifiConfiguration
661     * @param enabled true to enable and false to disable
662     */
663    public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
664        enforceChangePermission();
665        // null wifiConfig is a meaningful input for CMD_SET_AP
666        if (wifiConfig == null || wifiConfig.isValid()) {
667            mWifiController.obtainMessage(CMD_SET_AP, enabled ? 1 : 0, 0, wifiConfig).sendToTarget();
668        } else {
669            Slog.e(TAG, "Invalid WifiConfiguration");
670        }
671    }
672
673    /**
674     * see {@link WifiManager#getWifiApState()}
675     * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
676     *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
677     *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
678     *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
679     *         {@link WifiManager#WIFI_AP_STATE_FAILED}
680     */
681    public int getWifiApEnabledState() {
682        enforceAccessPermission();
683        return mWifiStateMachine.syncGetWifiApState();
684    }
685
686    /**
687     * see {@link WifiManager#getWifiApConfiguration()}
688     * @return soft access point configuration
689     */
690    public WifiConfiguration getWifiApConfiguration() {
691        enforceAccessPermission();
692        return mWifiStateMachine.syncGetWifiApConfiguration();
693    }
694
695    /**
696     * see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)}
697     * @param wifiConfig WifiConfiguration details for soft access point
698     */
699    public void setWifiApConfiguration(WifiConfiguration wifiConfig) {
700        enforceChangePermission();
701        if (wifiConfig == null)
702            return;
703        if (wifiConfig.isValid()) {
704            mWifiStateMachine.setWifiApConfiguration(wifiConfig);
705        } else {
706            Slog.e(TAG, "Invalid WifiConfiguration");
707        }
708    }
709
710    /**
711     * @param enable {@code true} to enable, {@code false} to disable.
712     * @return {@code true} if the enable/disable operation was
713     *         started or is already in the queue.
714     */
715    public boolean isScanAlwaysAvailable() {
716        enforceAccessPermission();
717        return mSettingsStore.isScanAlwaysAvailable();
718    }
719
720    /**
721     * see {@link android.net.wifi.WifiManager#disconnect()}
722     */
723    public void disconnect() {
724        enforceChangePermission();
725        mWifiStateMachine.disconnectCommand();
726    }
727
728    /**
729     * see {@link android.net.wifi.WifiManager#reconnect()}
730     */
731    public void reconnect() {
732        enforceChangePermission();
733        mWifiStateMachine.reconnectCommand();
734    }
735
736    /**
737     * see {@link android.net.wifi.WifiManager#reassociate()}
738     */
739    public void reassociate() {
740        enforceChangePermission();
741        mWifiStateMachine.reassociateCommand();
742    }
743
744    /**
745     * see {@link android.net.wifi.WifiManager#getConfiguredNetworks()}
746     * @return the list of configured networks
747     */
748    public List<WifiConfiguration> getConfiguredNetworks() {
749        enforceAccessPermission();
750        if (mWifiStateMachineChannel != null) {
751            return mWifiStateMachine.syncGetConfiguredNetworks(mWifiStateMachineChannel);
752        } else {
753            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
754            return null;
755        }
756    }
757
758    /**
759     * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)}
760     * @return the supplicant-assigned identifier for the new or updated
761     * network if the operation succeeds, or {@code -1} if it fails
762     */
763    public int addOrUpdateNetwork(WifiConfiguration config) {
764        enforceChangePermission();
765        if (config.proxySettings == ProxySettings.PAC) {
766            enforceConnectivityInternalPermission();
767        }
768        if (config.isValid()) {
769            if (mWifiStateMachineChannel != null) {
770                return mWifiStateMachine.syncAddOrUpdateNetwork(mWifiStateMachineChannel, config);
771            } else {
772                Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
773                return -1;
774            }
775        } else {
776            Slog.e(TAG, "bad network configuration");
777            return -1;
778        }
779    }
780
781     /**
782     * See {@link android.net.wifi.WifiManager#removeNetwork(int)}
783     * @param netId the integer that identifies the network configuration
784     * to the supplicant
785     * @return {@code true} if the operation succeeded
786     */
787    public boolean removeNetwork(int netId) {
788        enforceChangePermission();
789        if (mWifiStateMachineChannel != null) {
790            return mWifiStateMachine.syncRemoveNetwork(mWifiStateMachineChannel, netId);
791        } else {
792            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
793            return false;
794        }
795    }
796
797    /**
798     * See {@link android.net.wifi.WifiManager#enableNetwork(int, boolean)}
799     * @param netId the integer that identifies the network configuration
800     * to the supplicant
801     * @param disableOthers if true, disable all other networks.
802     * @return {@code true} if the operation succeeded
803     */
804    public boolean enableNetwork(int netId, boolean disableOthers) {
805        enforceChangePermission();
806        if (mWifiStateMachineChannel != null) {
807            return mWifiStateMachine.syncEnableNetwork(mWifiStateMachineChannel, netId,
808                    disableOthers);
809        } else {
810            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
811            return false;
812        }
813    }
814
815    /**
816     * See {@link android.net.wifi.WifiManager#disableNetwork(int)}
817     * @param netId the integer that identifies the network configuration
818     * to the supplicant
819     * @return {@code true} if the operation succeeded
820     */
821    public boolean disableNetwork(int netId) {
822        enforceChangePermission();
823        if (mWifiStateMachineChannel != null) {
824            return mWifiStateMachine.syncDisableNetwork(mWifiStateMachineChannel, netId);
825        } else {
826            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
827            return false;
828        }
829    }
830
831    /**
832     * See {@link android.net.wifi.WifiManager#getConnectionInfo()}
833     * @return the Wi-Fi information, contained in {@link WifiInfo}.
834     */
835    public WifiInfo getConnectionInfo() {
836        enforceAccessPermission();
837        /*
838         * Make sure we have the latest information, by sending
839         * a status request to the supplicant.
840         */
841        return mWifiStateMachine.syncRequestConnectionInfo();
842    }
843
844    /**
845     * Return the results of the most recent access point scan, in the form of
846     * a list of {@link ScanResult} objects.
847     * @return the list of results
848     */
849    public List<ScanResult> getScanResults(String callingPackage) {
850        enforceAccessPermission();
851        int userId = UserHandle.getCallingUserId();
852        int uid = Binder.getCallingUid();
853        long ident = Binder.clearCallingIdentity();
854        try {
855            if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage)
856                    != AppOpsManager.MODE_ALLOWED) {
857                return new ArrayList<ScanResult>();
858            }
859            if (!isCurrentOrRelatedUser()) {
860                return new ArrayList<ScanResult>();
861            }
862            return mWifiStateMachine.syncGetScanResultsList();
863        } finally {
864            Binder.restoreCallingIdentity(ident);
865        }
866    }
867
868    /**
869     * Returns true if the calling user is the current one or one related to it.
870     */
871    private boolean isCurrentOrRelatedUser() {
872        int userId = UserHandle.getCallingUserId();
873        int currentUser = ActivityManager.getCurrentUser();
874        if (userId == currentUser) {
875            return true;
876        }
877        List<UserInfo> relatedUsers = UserManager.get(mContext).getRelatedUsers(currentUser);
878        for (UserInfo relatedUser : relatedUsers) {
879            if (userId == relatedUser.id) {
880                return true;
881            }
882        }
883        return false;
884    }
885
886    /**
887     * Tell the supplicant to persist the current list of configured networks.
888     * @return {@code true} if the operation succeeded
889     *
890     * TODO: deprecate this
891     */
892    public boolean saveConfiguration() {
893        boolean result = true;
894        enforceChangePermission();
895        if (mWifiStateMachineChannel != null) {
896            return mWifiStateMachine.syncSaveConfig(mWifiStateMachineChannel);
897        } else {
898            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
899            return false;
900        }
901    }
902
903    /**
904     * Set the country code
905     * @param countryCode ISO 3166 country code.
906     * @param persist {@code true} if the setting should be remembered.
907     *
908     * The persist behavior exists so that wifi can fall back to the last
909     * persisted country code on a restart, when the locale information is
910     * not available from telephony.
911     */
912    public void setCountryCode(String countryCode, boolean persist) {
913        Slog.i(TAG, "WifiService trying to set country code to " + countryCode +
914                " with persist set to " + persist);
915        enforceChangePermission();
916        final long token = Binder.clearCallingIdentity();
917        try {
918            mWifiStateMachine.setCountryCode(countryCode, persist);
919        } finally {
920            Binder.restoreCallingIdentity(token);
921        }
922    }
923
924    /**
925     * Set the operational frequency band
926     * @param band One of
927     *     {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
928     *     {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ},
929     *     {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ},
930     * @param persist {@code true} if the setting should be remembered.
931     *
932     */
933    public void setFrequencyBand(int band, boolean persist) {
934        enforceChangePermission();
935        if (!isDualBandSupported()) return;
936        Slog.i(TAG, "WifiService trying to set frequency band to " + band +
937                " with persist set to " + persist);
938        final long token = Binder.clearCallingIdentity();
939        try {
940            mWifiStateMachine.setFrequencyBand(band, persist);
941        } finally {
942            Binder.restoreCallingIdentity(token);
943        }
944    }
945
946
947    /**
948     * Get the operational frequency band
949     */
950    public int getFrequencyBand() {
951        enforceAccessPermission();
952        return mWifiStateMachine.getFrequencyBand();
953    }
954
955    public boolean isDualBandSupported() {
956        //TODO: Should move towards adding a driver API that checks at runtime
957        return mContext.getResources().getBoolean(
958                com.android.internal.R.bool.config_wifi_dual_band_support);
959    }
960
961    /**
962     * Return the DHCP-assigned addresses from the last successful DHCP request,
963     * if any.
964     * @return the DHCP information
965     * @deprecated
966     */
967    public DhcpInfo getDhcpInfo() {
968        enforceAccessPermission();
969        DhcpResults dhcpResults = mWifiStateMachine.syncGetDhcpResults();
970        if (dhcpResults.linkProperties == null) return null;
971
972        DhcpInfo info = new DhcpInfo();
973        for (LinkAddress la : dhcpResults.linkProperties.getLinkAddresses()) {
974            InetAddress addr = la.getAddress();
975            if (addr instanceof Inet4Address) {
976                info.ipAddress = NetworkUtils.inetAddressToInt((Inet4Address)addr);
977                break;
978            }
979        }
980        for (RouteInfo r : dhcpResults.linkProperties.getRoutes()) {
981            if (r.isDefaultRoute()) {
982                InetAddress gateway = r.getGateway();
983                if (gateway instanceof Inet4Address) {
984                    info.gateway = NetworkUtils.inetAddressToInt((Inet4Address)gateway);
985                }
986            } else if (r.hasGateway() == false) {
987                LinkAddress dest = r.getDestination();
988                if (dest.getAddress() instanceof Inet4Address) {
989                    info.netmask = NetworkUtils.prefixLengthToNetmaskInt(
990                            dest.getNetworkPrefixLength());
991                }
992            }
993        }
994        int dnsFound = 0;
995        for (InetAddress dns : dhcpResults.linkProperties.getDnses()) {
996            if (dns instanceof Inet4Address) {
997                if (dnsFound == 0) {
998                    info.dns1 = NetworkUtils.inetAddressToInt((Inet4Address)dns);
999                } else {
1000                    info.dns2 = NetworkUtils.inetAddressToInt((Inet4Address)dns);
1001                }
1002                if (++dnsFound > 1) break;
1003            }
1004        }
1005        InetAddress serverAddress = dhcpResults.serverAddress;
1006        if (serverAddress instanceof Inet4Address) {
1007            info.serverAddress = NetworkUtils.inetAddressToInt((Inet4Address)serverAddress);
1008        }
1009        info.leaseDuration = dhcpResults.leaseDuration;
1010
1011        return info;
1012    }
1013
1014    /**
1015     * see {@link android.net.wifi.WifiManager#startWifi}
1016     *
1017     */
1018    public void startWifi() {
1019        enforceConnectivityInternalPermission();
1020        /* TODO: may be add permissions for access only to connectivity service
1021         * TODO: if a start issued, keep wifi alive until a stop issued irrespective
1022         * of WifiLock & device idle status unless wifi enabled status is toggled
1023         */
1024
1025        mWifiStateMachine.setDriverStart(true);
1026        mWifiStateMachine.reconnectCommand();
1027    }
1028
1029    /**
1030     * see {@link android.net.wifi.WifiManager#stopWifi}
1031     *
1032     */
1033    public void stopWifi() {
1034        enforceConnectivityInternalPermission();
1035        /*
1036         * TODO: if a stop is issued, wifi is brought up only by startWifi
1037         * unless wifi enabled status is toggled
1038         */
1039        mWifiStateMachine.setDriverStart(false);
1040    }
1041
1042    /**
1043     * see {@link android.net.wifi.WifiManager#addToBlacklist}
1044     *
1045     */
1046    public void addToBlacklist(String bssid) {
1047        enforceChangePermission();
1048
1049        mWifiStateMachine.addToBlacklist(bssid);
1050    }
1051
1052    /**
1053     * see {@link android.net.wifi.WifiManager#clearBlacklist}
1054     *
1055     */
1056    public void clearBlacklist() {
1057        enforceChangePermission();
1058
1059        mWifiStateMachine.clearBlacklist();
1060    }
1061
1062    /**
1063     * enable TDLS for the local NIC to remote NIC
1064     * The APPs don't know the remote MAC address to identify NIC though,
1065     * so we need to do additional work to find it from remote IP address
1066     */
1067
1068    class TdlsTaskParams {
1069        public String remoteIpAddress;
1070        public boolean enable;
1071    }
1072
1073    class TdlsTask extends AsyncTask<TdlsTaskParams, Integer, Integer> {
1074        @Override
1075        protected Integer doInBackground(TdlsTaskParams... params) {
1076
1077            // Retrieve parameters for the call
1078            TdlsTaskParams param = params[0];
1079            String remoteIpAddress = param.remoteIpAddress.trim();
1080            boolean enable = param.enable;
1081
1082            // Get MAC address of Remote IP
1083            String macAddress = null;
1084
1085            BufferedReader reader = null;
1086
1087            try {
1088                reader = new BufferedReader(new FileReader("/proc/net/arp"));
1089
1090                // Skip over the line bearing colum titles
1091                String line = reader.readLine();
1092
1093                while ((line = reader.readLine()) != null) {
1094                    String[] tokens = line.split("[ ]+");
1095                    if (tokens.length < 6) {
1096                        continue;
1097                    }
1098
1099                    // ARP column format is
1100                    // Address HWType HWAddress Flags Mask IFace
1101                    String ip = tokens[0];
1102                    String mac = tokens[3];
1103
1104                    if (remoteIpAddress.equals(ip)) {
1105                        macAddress = mac;
1106                        break;
1107                    }
1108                }
1109
1110                if (macAddress == null) {
1111                    Slog.w(TAG, "Did not find remoteAddress {" + remoteIpAddress + "} in " +
1112                            "/proc/net/arp");
1113                } else {
1114                    enableTdlsWithMacAddress(macAddress, enable);
1115                }
1116
1117            } catch (FileNotFoundException e) {
1118                Slog.e(TAG, "Could not open /proc/net/arp to lookup mac address");
1119            } catch (IOException e) {
1120                Slog.e(TAG, "Could not read /proc/net/arp to lookup mac address");
1121            } finally {
1122                try {
1123                    if (reader != null) {
1124                        reader.close();
1125                    }
1126                }
1127                catch (IOException e) {
1128                    // Do nothing
1129                }
1130            }
1131
1132            return 0;
1133        }
1134    }
1135
1136    public void enableTdls(String remoteAddress, boolean enable) {
1137        TdlsTaskParams params = new TdlsTaskParams();
1138        params.remoteIpAddress = remoteAddress;
1139        params.enable = enable;
1140        new TdlsTask().execute(params);
1141    }
1142
1143
1144    public void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable) {
1145        mWifiStateMachine.enableTdls(remoteMacAddress, enable);
1146    }
1147
1148    /**
1149     * Get a reference to handler. This is used by a client to establish
1150     * an AsyncChannel communication with WifiService
1151     */
1152    public Messenger getWifiServiceMessenger() {
1153        enforceAccessPermission();
1154        enforceChangePermission();
1155        return new Messenger(mClientHandler);
1156    }
1157
1158    /** Get a reference to WifiStateMachine handler for AsyncChannel communication */
1159    public Messenger getWifiStateMachineMessenger() {
1160        enforceAccessPermission();
1161        enforceChangePermission();
1162        return mWifiStateMachine.getMessenger();
1163    }
1164
1165    /**
1166     * Get the IP and proxy configuration file
1167     */
1168    public String getConfigFile() {
1169        enforceAccessPermission();
1170        return mWifiStateMachine.getConfigFile();
1171    }
1172
1173    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1174        @Override
1175        public void onReceive(Context context, Intent intent) {
1176            String action = intent.getAction();
1177            if (action.equals(Intent.ACTION_SCREEN_ON)) {
1178                mWifiController.sendMessage(CMD_SCREEN_ON);
1179            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
1180                mWifiController.sendMessage(CMD_USER_PRESENT);
1181            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1182                mWifiController.sendMessage(CMD_SCREEN_OFF);
1183            } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
1184                int pluggedType = intent.getIntExtra("plugged", 0);
1185                mWifiController.sendMessage(CMD_BATTERY_CHANGED, pluggedType, 0, null);
1186            } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
1187                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
1188                        BluetoothAdapter.STATE_DISCONNECTED);
1189                mWifiStateMachine.sendBluetoothAdapterStateChange(state);
1190            } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
1191                boolean emergencyMode = intent.getBooleanExtra("phoneinECMState", false);
1192                mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, emergencyMode ? 1 : 0, 0);
1193            }
1194        }
1195    };
1196
1197    /**
1198     * Observes settings changes to scan always mode.
1199     */
1200    private void registerForScanModeChange() {
1201        ContentObserver contentObserver = new ContentObserver(null) {
1202            @Override
1203            public void onChange(boolean selfChange) {
1204                mSettingsStore.handleWifiScanAlwaysAvailableToggled();
1205                mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED);
1206            }
1207        };
1208
1209        mContext.getContentResolver().registerContentObserver(
1210                Settings.Global.getUriFor(Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE),
1211                false, contentObserver);
1212    }
1213
1214    private void registerForBroadcasts() {
1215        IntentFilter intentFilter = new IntentFilter();
1216        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
1217        intentFilter.addAction(Intent.ACTION_USER_PRESENT);
1218        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
1219        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
1220        intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
1221        intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
1222        intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
1223        mContext.registerReceiver(mReceiver, intentFilter);
1224    }
1225
1226    @Override
1227    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1228        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1229                != PackageManager.PERMISSION_GRANTED) {
1230            pw.println("Permission Denial: can't dump WifiService from from pid="
1231                    + Binder.getCallingPid()
1232                    + ", uid=" + Binder.getCallingUid());
1233            return;
1234        }
1235        pw.println("Wi-Fi is " + mWifiStateMachine.syncGetWifiStateByName());
1236        pw.println("Stay-awake conditions: " +
1237                Settings.Global.getInt(mContext.getContentResolver(),
1238                                       Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0));
1239        pw.println("mMulticastEnabled " + mMulticastEnabled);
1240        pw.println("mMulticastDisabled " + mMulticastDisabled);
1241        mWifiController.dump(fd, pw, args);
1242        mSettingsStore.dump(fd, pw, args);
1243        mNotificationController.dump(fd, pw, args);
1244        mTrafficPoller.dump(fd, pw, args);
1245
1246        pw.println("Latest scan results:");
1247        List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
1248        if (scanResults != null && scanResults.size() != 0) {
1249            pw.println("  BSSID              Frequency   RSSI  Flags             SSID");
1250            for (ScanResult r : scanResults) {
1251                pw.printf("  %17s  %9d  %5d  %-16s  %s%n",
1252                                         r.BSSID,
1253                                         r.frequency,
1254                                         r.level,
1255                                         r.capabilities,
1256                                         r.SSID == null ? "" : r.SSID);
1257            }
1258        }
1259        pw.println();
1260        pw.println("Locks acquired: " + mFullLocksAcquired + " full, " +
1261                mFullHighPerfLocksAcquired + " full high perf, " +
1262                mScanLocksAcquired + " scan");
1263        pw.println("Locks released: " + mFullLocksReleased + " full, " +
1264                mFullHighPerfLocksReleased + " full high perf, " +
1265                mScanLocksReleased + " scan");
1266        pw.println();
1267        pw.println("Locks held:");
1268        mLocks.dump(pw);
1269
1270        mWifiWatchdogStateMachine.dump(fd, pw, args);
1271        pw.println();
1272        mWifiStateMachine.dump(fd, pw, args);
1273        pw.println();
1274    }
1275
1276    private class WifiLock extends DeathRecipient {
1277        WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) {
1278            super(lockMode, tag, binder, ws);
1279        }
1280
1281        public void binderDied() {
1282            synchronized (mLocks) {
1283                releaseWifiLockLocked(mBinder);
1284            }
1285        }
1286
1287        public String toString() {
1288            return "WifiLock{" + mTag + " type=" + mMode + " binder=" + mBinder + "}";
1289        }
1290    }
1291
1292    class LockList {
1293        private List<WifiLock> mList;
1294
1295        private LockList() {
1296            mList = new ArrayList<WifiLock>();
1297        }
1298
1299        synchronized boolean hasLocks() {
1300            return !mList.isEmpty();
1301        }
1302
1303        synchronized int getStrongestLockMode() {
1304            if (mList.isEmpty()) {
1305                return WifiManager.WIFI_MODE_FULL;
1306            }
1307
1308            if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) {
1309                return WifiManager.WIFI_MODE_FULL_HIGH_PERF;
1310            }
1311
1312            if (mFullLocksAcquired > mFullLocksReleased) {
1313                return WifiManager.WIFI_MODE_FULL;
1314            }
1315
1316            return WifiManager.WIFI_MODE_SCAN_ONLY;
1317        }
1318
1319        synchronized void updateWorkSource(WorkSource ws) {
1320            for (int i = 0; i < mLocks.mList.size(); i++) {
1321                ws.add(mLocks.mList.get(i).mWorkSource);
1322            }
1323        }
1324
1325        private void addLock(WifiLock lock) {
1326            if (findLockByBinder(lock.mBinder) < 0) {
1327                mList.add(lock);
1328            }
1329        }
1330
1331        private WifiLock removeLock(IBinder binder) {
1332            int index = findLockByBinder(binder);
1333            if (index >= 0) {
1334                WifiLock ret = mList.remove(index);
1335                ret.unlinkDeathRecipient();
1336                return ret;
1337            } else {
1338                return null;
1339            }
1340        }
1341
1342        private int findLockByBinder(IBinder binder) {
1343            int size = mList.size();
1344            for (int i = size - 1; i >= 0; i--) {
1345                if (mList.get(i).mBinder == binder)
1346                    return i;
1347            }
1348            return -1;
1349        }
1350
1351        private void dump(PrintWriter pw) {
1352            for (WifiLock l : mList) {
1353                pw.print("    ");
1354                pw.println(l);
1355            }
1356        }
1357    }
1358
1359    void enforceWakeSourcePermission(int uid, int pid) {
1360        if (uid == android.os.Process.myUid()) {
1361            return;
1362        }
1363        mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
1364                pid, uid, null);
1365    }
1366
1367    public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws) {
1368        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
1369        if (lockMode != WifiManager.WIFI_MODE_FULL &&
1370                lockMode != WifiManager.WIFI_MODE_SCAN_ONLY &&
1371                lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF) {
1372            Slog.e(TAG, "Illegal argument, lockMode= " + lockMode);
1373            if (DBG) throw new IllegalArgumentException("lockMode=" + lockMode);
1374            return false;
1375        }
1376        if (ws != null && ws.size() == 0) {
1377            ws = null;
1378        }
1379        if (ws != null) {
1380            enforceWakeSourcePermission(Binder.getCallingUid(), Binder.getCallingPid());
1381        }
1382        if (ws == null) {
1383            ws = new WorkSource(Binder.getCallingUid());
1384        }
1385        WifiLock wifiLock = new WifiLock(lockMode, tag, binder, ws);
1386        synchronized (mLocks) {
1387            return acquireWifiLockLocked(wifiLock);
1388        }
1389    }
1390
1391    private void noteAcquireWifiLock(WifiLock wifiLock) throws RemoteException {
1392        switch(wifiLock.mMode) {
1393            case WifiManager.WIFI_MODE_FULL:
1394            case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
1395            case WifiManager.WIFI_MODE_SCAN_ONLY:
1396                mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource);
1397                break;
1398        }
1399    }
1400
1401    private void noteReleaseWifiLock(WifiLock wifiLock) throws RemoteException {
1402        switch(wifiLock.mMode) {
1403            case WifiManager.WIFI_MODE_FULL:
1404            case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
1405            case WifiManager.WIFI_MODE_SCAN_ONLY:
1406                mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
1407                break;
1408        }
1409    }
1410
1411    private boolean acquireWifiLockLocked(WifiLock wifiLock) {
1412        if (DBG) Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
1413
1414        mLocks.addLock(wifiLock);
1415
1416        long ident = Binder.clearCallingIdentity();
1417        try {
1418            noteAcquireWifiLock(wifiLock);
1419            switch(wifiLock.mMode) {
1420            case WifiManager.WIFI_MODE_FULL:
1421                ++mFullLocksAcquired;
1422                break;
1423            case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
1424                ++mFullHighPerfLocksAcquired;
1425                break;
1426
1427            case WifiManager.WIFI_MODE_SCAN_ONLY:
1428                ++mScanLocksAcquired;
1429                break;
1430            }
1431            mWifiController.sendMessage(CMD_LOCKS_CHANGED);
1432            return true;
1433        } catch (RemoteException e) {
1434            return false;
1435        } finally {
1436            Binder.restoreCallingIdentity(ident);
1437        }
1438    }
1439
1440    public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) {
1441        int uid = Binder.getCallingUid();
1442        int pid = Binder.getCallingPid();
1443        if (ws != null && ws.size() == 0) {
1444            ws = null;
1445        }
1446        if (ws != null) {
1447            enforceWakeSourcePermission(uid, pid);
1448        }
1449        long ident = Binder.clearCallingIdentity();
1450        try {
1451            synchronized (mLocks) {
1452                int index = mLocks.findLockByBinder(lock);
1453                if (index < 0) {
1454                    throw new IllegalArgumentException("Wifi lock not active");
1455                }
1456                WifiLock wl = mLocks.mList.get(index);
1457                noteReleaseWifiLock(wl);
1458                wl.mWorkSource = ws != null ? new WorkSource(ws) : new WorkSource(uid);
1459                noteAcquireWifiLock(wl);
1460            }
1461        } catch (RemoteException e) {
1462        } finally {
1463            Binder.restoreCallingIdentity(ident);
1464        }
1465    }
1466
1467    public boolean releaseWifiLock(IBinder lock) {
1468        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
1469        synchronized (mLocks) {
1470            return releaseWifiLockLocked(lock);
1471        }
1472    }
1473
1474    private boolean releaseWifiLockLocked(IBinder lock) {
1475        boolean hadLock;
1476
1477        WifiLock wifiLock = mLocks.removeLock(lock);
1478
1479        if (DBG) Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock);
1480
1481        hadLock = (wifiLock != null);
1482
1483        long ident = Binder.clearCallingIdentity();
1484        try {
1485            if (hadLock) {
1486                noteReleaseWifiLock(wifiLock);
1487                switch(wifiLock.mMode) {
1488                    case WifiManager.WIFI_MODE_FULL:
1489                        ++mFullLocksReleased;
1490                        break;
1491                    case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
1492                        ++mFullHighPerfLocksReleased;
1493                        break;
1494                    case WifiManager.WIFI_MODE_SCAN_ONLY:
1495                        ++mScanLocksReleased;
1496                        break;
1497                }
1498                mWifiController.sendMessage(CMD_LOCKS_CHANGED);
1499            }
1500        } catch (RemoteException e) {
1501        } finally {
1502            Binder.restoreCallingIdentity(ident);
1503        }
1504
1505        return hadLock;
1506    }
1507
1508    private abstract class DeathRecipient
1509            implements IBinder.DeathRecipient {
1510        String mTag;
1511        int mMode;
1512        IBinder mBinder;
1513        WorkSource mWorkSource;
1514
1515        DeathRecipient(int mode, String tag, IBinder binder, WorkSource ws) {
1516            super();
1517            mTag = tag;
1518            mMode = mode;
1519            mBinder = binder;
1520            mWorkSource = ws;
1521            try {
1522                mBinder.linkToDeath(this, 0);
1523            } catch (RemoteException e) {
1524                binderDied();
1525            }
1526        }
1527
1528        void unlinkDeathRecipient() {
1529            mBinder.unlinkToDeath(this, 0);
1530        }
1531    }
1532
1533    private class Multicaster extends DeathRecipient {
1534        Multicaster(String tag, IBinder binder) {
1535            super(Binder.getCallingUid(), tag, binder, null);
1536        }
1537
1538        public void binderDied() {
1539            Slog.e(TAG, "Multicaster binderDied");
1540            synchronized (mMulticasters) {
1541                int i = mMulticasters.indexOf(this);
1542                if (i != -1) {
1543                    removeMulticasterLocked(i, mMode);
1544                }
1545            }
1546        }
1547
1548        public String toString() {
1549            return "Multicaster{" + mTag + " binder=" + mBinder + "}";
1550        }
1551
1552        public int getUid() {
1553            return mMode;
1554        }
1555    }
1556
1557    public void initializeMulticastFiltering() {
1558        enforceMulticastChangePermission();
1559
1560        synchronized (mMulticasters) {
1561            // if anybody had requested filters be off, leave off
1562            if (mMulticasters.size() != 0) {
1563                return;
1564            } else {
1565                mWifiStateMachine.startFilteringMulticastV4Packets();
1566            }
1567        }
1568    }
1569
1570    public void acquireMulticastLock(IBinder binder, String tag) {
1571        enforceMulticastChangePermission();
1572
1573        synchronized (mMulticasters) {
1574            mMulticastEnabled++;
1575            mMulticasters.add(new Multicaster(tag, binder));
1576            // Note that we could call stopFilteringMulticastV4Packets only when
1577            // our new size == 1 (first call), but this function won't
1578            // be called often and by making the stopPacket call each
1579            // time we're less fragile and self-healing.
1580            mWifiStateMachine.stopFilteringMulticastV4Packets();
1581        }
1582
1583        int uid = Binder.getCallingUid();
1584        final long ident = Binder.clearCallingIdentity();
1585        try {
1586            mBatteryStats.noteWifiMulticastEnabled(uid);
1587        } catch (RemoteException e) {
1588        } finally {
1589            Binder.restoreCallingIdentity(ident);
1590        }
1591    }
1592
1593    public void releaseMulticastLock() {
1594        enforceMulticastChangePermission();
1595
1596        int uid = Binder.getCallingUid();
1597        synchronized (mMulticasters) {
1598            mMulticastDisabled++;
1599            int size = mMulticasters.size();
1600            for (int i = size - 1; i >= 0; i--) {
1601                Multicaster m = mMulticasters.get(i);
1602                if ((m != null) && (m.getUid() == uid)) {
1603                    removeMulticasterLocked(i, uid);
1604                }
1605            }
1606        }
1607    }
1608
1609    private void removeMulticasterLocked(int i, int uid)
1610    {
1611        Multicaster removed = mMulticasters.remove(i);
1612
1613        if (removed != null) {
1614            removed.unlinkDeathRecipient();
1615        }
1616        if (mMulticasters.size() == 0) {
1617            mWifiStateMachine.startFilteringMulticastV4Packets();
1618        }
1619
1620        final long ident = Binder.clearCallingIdentity();
1621        try {
1622            mBatteryStats.noteWifiMulticastDisabled(uid);
1623        } catch (RemoteException e) {
1624        } finally {
1625            Binder.restoreCallingIdentity(ident);
1626        }
1627    }
1628
1629    public boolean isMulticastEnabled() {
1630        enforceAccessPermission();
1631
1632        synchronized (mMulticasters) {
1633            return (mMulticasters.size() > 0);
1634        }
1635    }
1636}
1637