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