WifiServiceImpl.java revision 6c11cd5d6ff04afdf1bbbc99a850025d46c7bdc9
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 static com.android.server.wifi.WifiController.CMD_AIRPLANE_TOGGLED;
20import static com.android.server.wifi.WifiController.CMD_BATTERY_CHANGED;
21import static com.android.server.wifi.WifiController.CMD_EMERGENCY_CALL_STATE_CHANGED;
22import static com.android.server.wifi.WifiController.CMD_EMERGENCY_MODE_CHANGED;
23import static com.android.server.wifi.WifiController.CMD_LOCKS_CHANGED;
24import static com.android.server.wifi.WifiController.CMD_SCAN_ALWAYS_MODE_CHANGED;
25import static com.android.server.wifi.WifiController.CMD_SCREEN_OFF;
26import static com.android.server.wifi.WifiController.CMD_SCREEN_ON;
27import static com.android.server.wifi.WifiController.CMD_SET_AP;
28import static com.android.server.wifi.WifiController.CMD_USER_PRESENT;
29import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED;
30
31import android.Manifest;
32import android.app.ActivityManager;
33import android.app.AppOpsManager;
34import android.bluetooth.BluetoothAdapter;
35import android.content.BroadcastReceiver;
36import android.content.Context;
37import android.content.Intent;
38import android.content.IntentFilter;
39import android.content.pm.PackageManager;
40import android.content.pm.UserInfo;
41import android.database.ContentObserver;
42import android.net.ConnectivityManager;
43import android.net.DhcpInfo;
44import android.net.DhcpResults;
45import android.net.Network;
46import android.net.NetworkScorerAppManager;
47import android.net.NetworkUtils;
48import android.net.Uri;
49import android.net.ip.IpManager;
50import android.net.wifi.IWifiManager;
51import android.net.wifi.PasspointManagementObjectDefinition;
52import android.net.wifi.ScanResult;
53import android.net.wifi.ScanSettings;
54import android.net.wifi.WifiActivityEnergyInfo;
55import android.net.wifi.WifiConfiguration;
56import android.net.wifi.WifiConnectionStatistics;
57import android.net.wifi.WifiEnterpriseConfig;
58import android.net.wifi.WifiInfo;
59import android.net.wifi.WifiLinkLayerStats;
60import android.net.wifi.WifiManager;
61import android.net.wifi.WifiScanner;
62import android.os.AsyncTask;
63import android.os.BatteryStats;
64import android.os.Binder;
65import android.os.Build;
66import android.os.Bundle;
67import android.os.Handler;
68import android.os.HandlerThread;
69import android.os.IBinder;
70import android.os.Looper;
71import android.os.Message;
72import android.os.Messenger;
73import android.os.PowerManager;
74import android.os.RemoteException;
75import android.os.ResultReceiver;
76import android.os.SystemClock;
77import android.os.UserHandle;
78import android.os.UserManager;
79import android.os.WorkSource;
80import android.provider.Settings;
81import android.text.TextUtils;
82import android.util.Log;
83import android.util.Slog;
84
85import com.android.internal.telephony.IccCardConstants;
86import com.android.internal.telephony.PhoneConstants;
87import com.android.internal.telephony.TelephonyIntents;
88import com.android.internal.util.AsyncChannel;
89import com.android.server.wifi.configparse.ConfigBuilder;
90
91import org.xml.sax.SAXException;
92
93import java.io.BufferedReader;
94import java.io.FileDescriptor;
95import java.io.FileNotFoundException;
96import java.io.FileReader;
97import java.io.IOException;
98import java.io.PrintWriter;
99import java.net.Inet4Address;
100import java.net.InetAddress;
101import java.security.GeneralSecurityException;
102import java.security.KeyStore;
103import java.security.cert.CertPath;
104import java.security.cert.CertPathValidator;
105import java.security.cert.CertPathValidatorException;
106import java.security.cert.CertificateFactory;
107import java.security.cert.PKIXParameters;
108import java.security.cert.X509Certificate;
109import java.util.ArrayList;
110import java.util.Arrays;
111import java.util.List;
112
113/**
114 * WifiService handles remote WiFi operation requests by implementing
115 * the IWifiManager interface.
116 *
117 * @hide
118 */
119public class WifiServiceImpl extends IWifiManager.Stub {
120    private static final String TAG = "WifiService";
121    private static final boolean DBG = true;
122    private static final boolean VDBG = false;
123
124    final WifiStateMachine mWifiStateMachine;
125
126    private final Context mContext;
127    private final FrameworkFacade mFacade;
128
129    private final PowerManager mPowerManager;
130    private final AppOpsManager mAppOps;
131    private final UserManager mUserManager;
132    private final WifiCountryCode mCountryCode;
133    // Debug counter tracking scan requests sent by WifiManager
134    private int scanRequestCounter = 0;
135
136    /* Tracks the open wi-fi network notification */
137    private WifiNotificationController mNotificationController;
138    /* Polls traffic stats and notifies clients */
139    private WifiTrafficPoller mTrafficPoller;
140    /* Tracks the persisted states for wi-fi & airplane mode */
141    final WifiSettingsStore mSettingsStore;
142    /* Logs connection events and some general router and scan stats */
143    private final WifiMetrics mWifiMetrics;
144    /* Manages affiliated certificates for current user */
145    private final WifiCertManager mCertManager;
146
147    private final WifiInjector mWifiInjector;
148    /* Backup/Restore Module */
149    private final WifiBackupRestore mWifiBackupRestore;
150
151    private WifiScanner mWifiScanner;
152
153    /**
154     * Asynchronous channel to WifiStateMachine
155     */
156    private AsyncChannel mWifiStateMachineChannel;
157
158    /**
159     * Handles client connections
160     */
161    private class ClientHandler extends Handler {
162
163        ClientHandler(Looper looper) {
164            super(looper);
165        }
166
167        @Override
168        public void handleMessage(Message msg) {
169            switch (msg.what) {
170                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
171                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
172                        if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
173                        // We track the clients by the Messenger
174                        // since it is expected to be always available
175                        mTrafficPoller.addClient(msg.replyTo);
176                    } else {
177                        Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
178                    }
179                    break;
180                }
181                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
182                    if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
183                        if (DBG) Slog.d(TAG, "Send failed, client connection lost");
184                    } else {
185                        if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
186                    }
187                    mTrafficPoller.removeClient(msg.replyTo);
188                    break;
189                }
190                case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
191                    AsyncChannel ac = new AsyncChannel();
192                    ac.connect(mContext, this, msg.replyTo);
193                    break;
194                }
195                /* Client commands are forwarded to state machine */
196                case WifiManager.CONNECT_NETWORK:
197                case WifiManager.SAVE_NETWORK: {
198                    WifiConfiguration config = (WifiConfiguration) msg.obj;
199                    int networkId = msg.arg1;
200                    if (msg.what == WifiManager.SAVE_NETWORK) {
201                        Slog.d("WiFiServiceImpl ", "SAVE"
202                                + " nid=" + Integer.toString(networkId)
203                                + " uid=" + msg.sendingUid
204                                + " name="
205                                + mContext.getPackageManager().getNameForUid(msg.sendingUid));
206                    }
207                    if (msg.what == WifiManager.CONNECT_NETWORK) {
208                        Slog.d("WiFiServiceImpl ", "CONNECT "
209                                + " nid=" + Integer.toString(networkId)
210                                + " uid=" + msg.sendingUid
211                                + " name="
212                                + mContext.getPackageManager().getNameForUid(msg.sendingUid));
213                    }
214
215                    if (config != null && isValid(config)) {
216                        if (DBG) Slog.d(TAG, "Connect with config" + config);
217                        mWifiStateMachine.sendMessage(Message.obtain(msg));
218                    } else if (config == null
219                            && networkId != WifiConfiguration.INVALID_NETWORK_ID) {
220                        if (DBG) Slog.d(TAG, "Connect with networkId" + networkId);
221                        mWifiStateMachine.sendMessage(Message.obtain(msg));
222                    } else {
223                        Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);
224                        if (msg.what == WifiManager.CONNECT_NETWORK) {
225                            replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED,
226                                    WifiManager.INVALID_ARGS);
227                        } else {
228                            replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED,
229                                    WifiManager.INVALID_ARGS);
230                        }
231                    }
232                    break;
233                }
234                case WifiManager.FORGET_NETWORK:
235                    mWifiStateMachine.sendMessage(Message.obtain(msg));
236                    break;
237                case WifiManager.START_WPS:
238                case WifiManager.CANCEL_WPS:
239                case WifiManager.DISABLE_NETWORK:
240                case WifiManager.RSSI_PKTCNT_FETCH: {
241                    mWifiStateMachine.sendMessage(Message.obtain(msg));
242                    break;
243                }
244                default: {
245                    Slog.d(TAG, "ClientHandler.handleMessage ignoring msg=" + msg);
246                    break;
247                }
248            }
249        }
250
251        private void replyFailed(Message msg, int what, int why) {
252            Message reply = Message.obtain();
253            reply.what = what;
254            reply.arg1 = why;
255            try {
256                msg.replyTo.send(reply);
257            } catch (RemoteException e) {
258                // There's not much we can do if reply can't be sent!
259            }
260        }
261    }
262    private ClientHandler mClientHandler;
263
264    /**
265     * Handles interaction with WifiStateMachine
266     */
267    private class WifiStateMachineHandler extends Handler {
268        private AsyncChannel mWsmChannel;
269
270        WifiStateMachineHandler(Looper looper) {
271            super(looper);
272            mWsmChannel = new AsyncChannel();
273            mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
274        }
275
276        @Override
277        public void handleMessage(Message msg) {
278            switch (msg.what) {
279                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
280                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
281                        mWifiStateMachineChannel = mWsmChannel;
282                    } else {
283                        Slog.e(TAG, "WifiStateMachine connection failure, error=" + msg.arg1);
284                        mWifiStateMachineChannel = null;
285                    }
286                    break;
287                }
288                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
289                    Slog.e(TAG, "WifiStateMachine channel lost, msg.arg1 =" + msg.arg1);
290                    mWifiStateMachineChannel = null;
291                    //Re-establish connection to state machine
292                    mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
293                    break;
294                }
295                default: {
296                    Slog.d(TAG, "WifiStateMachineHandler.handleMessage ignoring msg=" + msg);
297                    break;
298                }
299            }
300        }
301    }
302
303    WifiStateMachineHandler mWifiStateMachineHandler;
304    private WifiController mWifiController;
305    private final WifiLockManager mWifiLockManager;
306    private final WifiMulticastLockManager mWifiMulticastLockManager;
307
308    public WifiServiceImpl(Context context) {
309        mContext = context;
310        mWifiInjector = new WifiInjector(context);
311
312        mFacade = mWifiInjector.getFrameworkFacade();
313        mWifiMetrics = mWifiInjector.getWifiMetrics();
314        mTrafficPoller = mWifiInjector.getWifiTrafficPoller();
315        mUserManager = UserManager.get(mContext);
316        mCountryCode = mWifiInjector.getWifiCountryCode();
317        mWifiStateMachine = mWifiInjector.getWifiStateMachine();
318        mWifiStateMachine.enableRssiPolling(true);
319        mSettingsStore = mWifiInjector.getWifiSettingsStore();
320        mPowerManager = mContext.getSystemService(PowerManager.class);
321        mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
322        mCertManager = mWifiInjector.getWifiCertManager();
323
324        mNotificationController = mWifiInjector.getWifiNotificationController();
325
326        mWifiLockManager = mWifiInjector.getWifiLockManager();
327        mWifiMulticastLockManager = mWifiInjector.getWifiMulticastLockManager();
328        HandlerThread wifiServiceHandlerThread = mWifiInjector.getWifiServiceHandlerThread();
329        mClientHandler = new ClientHandler(wifiServiceHandlerThread.getLooper());
330        mWifiStateMachineHandler =
331                new WifiStateMachineHandler(wifiServiceHandlerThread.getLooper());
332        mWifiController = mWifiInjector.getWifiController();
333        mWifiBackupRestore = mWifiInjector.getWifiBackupRestore();
334
335        enableVerboseLoggingInternal(getVerboseLoggingLevel());
336    }
337
338
339    /**
340     * Check if Wi-Fi needs to be enabled and start
341     * if needed
342     *
343     * This function is used only at boot time
344     */
345    public void checkAndStartWifi() {
346        /* Check if wi-fi needs to be enabled */
347        boolean wifiEnabled = mSettingsStore.isWifiToggleEnabled();
348        Slog.i(TAG, "WifiService starting up with Wi-Fi " +
349                (wifiEnabled ? "enabled" : "disabled"));
350
351        registerForScanModeChange();
352        mContext.registerReceiver(
353                new BroadcastReceiver() {
354                    @Override
355                    public void onReceive(Context context, Intent intent) {
356                        if (mSettingsStore.handleAirplaneModeToggled()) {
357                            mWifiController.sendMessage(CMD_AIRPLANE_TOGGLED);
358                        }
359                        if (mSettingsStore.isAirplaneModeOn()) {
360                            Log.d(TAG, "resetting country code because Airplane mode is ON");
361                            mCountryCode.airplaneModeEnabled();
362                        }
363                    }
364                },
365                new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
366
367        mContext.registerReceiver(
368                new BroadcastReceiver() {
369                    @Override
370                    public void onReceive(Context context, Intent intent) {
371                        String state = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
372                        if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(state)) {
373                            Log.d(TAG, "resetting networks because SIM was removed");
374                            mWifiStateMachine.resetSimAuthNetworks(false);
375                            Log.d(TAG, "resetting country code because SIM is removed");
376                            mCountryCode.simCardRemoved();
377                        } else if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(state)) {
378                            Log.d(TAG, "resetting networks because SIM was loaded");
379                            mWifiStateMachine.resetSimAuthNetworks(true);
380                        }
381                    }
382                },
383                new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED));
384
385        // Adding optimizations of only receiving broadcasts when wifi is enabled
386        // can result in race conditions when apps toggle wifi in the background
387        // without active user involvement. Always receive broadcasts.
388        registerForBroadcasts();
389        registerForPackageOrUserRemoval();
390        mInIdleMode = mPowerManager.isDeviceIdleMode();
391
392        mWifiController.start();
393
394        // If we are already disabled (could be due to airplane mode), avoid changing persist
395        // state here
396        if (wifiEnabled) setWifiEnabled(wifiEnabled);
397    }
398
399    public void handleUserSwitch(int userId) {
400        mWifiStateMachine.handleUserSwitch(userId);
401    }
402
403    public void handleUserUnlock(int userId) {
404        mWifiStateMachine.handleUserUnlock(userId);
405    }
406
407    public void handleUserStop(int userId) {
408        mWifiStateMachine.handleUserStop(userId);
409    }
410
411    /**
412     * see {@link android.net.wifi.WifiManager#pingSupplicant()}
413     * @return {@code true} if the operation succeeds, {@code false} otherwise
414     */
415    @Override
416    public boolean pingSupplicant() {
417        enforceAccessPermission();
418        if (mWifiStateMachineChannel != null) {
419            return mWifiStateMachine.syncPingSupplicant(mWifiStateMachineChannel);
420        } else {
421            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
422            return false;
423        }
424    }
425
426    /**
427     * see {@link android.net.wifi.WifiManager#startScan}
428     * and {@link android.net.wifi.WifiManager#startCustomizedScan}
429     *
430     * @param settings If null, use default parameter, i.e. full scan.
431     * @param workSource If null, all blame is given to the calling uid.
432     */
433    @Override
434    public void startScan(ScanSettings settings, WorkSource workSource) {
435        enforceChangePermission();
436        synchronized (this) {
437            if (mWifiScanner == null) {
438                mWifiScanner = mWifiInjector.getWifiScanner();
439            }
440            if (mInIdleMode) {
441                // Need to send an immediate scan result broadcast in case the
442                // caller is waiting for a result ..
443
444                // clear calling identity to send broadcast
445                long callingIdentity = Binder.clearCallingIdentity();
446                try {
447                    // TODO: investigate if the logic to cancel scans when idle can move to
448                    // WifiScanningServiceImpl.  This will 1 - clean up WifiServiceImpl and 2 -
449                    // avoid plumbing an awkward path to report a cancelled/failed scan.  This will
450                    // be sent directly until b/31398592 is fixed.
451                    Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
452                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
453                    intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);
454                    mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
455                } finally {
456                    // restore calling identity
457                    Binder.restoreCallingIdentity(callingIdentity);
458                }
459                mScanPending = true;
460                return;
461            }
462        }
463        if (settings != null) {
464            settings = new ScanSettings(settings);
465            if (!settings.isValid()) {
466                Slog.e(TAG, "invalid scan setting");
467                return;
468            }
469        }
470        if (workSource != null) {
471            enforceWorkSourcePermission();
472            // WifiManager currently doesn't use names, so need to clear names out of the
473            // supplied WorkSource to allow future WorkSource combining.
474            workSource.clearNames();
475        }
476        if (workSource == null && Binder.getCallingUid() >= 0) {
477            workSource = new WorkSource(Binder.getCallingUid());
478        }
479        mWifiStateMachine.startScan(Binder.getCallingUid(), scanRequestCounter++,
480                settings, workSource);
481    }
482
483    @Override
484    public String getWpsNfcConfigurationToken(int netId) {
485        enforceConnectivityInternalPermission();
486        return mWifiStateMachine.syncGetWpsNfcConfigurationToken(netId);
487    }
488
489    boolean mInIdleMode;
490    boolean mScanPending;
491
492    void handleIdleModeChanged() {
493        boolean doScan = false;
494        synchronized (this) {
495            boolean idle = mPowerManager.isDeviceIdleMode();
496            if (mInIdleMode != idle) {
497                mInIdleMode = idle;
498                if (!idle) {
499                    if (mScanPending) {
500                        mScanPending = false;
501                        doScan = true;
502                    }
503                }
504            }
505        }
506        if (doScan) {
507            // Someone requested a scan while we were idle; do a full scan now.
508            startScan(null, null);
509        }
510    }
511
512    private void enforceAccessPermission() {
513        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
514                "WifiService");
515    }
516
517    private void enforceChangePermission() {
518        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
519                "WifiService");
520    }
521
522    private void enforceLocationHardwarePermission() {
523        mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE,
524                "LocationHardware");
525    }
526
527    private void enforceReadCredentialPermission() {
528        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_WIFI_CREDENTIAL,
529                                                "WifiService");
530    }
531
532    private void enforceWorkSourcePermission() {
533        mContext.enforceCallingPermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
534                "WifiService");
535
536    }
537
538    private void enforceMulticastChangePermission() {
539        mContext.enforceCallingOrSelfPermission(
540                android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
541                "WifiService");
542    }
543
544    private void enforceConnectivityInternalPermission() {
545        mContext.enforceCallingOrSelfPermission(
546                android.Manifest.permission.CONNECTIVITY_INTERNAL,
547                "ConnectivityService");
548    }
549
550    /**
551     * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
552     * @param enable {@code true} to enable, {@code false} to disable.
553     * @return {@code true} if the enable/disable operation was
554     *         started or is already in the queue.
555     */
556    @Override
557    public synchronized boolean setWifiEnabled(boolean enable) {
558        enforceChangePermission();
559        Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid()
560                    + ", uid=" + Binder.getCallingUid());
561
562        /*
563        * Caller might not have WRITE_SECURE_SETTINGS,
564        * only CHANGE_WIFI_STATE is enforced
565        */
566
567        long ident = Binder.clearCallingIdentity();
568        try {
569            if (! mSettingsStore.handleWifiToggled(enable)) {
570                // Nothing to do if wifi cannot be toggled
571                return true;
572            }
573        } finally {
574            Binder.restoreCallingIdentity(ident);
575        }
576
577        mWifiController.sendMessage(CMD_WIFI_TOGGLED);
578        return true;
579    }
580
581    /**
582     * see {@link WifiManager#getWifiState()}
583     * @return One of {@link WifiManager#WIFI_STATE_DISABLED},
584     *         {@link WifiManager#WIFI_STATE_DISABLING},
585     *         {@link WifiManager#WIFI_STATE_ENABLED},
586     *         {@link WifiManager#WIFI_STATE_ENABLING},
587     *         {@link WifiManager#WIFI_STATE_UNKNOWN}
588     */
589    @Override
590    public int getWifiEnabledState() {
591        enforceAccessPermission();
592        return mWifiStateMachine.syncGetWifiState();
593    }
594
595    /**
596     * see {@link android.net.wifi.WifiManager#setWifiApEnabled(WifiConfiguration, boolean)}
597     * @param wifiConfig SSID, security and channel details as
598     *        part of WifiConfiguration
599     * @param enabled true to enable and false to disable
600     */
601    @Override
602    public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
603        enforceChangePermission();
604        ConnectivityManager.enforceTetherChangePermission(mContext);
605        if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
606            throw new SecurityException("DISALLOW_CONFIG_TETHERING is enabled for this user.");
607        }
608        // null wifiConfig is a meaningful input for CMD_SET_AP
609        if (wifiConfig == null || isValid(wifiConfig)) {
610            mWifiController.obtainMessage(CMD_SET_AP, enabled ? 1 : 0, 0, wifiConfig).sendToTarget();
611        } else {
612            Slog.e(TAG, "Invalid WifiConfiguration");
613        }
614    }
615
616    /**
617     * see {@link WifiManager#getWifiApState()}
618     * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
619     *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
620     *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
621     *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
622     *         {@link WifiManager#WIFI_AP_STATE_FAILED}
623     */
624    @Override
625    public int getWifiApEnabledState() {
626        enforceAccessPermission();
627        return mWifiStateMachine.syncGetWifiApState();
628    }
629
630    /**
631     * see {@link WifiManager#getWifiApConfiguration()}
632     * @return soft access point configuration
633     */
634    @Override
635    public WifiConfiguration getWifiApConfiguration() {
636        enforceAccessPermission();
637        return mWifiStateMachine.syncGetWifiApConfiguration();
638    }
639
640    /**
641     * see {@link WifiManager#buildWifiConfig()}
642     * @return a WifiConfiguration.
643     */
644    @Override
645    public WifiConfiguration buildWifiConfig(String uriString, String mimeType, byte[] data) {
646        if (mimeType.equals(ConfigBuilder.WifiConfigType)) {
647            try {
648                return ConfigBuilder.buildConfig(uriString, data, mContext);
649            }
650            catch (IOException | GeneralSecurityException | SAXException e) {
651                Log.e(TAG, "Failed to parse wi-fi configuration: " + e);
652            }
653        }
654        else {
655            Log.i(TAG, "Unknown wi-fi config type: " + mimeType);
656        }
657        return null;
658    }
659
660    /**
661     * see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)}
662     * @param wifiConfig WifiConfiguration details for soft access point
663     */
664    @Override
665    public void setWifiApConfiguration(WifiConfiguration wifiConfig) {
666        enforceChangePermission();
667        if (wifiConfig == null)
668            return;
669        if (isValid(wifiConfig)) {
670            mWifiStateMachine.setWifiApConfiguration(wifiConfig);
671        } else {
672            Slog.e(TAG, "Invalid WifiConfiguration");
673        }
674    }
675
676    /**
677     * see {@link android.net.wifi.WifiManager#isScanAlwaysAvailable()}
678     */
679    @Override
680    public boolean isScanAlwaysAvailable() {
681        enforceAccessPermission();
682        return mSettingsStore.isScanAlwaysAvailable();
683    }
684
685    /**
686     * see {@link android.net.wifi.WifiManager#disconnect()}
687     */
688    @Override
689    public void disconnect() {
690        enforceChangePermission();
691        mWifiStateMachine.disconnectCommand();
692    }
693
694    /**
695     * see {@link android.net.wifi.WifiManager#reconnect()}
696     */
697    @Override
698    public void reconnect() {
699        enforceChangePermission();
700        mWifiStateMachine.reconnectCommand();
701    }
702
703    /**
704     * see {@link android.net.wifi.WifiManager#reassociate()}
705     */
706    @Override
707    public void reassociate() {
708        enforceChangePermission();
709        mWifiStateMachine.reassociateCommand();
710    }
711
712    /**
713     * see {@link android.net.wifi.WifiManager#getSupportedFeatures}
714     */
715    @Override
716    public int getSupportedFeatures() {
717        enforceAccessPermission();
718        if (mWifiStateMachineChannel != null) {
719            return mWifiStateMachine.syncGetSupportedFeatures(mWifiStateMachineChannel);
720        } else {
721            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
722            return 0;
723        }
724    }
725
726    @Override
727    public void requestActivityInfo(ResultReceiver result) {
728        Bundle bundle = new Bundle();
729        bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, reportActivityInfo());
730        result.send(0, bundle);
731    }
732
733    /**
734     * see {@link android.net.wifi.WifiManager#getControllerActivityEnergyInfo(int)}
735     */
736    @Override
737    public WifiActivityEnergyInfo reportActivityInfo() {
738        enforceAccessPermission();
739        if ((getSupportedFeatures() & WifiManager.WIFI_FEATURE_LINK_LAYER_STATS) == 0) {
740            return null;
741        }
742        WifiLinkLayerStats stats;
743        WifiActivityEnergyInfo energyInfo = null;
744        if (mWifiStateMachineChannel != null) {
745            stats = mWifiStateMachine.syncGetLinkLayerStats(mWifiStateMachineChannel);
746            if (stats != null) {
747                final long rxIdleCurrent = mContext.getResources().getInteger(
748                        com.android.internal.R.integer.config_wifi_idle_receive_cur_ma);
749                final long rxCurrent = mContext.getResources().getInteger(
750                        com.android.internal.R.integer.config_wifi_active_rx_cur_ma);
751                final long txCurrent = mContext.getResources().getInteger(
752                        com.android.internal.R.integer.config_wifi_tx_cur_ma);
753                final double voltage = mContext.getResources().getInteger(
754                        com.android.internal.R.integer.config_wifi_operating_voltage_mv)
755                        / 1000.0;
756
757                final long rxIdleTime = stats.on_time - stats.tx_time - stats.rx_time;
758                final long[] txTimePerLevel;
759                if (stats.tx_time_per_level != null) {
760                    txTimePerLevel = new long[stats.tx_time_per_level.length];
761                    for (int i = 0; i < txTimePerLevel.length; i++) {
762                        txTimePerLevel[i] = stats.tx_time_per_level[i];
763                        // TODO(b/27227497): Need to read the power consumed per level from config
764                    }
765                } else {
766                    // This will happen if the HAL get link layer API returned null.
767                    txTimePerLevel = new long[0];
768                }
769                final long energyUsed = (long)((stats.tx_time * txCurrent +
770                        stats.rx_time * rxCurrent +
771                        rxIdleTime * rxIdleCurrent) * voltage);
772                if (VDBG || rxIdleTime < 0 || stats.on_time < 0 || stats.tx_time < 0 ||
773                        stats.rx_time < 0 || energyUsed < 0) {
774                    StringBuilder sb = new StringBuilder();
775                    sb.append(" rxIdleCur=" + rxIdleCurrent);
776                    sb.append(" rxCur=" + rxCurrent);
777                    sb.append(" txCur=" + txCurrent);
778                    sb.append(" voltage=" + voltage);
779                    sb.append(" on_time=" + stats.on_time);
780                    sb.append(" tx_time=" + stats.tx_time);
781                    sb.append(" tx_time_per_level=" + Arrays.toString(txTimePerLevel));
782                    sb.append(" rx_time=" + stats.rx_time);
783                    sb.append(" rxIdleTime=" + rxIdleTime);
784                    sb.append(" energy=" + energyUsed);
785                    Log.d(TAG, " reportActivityInfo: " + sb.toString());
786                }
787
788                // Convert the LinkLayerStats into EnergyActivity
789                energyInfo = new WifiActivityEnergyInfo(SystemClock.elapsedRealtime(),
790                        WifiActivityEnergyInfo.STACK_STATE_STATE_IDLE, stats.tx_time,
791                        txTimePerLevel, stats.rx_time, rxIdleTime, energyUsed);
792            }
793            if (energyInfo != null && energyInfo.isValid()) {
794                return energyInfo;
795            } else {
796                return null;
797            }
798        } else {
799            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
800            return null;
801        }
802    }
803
804    /**
805     * see {@link android.net.wifi.WifiManager#getConfiguredNetworks()}
806     * @return the list of configured networks
807     */
808    @Override
809    public List<WifiConfiguration> getConfiguredNetworks() {
810        enforceAccessPermission();
811        if (mWifiStateMachineChannel != null) {
812            return mWifiStateMachine.syncGetConfiguredNetworks(Binder.getCallingUid(),
813                    mWifiStateMachineChannel);
814        } else {
815            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
816            return null;
817        }
818    }
819
820    /**
821     * see {@link android.net.wifi.WifiManager#getPrivilegedConfiguredNetworks()}
822     * @return the list of configured networks with real preSharedKey
823     */
824    @Override
825    public List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
826        enforceReadCredentialPermission();
827        enforceAccessPermission();
828        if (mWifiStateMachineChannel != null) {
829            return mWifiStateMachine.syncGetPrivilegedConfiguredNetwork(mWifiStateMachineChannel);
830        } else {
831            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
832            return null;
833        }
834    }
835
836    /**
837     * Returns a WifiConfiguration matching this ScanResult
838     * @param scanResult scanResult that represents the BSSID
839     * @return {@link WifiConfiguration} that matches this BSSID or null
840     */
841    @Override
842    public WifiConfiguration getMatchingWifiConfig(ScanResult scanResult) {
843        enforceAccessPermission();
844        return mWifiStateMachine.syncGetMatchingWifiConfig(scanResult, mWifiStateMachineChannel);
845    }
846
847    /**
848     * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)}
849     * @return the supplicant-assigned identifier for the new or updated
850     * network if the operation succeeds, or {@code -1} if it fails
851     */
852    @Override
853    public int addOrUpdateNetwork(WifiConfiguration config) {
854        enforceChangePermission();
855        if (isValid(config) && isValidPasspoint(config)) {
856
857            WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
858
859            if (config.isPasspoint() &&
860                    (enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS ||
861                            enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS)) {
862                if (config.updateIdentifier != null) {
863                    enforceAccessPermission();
864                }
865                else {
866                    try {
867                        verifyCert(enterpriseConfig.getCaCertificate());
868                    } catch (CertPathValidatorException cpve) {
869                        Slog.e(TAG, "CA Cert " +
870                                enterpriseConfig.getCaCertificate().getSubjectX500Principal() +
871                                " untrusted: " + cpve.getMessage());
872                        return -1;
873                    } catch (GeneralSecurityException | IOException e) {
874                        Slog.e(TAG, "Failed to verify certificate" +
875                                enterpriseConfig.getCaCertificate().getSubjectX500Principal() +
876                                ": " + e);
877                        return -1;
878                    }
879                }
880            }
881
882            //TODO: pass the Uid the WifiStateMachine as a message parameter
883            Slog.i("addOrUpdateNetwork", " uid = " + Integer.toString(Binder.getCallingUid())
884                    + " SSID " + config.SSID
885                    + " nid=" + Integer.toString(config.networkId));
886            if (config.networkId == WifiConfiguration.INVALID_NETWORK_ID) {
887                config.creatorUid = Binder.getCallingUid();
888            } else {
889                config.lastUpdateUid = Binder.getCallingUid();
890            }
891            if (mWifiStateMachineChannel != null) {
892                return mWifiStateMachine.syncAddOrUpdateNetwork(mWifiStateMachineChannel, config);
893            } else {
894                Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
895                return -1;
896            }
897        } else {
898            Slog.e(TAG, "bad network configuration");
899            return -1;
900        }
901    }
902
903    public static void verifyCert(X509Certificate caCert)
904            throws GeneralSecurityException, IOException {
905        CertificateFactory factory = CertificateFactory.getInstance("X.509");
906        CertPathValidator validator =
907                CertPathValidator.getInstance(CertPathValidator.getDefaultType());
908        CertPath path = factory.generateCertPath(
909                Arrays.asList(caCert));
910        KeyStore ks = KeyStore.getInstance("AndroidCAStore");
911        ks.load(null, null);
912        PKIXParameters params = new PKIXParameters(ks);
913        params.setRevocationEnabled(false);
914        validator.validate(path, params);
915    }
916
917    /**
918     * See {@link android.net.wifi.WifiManager#removeNetwork(int)}
919     * @param netId the integer that identifies the network configuration
920     * to the supplicant
921     * @return {@code true} if the operation succeeded
922     */
923    @Override
924    public boolean removeNetwork(int netId) {
925        enforceChangePermission();
926
927        if (mWifiStateMachineChannel != null) {
928            return mWifiStateMachine.syncRemoveNetwork(mWifiStateMachineChannel, netId);
929        } else {
930            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
931            return false;
932        }
933    }
934
935    /**
936     * See {@link android.net.wifi.WifiManager#enableNetwork(int, boolean)}
937     * @param netId the integer that identifies the network configuration
938     * to the supplicant
939     * @param disableOthers if true, disable all other networks.
940     * @return {@code true} if the operation succeeded
941     */
942    @Override
943    public boolean enableNetwork(int netId, boolean disableOthers) {
944        enforceChangePermission();
945        if (mWifiStateMachineChannel != null) {
946            return mWifiStateMachine.syncEnableNetwork(mWifiStateMachineChannel, netId,
947                    disableOthers);
948        } else {
949            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
950            return false;
951        }
952    }
953
954    /**
955     * See {@link android.net.wifi.WifiManager#disableNetwork(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    @Override
961    public boolean disableNetwork(int netId) {
962        enforceChangePermission();
963        if (mWifiStateMachineChannel != null) {
964            return mWifiStateMachine.syncDisableNetwork(mWifiStateMachineChannel, netId);
965        } else {
966            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
967            return false;
968        }
969    }
970
971    /**
972     * See {@link android.net.wifi.WifiManager#getConnectionInfo()}
973     * @return the Wi-Fi information, contained in {@link WifiInfo}.
974     */
975    @Override
976    public WifiInfo getConnectionInfo() {
977        enforceAccessPermission();
978        /*
979         * Make sure we have the latest information, by sending
980         * a status request to the supplicant.
981         */
982        return mWifiStateMachine.syncRequestConnectionInfo();
983    }
984
985    /**
986     * Return the results of the most recent access point scan, in the form of
987     * a list of {@link ScanResult} objects.
988     * @return the list of results
989     */
990    @Override
991    public List<ScanResult> getScanResults(String callingPackage) {
992        enforceAccessPermission();
993        int userId = UserHandle.getCallingUserId();
994        int uid = Binder.getCallingUid();
995        boolean canReadPeerMacAddresses = checkPeersMacAddress();
996        boolean isActiveNetworkScorer =
997                NetworkScorerAppManager.isCallerActiveScorer(mContext, uid);
998        boolean hasInteractUsersFull = checkInteractAcrossUsersFull();
999        long ident = Binder.clearCallingIdentity();
1000        try {
1001            if (!canReadPeerMacAddresses && !isActiveNetworkScorer
1002                    && !isLocationEnabled(callingPackage)) {
1003                return new ArrayList<ScanResult>();
1004            }
1005            if (!canReadPeerMacAddresses && !isActiveNetworkScorer
1006                    && !checkCallerCanAccessScanResults(callingPackage, uid)) {
1007                return new ArrayList<ScanResult>();
1008            }
1009            if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage)
1010                    != AppOpsManager.MODE_ALLOWED) {
1011                return new ArrayList<ScanResult>();
1012            }
1013            if (!isCurrentProfile(userId) && !hasInteractUsersFull) {
1014                return new ArrayList<ScanResult>();
1015            }
1016            return mWifiStateMachine.syncGetScanResultsList();
1017        } finally {
1018            Binder.restoreCallingIdentity(ident);
1019        }
1020    }
1021
1022    /**
1023     * Add a Hotspot 2.0 release 2 Management Object
1024     * @param mo The MO in XML form
1025     * @return -1 for failure
1026     */
1027    @Override
1028    public int addPasspointManagementObject(String mo) {
1029        return mWifiStateMachine.syncAddPasspointManagementObject(mWifiStateMachineChannel, mo);
1030    }
1031
1032    /**
1033     * Modify a Hotspot 2.0 release 2 Management Object
1034     * @param fqdn The FQDN of the service provider
1035     * @param mos A List of MO definitions to be updated
1036     * @return the number of nodes updated, or -1 for failure
1037     */
1038    @Override
1039    public int modifyPasspointManagementObject(String fqdn, List<PasspointManagementObjectDefinition> mos) {
1040        return mWifiStateMachine.syncModifyPasspointManagementObject(mWifiStateMachineChannel, fqdn, mos);
1041    }
1042
1043    /**
1044     * Query for a Hotspot 2.0 release 2 OSU icon
1045     * @param bssid The BSSID of the AP
1046     * @param fileName Icon file name
1047     */
1048    @Override
1049    public void queryPasspointIcon(long bssid, String fileName) {
1050        mWifiStateMachine.syncQueryPasspointIcon(mWifiStateMachineChannel, bssid, fileName);
1051    }
1052
1053    /**
1054     * Match the currently associated network against the SP matching the given FQDN
1055     * @param fqdn FQDN of the SP
1056     * @return ordinal [HomeProvider, RoamingProvider, Incomplete, None, Declined]
1057     */
1058    @Override
1059    public int matchProviderWithCurrentNetwork(String fqdn) {
1060        return mWifiStateMachine.matchProviderWithCurrentNetwork(mWifiStateMachineChannel, fqdn);
1061    }
1062
1063    /**
1064     * Deauthenticate and set the re-authentication hold off time for the current network
1065     * @param holdoff hold off time in milliseconds
1066     * @param ess set if the hold off pertains to an ESS rather than a BSS
1067     */
1068    @Override
1069    public void deauthenticateNetwork(long holdoff, boolean ess) {
1070        mWifiStateMachine.deauthenticateNetwork(mWifiStateMachineChannel, holdoff, ess);
1071    }
1072
1073    private boolean isLocationEnabled(String callingPackage) {
1074        boolean legacyForegroundApp = !isMApp(mContext, callingPackage)
1075                && isForegroundApp(callingPackage);
1076        return legacyForegroundApp || Settings.Secure.getInt(mContext.getContentResolver(),
1077                Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF)
1078                != Settings.Secure.LOCATION_MODE_OFF;
1079    }
1080
1081    /**
1082     * Returns true if the caller holds INTERACT_ACROSS_USERS_FULL.
1083     */
1084    private boolean checkInteractAcrossUsersFull() {
1085        return mContext.checkCallingOrSelfPermission(
1086                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
1087                == PackageManager.PERMISSION_GRANTED;
1088    }
1089
1090    /**
1091     * Returns true if the caller holds PEERS_MAC_ADDRESS.
1092     */
1093    private boolean checkPeersMacAddress() {
1094        return mContext.checkCallingOrSelfPermission(
1095                android.Manifest.permission.PEERS_MAC_ADDRESS) == PackageManager.PERMISSION_GRANTED;
1096    }
1097
1098    /**
1099     * Returns true if the calling user is the current one or a profile of the
1100     * current user..
1101     */
1102    private boolean isCurrentProfile(int userId) {
1103        int currentUser = ActivityManager.getCurrentUser();
1104        if (userId == currentUser) {
1105            return true;
1106        }
1107        List<UserInfo> profiles = mUserManager.getProfiles(currentUser);
1108        for (UserInfo user : profiles) {
1109            if (userId == user.id) {
1110                return true;
1111            }
1112        }
1113        return false;
1114    }
1115
1116    /**
1117     * Tell the supplicant to persist the current list of configured networks.
1118     * @return {@code true} if the operation succeeded
1119     *
1120     * TODO: deprecate this
1121     */
1122    @Override
1123    public boolean saveConfiguration() {
1124        enforceChangePermission();
1125        if (mWifiStateMachineChannel != null) {
1126            return mWifiStateMachine.syncSaveConfig(mWifiStateMachineChannel);
1127        } else {
1128            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
1129            return false;
1130        }
1131    }
1132
1133    /**
1134     * Set the country code
1135     * @param countryCode ISO 3166 country code.
1136     * @param persist {@code true} if the setting should be remembered.
1137     *
1138     * The persist behavior exists so that wifi can fall back to the last
1139     * persisted country code on a restart, when the locale information is
1140     * not available from telephony.
1141     */
1142    @Override
1143    public void setCountryCode(String countryCode, boolean persist) {
1144        Slog.i(TAG, "WifiService trying to set country code to " + countryCode +
1145                " with persist set to " + persist);
1146        enforceConnectivityInternalPermission();
1147        final long token = Binder.clearCallingIdentity();
1148        try {
1149            if (mCountryCode.setCountryCode(countryCode, persist) && persist) {
1150                // Save this country code to persistent storage
1151                mFacade.setStringSetting(mContext,
1152                        Settings.Global.WIFI_COUNTRY_CODE,
1153                        countryCode);
1154            }
1155        } finally {
1156            Binder.restoreCallingIdentity(token);
1157        }
1158    }
1159
1160     /**
1161     * Get the country code
1162     * @return Get the best choice country code for wifi, regardless of if it was set or
1163     * not.
1164     * Returns null when there is no country code available.
1165     */
1166    @Override
1167    public String getCountryCode() {
1168        enforceConnectivityInternalPermission();
1169        String country = mCountryCode.getCountryCode();
1170        return country;
1171    }
1172    /**
1173     * Set the operational frequency band
1174     * @param band One of
1175     *     {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
1176     *     {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ},
1177     *     {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ},
1178     * @param persist {@code true} if the setting should be remembered.
1179     *
1180     */
1181    @Override
1182    public void setFrequencyBand(int band, boolean persist) {
1183        enforceChangePermission();
1184        if (!isDualBandSupported()) return;
1185        Slog.i(TAG, "WifiService trying to set frequency band to " + band +
1186                " with persist set to " + persist);
1187        final long token = Binder.clearCallingIdentity();
1188        try {
1189            mWifiStateMachine.setFrequencyBand(band, persist);
1190        } finally {
1191            Binder.restoreCallingIdentity(token);
1192        }
1193    }
1194
1195
1196    /**
1197     * Get the operational frequency band
1198     */
1199    @Override
1200    public int getFrequencyBand() {
1201        enforceAccessPermission();
1202        return mWifiStateMachine.getFrequencyBand();
1203    }
1204
1205    @Override
1206    public boolean isDualBandSupported() {
1207        //TODO: Should move towards adding a driver API that checks at runtime
1208        return mContext.getResources().getBoolean(
1209                com.android.internal.R.bool.config_wifi_dual_band_support);
1210    }
1211
1212    /**
1213     * Return the DHCP-assigned addresses from the last successful DHCP request,
1214     * if any.
1215     * @return the DHCP information
1216     * @deprecated
1217     */
1218    @Override
1219    @Deprecated
1220    public DhcpInfo getDhcpInfo() {
1221        enforceAccessPermission();
1222        DhcpResults dhcpResults = mWifiStateMachine.syncGetDhcpResults();
1223
1224        DhcpInfo info = new DhcpInfo();
1225
1226        if (dhcpResults.ipAddress != null &&
1227                dhcpResults.ipAddress.getAddress() instanceof Inet4Address) {
1228            info.ipAddress = NetworkUtils.inetAddressToInt((Inet4Address) dhcpResults.ipAddress.getAddress());
1229        }
1230
1231        if (dhcpResults.gateway != null) {
1232            info.gateway = NetworkUtils.inetAddressToInt((Inet4Address) dhcpResults.gateway);
1233        }
1234
1235        int dnsFound = 0;
1236        for (InetAddress dns : dhcpResults.dnsServers) {
1237            if (dns instanceof Inet4Address) {
1238                if (dnsFound == 0) {
1239                    info.dns1 = NetworkUtils.inetAddressToInt((Inet4Address)dns);
1240                } else {
1241                    info.dns2 = NetworkUtils.inetAddressToInt((Inet4Address)dns);
1242                }
1243                if (++dnsFound > 1) break;
1244            }
1245        }
1246        Inet4Address serverAddress = dhcpResults.serverAddress;
1247        if (serverAddress != null) {
1248            info.serverAddress = NetworkUtils.inetAddressToInt(serverAddress);
1249        }
1250        info.leaseDuration = dhcpResults.leaseDuration;
1251
1252        return info;
1253    }
1254
1255    /**
1256     * enable TDLS for the local NIC to remote NIC
1257     * The APPs don't know the remote MAC address to identify NIC though,
1258     * so we need to do additional work to find it from remote IP address
1259     */
1260
1261    class TdlsTaskParams {
1262        public String remoteIpAddress;
1263        public boolean enable;
1264    }
1265
1266    class TdlsTask extends AsyncTask<TdlsTaskParams, Integer, Integer> {
1267        @Override
1268        protected Integer doInBackground(TdlsTaskParams... params) {
1269
1270            // Retrieve parameters for the call
1271            TdlsTaskParams param = params[0];
1272            String remoteIpAddress = param.remoteIpAddress.trim();
1273            boolean enable = param.enable;
1274
1275            // Get MAC address of Remote IP
1276            String macAddress = null;
1277
1278            BufferedReader reader = null;
1279
1280            try {
1281                reader = new BufferedReader(new FileReader("/proc/net/arp"));
1282
1283                // Skip over the line bearing colum titles
1284                String line = reader.readLine();
1285
1286                while ((line = reader.readLine()) != null) {
1287                    String[] tokens = line.split("[ ]+");
1288                    if (tokens.length < 6) {
1289                        continue;
1290                    }
1291
1292                    // ARP column format is
1293                    // Address HWType HWAddress Flags Mask IFace
1294                    String ip = tokens[0];
1295                    String mac = tokens[3];
1296
1297                    if (remoteIpAddress.equals(ip)) {
1298                        macAddress = mac;
1299                        break;
1300                    }
1301                }
1302
1303                if (macAddress == null) {
1304                    Slog.w(TAG, "Did not find remoteAddress {" + remoteIpAddress + "} in " +
1305                            "/proc/net/arp");
1306                } else {
1307                    enableTdlsWithMacAddress(macAddress, enable);
1308                }
1309
1310            } catch (FileNotFoundException e) {
1311                Slog.e(TAG, "Could not open /proc/net/arp to lookup mac address");
1312            } catch (IOException e) {
1313                Slog.e(TAG, "Could not read /proc/net/arp to lookup mac address");
1314            } finally {
1315                try {
1316                    if (reader != null) {
1317                        reader.close();
1318                    }
1319                }
1320                catch (IOException e) {
1321                    // Do nothing
1322                }
1323            }
1324
1325            return 0;
1326        }
1327    }
1328
1329    @Override
1330    public void enableTdls(String remoteAddress, boolean enable) {
1331        if (remoteAddress == null) {
1332          throw new IllegalArgumentException("remoteAddress cannot be null");
1333        }
1334
1335        TdlsTaskParams params = new TdlsTaskParams();
1336        params.remoteIpAddress = remoteAddress;
1337        params.enable = enable;
1338        new TdlsTask().execute(params);
1339    }
1340
1341
1342    @Override
1343    public void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable) {
1344        if (remoteMacAddress == null) {
1345          throw new IllegalArgumentException("remoteMacAddress cannot be null");
1346        }
1347
1348        mWifiStateMachine.enableTdls(remoteMacAddress, enable);
1349    }
1350
1351    /**
1352     * Get a reference to handler. This is used by a client to establish
1353     * an AsyncChannel communication with WifiService
1354     */
1355    @Override
1356    public Messenger getWifiServiceMessenger() {
1357        enforceAccessPermission();
1358        enforceChangePermission();
1359        return new Messenger(mClientHandler);
1360    }
1361
1362    /**
1363     * Disable an ephemeral network, i.e. network that is created thru a WiFi Scorer
1364     */
1365    @Override
1366    public void disableEphemeralNetwork(String SSID) {
1367        enforceAccessPermission();
1368        enforceChangePermission();
1369        mWifiStateMachine.disableEphemeralNetwork(SSID);
1370    }
1371
1372    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1373        @Override
1374        public void onReceive(Context context, Intent intent) {
1375            String action = intent.getAction();
1376            if (action.equals(Intent.ACTION_SCREEN_ON)) {
1377                mWifiController.sendMessage(CMD_SCREEN_ON);
1378            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
1379                mWifiController.sendMessage(CMD_USER_PRESENT);
1380            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1381                mWifiController.sendMessage(CMD_SCREEN_OFF);
1382            } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
1383                int pluggedType = intent.getIntExtra("plugged", 0);
1384                mWifiController.sendMessage(CMD_BATTERY_CHANGED, pluggedType, 0, null);
1385            } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
1386                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
1387                        BluetoothAdapter.STATE_DISCONNECTED);
1388                mWifiStateMachine.sendBluetoothAdapterStateChange(state);
1389            } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
1390                boolean emergencyMode = intent.getBooleanExtra("phoneinECMState", false);
1391                mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, emergencyMode ? 1 : 0, 0);
1392            } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED)) {
1393                boolean inCall = intent.getBooleanExtra(PhoneConstants.PHONE_IN_EMERGENCY_CALL, false);
1394                mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, inCall ? 1 : 0, 0);
1395            } else if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) {
1396                handleIdleModeChanged();
1397            }
1398        }
1399    };
1400
1401    /**
1402     * Observes settings changes to scan always mode.
1403     */
1404    private void registerForScanModeChange() {
1405        ContentObserver contentObserver = new ContentObserver(null) {
1406            @Override
1407            public void onChange(boolean selfChange) {
1408                mSettingsStore.handleWifiScanAlwaysAvailableToggled();
1409                mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED);
1410            }
1411        };
1412
1413        mContext.getContentResolver().registerContentObserver(
1414                Settings.Global.getUriFor(Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE),
1415                false, contentObserver);
1416    }
1417
1418    private void registerForBroadcasts() {
1419        IntentFilter intentFilter = new IntentFilter();
1420        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
1421        intentFilter.addAction(Intent.ACTION_USER_PRESENT);
1422        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
1423        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
1424        intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
1425        intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
1426        intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
1427        intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
1428
1429        boolean trackEmergencyCallState = mContext.getResources().getBoolean(
1430                com.android.internal.R.bool.config_wifi_turn_off_during_emergency_call);
1431        if (trackEmergencyCallState) {
1432            intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED);
1433        }
1434
1435        mContext.registerReceiver(mReceiver, intentFilter);
1436    }
1437
1438    private void registerForPackageOrUserRemoval() {
1439        IntentFilter intentFilter = new IntentFilter();
1440        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1441        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
1442        mContext.registerReceiverAsUser(new BroadcastReceiver() {
1443            @Override
1444            public void onReceive(Context context, Intent intent) {
1445                switch (intent.getAction()) {
1446                    case Intent.ACTION_PACKAGE_REMOVED: {
1447                        if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
1448                            return;
1449                        }
1450                        int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1451                        Uri uri = intent.getData();
1452                        if (uid == -1 || uri == null) {
1453                            return;
1454                        }
1455                        String pkgName = uri.getSchemeSpecificPart();
1456                        mWifiStateMachine.removeAppConfigs(pkgName, uid);
1457                        break;
1458                    }
1459                    case Intent.ACTION_USER_REMOVED: {
1460                        int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
1461                        mWifiStateMachine.removeUserConfigs(userHandle);
1462                        break;
1463                    }
1464                }
1465            }
1466        }, UserHandle.ALL, intentFilter, null, null);
1467    }
1468
1469    @Override
1470    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1471        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1472                != PackageManager.PERMISSION_GRANTED) {
1473            pw.println("Permission Denial: can't dump WifiService from from pid="
1474                    + Binder.getCallingPid()
1475                    + ", uid=" + Binder.getCallingUid());
1476            return;
1477        }
1478        if (args.length > 0 && WifiMetrics.PROTO_DUMP_ARG.equals(args[0])) {
1479            // WifiMetrics proto bytes were requested. Dump only these.
1480            mWifiStateMachine.updateWifiMetrics();
1481            mWifiMetrics.dump(fd, pw, args);
1482        } else if (args.length > 0 && IpManager.DUMP_ARG.equals(args[0])) {
1483            // IpManager dump was requested. Pass it along and take no further action.
1484            String[] ipManagerArgs = new String[args.length - 1];
1485            System.arraycopy(args, 1, ipManagerArgs, 0, ipManagerArgs.length);
1486            mWifiStateMachine.dumpIpManager(fd, pw, ipManagerArgs);
1487        } else {
1488            pw.println("Wi-Fi is " + mWifiStateMachine.syncGetWifiStateByName());
1489            pw.println("Stay-awake conditions: " +
1490                    Settings.Global.getInt(mContext.getContentResolver(),
1491                                           Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0));
1492            pw.println("mInIdleMode " + mInIdleMode);
1493            pw.println("mScanPending " + mScanPending);
1494            mWifiController.dump(fd, pw, args);
1495            mSettingsStore.dump(fd, pw, args);
1496            mNotificationController.dump(fd, pw, args);
1497            mTrafficPoller.dump(fd, pw, args);
1498
1499            pw.println("Latest scan results:");
1500            List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
1501            long nowMs = System.currentTimeMillis();
1502            if (scanResults != null && scanResults.size() != 0) {
1503                pw.println("    BSSID              Frequency  RSSI    Age      SSID " +
1504                        "                                Flags");
1505                for (ScanResult r : scanResults) {
1506                    long ageSec = 0;
1507                    long ageMilli = 0;
1508                    if (nowMs > r.seen && r.seen > 0) {
1509                        ageSec = (nowMs - r.seen) / 1000;
1510                        ageMilli = (nowMs - r.seen) % 1000;
1511                    }
1512                    String candidate = " ";
1513                    if (r.isAutoJoinCandidate > 0) candidate = "+";
1514                    pw.printf("  %17s  %9d  %5d  %3d.%03d%s   %-32s  %s\n",
1515                                             r.BSSID,
1516                                             r.frequency,
1517                                             r.level,
1518                                             ageSec, ageMilli,
1519                                             candidate,
1520                                             r.SSID == null ? "" : r.SSID,
1521                                             r.capabilities);
1522                }
1523            }
1524            pw.println();
1525            pw.println("Locks held:");
1526            mWifiLockManager.dump(pw);
1527            pw.println();
1528            mWifiMulticastLockManager.dump(pw);
1529            pw.println();
1530            mWifiStateMachine.dump(fd, pw, args);
1531            pw.println();
1532            mWifiBackupRestore.dump(fd, pw, args);
1533            pw.println();
1534        }
1535    }
1536
1537    @Override
1538    public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws) {
1539        if (mWifiLockManager.acquireWifiLock(lockMode, tag, binder, ws)) {
1540            mWifiController.sendMessage(CMD_LOCKS_CHANGED);
1541            return true;
1542        }
1543        return false;
1544    }
1545
1546    @Override
1547    public void updateWifiLockWorkSource(IBinder binder, WorkSource ws) {
1548        mWifiLockManager.updateWifiLockWorkSource(binder, ws);
1549    }
1550
1551    @Override
1552    public boolean releaseWifiLock(IBinder binder) {
1553        if (mWifiLockManager.releaseWifiLock(binder)) {
1554            mWifiController.sendMessage(CMD_LOCKS_CHANGED);
1555            return true;
1556        }
1557        return false;
1558    }
1559
1560    @Override
1561    public void initializeMulticastFiltering() {
1562        enforceMulticastChangePermission();
1563        mWifiMulticastLockManager.initializeFiltering();
1564    }
1565
1566    @Override
1567    public void acquireMulticastLock(IBinder binder, String tag) {
1568        enforceMulticastChangePermission();
1569        mWifiMulticastLockManager.acquireLock(binder, tag);
1570    }
1571
1572    @Override
1573    public void releaseMulticastLock() {
1574        enforceMulticastChangePermission();
1575        mWifiMulticastLockManager.releaseLock();
1576    }
1577
1578    @Override
1579    public boolean isMulticastEnabled() {
1580        enforceAccessPermission();
1581        return mWifiMulticastLockManager.isMulticastEnabled();
1582    }
1583
1584    @Override
1585    public void enableVerboseLogging(int verbose) {
1586        enforceAccessPermission();
1587        mFacade.setIntegerSetting(
1588                mContext, Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, verbose);
1589        enableVerboseLoggingInternal(verbose);
1590    }
1591
1592    private void enableVerboseLoggingInternal(int verbose) {
1593        mWifiStateMachine.enableVerboseLogging(verbose);
1594        mWifiLockManager.enableVerboseLogging(verbose);
1595        mWifiMulticastLockManager.enableVerboseLogging(verbose);
1596        mWifiInjector.getWifiLastResortWatchdog().enableVerboseLogging(verbose);
1597        mWifiInjector.getWifiBackupRestore().enableVerboseLogging(verbose);
1598    }
1599
1600    @Override
1601    public int getVerboseLoggingLevel() {
1602        enforceAccessPermission();
1603        return mFacade.getIntegerSetting(
1604                mContext, Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, 0);
1605    }
1606
1607    @Override
1608    public void enableAggressiveHandover(int enabled) {
1609        enforceAccessPermission();
1610        mWifiStateMachine.enableAggressiveHandover(enabled);
1611    }
1612
1613    @Override
1614    public int getAggressiveHandover() {
1615        enforceAccessPermission();
1616        return mWifiStateMachine.getAggressiveHandover();
1617    }
1618
1619    @Override
1620    public void setAllowScansWithTraffic(int enabled) {
1621        enforceAccessPermission();
1622        mWifiStateMachine.setAllowScansWithTraffic(enabled);
1623    }
1624
1625    @Override
1626    public int getAllowScansWithTraffic() {
1627        enforceAccessPermission();
1628        return mWifiStateMachine.getAllowScansWithTraffic();
1629    }
1630
1631    @Override
1632    public boolean setEnableAutoJoinWhenAssociated(boolean enabled) {
1633        enforceChangePermission();
1634        return mWifiStateMachine.setEnableAutoJoinWhenAssociated(enabled);
1635    }
1636
1637    @Override
1638    public boolean getEnableAutoJoinWhenAssociated() {
1639        enforceAccessPermission();
1640        return mWifiStateMachine.getEnableAutoJoinWhenAssociated();
1641    }
1642
1643    /* Return the Wifi Connection statistics object */
1644    @Override
1645    public WifiConnectionStatistics getConnectionStatistics() {
1646        enforceAccessPermission();
1647        enforceReadCredentialPermission();
1648        if (mWifiStateMachineChannel != null) {
1649            return mWifiStateMachine.syncGetConnectionStatistics(mWifiStateMachineChannel);
1650        } else {
1651            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
1652            return null;
1653        }
1654    }
1655
1656    @Override
1657    public void factoryReset() {
1658        enforceConnectivityInternalPermission();
1659
1660        if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) {
1661            return;
1662        }
1663
1664        if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
1665            // Turn mobile hotspot off
1666            setWifiApEnabled(null, false);
1667        }
1668
1669        if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI)) {
1670            // Enable wifi
1671            setWifiEnabled(true);
1672            // Delete all Wifi SSIDs
1673            List<WifiConfiguration> networks = getConfiguredNetworks();
1674            if (networks != null) {
1675                for (WifiConfiguration config : networks) {
1676                    removeNetwork(config.networkId);
1677                }
1678                saveConfiguration();
1679            }
1680        }
1681    }
1682
1683    /* private methods */
1684    static boolean logAndReturnFalse(String s) {
1685        Log.d(TAG, s);
1686        return false;
1687    }
1688
1689    public static boolean isValid(WifiConfiguration config) {
1690        String validity = checkValidity(config);
1691        return validity == null || logAndReturnFalse(validity);
1692    }
1693
1694    public static boolean isValidPasspoint(WifiConfiguration config) {
1695        String validity = checkPasspointValidity(config);
1696        return validity == null || logAndReturnFalse(validity);
1697    }
1698
1699    public static String checkValidity(WifiConfiguration config) {
1700        if (config.allowedKeyManagement == null)
1701            return "allowed kmgmt";
1702
1703        if (config.allowedKeyManagement.cardinality() > 1) {
1704            if (config.allowedKeyManagement.cardinality() != 2) {
1705                return "cardinality != 2";
1706            }
1707            if (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)) {
1708                return "not WPA_EAP";
1709            }
1710            if ((!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X))
1711                    && (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK))) {
1712                return "not PSK or 8021X";
1713            }
1714        }
1715        return null;
1716    }
1717
1718    public static String checkPasspointValidity(WifiConfiguration config) {
1719        if (!TextUtils.isEmpty(config.FQDN)) {
1720            /* this is passpoint configuration; it must not have an SSID */
1721            if (!TextUtils.isEmpty(config.SSID)) {
1722                return "SSID not expected for Passpoint: '" + config.SSID +
1723                        "' FQDN " + toHexString(config.FQDN);
1724            }
1725            /* this is passpoint configuration; it must have a providerFriendlyName */
1726            if (TextUtils.isEmpty(config.providerFriendlyName)) {
1727                return "no provider friendly name";
1728            }
1729            WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
1730            /* this is passpoint configuration; it must have enterprise config */
1731            if (enterpriseConfig == null
1732                    || enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.NONE ) {
1733                return "no enterprise config";
1734            }
1735            if ((enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS ||
1736                    enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS ||
1737                    enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP) &&
1738                    enterpriseConfig.getCaCertificate() == null) {
1739                return "no CA certificate";
1740            }
1741        }
1742        return null;
1743    }
1744
1745    @Override
1746    public Network getCurrentNetwork() {
1747        enforceAccessPermission();
1748        return mWifiStateMachine.getCurrentNetwork();
1749    }
1750
1751    public static String toHexString(String s) {
1752        if (s == null) {
1753            return "null";
1754        }
1755        StringBuilder sb = new StringBuilder();
1756        sb.append('\'').append(s).append('\'');
1757        for (int n = 0; n < s.length(); n++) {
1758            sb.append(String.format(" %02x", s.charAt(n) & 0xffff));
1759        }
1760        return sb.toString();
1761    }
1762
1763    /**
1764     * Checks that calling process has android.Manifest.permission.ACCESS_COARSE_LOCATION or
1765     * android.Manifest.permission.ACCESS_FINE_LOCATION and a corresponding app op is allowed
1766     */
1767    private boolean checkCallerCanAccessScanResults(String callingPackage, int uid) {
1768        if (ActivityManager.checkUidPermission(Manifest.permission.ACCESS_FINE_LOCATION, uid)
1769                == PackageManager.PERMISSION_GRANTED
1770                && checkAppOppAllowed(AppOpsManager.OP_FINE_LOCATION, callingPackage, uid)) {
1771            return true;
1772        }
1773
1774        if (ActivityManager.checkUidPermission(Manifest.permission.ACCESS_COARSE_LOCATION, uid)
1775                == PackageManager.PERMISSION_GRANTED
1776                && checkAppOppAllowed(AppOpsManager.OP_COARSE_LOCATION, callingPackage, uid)) {
1777            return true;
1778        }
1779        boolean apiLevel23App = isMApp(mContext, callingPackage);
1780        // Pre-M apps running in the foreground should continue getting scan results
1781        if (!apiLevel23App && isForegroundApp(callingPackage)) {
1782            return true;
1783        }
1784        Log.e(TAG, "Permission denial: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION "
1785                + "permission to get scan results");
1786        return false;
1787    }
1788
1789    private boolean checkAppOppAllowed(int op, String callingPackage, int uid) {
1790        return mAppOps.noteOp(op, uid, callingPackage) == AppOpsManager.MODE_ALLOWED;
1791    }
1792
1793    private static boolean isMApp(Context context, String pkgName) {
1794        try {
1795            return context.getPackageManager().getApplicationInfo(pkgName, 0)
1796                    .targetSdkVersion >= Build.VERSION_CODES.M;
1797        } catch (PackageManager.NameNotFoundException e) {
1798            // In case of exception, assume M app (more strict checking)
1799        }
1800        return true;
1801    }
1802
1803    public void hideCertFromUnaffiliatedUsers(String alias) {
1804        mCertManager.hideCertFromUnaffiliatedUsers(alias);
1805    }
1806
1807    public String[] listClientCertsForCurrentUser() {
1808        return mCertManager.listClientCertsForCurrentUser();
1809    }
1810
1811    /**
1812     * Return true if the specified package name is a foreground app.
1813     *
1814     * @param pkgName application package name.
1815     */
1816    private boolean isForegroundApp(String pkgName) {
1817        ActivityManager am = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
1818        List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
1819        return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
1820    }
1821
1822    /**
1823     * Enable/disable WifiConnectivityManager at runtime
1824     *
1825     * @param enabled true-enable; false-disable
1826     */
1827    @Override
1828    public void enableWifiConnectivityManager(boolean enabled) {
1829        enforceConnectivityInternalPermission();
1830        mWifiStateMachine.enableWifiConnectivityManager(enabled);
1831    }
1832
1833    /**
1834     * Retrieve the data to be backed to save the current state.
1835     *
1836     * @return  Raw byte stream of the data to be backed up.
1837     */
1838    @Override
1839    public byte[] retrieveBackupData() {
1840        enforceReadCredentialPermission();
1841        enforceAccessPermission();
1842        if (mWifiStateMachineChannel == null) {
1843            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
1844            return null;
1845        }
1846
1847        Slog.d(TAG, "Retrieving backup data");
1848        List<WifiConfiguration> wifiConfigurations =
1849                mWifiStateMachine.syncGetPrivilegedConfiguredNetwork(mWifiStateMachineChannel);
1850        byte[] backupData =
1851                mWifiBackupRestore.retrieveBackupDataFromConfigurations(wifiConfigurations);
1852        Slog.d(TAG, "Retrieved backup data");
1853        return backupData;
1854    }
1855
1856    /**
1857     * Helper method to restore networks retrieved from backup data.
1858     *
1859     * @param configurations list of WifiConfiguration objects parsed from the backup data.
1860     */
1861    private void restoreNetworks(List<WifiConfiguration> configurations) {
1862        if (configurations == null) {
1863            Slog.e(TAG, "Backup data parse failed");
1864            return;
1865        }
1866        for (WifiConfiguration configuration : configurations) {
1867            int networkId = mWifiStateMachine.syncAddOrUpdateNetwork(
1868                    mWifiStateMachineChannel, configuration);
1869            if (networkId == WifiConfiguration.INVALID_NETWORK_ID) {
1870                Slog.e(TAG, "Restore network failed: " + configuration.configKey());
1871                continue;
1872            }
1873            // Enable all networks restored.
1874            mWifiStateMachine.syncEnableNetwork(mWifiStateMachineChannel, networkId, false);
1875        }
1876    }
1877
1878    /**
1879     * Restore state from the backed up data.
1880     *
1881     * @param data Raw byte stream of the backed up data.
1882     */
1883    @Override
1884    public void restoreBackupData(byte[] data) {
1885        enforceChangePermission();
1886        if (mWifiStateMachineChannel == null) {
1887            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
1888            return;
1889        }
1890
1891        Slog.d(TAG, "Restoring backup data");
1892        List<WifiConfiguration> wifiConfigurations =
1893                mWifiBackupRestore.retrieveConfigurationsFromBackupData(data);
1894        restoreNetworks(wifiConfigurations);
1895        Slog.d(TAG, "Restored backup data");
1896    }
1897
1898    /**
1899     * Restore state from the older supplicant back up data.
1900     * The old backup data was essentially a backup of wpa_supplicant.conf & ipconfig.txt file.
1901     *
1902     * @param supplicantData Raw byte stream of wpa_supplicant.conf
1903     * @param ipConfigData Raw byte stream of ipconfig.txt
1904     */
1905    public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) {
1906        enforceChangePermission();
1907        if (mWifiStateMachineChannel == null) {
1908            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
1909            return;
1910        }
1911
1912        Slog.d(TAG, "Restoring supplicant backup data");
1913        List<WifiConfiguration> wifiConfigurations =
1914                mWifiBackupRestore.retrieveConfigurationsFromSupplicantBackupData(
1915                        supplicantData, ipConfigData);
1916        restoreNetworks(wifiConfigurations);
1917        Slog.d(TAG, "Restored supplicant backup data");
1918    }
1919}
1920