1/*
2 * Copyright (C) 2017 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.googlecode.android_scripting.facade.wifi;
18
19import android.app.Service;
20import android.content.BroadcastReceiver;
21import android.content.ContentResolver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.net.ConnectivityManager;
26import android.net.DhcpInfo;
27import android.net.Network;
28import android.net.NetworkInfo;
29import android.net.NetworkInfo.DetailedState;
30import android.net.wifi.hotspot2.ConfigParser;
31import android.net.wifi.hotspot2.PasspointConfiguration;
32import android.net.wifi.ScanResult;
33import android.net.wifi.WifiActivityEnergyInfo;
34import android.net.wifi.WifiConfiguration;
35import android.net.wifi.WifiConfiguration.AuthAlgorithm;
36import android.net.wifi.WifiConfiguration.KeyMgmt;
37import android.net.wifi.WifiEnterpriseConfig;
38import android.net.wifi.WifiInfo;
39import android.net.wifi.WifiManager;
40import android.net.wifi.WifiManager.WifiLock;
41import android.net.wifi.WpsInfo;
42import android.os.Bundle;
43import android.provider.Settings.Global;
44import android.provider.Settings.SettingNotFoundException;
45import android.util.Base64;
46import java.util.Arrays;
47
48import com.googlecode.android_scripting.Log;
49import com.googlecode.android_scripting.facade.EventFacade;
50import com.googlecode.android_scripting.facade.FacadeManager;
51import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
52import com.googlecode.android_scripting.rpc.Rpc;
53import com.googlecode.android_scripting.rpc.RpcDeprecated;
54import com.googlecode.android_scripting.rpc.RpcOptional;
55import com.googlecode.android_scripting.rpc.RpcParameter;
56
57import org.json.JSONArray;
58import org.json.JSONException;
59import org.json.JSONObject;
60
61import java.io.ByteArrayInputStream;
62import java.io.ByteArrayOutputStream;
63import java.io.IOException;
64import java.io.InputStream;
65import java.io.ObjectOutput;
66import java.io.ObjectOutputStream;
67import java.security.GeneralSecurityException;
68import java.security.KeyFactory;
69import java.security.NoSuchAlgorithmException;
70import java.security.PrivateKey;
71import java.security.PublicKey;
72import java.security.cert.CertificateException;
73import java.security.cert.CertificateFactory;
74import java.security.cert.X509Certificate;
75import java.security.spec.InvalidKeySpecException;
76import java.security.spec.PKCS8EncodedKeySpec;
77import java.security.spec.X509EncodedKeySpec;
78import java.util.ArrayList;
79import java.util.List;
80
81/**
82 * WifiManager functions.
83 */
84// TODO: make methods handle various wifi states properly
85// e.g. wifi connection result will be null when flight mode is on
86public class WifiManagerFacade extends RpcReceiver {
87    private final static String mEventType = "WifiManager";
88    // MIME type for passpoint config.
89    private final static String TYPE_WIFICONFIG = "application/x-wifi-config";
90    private final Service mService;
91    private final WifiManager mWifi;
92    private final EventFacade mEventFacade;
93
94    private final IntentFilter mScanFilter;
95    private final IntentFilter mStateChangeFilter;
96    private final IntentFilter mTetherFilter;
97    private final WifiScanReceiver mScanResultsAvailableReceiver;
98    private final WifiStateChangeReceiver mStateChangeReceiver;
99    private boolean mTrackingWifiStateChange;
100    private boolean mTrackingTetherStateChange;
101
102    private final BroadcastReceiver mTetherStateReceiver = new BroadcastReceiver() {
103        @Override
104        public void onReceive(Context context, Intent intent) {
105            String action = intent.getAction();
106            if (WifiManager.WIFI_AP_STATE_CHANGED_ACTION.equals(action)) {
107                Log.d("Wifi AP state changed.");
108                int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE,
109                        WifiManager.WIFI_AP_STATE_FAILED);
110                if (state == WifiManager.WIFI_AP_STATE_ENABLED) {
111                    mEventFacade.postEvent("WifiManagerApEnabled", null);
112                } else if (state == WifiManager.WIFI_AP_STATE_DISABLED) {
113                    mEventFacade.postEvent("WifiManagerApDisabled", null);
114                }
115            } else if (ConnectivityManager.ACTION_TETHER_STATE_CHANGED.equals(action)) {
116                Log.d("Tether state changed.");
117                ArrayList<String> available = intent.getStringArrayListExtra(
118                        ConnectivityManager.EXTRA_AVAILABLE_TETHER);
119                ArrayList<String> active = intent.getStringArrayListExtra(
120                        ConnectivityManager.EXTRA_ACTIVE_TETHER);
121                ArrayList<String> errored = intent.getStringArrayListExtra(
122                        ConnectivityManager.EXTRA_ERRORED_TETHER);
123                Bundle msg = new Bundle();
124                msg.putStringArrayList("AVAILABLE_TETHER", available);
125                msg.putStringArrayList("ACTIVE_TETHER", active);
126                msg.putStringArrayList("ERRORED_TETHER", errored);
127                mEventFacade.postEvent("TetherStateChanged", msg);
128            }
129        }
130    };
131
132    private WifiLock mLock = null;
133    private boolean mIsConnected = false;
134
135    public WifiManagerFacade(FacadeManager manager) {
136        super(manager);
137        mService = manager.getService();
138        mWifi = (WifiManager) mService.getSystemService(Context.WIFI_SERVICE);
139        mEventFacade = manager.getReceiver(EventFacade.class);
140
141        mScanFilter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
142        mStateChangeFilter = new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION);
143        mStateChangeFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
144        mStateChangeFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
145        mStateChangeFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY - 1);
146
147        mTetherFilter = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
148        mTetherFilter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
149
150        mScanResultsAvailableReceiver = new WifiScanReceiver(mEventFacade);
151        mStateChangeReceiver = new WifiStateChangeReceiver();
152        mTrackingWifiStateChange = false;
153        mTrackingTetherStateChange = false;
154    }
155
156    private void makeLock(int wifiMode) {
157        if (mLock == null) {
158            mLock = mWifi.createWifiLock(wifiMode, "sl4a");
159            mLock.acquire();
160        }
161    }
162
163    /**
164     * Handle Broadcast receiver for Scan Result
165     *
166     * @parm eventFacade Object of EventFacade
167     */
168    class WifiScanReceiver extends BroadcastReceiver {
169        private final EventFacade mEventFacade;
170
171        WifiScanReceiver(EventFacade eventFacade) {
172            mEventFacade = eventFacade;
173        }
174
175        @Override
176        public void onReceive(Context c, Intent intent) {
177            String action = intent.getAction();
178            if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
179                Bundle mResults = new Bundle();
180                Log.d("Wifi connection scan finished, results available.");
181                mResults.putLong("Timestamp", System.currentTimeMillis() / 1000);
182                mEventFacade.postEvent(mEventType + "ScanResultsAvailable", mResults);
183                mService.unregisterReceiver(mScanResultsAvailableReceiver);
184            }
185        }
186    }
187
188    class WifiActionListener implements WifiManager.ActionListener {
189        private final EventFacade mEventFacade;
190        private final String TAG;
191
192        public WifiActionListener(EventFacade eventFacade, String tag) {
193            mEventFacade = eventFacade;
194            this.TAG = tag;
195        }
196
197        @Override
198        public void onSuccess() {
199            Log.d("WifiActionListener  onSuccess called for " + mEventType + TAG + "OnSuccess");
200            mEventFacade.postEvent(mEventType + TAG + "OnSuccess", null);
201        }
202
203        @Override
204        public void onFailure(int reason) {
205            Log.d("WifiActionListener  onFailure called for" + mEventType);
206            Bundle msg = new Bundle();
207            msg.putInt("reason", reason);
208            mEventFacade.postEvent(mEventType + TAG + "OnFailure", msg);
209        }
210    }
211
212    public class WifiStateChangeReceiver extends BroadcastReceiver {
213        String mCachedWifiInfo = "";
214
215        @Override
216        public void onReceive(Context context, Intent intent) {
217            String action = intent.getAction();
218            if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
219                Log.d("Wifi network state changed.");
220                NetworkInfo nInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
221                WifiInfo wInfo = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
222                Log.d("NetworkInfo " + nInfo);
223                Log.d("WifiInfo " + wInfo);
224                // If network info is of type wifi, send wifi events.
225                if (nInfo.getType() == ConnectivityManager.TYPE_WIFI) {
226                    if (wInfo != null && nInfo.getDetailedState().equals(DetailedState.CONNECTED)) {
227                        String bssid = wInfo.getBSSID();
228                        if (bssid != null && !mCachedWifiInfo.equals(wInfo.toString())) {
229                            Log.d("WifiNetworkConnected");
230                            mEventFacade.postEvent("WifiNetworkConnected", wInfo);
231                        }
232                        mCachedWifiInfo = wInfo.toString();
233                    } else {
234                        if (nInfo.getDetailedState().equals(DetailedState.DISCONNECTED)) {
235                            if (!mCachedWifiInfo.equals("")) {
236                                mCachedWifiInfo = "";
237                                mEventFacade.postEvent("WifiNetworkDisconnected", null);
238                            }
239                        }
240                    }
241                }
242            } else if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
243                Log.d("Supplicant connection state changed.");
244                mIsConnected = intent
245                        .getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false);
246                Bundle msg = new Bundle();
247                msg.putBoolean("Connected", mIsConnected);
248                mEventFacade.postEvent("SupplicantConnectionChanged", msg);
249            }
250        }
251    }
252
253    public class WifiWpsCallback extends WifiManager.WpsCallback {
254        private static final String tag = "WifiWps";
255
256        @Override
257        public void onStarted(String pin) {
258            Bundle msg = new Bundle();
259            msg.putString("pin", pin);
260            mEventFacade.postEvent(tag + "OnStarted", msg);
261        }
262
263        @Override
264        public void onSucceeded() {
265            Log.d("Wps op succeeded");
266            mEventFacade.postEvent(tag + "OnSucceeded", null);
267        }
268
269        @Override
270        public void onFailed(int reason) {
271            Bundle msg = new Bundle();
272            msg.putInt("reason", reason);
273            mEventFacade.postEvent(tag + "OnFailed", msg);
274        }
275    }
276
277    private void applyingkeyMgmt(WifiConfiguration config, ScanResult result) {
278        if (result.capabilities.contains("WEP")) {
279            config.allowedKeyManagement.set(KeyMgmt.NONE);
280            config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
281            config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
282        } else if (result.capabilities.contains("PSK")) {
283            config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
284        } else if (result.capabilities.contains("EAP")) {
285            // this is probably wrong, as we don't have a way to enter the enterprise config
286            config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
287            config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
288        } else {
289            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
290        }
291    }
292
293    private WifiConfiguration genWifiConfig(JSONObject j) throws JSONException {
294        if (j == null) {
295            return null;
296        }
297        WifiConfiguration config = new WifiConfiguration();
298        if (j.has("SSID")) {
299            config.SSID = "\"" + j.getString("SSID") + "\"";
300        } else if (j.has("ssid")) {
301            config.SSID = "\"" + j.getString("ssid") + "\"";
302        }
303        if (j.has("password")) {
304            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
305            config.preSharedKey = "\"" + j.getString("password") + "\"";
306        } else {
307            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
308        }
309        if (j.has("BSSID")) {
310            config.BSSID = j.getString("BSSID");
311        }
312        if (j.has("hiddenSSID")) {
313            config.hiddenSSID = j.getBoolean("hiddenSSID");
314        }
315        if (j.has("priority")) {
316            config.priority = j.getInt("priority");
317        }
318        if (j.has("apBand")) {
319            config.apBand = j.getInt("apBand");
320        }
321        if (j.has("preSharedKey")) {
322            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
323            config.preSharedKey = j.getString("preSharedKey");
324        }
325        if (j.has("wepKeys")) {
326            // Looks like we only support static WEP.
327            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
328            config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
329            config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
330            JSONArray keys = j.getJSONArray("wepKeys");
331            String[] wepKeys = new String[keys.length()];
332            for (int i = 0; i < keys.length(); i++) {
333                wepKeys[i] = keys.getString(i);
334            }
335            config.wepKeys = wepKeys;
336        }
337        if (j.has("wepTxKeyIndex")) {
338            config.wepTxKeyIndex = j.getInt("wepTxKeyIndex");
339        }
340        return config;
341    }
342
343    private WifiConfiguration genWifiEnterpriseConfig(JSONObject j) throws JSONException,
344            GeneralSecurityException {
345        if (j == null) {
346            return null;
347        }
348        WifiConfiguration config = new WifiConfiguration();
349        config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
350        config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
351        if (j.has("SSID")) {
352            config.SSID = "\"" + j.getString("SSID") + "\"";
353        } else if (j.has("ssid")) {
354            config.SSID = "\"" + j.getString("ssid") + "\"";
355        }
356        if (j.has("FQDN")) {
357            config.FQDN = j.getString("FQDN");
358        }
359        if (j.has("providerFriendlyName")) {
360            config.providerFriendlyName = j.getString("providerFriendlyName");
361        }
362        if (j.has("roamingConsortiumIds")) {
363            JSONArray ids = j.getJSONArray("roamingConsortiumIds");
364            long[] rIds = new long[ids.length()];
365            for (int i = 0; i < ids.length(); i++) {
366                rIds[i] = ids.getLong(i);
367            }
368            config.roamingConsortiumIds = rIds;
369        }
370        WifiEnterpriseConfig eConfig = new WifiEnterpriseConfig();
371        if (j.has(WifiEnterpriseConfig.EAP_KEY)) {
372            int eap = j.getInt(WifiEnterpriseConfig.EAP_KEY);
373            eConfig.setEapMethod(eap);
374        }
375        if (j.has(WifiEnterpriseConfig.PHASE2_KEY)) {
376            int p2Method = j.getInt(WifiEnterpriseConfig.PHASE2_KEY);
377            eConfig.setPhase2Method(p2Method);
378        }
379        if (j.has(WifiEnterpriseConfig.CA_CERT_KEY)) {
380            String certStr = j.getString(WifiEnterpriseConfig.CA_CERT_KEY);
381            Log.v("CA Cert String is " + certStr);
382            eConfig.setCaCertificate(strToX509Cert(certStr));
383        }
384        if (j.has(WifiEnterpriseConfig.CLIENT_CERT_KEY)
385                && j.has(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY)) {
386            String certStr = j.getString(WifiEnterpriseConfig.CLIENT_CERT_KEY);
387            String keyStr = j.getString(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY);
388            Log.v("Client Cert String is " + certStr);
389            Log.v("Client Key String is " + keyStr);
390            X509Certificate cert = strToX509Cert(certStr);
391            PrivateKey privKey = strToPrivateKey(keyStr);
392            Log.v("Cert is " + cert);
393            Log.v("Private Key is " + privKey);
394            eConfig.setClientKeyEntry(privKey, cert);
395        }
396        if (j.has(WifiEnterpriseConfig.IDENTITY_KEY)) {
397            String identity = j.getString(WifiEnterpriseConfig.IDENTITY_KEY);
398            Log.v("Setting identity to " + identity);
399            eConfig.setIdentity(identity);
400        }
401        if (j.has(WifiEnterpriseConfig.PASSWORD_KEY)) {
402            String pwd = j.getString(WifiEnterpriseConfig.PASSWORD_KEY);
403            Log.v("Setting password to " + pwd);
404            eConfig.setPassword(pwd);
405        }
406        if (j.has(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY)) {
407            String altSub = j.getString(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY);
408            Log.v("Setting Alt Subject to " + altSub);
409            eConfig.setAltSubjectMatch(altSub);
410        }
411        if (j.has(WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY)) {
412            String domSuffix = j.getString(WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY);
413            Log.v("Setting Domain Suffix Match to " + domSuffix);
414            eConfig.setDomainSuffixMatch(domSuffix);
415        }
416        if (j.has(WifiEnterpriseConfig.REALM_KEY)) {
417            String realm = j.getString(WifiEnterpriseConfig.REALM_KEY);
418            Log.v("Setting Domain Suffix Match to " + realm);
419            eConfig.setRealm(realm);
420        }
421        config.enterpriseConfig = eConfig;
422        return config;
423    }
424
425    private boolean matchScanResult(ScanResult result, String id) {
426        if (result.BSSID.equals(id) || result.SSID.equals(id)) {
427            return true;
428        }
429        return false;
430    }
431
432    private WpsInfo parseWpsInfo(String infoStr) throws JSONException {
433        if (infoStr == null) {
434            return null;
435        }
436        JSONObject j = new JSONObject(infoStr);
437        WpsInfo info = new WpsInfo();
438        if (j.has("setup")) {
439            info.setup = j.getInt("setup");
440        }
441        if (j.has("BSSID")) {
442            info.BSSID = j.getString("BSSID");
443        }
444        if (j.has("pin")) {
445            info.pin = j.getString("pin");
446        }
447        return info;
448    }
449
450    private byte[] base64StrToBytes(String input) {
451        return Base64.decode(input, Base64.DEFAULT);
452    }
453
454    private X509Certificate strToX509Cert(String certStr) throws CertificateException {
455        byte[] certBytes = base64StrToBytes(certStr);
456        InputStream certStream = new ByteArrayInputStream(certBytes);
457        CertificateFactory cf = CertificateFactory.getInstance("X509");
458        return (X509Certificate) cf.generateCertificate(certStream);
459    }
460
461    private PrivateKey strToPrivateKey(String key) throws NoSuchAlgorithmException,
462            InvalidKeySpecException {
463        byte[] keyBytes = base64StrToBytes(key);
464        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
465        KeyFactory fact = KeyFactory.getInstance("RSA");
466        PrivateKey priv = fact.generatePrivate(keySpec);
467        return priv;
468    }
469
470    private PublicKey strToPublicKey(String key) throws NoSuchAlgorithmException,
471            InvalidKeySpecException {
472        byte[] keyBytes = base64StrToBytes(key);
473        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
474        KeyFactory fact = KeyFactory.getInstance("RSA");
475        PublicKey pub = fact.generatePublic(keySpec);
476        return pub;
477    }
478
479    private WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) {
480        if (result == null)
481            return null;
482        WifiConfiguration config = new WifiConfiguration();
483        config.SSID = "\"" + result.SSID + "\"";
484        applyingkeyMgmt(config, result);
485        config.BSSID = result.BSSID;
486        return config;
487    }
488
489    @Rpc(description = "test.")
490    public String wifiTest(
491            @RpcParameter(name = "certString") String certString) throws CertificateException, IOException {
492        // TODO(angli): Make this work. Convert a X509Certificate back to a string.
493        X509Certificate caCert = strToX509Cert(certString);
494        caCert.getEncoded();
495        ByteArrayOutputStream bos = new ByteArrayOutputStream();
496        ObjectOutput out = new ObjectOutputStream(bos);
497        out.writeObject(caCert);
498        byte[] data = bos.toByteArray();
499        bos.close();
500        return Base64.encodeToString(data, Base64.DEFAULT);
501    }
502
503    @Rpc(description = "Add a network.")
504    public Integer wifiAddNetwork(@RpcParameter(name = "wifiConfig") JSONObject wifiConfig)
505            throws JSONException {
506        return mWifi.addNetwork(genWifiConfig(wifiConfig));
507    }
508
509    @Rpc(description = "Cancel Wi-fi Protected Setup.")
510    public void wifiCancelWps() throws JSONException {
511        WifiWpsCallback listener = new WifiWpsCallback();
512        mWifi.cancelWps(listener);
513    }
514
515    @Rpc(description = "Checks Wifi state.", returns = "True if Wifi is enabled.")
516    public Boolean wifiCheckState() {
517        return mWifi.getWifiState() == WifiManager.WIFI_STATE_ENABLED;
518    }
519
520    /**
521    * @deprecated Use {@link #wifiConnectByConfig(config)} instead.
522    */
523    @Rpc(description = "Connects to the network with the given configuration")
524    @Deprecated
525    public Boolean wifiConnect(@RpcParameter(name = "config") JSONObject config)
526            throws JSONException {
527        try {
528            wifiConnectByConfig(config);
529        } catch (GeneralSecurityException e) {
530            String msg = "Caught GeneralSecurityException with the provided"
531                         + "configuration";
532            throw new RuntimeException(msg);
533        }
534        return true;
535    }
536
537    /**
538    * @deprecated Use {@link #wifiConnectByConfig(config)} instead.
539    */
540    @Rpc(description = "Connects to the network with the given configuration")
541    @Deprecated
542    public Boolean wifiEnterpriseConnect(@RpcParameter(name = "config")
543            JSONObject config) throws JSONException, GeneralSecurityException {
544        try {
545            wifiConnectByConfig(config);
546        } catch (GeneralSecurityException e) {
547            throw e;
548        }
549        return true;
550    }
551
552    /**
553     * Connects to a wifi network using configuration.
554     * @param config JSONObject Dictionary of wifi connection parameters
555     * @throws JSONException
556     * @throws GeneralSecurityException
557     */
558    @Rpc(description = "Connects to the network with the given configuration")
559    public void wifiConnectByConfig(@RpcParameter(name = "config") JSONObject config)
560            throws JSONException, GeneralSecurityException {
561        WifiConfiguration wifiConfig;
562        WifiActionListener listener;
563        // Check if this is 802.1x or 802.11x config.
564        if (config.has(WifiEnterpriseConfig.EAP_KEY)) {
565            wifiConfig = genWifiEnterpriseConfig(config);
566        } else {
567            wifiConfig = genWifiConfig(config);
568        }
569        listener = new WifiActionListener(mEventFacade,
570                WifiConstants.WIFI_CONNECT_BY_CONFIG_CALLBACK);
571        mWifi.connect(wifiConfig, listener);
572    }
573
574   /**
575     * Generate a Passpoint configuration from JSON config.
576     * @param config JSON config containing base64 encoded Passpoint profile
577     */
578    @Rpc(description = "Generate Passpoint configuration", returns = "PasspointConfiguration object")
579    public PasspointConfiguration genWifiPasspointConfig(@RpcParameter(
580            name = "config") JSONObject config)
581            throws JSONException,CertificateException, IOException {
582        String profileStr = "";
583        if (config == null) {
584            return null;
585        }
586        if (config.has("profile")) {
587            profileStr =  config.getString("profile");
588        }
589        return ConfigParser.parsePasspointConfig(TYPE_WIFICONFIG,
590                profileStr.getBytes());
591    }
592
593   /**
594     * Add or update a Passpoint configuration.
595     * @param config base64 encoded message containing Passpoint profile
596     * @throws JSONException
597     */
598    @Rpc(description = "Add or update a Passpoint configuration")
599    public void addUpdatePasspointConfig(@RpcParameter(
600            name = "config") JSONObject config)
601            throws JSONException,CertificateException, IOException {
602        PasspointConfiguration passpointConfig = genWifiPasspointConfig(config);
603        mWifi.addOrUpdatePasspointConfiguration(passpointConfig);
604    }
605
606    /**
607     * Remove a Passpoint configuration.
608     * @param fqdn The FQDN of the passpoint configuration to be removed
609     * @return true on success; false otherwise
610     */
611    @Rpc(description = "Remove a Passpoint configuration")
612    public void removePasspointConfig(
613            @RpcParameter(name = "fqdn") String fqdn) {
614        mWifi.removePasspointConfiguration(fqdn);
615    }
616
617    /**
618     * Get list of Passpoint configurations.
619     * @return A list of FQDNs of the Passpoint configurations
620     */
621    @Rpc(description = "Return the list of installed Passpoint configurations", returns = "A list of Passpoint configurations")
622    public List<String> getPasspointConfigs() {
623        List<String> fqdnList = new ArrayList<String>();
624        for(PasspointConfiguration passpoint :
625            mWifi.getPasspointConfigurations()) {
626            fqdnList.add(passpoint.getHomeSp().getFqdn());
627        }
628        return fqdnList;
629    }
630
631     /**
632     * Connects to a wifi network using networkId.
633     * @param networkId the network identity for the network in the supplicant
634     */
635    @Rpc(description = "Connects to the network with the given networkId")
636    public void wifiConnectByNetworkId(
637            @RpcParameter(name = "networkId") Integer networkId) {
638        WifiActionListener listener;
639        listener = new WifiActionListener(mEventFacade,
640                WifiConstants.WIFI_CONNECT_BY_NETID_CALLBACK);
641        mWifi.connect(networkId, listener);
642    }
643
644    @Rpc(description = "Disconnects from the currently active access point.", returns = "True if the operation succeeded.")
645    public Boolean wifiDisconnect() {
646        return mWifi.disconnect();
647    }
648
649    @Rpc(description = "Enable/disable autojoin scan and switch network when connected.")
650    public Boolean wifiSetEnableAutoJoinWhenAssociated(@RpcParameter(name = "enable") Boolean enable) {
651        return mWifi.setEnableAutoJoinWhenAssociated(enable);
652    }
653
654    @Rpc(description = "Enable a configured network. Initiate a connection if disableOthers is true", returns = "True if the operation succeeded.")
655    public Boolean wifiEnableNetwork(@RpcParameter(name = "netId") Integer netId,
656            @RpcParameter(name = "disableOthers") Boolean disableOthers) {
657        return mWifi.enableNetwork(netId, disableOthers);
658    }
659
660    @Rpc(description = "Enable WiFi verbose logging.")
661    public void wifiEnableVerboseLogging(@RpcParameter(name = "level") Integer level) {
662        mWifi.enableVerboseLogging(level);
663    }
664
665    @Rpc(description = "Resets all WifiManager settings.")
666    public void wifiFactoryReset() {
667        mWifi.factoryReset();
668    }
669
670    /**
671     * Forget a wifi network by networkId.
672     *
673     * @param networkId Id of wifi network
674     */
675    @Rpc(description = "Forget a wifi network by networkId")
676    public void wifiForgetNetwork(@RpcParameter(name = "wifiSSID") Integer networkId) {
677        WifiActionListener listener = new WifiActionListener(mEventFacade,
678                WifiConstants.WIFI_FORGET_NETWORK_CALLBACK);
679        mWifi.forget(networkId, listener);
680    }
681
682    @Rpc(description = "Gets the Wi-Fi AP Configuration.")
683    public WifiConfiguration wifiGetApConfiguration() {
684        return mWifi.getWifiApConfiguration();
685    }
686
687    @Rpc(description = "Return a list of all the configured wifi networks.")
688    public List<WifiConfiguration> wifiGetConfiguredNetworks() {
689        return mWifi.getConfiguredNetworks();
690    }
691
692    @Rpc(description = "Returns information about the currently active access point.")
693    public WifiInfo wifiGetConnectionInfo() {
694        return mWifi.getConnectionInfo();
695    }
696
697    @Rpc(description = "Returns wifi activity and energy usage info.")
698    public WifiActivityEnergyInfo wifiGetControllerActivityEnergyInfo() {
699        return mWifi.getControllerActivityEnergyInfo(0);
700    }
701
702    @Rpc(description = "Get the country code used by WiFi.")
703    public String wifiGetCountryCode() {
704        return mWifi.getCountryCode();
705    }
706
707    @Rpc(description = "Get the current network.")
708    public Network wifiGetCurrentNetwork() {
709        return mWifi.getCurrentNetwork();
710    }
711
712    @Rpc(description = "Get the info from last successful DHCP request.")
713    public DhcpInfo wifiGetDhcpInfo() {
714        return mWifi.getDhcpInfo();
715    }
716
717    @Rpc(description = "Get setting for Framework layer autojoin enable status.")
718    public Boolean wifiGetEnableAutoJoinWhenAssociated() {
719        return mWifi.getEnableAutoJoinWhenAssociated();
720    }
721
722    @Rpc(description = "Get privileged configured networks.")
723    public List<WifiConfiguration> wifiGetPrivilegedConfiguredNetworks() {
724        return mWifi.getPrivilegedConfiguredNetworks();
725    }
726
727    @Rpc(description = "Returns the list of access points found during the most recent Wifi scan.")
728    public List<ScanResult> wifiGetScanResults() {
729        return mWifi.getScanResults();
730    }
731
732    @Rpc(description = "Get the current level of WiFi verbose logging.")
733    public Integer wifiGetVerboseLoggingLevel() {
734        return mWifi.getVerboseLoggingLevel();
735    }
736
737    @Rpc(description = "true if this adapter supports 5 GHz band.")
738    public Boolean wifiIs5GHzBandSupported() {
739        return mWifi.is5GHzBandSupported();
740    }
741
742    @Rpc(description = "true if this adapter supports multiple simultaneous connections.")
743    public Boolean wifiIsAdditionalStaSupported() {
744        return mWifi.isAdditionalStaSupported();
745    }
746
747    @Rpc(description = "Return true if WiFi is enabled.")
748    public Boolean wifiGetisWifiEnabled() {
749        return mWifi.isWifiEnabled();
750    }
751
752    @Rpc(description = "Return whether Wi-Fi AP is enabled or disabled.")
753    public Boolean wifiIsApEnabled() {
754        return mWifi.isWifiApEnabled();
755    }
756
757    @Rpc(description = "Check if Device-to-AP RTT is supported.")
758    public Boolean wifiIsDeviceToApRttSupported() {
759        return mWifi.isDeviceToApRttSupported();
760    }
761
762    @Rpc(description = "Check if Device-to-device RTT is supported.")
763    public Boolean wifiIsDeviceToDeviceRttSupported() {
764        return mWifi.isDeviceToDeviceRttSupported();
765    }
766
767    @Rpc(description = "Check if the chipset supports dual frequency band (2.4 GHz and 5 GHz).")
768    public Boolean wifiIsDualBandSupported() {
769        return mWifi.isDualBandSupported();
770    }
771
772    @Rpc(description = "Check if this adapter supports advanced power/performance counters.")
773    public Boolean wifiIsEnhancedPowerReportingSupported() {
774        return mWifi.isEnhancedPowerReportingSupported();
775    }
776
777    @Rpc(description = "Check if multicast is enabled.")
778    public Boolean wifiIsMulticastEnabled() {
779        return mWifi.isMulticastEnabled();
780    }
781
782    @Rpc(description = "true if this adapter supports Wi-Fi Aware APIs.")
783    public Boolean wifiIsAwareSupported() {
784        return mWifi.isWifiAwareSupported();
785    }
786
787    @Rpc(description = "true if this adapter supports Off Channel Tunnel Directed Link Setup.")
788    public Boolean wifiIsOffChannelTdlsSupported() {
789        return mWifi.isOffChannelTdlsSupported();
790    }
791
792    @Rpc(description = "true if this adapter supports WifiP2pManager (Wi-Fi Direct).")
793    public Boolean wifiIsP2pSupported() {
794        return mWifi.isP2pSupported();
795    }
796
797    @Rpc(description = "true if this adapter supports passpoint.")
798    public Boolean wifiIsPasspointSupported() {
799        return mWifi.isPasspointSupported();
800    }
801
802    @Rpc(description = "true if this adapter supports portable Wi-Fi hotspot.")
803    public Boolean wifiIsPortableHotspotSupported() {
804        return mWifi.isPortableHotspotSupported();
805    }
806
807    @Rpc(description = "true if this adapter supports offloaded connectivity scan.")
808    public Boolean wifiIsPreferredNetworkOffloadSupported() {
809        return mWifi.isPreferredNetworkOffloadSupported();
810    }
811
812    @Rpc(description = "Check if wifi scanner is supported on this device.")
813    public Boolean wifiIsScannerSupported() {
814        return mWifi.isWifiScannerSupported();
815    }
816
817    @Rpc(description = "Check if tdls is supported on this device.")
818    public Boolean wifiIsTdlsSupported() {
819        return mWifi.isTdlsSupported();
820    }
821
822    @Rpc(description = "Acquires a full Wifi lock.")
823    public void wifiLockAcquireFull() {
824        makeLock(WifiManager.WIFI_MODE_FULL);
825    }
826
827    @Rpc(description = "Acquires a scan only Wifi lock.")
828    public void wifiLockAcquireScanOnly() {
829        makeLock(WifiManager.WIFI_MODE_SCAN_ONLY);
830    }
831
832    @Rpc(description = "Releases a previously acquired Wifi lock.")
833    public void wifiLockRelease() {
834        if (mLock != null) {
835            mLock.release();
836            mLock = null;
837        }
838    }
839
840    @Rpc(description = "Reassociates with the currently active access point.", returns = "True if the operation succeeded.")
841    public Boolean wifiReassociate() {
842        return mWifi.reassociate();
843    }
844
845    @Rpc(description = "Reconnects to the currently active access point.", returns = "True if the operation succeeded.")
846    public Boolean wifiReconnect() {
847        return mWifi.reconnect();
848    }
849
850    @Rpc(description = "Remove a configured network.", returns = "True if the operation succeeded.")
851    public Boolean wifiRemoveNetwork(@RpcParameter(name = "netId") Integer netId) {
852        return mWifi.removeNetwork(netId);
853    }
854
855    private WifiConfiguration createSoftApWifiConfiguration(JSONObject configJson)
856            throws JSONException {
857        WifiConfiguration config = genWifiConfig(configJson);
858        // Need to strip of extra quotation marks for SSID and password.
859        String ssid = config.SSID;
860        if (ssid != null) {
861            config.SSID = ssid.substring(1, ssid.length() - 1);
862        }
863        String pwd = config.preSharedKey;                                                                      if (pwd != null) {
864            config.preSharedKey = pwd.substring(1, pwd.length() - 1);
865        }
866        return config;
867    }
868
869    @Rpc(description = "Set configuration for soft AP.")
870    public Boolean wifiSetWifiApConfiguration(
871            @RpcParameter(name = "configJson") JSONObject configJson) throws JSONException {
872        WifiConfiguration config = createSoftApWifiConfiguration(configJson);
873        return mWifi.setWifiApConfiguration(config);
874    }
875
876    @Rpc(description = "Start/stop wifi soft AP.")
877    public Boolean wifiSetApEnabled(
878            @RpcParameter(name = "enable") Boolean enable,
879            @RpcParameter(name = "configJson") JSONObject configJson) throws JSONException {
880        int wifiState = mWifi.getWifiState();
881        if (enable) {
882            WifiConfiguration config = createSoftApWifiConfiguration(configJson);
883            return mWifi.setWifiApEnabled(config, enable);
884        } else {
885            return mWifi.setWifiApEnabled(null, false);
886        }
887    }
888
889    @Rpc(description = "Set the country code used by WiFi.")
890    public void wifiSetCountryCode(
891            @RpcParameter(name = "country") String country,
892            @RpcParameter(name = "persist") Boolean persist) {
893        mWifi.setCountryCode(country, persist);
894    }
895
896    @Rpc(description = "Enable/disable tdls with a mac address.")
897    public void wifiSetTdlsEnabledWithMacAddress(
898            @RpcParameter(name = "remoteMacAddress") String remoteMacAddress,
899            @RpcParameter(name = "enable") Boolean enable) {
900        mWifi.setTdlsEnabledWithMacAddress(remoteMacAddress, enable);
901    }
902
903    @Rpc(description = "Starts a scan for Wifi access points.", returns = "True if the scan was initiated successfully.")
904    public Boolean wifiStartScan() {
905        mService.registerReceiver(mScanResultsAvailableReceiver, mScanFilter);
906        return mWifi.startScan();
907    }
908
909    @Rpc(description = "Start Wi-fi Protected Setup.")
910    public void wifiStartWps(
911            @RpcParameter(name = "config", description = "A json string with fields \"setup\", \"BSSID\", and \"pin\"") String config)
912                    throws JSONException {
913        WpsInfo info = parseWpsInfo(config);
914        WifiWpsCallback listener = new WifiWpsCallback();
915        Log.d("Starting wps with: " + info);
916        mWifi.startWps(info, listener);
917    }
918
919    @Rpc(description = "Start listening for wifi state change related broadcasts.")
920    public void wifiStartTrackingStateChange() {
921        mService.registerReceiver(mStateChangeReceiver, mStateChangeFilter);
922        mTrackingWifiStateChange = true;
923    }
924
925    @Rpc(description = "Stop listening for wifi state change related broadcasts.")
926    public void wifiStopTrackingStateChange() {
927        if (mTrackingWifiStateChange == true) {
928            mService.unregisterReceiver(mStateChangeReceiver);
929            mTrackingWifiStateChange = false;
930        }
931    }
932
933    @Rpc(description = "Start listening for tether state change related broadcasts.")
934    public void wifiStartTrackingTetherStateChange() {
935        mService.registerReceiver(mTetherStateReceiver, mTetherFilter);
936        mTrackingTetherStateChange = true;
937    }
938
939    @Rpc(description = "Stop listening for wifi state change related broadcasts.")
940    public void wifiStopTrackingTetherStateChange() {
941        if (mTrackingTetherStateChange == true) {
942            mService.unregisterReceiver(mTetherStateReceiver);
943            mTrackingTetherStateChange = false;
944        }
945    }
946
947    @Rpc(description = "Toggle Wifi on and off.", returns = "True if Wifi is enabled.")
948    public Boolean wifiToggleState(@RpcParameter(name = "enabled") @RpcOptional Boolean enabled) {
949        if (enabled == null) {
950            enabled = !wifiCheckState();
951        }
952        mWifi.setWifiEnabled(enabled);
953        return enabled;
954    }
955
956    @Rpc(description = "Toggle Wifi scan always available on and off.", returns = "True if Wifi scan is always available.")
957    public Boolean wifiToggleScanAlwaysAvailable(
958            @RpcParameter(name = "enabled") @RpcOptional Boolean enabled)
959                    throws SettingNotFoundException {
960        ContentResolver cr = mService.getContentResolver();
961        int isSet = 0;
962        if (enabled == null) {
963            isSet = Global.getInt(cr, Global.WIFI_SCAN_ALWAYS_AVAILABLE);
964            isSet ^= 1;
965        } else if (enabled == true) {
966            isSet = 1;
967        }
968        Global.putInt(cr, Global.WIFI_SCAN_ALWAYS_AVAILABLE, isSet);
969        if (isSet == 1) {
970            return true;
971        }
972        return false;
973    }
974
975    @Rpc(description = "Enable/disable WifiConnectivityManager.")
976    public void wifiEnableWifiConnectivityManager(
977            @RpcParameter(name = "enable") Boolean enable) {
978        mWifi.enableWifiConnectivityManager(enable);
979    }
980
981    @Override
982    public void shutdown() {
983        wifiLockRelease();
984        if (mTrackingWifiStateChange == true) {
985            wifiStopTrackingStateChange();
986        }
987        if (mTrackingTetherStateChange == true) {
988            wifiStopTrackingTetherStateChange();
989        }
990    }
991}
992