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