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