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