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