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