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