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