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 static android.net.NetworkCapabilities.TRANSPORT_WIFI;
     20 
     21 import android.app.Service;
     22 import android.content.BroadcastReceiver;
     23 import android.content.ContentResolver;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.IntentFilter;
     27 import android.net.ConnectivityManager;
     28 import android.net.ConnectivityManager.NetworkCallback;
     29 import android.net.DhcpInfo;
     30 import android.net.MacAddress;
     31 import android.net.Network;
     32 import android.net.NetworkInfo;
     33 import android.net.NetworkInfo.DetailedState;
     34 import android.net.NetworkRequest;
     35 import android.net.NetworkSpecifier;
     36 import android.net.Uri;
     37 import android.net.wifi.EasyConnectStatusCallback;
     38 import android.net.wifi.ScanResult;
     39 import android.net.wifi.WifiActivityEnergyInfo;
     40 import android.net.wifi.WifiConfiguration;
     41 import android.net.wifi.WifiConfiguration.AuthAlgorithm;
     42 import android.net.wifi.WifiConfiguration.KeyMgmt;
     43 import android.net.wifi.WifiEnterpriseConfig;
     44 import android.net.wifi.WifiInfo;
     45 import android.net.wifi.WifiManager;
     46 import android.net.wifi.WifiManager.NetworkRequestMatchCallback;
     47 import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback;
     48 import android.net.wifi.WifiManager.WifiLock;
     49 import android.net.wifi.WifiNetworkSpecifier;
     50 import android.net.wifi.WifiNetworkSuggestion;
     51 import android.net.wifi.WifiSsid;
     52 import android.net.wifi.WpsInfo;
     53 import android.net.wifi.hotspot2.ConfigParser;
     54 import android.net.wifi.hotspot2.OsuProvider;
     55 import android.net.wifi.hotspot2.PasspointConfiguration;
     56 import android.net.wifi.hotspot2.ProvisioningCallback;
     57 import android.os.Bundle;
     58 import android.os.Handler;
     59 import android.os.HandlerThread;
     60 import android.os.PatternMatcher;
     61 import android.provider.Settings.Global;
     62 import android.provider.Settings.SettingNotFoundException;
     63 import android.text.TextUtils;
     64 import android.util.Base64;
     65 import android.util.SparseArray;
     66 
     67 import com.android.internal.annotations.GuardedBy;
     68 
     69 import com.googlecode.android_scripting.Log;
     70 import com.googlecode.android_scripting.facade.EventFacade;
     71 import com.googlecode.android_scripting.facade.FacadeManager;
     72 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
     73 import com.googlecode.android_scripting.rpc.Rpc;
     74 import com.googlecode.android_scripting.rpc.RpcOptional;
     75 import com.googlecode.android_scripting.rpc.RpcParameter;
     76 
     77 import org.json.JSONArray;
     78 import org.json.JSONException;
     79 import org.json.JSONObject;
     80 
     81 import java.io.ByteArrayInputStream;
     82 import java.io.ByteArrayOutputStream;
     83 import java.io.IOException;
     84 import java.io.InputStream;
     85 import java.io.ObjectOutput;
     86 import java.io.ObjectOutputStream;
     87 import java.nio.charset.StandardCharsets;
     88 import java.security.GeneralSecurityException;
     89 import java.security.KeyFactory;
     90 import java.security.NoSuchAlgorithmException;
     91 import java.security.PrivateKey;
     92 import java.security.PublicKey;
     93 import java.security.cert.CertificateException;
     94 import java.security.cert.CertificateFactory;
     95 import java.security.cert.X509Certificate;
     96 import java.security.spec.InvalidKeySpecException;
     97 import java.security.spec.PKCS8EncodedKeySpec;
     98 import java.security.spec.X509EncodedKeySpec;
     99 import java.util.ArrayList;
    100 import java.util.Arrays;
    101 import java.util.HashMap;
    102 import java.util.Iterator;
    103 import java.util.List;
    104 import java.util.Map;
    105 
    106 /**
    107  * WifiManager functions.
    108  */
    109 // TODO: make methods handle various wifi states properly
    110 // e.g. wifi connection result will be null when flight mode is on
    111 public class WifiManagerFacade extends RpcReceiver {
    112     private final static String mEventType = "WifiManager";
    113     // MIME type for passpoint config.
    114     private final static String TYPE_WIFICONFIG = "application/x-wifi-config";
    115     private final Service mService;
    116     private final WifiManager mWifi;
    117     private final ConnectivityManager mCm;
    118     private final EventFacade mEventFacade;
    119 
    120     private final IntentFilter mScanFilter;
    121     private final IntentFilter mStateChangeFilter;
    122     private final IntentFilter mTetherFilter;
    123     private final IntentFilter mNetworkSuggestionStateChangeFilter;
    124     private final WifiScanReceiver mScanResultsAvailableReceiver;
    125     private final WifiStateChangeReceiver mStateChangeReceiver;
    126     private final WifiNetworkSuggestionStateChangeReceiver mNetworkSuggestionStateChangeReceiver;
    127     private final HandlerThread mCallbackHandlerThread;
    128     private final Object mCallbackLock = new Object();
    129     private final Map<NetworkSpecifier, NetworkCallback> mNetworkCallbacks = new HashMap<>();
    130     private boolean mTrackingWifiStateChange;
    131     private boolean mTrackingTetherStateChange;
    132     private boolean mTrackingNetworkSuggestionStateChange;
    133     @GuardedBy("mCallbackLock")
    134     private NetworkRequestUserSelectionCallback mNetworkRequestUserSelectionCallback;
    135     private final SparseArray<SoftApCallbackImp> mSoftapCallbacks;
    136 
    137     private final BroadcastReceiver mTetherStateReceiver = new BroadcastReceiver() {
    138         @Override
    139         public void onReceive(Context context, Intent intent) {
    140             String action = intent.getAction();
    141             if (WifiManager.WIFI_AP_STATE_CHANGED_ACTION.equals(action)) {
    142                 Log.d("Wifi AP state changed.");
    143                 int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE,
    144                         WifiManager.WIFI_AP_STATE_FAILED);
    145                 if (state == WifiManager.WIFI_AP_STATE_ENABLED) {
    146                     mEventFacade.postEvent("WifiManagerApEnabled", null);
    147                 } else if (state == WifiManager.WIFI_AP_STATE_DISABLED) {
    148                     mEventFacade.postEvent("WifiManagerApDisabled", null);
    149                 }
    150             } else if (ConnectivityManager.ACTION_TETHER_STATE_CHANGED.equals(action)) {
    151                 Log.d("Tether state changed.");
    152                 ArrayList<String> available = intent.getStringArrayListExtra(
    153                         ConnectivityManager.EXTRA_AVAILABLE_TETHER);
    154                 ArrayList<String> active = intent.getStringArrayListExtra(
    155                         ConnectivityManager.EXTRA_ACTIVE_TETHER);
    156                 ArrayList<String> errored = intent.getStringArrayListExtra(
    157                         ConnectivityManager.EXTRA_ERRORED_TETHER);
    158                 Bundle msg = new Bundle();
    159                 msg.putStringArrayList("AVAILABLE_TETHER", available);
    160                 msg.putStringArrayList("ACTIVE_TETHER", active);
    161                 msg.putStringArrayList("ERRORED_TETHER", errored);
    162                 mEventFacade.postEvent("TetherStateChanged", msg);
    163             }
    164         }
    165     };
    166 
    167     private final NetworkRequestMatchCallback mNetworkRequestMatchCallback =
    168             new NetworkRequestMatchCallback() {
    169                 private static final String EVENT_TAG = mEventType + "NetworkRequestMatchCallback";
    170 
    171                 @Override
    172                 public void onUserSelectionCallbackRegistration(
    173                         NetworkRequestUserSelectionCallback userSelectionCallback) {
    174                     synchronized (mCallbackLock) {
    175                         mNetworkRequestUserSelectionCallback = userSelectionCallback;
    176                     }
    177                 }
    178 
    179                 @Override
    180                 public void onAbort() {
    181                     mEventFacade.postEvent(EVENT_TAG + "OnAbort", null);
    182                 }
    183 
    184                 @Override
    185                 public void onMatch(List<ScanResult> scanResults) {
    186                     mEventFacade.postEvent(EVENT_TAG + "OnMatch", scanResults);
    187                 }
    188 
    189                 @Override
    190                 public void onUserSelectionConnectSuccess(WifiConfiguration wifiConfiguration) {
    191                     mEventFacade.postEvent(EVENT_TAG + "OnUserSelectionConnectSuccess",
    192                             wifiConfiguration);
    193                 }
    194 
    195                 @Override
    196                 public void onUserSelectionConnectFailure(WifiConfiguration wifiConfiguration) {
    197                     mEventFacade.postEvent(EVENT_TAG + "OnUserSelectionConnectFailure",
    198                             wifiConfiguration);
    199                 }
    200             };
    201 
    202     private final class NetworkCallbackImpl extends NetworkCallback {
    203         private static final String EVENT_TAG = mEventType + "NetworkCallback";
    204 
    205         @Override
    206         public void onAvailable(Network network) {
    207             mEventFacade.postEvent(EVENT_TAG + "OnAvailable", mWifi.getConnectionInfo());
    208         }
    209 
    210         @Override
    211         public void onUnavailable() {
    212             mEventFacade.postEvent(EVENT_TAG + "OnUnavailable", null);
    213         }
    214 
    215         @Override
    216         public void onLost(Network network) {
    217             mEventFacade.postEvent(EVENT_TAG + "OnLost", null);
    218         }
    219     };
    220 
    221     private static class SoftApCallbackImp implements WifiManager.SoftApCallback {
    222         // A monotonic increasing counter for softap callback ids.
    223         private static int sCount = 0;
    224 
    225         private final int mId;
    226         private final EventFacade mEventFacade;
    227         private final String mEventStr;
    228 
    229         SoftApCallbackImp(EventFacade eventFacade) {
    230             sCount++;
    231             mId = sCount;
    232             mEventFacade = eventFacade;
    233             mEventStr = mEventType + "SoftApCallback-" + mId + "-";
    234         }
    235 
    236         @Override
    237         public void onStateChanged(int state, int failureReason) {
    238             Bundle msg = new Bundle();
    239             msg.putInt("State", state);
    240             msg.putInt("FailureReason", failureReason);
    241             mEventFacade.postEvent(mEventStr + "OnStateChanged", msg);
    242         }
    243 
    244         @Override
    245         public void onNumClientsChanged(int numClients) {
    246             Bundle msg = new Bundle();
    247             msg.putInt("NumClients", numClients);
    248             mEventFacade.postEvent(mEventStr + "OnNumClientsChanged", msg);
    249         }
    250     };
    251 
    252     private WifiLock mLock = null;
    253     private boolean mIsConnected = false;
    254 
    255     public WifiManagerFacade(FacadeManager manager) {
    256         super(manager);
    257         mService = manager.getService();
    258         mWifi = (WifiManager) mService.getSystemService(Context.WIFI_SERVICE);
    259         mCm = (ConnectivityManager) mService.getSystemService(Context.CONNECTIVITY_SERVICE);
    260         mEventFacade = manager.getReceiver(EventFacade.class);
    261         mCallbackHandlerThread = new HandlerThread("WifiManagerFacade");
    262 
    263         mScanFilter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
    264         mStateChangeFilter = new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    265         mStateChangeFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
    266         mStateChangeFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
    267         mStateChangeFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
    268         mStateChangeFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY - 1);
    269 
    270         mTetherFilter = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
    271         mTetherFilter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
    272 
    273         mNetworkSuggestionStateChangeFilter = new IntentFilter(
    274                 WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION);
    275 
    276         mScanResultsAvailableReceiver = new WifiScanReceiver(mEventFacade);
    277         mStateChangeReceiver = new WifiStateChangeReceiver();
    278         mNetworkSuggestionStateChangeReceiver = new WifiNetworkSuggestionStateChangeReceiver();
    279         mTrackingWifiStateChange = false;
    280         mTrackingTetherStateChange = false;
    281         mTrackingNetworkSuggestionStateChange = false;
    282         mCallbackHandlerThread.start();
    283         mSoftapCallbacks = new SparseArray<>();
    284     }
    285 
    286     private void makeLock(int wifiMode) {
    287         if (mLock == null) {
    288             mLock = mWifi.createWifiLock(wifiMode, "sl4a");
    289             mLock.acquire();
    290         }
    291     }
    292 
    293     /**
    294      * Handle Broadcast receiver for Scan Result
    295      *
    296      * @parm eventFacade Object of EventFacade
    297      */
    298     class WifiScanReceiver extends BroadcastReceiver {
    299         private final EventFacade mEventFacade;
    300 
    301         WifiScanReceiver(EventFacade eventFacade) {
    302             mEventFacade = eventFacade;
    303         }
    304 
    305         @Override
    306         public void onReceive(Context c, Intent intent) {
    307             String action = intent.getAction();
    308             if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
    309                 if (!intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)) {
    310                     Log.w("Wifi connection scan failed, ignoring.");
    311                     mEventFacade.postEvent(mEventType + "ScanFailure", null);
    312                 } else {
    313                     Bundle mResults = new Bundle();
    314                     Log.d("Wifi connection scan finished, results available.");
    315                     mResults.putLong("Timestamp", System.currentTimeMillis() / 1000);
    316                     mEventFacade.postEvent(mEventType + "ScanResultsAvailable", mResults);
    317                 }
    318                 mService.unregisterReceiver(mScanResultsAvailableReceiver);
    319             }
    320         }
    321     }
    322 
    323     class WifiActionListener implements WifiManager.ActionListener {
    324         private final EventFacade mEventFacade;
    325         private final String TAG;
    326 
    327         public WifiActionListener(EventFacade eventFacade, String tag) {
    328             mEventFacade = eventFacade;
    329             this.TAG = tag;
    330         }
    331 
    332         @Override
    333         public void onSuccess() {
    334             Log.d("WifiActionListener  onSuccess called for " + mEventType + TAG + "OnSuccess");
    335             mEventFacade.postEvent(mEventType + TAG + "OnSuccess", null);
    336         }
    337 
    338         @Override
    339         public void onFailure(int reason) {
    340             Log.d("WifiActionListener  onFailure called for" + mEventType);
    341             Bundle msg = new Bundle();
    342             msg.putInt("reason", reason);
    343             mEventFacade.postEvent(mEventType + TAG + "OnFailure", msg);
    344         }
    345     }
    346 
    347     public class WifiStateChangeReceiver extends BroadcastReceiver {
    348         String mCachedWifiInfo = "";
    349 
    350         @Override
    351         public void onReceive(Context context, Intent intent) {
    352             String action = intent.getAction();
    353             if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
    354                 Log.d("Wifi network state changed.");
    355                 NetworkInfo nInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
    356                 Log.d("NetworkInfo " + nInfo);
    357                 // If network info is of type wifi, send wifi events.
    358                 if (nInfo.getType() == ConnectivityManager.TYPE_WIFI) {
    359                     if (nInfo.getDetailedState().equals(DetailedState.CONNECTED)) {
    360                         WifiInfo wInfo = mWifi.getConnectionInfo();
    361                         String bssid = wInfo.getBSSID();
    362                         if (bssid != null && !mCachedWifiInfo.equals(wInfo.toString())) {
    363                             Log.d("WifiNetworkConnected");
    364                             mEventFacade.postEvent("WifiNetworkConnected", wInfo);
    365                         }
    366                         mCachedWifiInfo = wInfo.toString();
    367                     } else {
    368                         if (nInfo.getDetailedState().equals(DetailedState.DISCONNECTED)) {
    369                             if (!mCachedWifiInfo.equals("")) {
    370                                 mCachedWifiInfo = "";
    371                                 mEventFacade.postEvent("WifiNetworkDisconnected", null);
    372                             }
    373                         }
    374                     }
    375                 }
    376             } else if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
    377                 Log.d("Supplicant connection state changed.");
    378                 mIsConnected = intent
    379                         .getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false);
    380                 Bundle msg = new Bundle();
    381                 msg.putBoolean("Connected", mIsConnected);
    382                 mEventFacade.postEvent("SupplicantConnectionChanged", msg);
    383             } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
    384                 int state = intent.getIntExtra(
    385                         WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED);
    386                 Log.d("Wifi state changed to " + state);
    387                 boolean enabled;
    388                 if (state == WifiManager.WIFI_STATE_DISABLED) {
    389                     enabled = false;
    390                 } else if (state == WifiManager.WIFI_STATE_ENABLED) {
    391                     enabled = true;
    392                 } else {
    393                     // we only care about enabled/disabled.
    394                     Log.v("Ignoring intermediate wifi state change event...");
    395                     return;
    396                 }
    397                 Bundle msg = new Bundle();
    398                 msg.putBoolean("enabled", enabled);
    399                 mEventFacade.postEvent("WifiStateChanged", msg);
    400             }
    401         }
    402     }
    403 
    404     public class WifiNetworkSuggestionStateChangeReceiver extends BroadcastReceiver {
    405         @Override
    406         public void onReceive(Context context, Intent intent) {
    407             String action = intent.getAction();
    408             if (action.equals(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION)) {
    409                 WifiNetworkSuggestion networkSuggestion =
    410                         intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_SUGGESTION);
    411                 mEventFacade.postEvent(
    412                         "WifiNetworkSuggestionPostConnection",
    413                         networkSuggestion.wifiConfiguration.SSID);
    414             }
    415         }
    416     }
    417 
    418     public class WifiWpsCallback extends WifiManager.WpsCallback {
    419         private static final String tag = "WifiWps";
    420 
    421         @Override
    422         public void onStarted(String pin) {
    423             Bundle msg = new Bundle();
    424             msg.putString("pin", pin);
    425             mEventFacade.postEvent(tag + "OnStarted", msg);
    426         }
    427 
    428         @Override
    429         public void onSucceeded() {
    430             Log.d("Wps op succeeded");
    431             mEventFacade.postEvent(tag + "OnSucceeded", null);
    432         }
    433 
    434         @Override
    435         public void onFailed(int reason) {
    436             Bundle msg = new Bundle();
    437             msg.putInt("reason", reason);
    438             mEventFacade.postEvent(tag + "OnFailed", msg);
    439         }
    440     }
    441 
    442     private void applyingkeyMgmt(WifiConfiguration config, ScanResult result) {
    443         if (result.capabilities.contains("WEP")) {
    444             config.allowedKeyManagement.set(KeyMgmt.NONE);
    445             config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
    446             config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
    447         } else if (result.capabilities.contains("PSK")) {
    448             config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
    449         } else if (result.capabilities.contains("EAP")) {
    450             // this is probably wrong, as we don't have a way to enter the enterprise config
    451             config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
    452             config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
    453         } else {
    454             config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
    455         }
    456     }
    457 
    458     private WifiConfiguration genWifiConfig(JSONObject j) throws JSONException {
    459         if (j == null) {
    460             return null;
    461         }
    462         WifiConfiguration config = new WifiConfiguration();
    463         if (j.has("SSID")) {
    464             config.SSID = "\"" + j.getString("SSID") + "\"";
    465         } else if (j.has("ssid")) {
    466             config.SSID = "\"" + j.getString("ssid") + "\"";
    467         }
    468         if (j.has("password")) {
    469             String security;
    470 
    471             // Check if new security type SAE (WPA3) is present. Default to PSK
    472             if (j.has("security")) {
    473                 if (TextUtils.equals(j.getString("security"), "SAE")) {
    474                     config.allowedKeyManagement.set(KeyMgmt.SAE);
    475                     config.requirePMF = true;
    476                 } else {
    477                     config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
    478                 }
    479             } else {
    480                 config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
    481             }
    482             config.preSharedKey = "\"" + j.getString("password") + "\"";
    483         } else if (j.has("preSharedKey")) {
    484             config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
    485             config.preSharedKey = j.getString("preSharedKey");
    486         } else {
    487             if (j.has("security")) {
    488                 if (TextUtils.equals(j.getString("security"), "OWE")) {
    489                     config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
    490                     config.requirePMF = true;
    491                 } else {
    492                     config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
    493                 }
    494             } else {
    495                 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
    496             }
    497         }
    498         if (j.has("BSSID")) {
    499             config.BSSID = j.getString("BSSID");
    500         }
    501         if (j.has("hiddenSSID")) {
    502             config.hiddenSSID = j.getBoolean("hiddenSSID");
    503         }
    504         if (j.has("priority")) {
    505             config.priority = j.getInt("priority");
    506         }
    507         if (j.has("apBand")) {
    508             config.apBand = j.getInt("apBand");
    509         }
    510         if (j.has("wepKeys")) {
    511             // Looks like we only support static WEP.
    512             config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
    513             config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
    514             config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
    515             JSONArray keys = j.getJSONArray("wepKeys");
    516             String[] wepKeys = new String[keys.length()];
    517             for (int i = 0; i < keys.length(); i++) {
    518                 wepKeys[i] = keys.getString(i);
    519             }
    520             config.wepKeys = wepKeys;
    521         }
    522         if (j.has("wepTxKeyIndex")) {
    523             config.wepTxKeyIndex = j.getInt("wepTxKeyIndex");
    524         }
    525         if (j.has("meteredOverride")) {
    526             config.meteredOverride = j.getInt("meteredOverride");
    527         }
    528         if (j.has("macRand")) {
    529             config.macRandomizationSetting = j.getInt("macRand");
    530         }
    531         return config;
    532     }
    533 
    534     private WifiEnterpriseConfig genWifiEnterpriseConfig(JSONObject j) throws JSONException,
    535             GeneralSecurityException {
    536         WifiEnterpriseConfig eConfig = new WifiEnterpriseConfig();
    537         if (j.has(WifiEnterpriseConfig.EAP_KEY)) {
    538             int eap = j.getInt(WifiEnterpriseConfig.EAP_KEY);
    539             eConfig.setEapMethod(eap);
    540         }
    541         if (j.has(WifiEnterpriseConfig.PHASE2_KEY)) {
    542             int p2Method = j.getInt(WifiEnterpriseConfig.PHASE2_KEY);
    543             eConfig.setPhase2Method(p2Method);
    544         }
    545         if (j.has(WifiEnterpriseConfig.CA_CERT_KEY)) {
    546             String certStr = j.getString(WifiEnterpriseConfig.CA_CERT_KEY);
    547             Log.v("CA Cert String is " + certStr);
    548             eConfig.setCaCertificate(strToX509Cert(certStr));
    549         }
    550         if (j.has(WifiEnterpriseConfig.CLIENT_CERT_KEY)
    551                 && j.has(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY)) {
    552             String certStr = j.getString(WifiEnterpriseConfig.CLIENT_CERT_KEY);
    553             String keyStr = j.getString(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY);
    554             Log.v("Client Cert String is " + certStr);
    555             Log.v("Client Key String is " + keyStr);
    556             X509Certificate cert = strToX509Cert(certStr);
    557             PrivateKey privKey = strToPrivateKey(keyStr);
    558             Log.v("Cert is " + cert);
    559             Log.v("Private Key is " + privKey);
    560             eConfig.setClientKeyEntry(privKey, cert);
    561         }
    562         if (j.has(WifiEnterpriseConfig.IDENTITY_KEY)) {
    563             String identity = j.getString(WifiEnterpriseConfig.IDENTITY_KEY);
    564             Log.v("Setting identity to " + identity);
    565             eConfig.setIdentity(identity);
    566         }
    567         if (j.has(WifiEnterpriseConfig.PASSWORD_KEY)) {
    568             String pwd = j.getString(WifiEnterpriseConfig.PASSWORD_KEY);
    569             Log.v("Setting password to " + pwd);
    570             eConfig.setPassword(pwd);
    571         }
    572         if (j.has(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY)) {
    573             String altSub = j.getString(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY);
    574             Log.v("Setting Alt Subject to " + altSub);
    575             eConfig.setAltSubjectMatch(altSub);
    576         }
    577         if (j.has(WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY)) {
    578             String domSuffix = j.getString(WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY);
    579             Log.v("Setting Domain Suffix Match to " + domSuffix);
    580             eConfig.setDomainSuffixMatch(domSuffix);
    581         }
    582         if (j.has(WifiEnterpriseConfig.REALM_KEY)) {
    583             String realm = j.getString(WifiEnterpriseConfig.REALM_KEY);
    584             Log.v("Setting Domain Suffix Match to " + realm);
    585             eConfig.setRealm(realm);
    586         }
    587         return eConfig;
    588     }
    589 
    590     private WifiConfiguration genWifiConfigWithEnterpriseConfig(JSONObject j) throws JSONException,
    591             GeneralSecurityException {
    592         if (j == null) {
    593             return null;
    594         }
    595         WifiConfiguration config = new WifiConfiguration();
    596         config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
    597         config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
    598 
    599         if (j.has("security")) {
    600             if (TextUtils.equals(j.getString("security"), "SUITE_B_192")) {
    601                 config.allowedKeyManagement.set(KeyMgmt.SUITE_B_192);
    602                 config.requirePMF = true;
    603             }
    604         }
    605 
    606         if (j.has("SSID")) {
    607             config.SSID = "\"" + j.getString("SSID") + "\"";
    608         } else if (j.has("ssid")) {
    609             config.SSID = "\"" + j.getString("ssid") + "\"";
    610         }
    611         if (j.has("FQDN")) {
    612             config.FQDN = j.getString("FQDN");
    613         }
    614         if (j.has("providerFriendlyName")) {
    615             config.providerFriendlyName = j.getString("providerFriendlyName");
    616         }
    617         if (j.has("roamingConsortiumIds")) {
    618             JSONArray ids = j.getJSONArray("roamingConsortiumIds");
    619             long[] rIds = new long[ids.length()];
    620             for (int i = 0; i < ids.length(); i++) {
    621                 rIds[i] = ids.getLong(i);
    622             }
    623             config.roamingConsortiumIds = rIds;
    624         }
    625         config.enterpriseConfig = genWifiEnterpriseConfig(j);
    626         return config;
    627     }
    628 
    629     private NetworkSpecifier genWifiNetworkSpecifier(JSONObject j) throws JSONException,
    630             GeneralSecurityException {
    631         if (j == null) {
    632             return null;
    633         }
    634         WifiNetworkSpecifier.Builder builder = new WifiNetworkSpecifier.Builder();
    635         if (j.has("SSID")) {
    636             builder = builder.setSsid(j.getString("SSID"));
    637         } else if (j.has("ssidPattern")) {
    638             builder = builder.setSsidPattern(
    639                     new PatternMatcher(j.getString("ssidPattern"),
    640                             PatternMatcher.PATTERN_ADVANCED_GLOB));
    641         }
    642         if (j.has("BSSID")) {
    643             builder = builder.setBssid(MacAddress.fromString(j.getString("BSSID")));
    644         } else if (j.has("bssidPattern")) {
    645             builder = builder.setBssidPattern(
    646                     MacAddress.fromString(j.getJSONArray("bssidPattern").getString(0)),
    647                     MacAddress.fromString(j.getJSONArray("bssidPattern").getString(1)));
    648         }
    649         if (j.has("hiddenSSID")) {
    650             builder = builder.setIsHiddenSsid(j.getBoolean("hiddenSSID"));
    651         }
    652         if (j.has("isEnhancedOpen")) {
    653             builder = builder.setIsEnhancedOpen(j.getBoolean("isEnhancedOpen"));
    654         }
    655         boolean isWpa3 = false;
    656         if (j.has("isWpa3") && j.getBoolean("isWpa3")) {
    657             isWpa3 = true;
    658         }
    659         if (j.has("password")) {
    660             if (!isWpa3) {
    661                 builder = builder.setWpa2Passphrase(j.getString("password"));
    662             } else {
    663                 builder = builder.setWpa3Passphrase(j.getString("password"));
    664             }
    665         }
    666         if (j.has(WifiEnterpriseConfig.EAP_KEY)) {
    667             if (!isWpa3) {
    668                 builder = builder.setWpa2EnterpriseConfig(genWifiEnterpriseConfig(j));
    669             } else {
    670                 builder = builder.setWpa3EnterpriseConfig(genWifiEnterpriseConfig(j));
    671             }
    672         }
    673         return builder.build();
    674     }
    675 
    676     private WifiNetworkSuggestion genWifiNetworkSuggestion(JSONObject j) throws JSONException,
    677             GeneralSecurityException {
    678         if (j == null) {
    679             return null;
    680         }
    681         WifiNetworkSuggestion.Builder builder = new WifiNetworkSuggestion.Builder();
    682         if (j.has("SSID")) {
    683             builder = builder.setSsid(j.getString("SSID"));
    684         }
    685         if (j.has("BSSID")) {
    686             builder = builder.setBssid(MacAddress.fromString(j.getString("BSSID")));
    687         }
    688         if (j.has("hiddenSSID")) {
    689             builder = builder.setIsHiddenSsid(j.getBoolean("hiddenSSID"));
    690         }
    691         if (j.has("isEnhancedOpen")) {
    692             builder = builder.setIsEnhancedOpen(j.getBoolean("isEnhancedOpen"));
    693         }
    694         boolean isWpa3 = false;
    695         if (j.has("isWpa3") && j.getBoolean("isWpa3")) {
    696             isWpa3 = true;
    697         }
    698         if (j.has("password")) {
    699             if (!isWpa3) {
    700                 builder = builder.setWpa2Passphrase(j.getString("password"));
    701             } else {
    702                 builder = builder.setWpa3Passphrase(j.getString("password"));
    703             }
    704         }
    705         if (j.has(WifiEnterpriseConfig.EAP_KEY)) {
    706             if (!isWpa3) {
    707                 builder = builder.setWpa2EnterpriseConfig(genWifiEnterpriseConfig(j));
    708             } else {
    709                 builder = builder.setWpa3EnterpriseConfig(genWifiEnterpriseConfig(j));
    710             }
    711         }
    712         if (j.has("isAppInteractionRequired")) {
    713             builder = builder.setIsAppInteractionRequired(j.getBoolean("isAppInteractionRequired"));
    714         }
    715         if (j.has("isUserInteractionRequired")) {
    716             builder = builder.setIsUserInteractionRequired(
    717                     j.getBoolean("isUserInteractionRequired"));
    718         }
    719         if (j.has("isMetered")) {
    720             builder = builder.setIsMetered(j.getBoolean("isMetered"));
    721         }
    722         if (j.has("priority")) {
    723             builder = builder.setPriority(j.getInt("priority"));
    724         }
    725         return builder.build();
    726     }
    727 
    728     private List<WifiNetworkSuggestion> genWifiNetworkSuggestions(
    729             JSONArray jsonNetworkSuggestionsArray) throws JSONException, GeneralSecurityException {
    730         if (jsonNetworkSuggestionsArray == null) {
    731             return null;
    732         }
    733         List<WifiNetworkSuggestion> networkSuggestions = new ArrayList<>();
    734         for (int i = 0; i < jsonNetworkSuggestionsArray.length(); i++) {
    735             networkSuggestions.add(
    736                     genWifiNetworkSuggestion(jsonNetworkSuggestionsArray.getJSONObject(i)));
    737         }
    738         return networkSuggestions;
    739     }
    740 
    741     private boolean matchScanResult(ScanResult result, String id) {
    742         if (result.BSSID.equals(id) || result.SSID.equals(id)) {
    743             return true;
    744         }
    745         return false;
    746     }
    747 
    748     private WpsInfo parseWpsInfo(String infoStr) throws JSONException {
    749         if (infoStr == null) {
    750             return null;
    751         }
    752         JSONObject j = new JSONObject(infoStr);
    753         WpsInfo info = new WpsInfo();
    754         if (j.has("setup")) {
    755             info.setup = j.getInt("setup");
    756         }
    757         if (j.has("BSSID")) {
    758             info.BSSID = j.getString("BSSID");
    759         }
    760         if (j.has("pin")) {
    761             info.pin = j.getString("pin");
    762         }
    763         return info;
    764     }
    765 
    766     private byte[] base64StrToBytes(String input) {
    767         return Base64.decode(input, Base64.DEFAULT);
    768     }
    769 
    770     private X509Certificate strToX509Cert(String certStr) throws CertificateException {
    771         byte[] certBytes = base64StrToBytes(certStr);
    772         InputStream certStream = new ByteArrayInputStream(certBytes);
    773         CertificateFactory cf = CertificateFactory.getInstance("X509");
    774         return (X509Certificate) cf.generateCertificate(certStream);
    775     }
    776 
    777     private PrivateKey strToPrivateKey(String key) throws NoSuchAlgorithmException,
    778             InvalidKeySpecException {
    779         byte[] keyBytes = base64StrToBytes(key);
    780         PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
    781         KeyFactory fact = KeyFactory.getInstance("RSA");
    782         PrivateKey priv = fact.generatePrivate(keySpec);
    783         return priv;
    784     }
    785 
    786     private PublicKey strToPublicKey(String key) throws NoSuchAlgorithmException,
    787             InvalidKeySpecException {
    788         byte[] keyBytes = base64StrToBytes(key);
    789         X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
    790         KeyFactory fact = KeyFactory.getInstance("RSA");
    791         PublicKey pub = fact.generatePublic(keySpec);
    792         return pub;
    793     }
    794 
    795     private WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) {
    796         if (result == null)
    797             return null;
    798         WifiConfiguration config = new WifiConfiguration();
    799         config.SSID = "\"" + result.SSID + "\"";
    800         applyingkeyMgmt(config, result);
    801         config.BSSID = result.BSSID;
    802         return config;
    803     }
    804 
    805     @Rpc(description = "test.")
    806     public String wifiTest(
    807             @RpcParameter(name = "certString") String certString) throws CertificateException, IOException {
    808         // TODO(angli): Make this work. Convert a X509Certificate back to a string.
    809         X509Certificate caCert = strToX509Cert(certString);
    810         caCert.getEncoded();
    811         ByteArrayOutputStream bos = new ByteArrayOutputStream();
    812         ObjectOutput out = new ObjectOutputStream(bos);
    813         out.writeObject(caCert);
    814         byte[] data = bos.toByteArray();
    815         bos.close();
    816         return Base64.encodeToString(data, Base64.DEFAULT);
    817     }
    818 
    819     @Rpc(description = "Add a network.")
    820     @Deprecated
    821     public Integer wifiAddNetwork(@RpcParameter(name = "wifiConfig") JSONObject wifiConfig)
    822             throws JSONException {
    823         return wifiaddOrUpdateNetwork(wifiConfig);
    824     }
    825 
    826     @Rpc(description = "Add or update a network.")
    827     public Integer wifiaddOrUpdateNetwork(@RpcParameter(name = "wifiConfig") JSONObject wifiConfig)
    828             throws JSONException {
    829         return mWifi.addNetwork(genWifiConfig(wifiConfig));
    830     }
    831 
    832     @Rpc(description = "Cancel Wi-fi Protected Setup.")
    833     public void wifiCancelWps() throws JSONException {
    834         WifiWpsCallback listener = new WifiWpsCallback();
    835         mWifi.cancelWps(listener);
    836     }
    837 
    838     @Rpc(description = "Checks Wifi state.", returns = "True if Wifi is enabled.")
    839     public Boolean wifiCheckState() {
    840         return mWifi.getWifiState() == WifiManager.WIFI_STATE_ENABLED;
    841     }
    842 
    843     /**
    844     * @deprecated Use {@link #wifiConnectByConfig(config)} instead.
    845     */
    846     @Rpc(description = "Connects to the network with the given configuration")
    847     @Deprecated
    848     public Boolean wifiConnect(@RpcParameter(name = "config") JSONObject config)
    849             throws JSONException {
    850         try {
    851             wifiConnectByConfig(config);
    852         } catch (GeneralSecurityException e) {
    853             String msg = "Caught GeneralSecurityException with the provided"
    854                          + "configuration";
    855             throw new RuntimeException(msg);
    856         }
    857         return true;
    858     }
    859 
    860     /**
    861     * @deprecated Use {@link #wifiConnectByConfig(config)} instead.
    862     */
    863     @Rpc(description = "Connects to the network with the given configuration")
    864     @Deprecated
    865     public Boolean wifiEnterpriseConnect(@RpcParameter(name = "config")
    866             JSONObject config) throws JSONException, GeneralSecurityException {
    867         try {
    868             wifiConnectByConfig(config);
    869         } catch (GeneralSecurityException e) {
    870             throw e;
    871         }
    872         return true;
    873     }
    874 
    875     /**
    876      * Connects to a wifi network using configuration.
    877      * @param config JSONObject Dictionary of wifi connection parameters
    878      * @throws JSONException
    879      * @throws GeneralSecurityException
    880      */
    881     @Rpc(description = "Connects to the network with the given configuration")
    882     public void wifiConnectByConfig(@RpcParameter(name = "config") JSONObject config)
    883             throws JSONException, GeneralSecurityException {
    884         WifiConfiguration wifiConfig;
    885         WifiActionListener listener;
    886         // Check if this is 802.1x or 802.11x config.
    887         if (config.has(WifiEnterpriseConfig.EAP_KEY)) {
    888             wifiConfig = genWifiConfigWithEnterpriseConfig(config);
    889         } else {
    890             wifiConfig = genWifiConfig(config);
    891         }
    892         listener = new WifiActionListener(mEventFacade,
    893                 WifiConstants.WIFI_CONNECT_BY_CONFIG_CALLBACK);
    894         mWifi.connect(wifiConfig, listener);
    895     }
    896 
    897     /**
    898     * Gets the Wi-Fi factory MAC addresses.
    899     * @return An array of String represnting Wi-Fi MAC addresses,
    900     *         Or an empty Srting if failed.
    901     */
    902     @Rpc(description = "Gets the Wi-Fi factory MAC addresses", returns = "An array of String, representing the MAC address")
    903     public String[] wifigetFactorymacAddresses(){
    904         return mWifi.getFactoryMacAddresses();
    905     }
    906 
    907     @Rpc(description = "Gets the randomized MAC address", returns = "A MAC address or null")
    908     public MacAddress wifigetRandomizedMacAddress(@RpcParameter(name = "config") JSONObject config)
    909             throws JSONException{
    910         List<WifiConfiguration> configList = mWifi.getConfiguredNetworks();
    911         for(WifiConfiguration WifiConfig : configList){
    912             String ssid = WifiConfig.SSID;
    913             ssid = ssid.replace("\"", "");
    914             if (ssid.equals(config.getString("SSID"))){
    915                 return WifiConfig.getRandomizedMacAddress();
    916             }
    917         }
    918         Log.d("Did not find a matching object in wifiManager.");
    919         return null;
    920     }
    921    /**
    922      * Generate a Passpoint configuration from JSON config.
    923      * @param config JSON config containing base64 encoded Passpoint profile
    924      */
    925     @Rpc(description = "Generate Passpoint configuration", returns = "PasspointConfiguration object")
    926     public PasspointConfiguration genWifiPasspointConfig(@RpcParameter(
    927             name = "config") JSONObject config)
    928             throws JSONException,CertificateException, IOException {
    929         String profileStr = "";
    930         if (config == null) {
    931             return null;
    932         }
    933         if (config.has("profile")) {
    934             profileStr =  config.getString("profile");
    935         }
    936         return ConfigParser.parsePasspointConfig(TYPE_WIFICONFIG,
    937                 profileStr.getBytes());
    938     }
    939 
    940    /**
    941      * Add or update a Passpoint configuration.
    942      * @param config base64 encoded message containing Passpoint profile
    943      * @throws JSONException
    944      */
    945     @Rpc(description = "Add or update a Passpoint configuration")
    946     public void addUpdatePasspointConfig(@RpcParameter(
    947             name = "config") JSONObject config)
    948             throws JSONException,CertificateException, IOException {
    949         PasspointConfiguration passpointConfig = genWifiPasspointConfig(config);
    950         mWifi.addOrUpdatePasspointConfiguration(passpointConfig);
    951     }
    952 
    953     /**
    954      * Remove a Passpoint configuration.
    955      * @param fqdn The FQDN of the passpoint configuration to be removed
    956      * @return true on success; false otherwise
    957      */
    958     @Rpc(description = "Remove a Passpoint configuration")
    959     public void removePasspointConfig(
    960             @RpcParameter(name = "fqdn") String fqdn) {
    961         mWifi.removePasspointConfiguration(fqdn);
    962     }
    963 
    964     /**
    965      * Get list of Passpoint configurations.
    966      * @return A list of FQDNs of the Passpoint configurations
    967      */
    968     @Rpc(description = "Return the list of installed Passpoint configurations", returns = "A list of Passpoint configurations")
    969     public List<String> getPasspointConfigs() {
    970         List<String> fqdnList = new ArrayList<String>();
    971         for(PasspointConfiguration passpoint :
    972             mWifi.getPasspointConfigurations()) {
    973             fqdnList.add(passpoint.getHomeSp().getFqdn());
    974         }
    975         return fqdnList;
    976     }
    977 
    978     private class ProvisioningCallbackFacade extends ProvisioningCallback {
    979         private final EventFacade mEventFacade;
    980 
    981         ProvisioningCallbackFacade(EventFacade eventFacade) {
    982             mEventFacade = eventFacade;
    983         }
    984 
    985         @Override
    986         public void onProvisioningFailure(int status) {
    987             Log.v("Provisioning Failure " + status);
    988             Bundle msg = new Bundle();
    989             msg.putString("tag", "failure");
    990             msg.putInt("reason", status);
    991             mEventFacade.postEvent("onProvisioningCallback", msg);
    992         }
    993 
    994         @Override
    995         public void onProvisioningStatus(int status) {
    996             Log.v("Provisioning status " + status);
    997             Bundle msg = new Bundle();
    998             msg.putString("tag", "status");
    999             msg.putInt("status", status);
   1000             mEventFacade.postEvent("onProvisioningCallback", msg);
   1001         }
   1002 
   1003         @Override
   1004         public void onProvisioningComplete() {
   1005             Log.v("Provisioning Complete");
   1006             Bundle msg = new Bundle();
   1007             msg.putString("tag", "success");
   1008             mEventFacade.postEvent("onProvisioningCallback", msg);
   1009         }
   1010     }
   1011 
   1012     private OsuProvider buildTestOsuProvider(JSONObject config) {
   1013         String osuServiceDescription = "Google Passpoint Test Service";
   1014         List<Integer> osuMethodList =
   1015                 Arrays.asList(OsuProvider.METHOD_SOAP_XML_SPP);
   1016 
   1017         try {
   1018             if (!config.has("osuSSID")) {
   1019                 Log.e("missing osuSSID from the config");
   1020                 return null;
   1021             }
   1022             WifiSsid osuSsid = WifiSsid.createFromByteArray(
   1023                     config.getString("osuSSID").getBytes(StandardCharsets.UTF_8));
   1024 
   1025             if (!config.has("osuUri")) {
   1026                 Log.e("missing osuUri from the config");
   1027                 return null;
   1028             }
   1029             Uri osuServerUri = Uri.parse(config.getString("osuUri"));
   1030 
   1031             Log.v("OSU Server URI " + osuServerUri.toString());
   1032             if (!config.has("osuFriendlyName")) {
   1033                 Log.e("missing osuFriendlyName from the config");
   1034                 return null;
   1035             }
   1036             String osuFriendlyName = config.getString("osuFriendlyName");
   1037 
   1038             if (config.has("description")) {
   1039                 osuServiceDescription = config.getString("description");
   1040             }
   1041             Map<String, String> osuFriendlyNames = new HashMap<>();
   1042             osuFriendlyNames.put("en", osuFriendlyName);
   1043             return new OsuProvider(osuSsid, osuFriendlyNames, osuServiceDescription,
   1044                     osuServerUri, null, osuMethodList, null);
   1045         } catch (JSONException e) {
   1046             Log.e("JSON Parsing error: " + e);
   1047             return null;
   1048         }
   1049     }
   1050 
   1051     /**
   1052      * Start subscription provisioning
   1053      */
   1054     @Rpc(description = "Starts subscription provisioning flow")
   1055     public void startSubscriptionProvisioning(
   1056             @RpcParameter(name = "configJson") JSONObject configJson) {
   1057         ProvisioningCallback callback = new ProvisioningCallbackFacade(mEventFacade);
   1058         mWifi.startSubscriptionProvisioning(buildTestOsuProvider(configJson),
   1059                 mService.getMainExecutor(), callback);
   1060     }
   1061 
   1062     /**
   1063      * Connects to a wifi network using networkId.
   1064      * @param networkId the network identity for the network in the supplicant
   1065      */
   1066     @Rpc(description = "Connects to the network with the given networkId")
   1067     public void wifiConnectByNetworkId(
   1068             @RpcParameter(name = "networkId") Integer networkId) {
   1069         WifiActionListener listener;
   1070         listener = new WifiActionListener(mEventFacade,
   1071                 WifiConstants.WIFI_CONNECT_BY_NETID_CALLBACK);
   1072         mWifi.connect(networkId, listener);
   1073     }
   1074 
   1075     @Rpc(description = "Disconnects from the currently active access point.", returns = "True if the operation succeeded.")
   1076     public Boolean wifiDisconnect() {
   1077         return mWifi.disconnect();
   1078     }
   1079 
   1080     @Rpc(description = "Enable/disable autojoin scan and switch network when connected.")
   1081     public Boolean wifiSetEnableAutoJoinWhenAssociated(@RpcParameter(name = "enable") Boolean enable) {
   1082         return mWifi.setEnableAutoJoinWhenAssociated(enable);
   1083     }
   1084 
   1085     @Rpc(description = "Enable a configured network. Initiate a connection if disableOthers is true", returns = "True if the operation succeeded.")
   1086     public Boolean wifiEnableNetwork(@RpcParameter(name = "netId") Integer netId,
   1087             @RpcParameter(name = "disableOthers") Boolean disableOthers) {
   1088         return mWifi.enableNetwork(netId, disableOthers);
   1089     }
   1090 
   1091     @Rpc(description = "Enable WiFi verbose logging.")
   1092     public void wifiEnableVerboseLogging(@RpcParameter(name = "level") Integer level) {
   1093         mWifi.enableVerboseLogging(level);
   1094     }
   1095 
   1096     @Rpc(description = "Resets all WifiManager settings.")
   1097     public void wifiFactoryReset() {
   1098         mWifi.factoryReset();
   1099     }
   1100 
   1101     /**
   1102      * Forget a wifi network by networkId.
   1103      *
   1104      * @param networkId Id of wifi network
   1105      */
   1106     @Rpc(description = "Forget a wifi network by networkId")
   1107     public void wifiForgetNetwork(@RpcParameter(name = "wifiSSID") Integer networkId) {
   1108         WifiActionListener listener = new WifiActionListener(mEventFacade,
   1109                 WifiConstants.WIFI_FORGET_NETWORK_CALLBACK);
   1110         mWifi.forget(networkId, listener);
   1111     }
   1112 
   1113     /**
   1114      * Disable ephemeral network.
   1115      *
   1116      * @param ssid SSID of wifi network
   1117      */
   1118     @Rpc(description = "Forget a wifi network by networkId")
   1119     public void wifiDisableEphemeralNetwork(@RpcParameter(name = "ssid") String ssid) {
   1120         mWifi.disableEphemeralNetwork("\"" + ssid + "\"");
   1121     }
   1122 
   1123     @Rpc(description = "Gets the Wi-Fi AP Configuration.")
   1124     public WifiConfiguration wifiGetApConfiguration() {
   1125         return mWifi.getWifiApConfiguration();
   1126     }
   1127 
   1128     @Rpc(description = "Return a list of all the configured wifi networks.")
   1129     public List<WifiConfiguration> wifiGetConfiguredNetworks() {
   1130         return mWifi.getConfiguredNetworks();
   1131     }
   1132 
   1133     @Rpc(description = "Returns information about the currently active access point.")
   1134     public WifiInfo wifiGetConnectionInfo() {
   1135         return mWifi.getConnectionInfo();
   1136     }
   1137 
   1138     @Rpc(description = "Returns wifi activity and energy usage info.")
   1139     public WifiActivityEnergyInfo wifiGetControllerActivityEnergyInfo() {
   1140         return mWifi.getControllerActivityEnergyInfo();
   1141     }
   1142 
   1143     @Rpc(description = "Get the country code used by WiFi.")
   1144     public String wifiGetCountryCode() {
   1145         return mWifi.getCountryCode();
   1146     }
   1147 
   1148     @Rpc(description = "Get the current network.")
   1149     public Network wifiGetCurrentNetwork() {
   1150         return mWifi.getCurrentNetwork();
   1151     }
   1152 
   1153     @Rpc(description = "Get the info from last successful DHCP request.")
   1154     public DhcpInfo wifiGetDhcpInfo() {
   1155         return mWifi.getDhcpInfo();
   1156     }
   1157 
   1158     @Rpc(description = "Get setting for Framework layer autojoin enable status.")
   1159     public Boolean wifiGetEnableAutoJoinWhenAssociated() {
   1160         return mWifi.getEnableAutoJoinWhenAssociated();
   1161     }
   1162 
   1163     @Rpc(description = "Get privileged configured networks.")
   1164     public List<WifiConfiguration> wifiGetPrivilegedConfiguredNetworks() {
   1165         return mWifi.getPrivilegedConfiguredNetworks();
   1166     }
   1167 
   1168     @Rpc(description = "Returns the list of access points found during the most recent Wifi scan.")
   1169     public List<ScanResult> wifiGetScanResults() {
   1170         return mWifi.getScanResults();
   1171     }
   1172 
   1173     @Rpc(description = "Get the current level of WiFi verbose logging.")
   1174     public Integer wifiGetVerboseLoggingLevel() {
   1175         return mWifi.getVerboseLoggingLevel();
   1176     }
   1177 
   1178     @Rpc(description = "true if this adapter supports 5 GHz band.")
   1179     public Boolean wifiIs5GHzBandSupported() {
   1180         return mWifi.is5GHzBandSupported();
   1181     }
   1182 
   1183     @Rpc(description = "true if this adapter supports multiple simultaneous connections.")
   1184     public Boolean wifiIsAdditionalStaSupported() {
   1185         return mWifi.isAdditionalStaSupported();
   1186     }
   1187 
   1188     @Rpc(description = "Return true if WiFi is enabled.")
   1189     public Boolean wifiGetisWifiEnabled() {
   1190         return mWifi.isWifiEnabled();
   1191     }
   1192 
   1193     @Rpc(description = "Return whether Wi-Fi AP is enabled or disabled.")
   1194     public Boolean wifiIsApEnabled() {
   1195         return mWifi.isWifiApEnabled();
   1196     }
   1197 
   1198     @Rpc(description = "Check if Device-to-AP RTT is supported.")
   1199     public Boolean wifiIsDeviceToApRttSupported() {
   1200         return mWifi.isDeviceToApRttSupported();
   1201     }
   1202 
   1203     @Rpc(description = "Check if Device-to-device RTT is supported.")
   1204     public Boolean wifiIsDeviceToDeviceRttSupported() {
   1205         return mWifi.isDeviceToDeviceRttSupported();
   1206     }
   1207 
   1208     @Rpc(description = "Check if the chipset supports dual frequency band (2.4 GHz and 5 GHz).")
   1209     public Boolean wifiIsDualBandSupported() {
   1210         return mWifi.isDualBandSupported();
   1211     }
   1212 
   1213     @Rpc(description = "Check if this adapter supports advanced power/performance counters.")
   1214     public Boolean wifiIsEnhancedPowerReportingSupported() {
   1215         return mWifi.isEnhancedPowerReportingSupported();
   1216     }
   1217 
   1218     @Rpc(description = "Check if multicast is enabled.")
   1219     public Boolean wifiIsMulticastEnabled() {
   1220         return mWifi.isMulticastEnabled();
   1221     }
   1222 
   1223     @Rpc(description = "true if this adapter supports Wi-Fi Aware APIs.")
   1224     public Boolean wifiIsAwareSupported() {
   1225         return mWifi.isWifiAwareSupported();
   1226     }
   1227 
   1228     @Rpc(description = "true if this adapter supports Off Channel Tunnel Directed Link Setup.")
   1229     public Boolean wifiIsOffChannelTdlsSupported() {
   1230         return mWifi.isOffChannelTdlsSupported();
   1231     }
   1232 
   1233     @Rpc(description = "true if this adapter supports WifiP2pManager (Wi-Fi Direct).")
   1234     public Boolean wifiIsP2pSupported() {
   1235         return mWifi.isP2pSupported();
   1236     }
   1237 
   1238     @Rpc(description = "true if this adapter supports passpoint.")
   1239     public Boolean wifiIsPasspointSupported() {
   1240         return mWifi.isPasspointSupported();
   1241     }
   1242 
   1243     @Rpc(description = "true if this adapter supports portable Wi-Fi hotspot.")
   1244     public Boolean wifiIsPortableHotspotSupported() {
   1245         return mWifi.isPortableHotspotSupported();
   1246     }
   1247 
   1248     @Rpc(description = "true if this adapter supports offloaded connectivity scan.")
   1249     public Boolean wifiIsPreferredNetworkOffloadSupported() {
   1250         return mWifi.isPreferredNetworkOffloadSupported();
   1251     }
   1252 
   1253     @Rpc(description = "Check if wifi scanner is supported on this device.")
   1254     public Boolean wifiIsScannerSupported() {
   1255         return mWifi.isWifiScannerSupported();
   1256     }
   1257 
   1258     @Rpc(description = "Check if tdls is supported on this device.")
   1259     public Boolean wifiIsTdlsSupported() {
   1260         return mWifi.isTdlsSupported();
   1261     }
   1262 
   1263     /**
   1264      * @return true if this device supports WPA3-Personal SAE
   1265      */
   1266     @Rpc(description = "Check if WPA3-Personal SAE is supported on this device.")
   1267     public Boolean wifiIsWpa3SaeSupported() {
   1268         return mWifi.isWpa3SaeSupported();
   1269     }
   1270     /**
   1271      * @return true if this device supports WPA3-Enterprise Suite-B-192
   1272      */
   1273     @Rpc(description = "Check if WPA3-Enterprise Suite-B-192 is supported on this device.")
   1274     public Boolean wifiIsWpa3SuiteBSupported() {
   1275         return mWifi.isWpa3SuiteBSupported();
   1276     }
   1277     /**
   1278      * @return true if this device supports Wi-Fi Enhanced Open (OWE)
   1279      */
   1280     @Rpc(description = "Check if Enhanced Open (OWE) is supported on this device.")
   1281     public Boolean wifiIsEnhancedOpenSupported() {
   1282         return mWifi.isEnhancedOpenSupported();
   1283     }
   1284     /**
   1285      * @return true if this device supports Wi-Fi Device Provisioning Protocol (Easy-connect)
   1286      */
   1287     @Rpc(description = "Check if Easy Connect (DPP) is supported on this device.")
   1288     public Boolean wifiIsEasyConnectSupported() {
   1289         return mWifi.isEasyConnectSupported();
   1290     }
   1291 
   1292     @Rpc(description = "Acquires a full Wifi lock.")
   1293     public void wifiLockAcquireFull() {
   1294         makeLock(WifiManager.WIFI_MODE_FULL);
   1295     }
   1296 
   1297     @Rpc(description = "Acquires a scan only Wifi lock.")
   1298     public void wifiLockAcquireScanOnly() {
   1299         makeLock(WifiManager.WIFI_MODE_SCAN_ONLY);
   1300     }
   1301 
   1302     @Rpc(description = "Releases a previously acquired Wifi lock.")
   1303     public void wifiLockRelease() {
   1304         if (mLock != null) {
   1305             mLock.release();
   1306             mLock = null;
   1307         }
   1308     }
   1309 
   1310     @Rpc(description = "Reassociates with the currently active access point.", returns = "True if the operation succeeded.")
   1311     public Boolean wifiReassociate() {
   1312         return mWifi.reassociate();
   1313     }
   1314 
   1315     @Rpc(description = "Reconnects to the currently active access point.", returns = "True if the operation succeeded.")
   1316     public Boolean wifiReconnect() {
   1317         return mWifi.reconnect();
   1318     }
   1319 
   1320     @Rpc(description = "Remove a configured network.", returns = "True if the operation succeeded.")
   1321     public Boolean wifiRemoveNetwork(@RpcParameter(name = "netId") Integer netId) {
   1322         return mWifi.removeNetwork(netId);
   1323     }
   1324 
   1325     private WifiConfiguration createSoftApWifiConfiguration(JSONObject configJson)
   1326             throws JSONException {
   1327         WifiConfiguration config = genWifiConfig(configJson);
   1328         // Need to strip of extra quotation marks for SSID and password.
   1329         String ssid = config.SSID;
   1330         if (ssid != null) {
   1331             config.SSID = ssid.substring(1, ssid.length() - 1);
   1332         }
   1333 
   1334         config.allowedKeyManagement.clear();
   1335         String pwd = config.preSharedKey;
   1336         if (pwd != null) {
   1337             config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
   1338             config.preSharedKey = pwd.substring(1, pwd.length() - 1);
   1339         } else {
   1340             config.allowedKeyManagement.set(KeyMgmt.NONE);
   1341         }
   1342         return config;
   1343     }
   1344 
   1345     @Rpc(description = "Set configuration for soft AP.")
   1346     public Boolean wifiSetWifiApConfiguration(
   1347             @RpcParameter(name = "configJson") JSONObject configJson) throws JSONException {
   1348         WifiConfiguration config = createSoftApWifiConfiguration(configJson);
   1349         return mWifi.setWifiApConfiguration(config);
   1350     }
   1351 
   1352     /**
   1353      * Register softap callback.
   1354      *
   1355      * @return the id associated with the {@link SoftApCallbackImp}
   1356      * used for registering callback.
   1357      */
   1358     @Rpc(description = "Register softap callback function.",
   1359             returns = "Id of the callback associated with registering.")
   1360     public Integer registerSoftApCallback() {
   1361         SoftApCallbackImp softApCallback = new SoftApCallbackImp(mEventFacade);
   1362         mSoftapCallbacks.put(softApCallback.mId, softApCallback);
   1363         mWifi.registerSoftApCallback(softApCallback,
   1364                 new Handler(mCallbackHandlerThread.getLooper()));
   1365         return softApCallback.mId;
   1366     }
   1367 
   1368     /**
   1369      * Unregister softap callback role for the {@link SoftApCallbackImp} identified by the given
   1370      * {@code callbackId}.
   1371      *
   1372      * @param callbackId the id associated with the {@link SoftApCallbackImp}
   1373      * used for registering callback.
   1374      *
   1375      */
   1376     @Rpc(description = "Unregister softap callback function.")
   1377     public void unregisterSoftApCallback(@RpcParameter(name = "callbackId") Integer callbackId) {
   1378         mWifi.unregisterSoftApCallback(mSoftapCallbacks.get(callbackId));
   1379         mSoftapCallbacks.delete(callbackId);
   1380     }
   1381 
   1382     @Rpc(description = "Set the country code used by WiFi.")
   1383     public void wifiSetCountryCode(
   1384             @RpcParameter(name = "country") String country) {
   1385         mWifi.setCountryCode(country);
   1386     }
   1387 
   1388     @Rpc(description = "Enable/disable tdls with a mac address.")
   1389     public void wifiSetTdlsEnabledWithMacAddress(
   1390             @RpcParameter(name = "remoteMacAddress") String remoteMacAddress,
   1391             @RpcParameter(name = "enable") Boolean enable) {
   1392         mWifi.setTdlsEnabledWithMacAddress(remoteMacAddress, enable);
   1393     }
   1394 
   1395     @Rpc(description = "Starts a scan for Wifi access points.", returns = "True if the scan was initiated successfully.")
   1396     public Boolean wifiStartScan() {
   1397         mService.registerReceiver(mScanResultsAvailableReceiver, mScanFilter);
   1398         return mWifi.startScan();
   1399     }
   1400 
   1401     @Rpc(description = "Start Wi-fi Protected Setup.")
   1402     public void wifiStartWps(
   1403             @RpcParameter(name = "config", description = "A json string with fields \"setup\", \"BSSID\", and \"pin\"") String config)
   1404                     throws JSONException {
   1405         WpsInfo info = parseWpsInfo(config);
   1406         WifiWpsCallback listener = new WifiWpsCallback();
   1407         Log.d("Starting wps with: " + info);
   1408         mWifi.startWps(info, listener);
   1409     }
   1410 
   1411     @Rpc(description = "Start listening for wifi state change related broadcasts.")
   1412     public void wifiStartTrackingStateChange() {
   1413         mService.registerReceiver(mStateChangeReceiver, mStateChangeFilter);
   1414         mTrackingWifiStateChange = true;
   1415     }
   1416 
   1417     @Rpc(description = "Stop listening for wifi state change related broadcasts.")
   1418     public void wifiStopTrackingStateChange() {
   1419         if (mTrackingWifiStateChange == true) {
   1420             mService.unregisterReceiver(mStateChangeReceiver);
   1421             mTrackingWifiStateChange = false;
   1422         }
   1423     }
   1424 
   1425     @Rpc(description = "Start listening for tether state change related broadcasts.")
   1426     public void wifiStartTrackingTetherStateChange() {
   1427         mService.registerReceiver(mTetherStateReceiver, mTetherFilter);
   1428         mTrackingTetherStateChange = true;
   1429     }
   1430 
   1431     @Rpc(description = "Stop listening for wifi state change related broadcasts.")
   1432     public void wifiStopTrackingTetherStateChange() {
   1433         if (mTrackingTetherStateChange == true) {
   1434             mService.unregisterReceiver(mTetherStateReceiver);
   1435             mTrackingTetherStateChange = false;
   1436         }
   1437     }
   1438 
   1439     @Rpc(description = "Start listening for network suggestion change related broadcasts.")
   1440     public void wifiStartTrackingNetworkSuggestionStateChange() {
   1441         mService.registerReceiver(
   1442                 mNetworkSuggestionStateChangeReceiver, mNetworkSuggestionStateChangeFilter);
   1443         mTrackingNetworkSuggestionStateChange = true;
   1444     }
   1445 
   1446     @Rpc(description = "Stop listening for network suggestion change related broadcasts.")
   1447     public void wifiStopTrackingNetworkSuggestionStateChange() {
   1448         if (mTrackingNetworkSuggestionStateChange) {
   1449             mService.unregisterReceiver(mNetworkSuggestionStateChangeReceiver);
   1450             mTrackingNetworkSuggestionStateChange = false;
   1451         }
   1452     }
   1453 
   1454     @Rpc(description = "Toggle Wifi on and off.", returns = "True if Wifi is enabled.")
   1455     public Boolean wifiToggleState(@RpcParameter(name = "enabled") @RpcOptional Boolean enabled) {
   1456         if (enabled == null) {
   1457             enabled = !wifiCheckState();
   1458         }
   1459         mWifi.setWifiEnabled(enabled);
   1460         return enabled;
   1461     }
   1462 
   1463     @Rpc(description = "Toggle Wifi scan always available on and off.", returns = "True if Wifi scan is always available.")
   1464     public Boolean wifiToggleScanAlwaysAvailable(
   1465             @RpcParameter(name = "enabled") @RpcOptional Boolean enabled)
   1466                     throws SettingNotFoundException {
   1467         ContentResolver cr = mService.getContentResolver();
   1468         int isSet = 0;
   1469         if (enabled == null) {
   1470             isSet = Global.getInt(cr, Global.WIFI_SCAN_ALWAYS_AVAILABLE);
   1471             isSet ^= 1;
   1472         } else if (enabled == true) {
   1473             isSet = 1;
   1474         }
   1475         Global.putInt(cr, Global.WIFI_SCAN_ALWAYS_AVAILABLE, isSet);
   1476         if (isSet == 1) {
   1477             return true;
   1478         }
   1479         return false;
   1480     }
   1481 
   1482     @Rpc(description = "Enable/disable WifiConnectivityManager.")
   1483     public void wifiEnableWifiConnectivityManager(
   1484             @RpcParameter(name = "enable") Boolean enable) {
   1485         mWifi.enableWifiConnectivityManager(enable);
   1486     }
   1487 
   1488     private void wifiRequestNetworkWithSpecifierInternal(NetworkSpecifier wns, int timeoutInMs)
   1489             throws GeneralSecurityException {
   1490         NetworkRequest networkRequest = new NetworkRequest.Builder()
   1491                 .addTransportType(TRANSPORT_WIFI)
   1492                 .setNetworkSpecifier(wns)
   1493                 .build();
   1494         NetworkCallback networkCallback = new NetworkCallbackImpl();
   1495         if (timeoutInMs != 0) {
   1496             mCm.requestNetwork(networkRequest, networkCallback,
   1497                     new Handler(mCallbackHandlerThread.getLooper()), timeoutInMs);
   1498         } else {
   1499             mCm.requestNetwork(networkRequest, networkCallback,
   1500                     new Handler(mCallbackHandlerThread.getLooper()));
   1501         }
   1502         // Store the callback for release later.
   1503         mNetworkCallbacks.put(wns, networkCallback);
   1504     }
   1505 
   1506     /**
   1507      * Initiates a network request {@link NetworkRequest} using {@link WifiNetworkSpecifier}.
   1508      *
   1509      * @param wifiNetworkSpecifier JSONObject Dictionary of wifi network specifier parameters
   1510      * @throws JSONException
   1511      * @throws GeneralSecurityException
   1512      */
   1513     @Rpc(description = "Initiates a network request using the provided network specifier")
   1514     public void wifiRequestNetworkWithSpecifier(
   1515             @RpcParameter(name = "wifiNetworkSpecifier") JSONObject wifiNetworkSpecifier)
   1516             throws JSONException, GeneralSecurityException {
   1517         wifiRequestNetworkWithSpecifierInternal(genWifiNetworkSpecifier(wifiNetworkSpecifier), 0);
   1518     }
   1519 
   1520     /**
   1521      * Initiates a network request {@link NetworkRequest} using {@link WifiNetworkSpecifier}.
   1522      *
   1523      * @param wifiNetworkSpecifier JSONObject Dictionary of wifi network specifier parameters
   1524      * @param timeoutInMs Timeout for the request.
   1525      * @throws JSONException
   1526      * @throws GeneralSecurityException
   1527      */
   1528     @Rpc(description = "Initiates a network request using the provided network specifier")
   1529     public void wifiRequestNetworkWithSpecifierWithTimeout(
   1530             @RpcParameter(name = "wifiNetworkSpecifier") JSONObject wifiNetworkSpecifier,
   1531             @RpcParameter(name = "timeout") Integer timeoutInMs)
   1532             throws JSONException, GeneralSecurityException {
   1533         wifiRequestNetworkWithSpecifierInternal(
   1534                 genWifiNetworkSpecifier(wifiNetworkSpecifier), timeoutInMs);
   1535     }
   1536 
   1537     /**
   1538      * Releases network request using {@link WifiNetworkSpecifier}.
   1539      *
   1540      * @throws JSONException
   1541      * @throws GeneralSecurityException
   1542      */
   1543     @Rpc(description = "Releases network request corresponding to the network specifier")
   1544     public void wifiReleaseNetwork(
   1545             @RpcParameter(name = "wifiNetworkSpecifier") JSONObject wifiNetworkSpecifier)
   1546             throws JSONException, GeneralSecurityException {
   1547         NetworkSpecifier wns = genWifiNetworkSpecifier(wifiNetworkSpecifier);
   1548         NetworkCallback networkCallback = mNetworkCallbacks.remove(wns);
   1549         if (networkCallback == null) {
   1550             throw new IllegalArgumentException("network callback is null");
   1551         }
   1552         mCm.unregisterNetworkCallback(networkCallback);
   1553     }
   1554 
   1555     /**
   1556      * Releases all pending network requests.
   1557      *
   1558      * @throws JSONException
   1559      * @throws GeneralSecurityException
   1560      */
   1561     @Rpc(description = "Releases all pending network requests")
   1562     public void wifiReleaseNetworkAll() {
   1563         Iterator<Map.Entry<NetworkSpecifier, NetworkCallback>> it =
   1564                 mNetworkCallbacks.entrySet().iterator();
   1565         while (it.hasNext()) {
   1566             Map.Entry<NetworkSpecifier, NetworkCallback> entry = it.next();
   1567             NetworkCallback networkCallback = entry.getValue();
   1568             it.remove();
   1569             mCm.unregisterNetworkCallback(networkCallback);
   1570         }
   1571     }
   1572 
   1573     /**
   1574      * Register network request match callback to simulate the UI flow.
   1575      *
   1576      * @throws JSONException
   1577      * @throws GeneralSecurityException
   1578      */
   1579     @Rpc(description = "Register network request match callback")
   1580     public void wifiRegisterNetworkRequestMatchCallback()
   1581             throws JSONException, GeneralSecurityException {
   1582         // Listen for UI interaction callbacks
   1583         mWifi.registerNetworkRequestMatchCallback(
   1584                 mNetworkRequestMatchCallback, new Handler(mCallbackHandlerThread.getLooper()));
   1585     }
   1586 
   1587     /**
   1588      * Triggers connect to a specific wifi network.
   1589      *
   1590      * @param jsonConfig JSONObject Dictionary of wifi connection parameters
   1591      * @throws JSONException
   1592      * @throws GeneralSecurityException
   1593      */
   1594     @Rpc(description = "Connects to the specified network for the ongoing network request")
   1595     public void wifiSendUserSelectionForNetworkRequestMatch(
   1596             @RpcParameter(name = "jsonConfig") JSONObject jsonConfig)
   1597             throws JSONException, GeneralSecurityException {
   1598         synchronized (mCallbackLock) {
   1599             if (mNetworkRequestUserSelectionCallback == null) {
   1600                 throw new IllegalStateException("user callback is null");
   1601             }
   1602             // Copy the SSID for user selection.
   1603             WifiConfiguration config = new WifiConfiguration();
   1604             if (jsonConfig.has("SSID")) {
   1605                 config.SSID = "\"" + jsonConfig.getString("SSID") + "\"";
   1606             }
   1607             mNetworkRequestUserSelectionCallback.select(config);
   1608         }
   1609     }
   1610 
   1611     /**
   1612      * Rejects network request.
   1613      *
   1614      * @throws JSONException
   1615      * @throws GeneralSecurityException
   1616      */
   1617     @Rpc(description = "Rejects ongoing network request")
   1618     public void wifiSendUserRejectionForNetworkRequestMatch()
   1619             throws JSONException, GeneralSecurityException {
   1620         synchronized (mCallbackLock) {
   1621             if (mNetworkRequestUserSelectionCallback == null) {
   1622                 throw new IllegalStateException("user callback is null");
   1623             }
   1624             mNetworkRequestUserSelectionCallback.reject();
   1625         }
   1626     }
   1627 
   1628     /**
   1629      * Add network suggestions.
   1630 
   1631      * @param wifiNetworkSuggestions Array of JSONObject Dictionary of wifi network suggestion
   1632      *                               parameters
   1633      * @throws JSONException
   1634      * @throws GeneralSecurityException
   1635      */
   1636     @Rpc(description = "Add network suggestions to the platform")
   1637     public boolean wifiAddNetworkSuggestions(
   1638             @RpcParameter(name = "wifiNetworkSuggestions") JSONArray wifiNetworkSuggestions)
   1639             throws JSONException, GeneralSecurityException {
   1640         return mWifi.addNetworkSuggestions(genWifiNetworkSuggestions(wifiNetworkSuggestions))
   1641                 == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS;
   1642     }
   1643 
   1644     /**
   1645      * Remove network suggestions.
   1646 
   1647      * @param wifiNetworkSuggestions Array of JSONObject Dictionary of wifi network suggestion
   1648      *                               parameters
   1649      * @throws JSONException
   1650      * @throws GeneralSecurityException
   1651      */
   1652     @Rpc(description = "Remove network suggestions from the platform")
   1653     public boolean wifiRemoveNetworkSuggestions(
   1654             @RpcParameter(name = "wifiNetworkSuggestions") JSONArray wifiNetworkSuggestions)
   1655             throws JSONException, GeneralSecurityException {
   1656         return mWifi.removeNetworkSuggestions(genWifiNetworkSuggestions(wifiNetworkSuggestions))
   1657                 == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS;
   1658     }
   1659 
   1660     @Override
   1661     public void shutdown() {
   1662         wifiReleaseNetworkAll();
   1663         wifiLockRelease();
   1664         if (mTrackingWifiStateChange == true) {
   1665             wifiStopTrackingStateChange();
   1666         }
   1667         if (mTrackingTetherStateChange == true) {
   1668             wifiStopTrackingTetherStateChange();
   1669         }
   1670     }
   1671 
   1672     private class EasyConnectCallback extends EasyConnectStatusCallback {
   1673         private static final String EASY_CONNECT_CALLBACK_TAG = "onDppCallback";
   1674 
   1675         @Override
   1676         public void onEnrolleeSuccess(int newWifiConfigurationId) {
   1677             Bundle msg = new Bundle();
   1678             msg.putString("Type", "onEnrolleeSuccess");
   1679             msg.putInt("NetworkId", newWifiConfigurationId);
   1680             Log.d("Posting event: onEnrolleeSuccess");
   1681             mEventFacade.postEvent(EASY_CONNECT_CALLBACK_TAG, msg);
   1682         }
   1683 
   1684         @Override
   1685         public void onConfiguratorSuccess(int code) {
   1686             Bundle msg = new Bundle();
   1687             msg.putString("Type", "onConfiguratorSuccess");
   1688             msg.putInt("Status", code);
   1689             Log.d("Posting event: onConfiguratorSuccess");
   1690             mEventFacade.postEvent(EASY_CONNECT_CALLBACK_TAG, msg);
   1691         }
   1692 
   1693         @Override
   1694         public void onFailure(int code) {
   1695             Bundle msg = new Bundle();
   1696             msg.putString("Type", "onFailure");
   1697             msg.putInt("Status", code);
   1698             Log.d("Posting event: onFailure");
   1699             mEventFacade.postEvent(EASY_CONNECT_CALLBACK_TAG, msg);
   1700         }
   1701 
   1702         @Override
   1703         public void onProgress(int code) {
   1704             Bundle msg = new Bundle();
   1705             msg.putString("Type", "onProgress");
   1706             msg.putInt("Status", code);
   1707             Log.d("Posting event: onProgress");
   1708             mEventFacade.postEvent(EASY_CONNECT_CALLBACK_TAG, msg);
   1709         }
   1710     }
   1711 
   1712     /**
   1713      * Start Easy Connect (DPP) in Initiator-Configurator role: Send Wi-Fi configuration to a peer
   1714      *
   1715      * @param enrolleeUri Peer URI
   1716      * @param selectedNetworkId Wi-Fi configuration ID
   1717      */
   1718     @Rpc(description = "Easy Connect Initiator-Configurator: Send Wi-Fi configuration to peer")
   1719     public void startEasyConnectAsConfiguratorInitiator(@RpcParameter(name = "enrolleeUri") String
   1720             enrolleeUri, @RpcParameter(name = "selectedNetworkId") Integer selectedNetworkId,
   1721             @RpcParameter(name = "netRole") String netRole)
   1722             throws JSONException {
   1723         EasyConnectCallback dppStatusCallback = new EasyConnectCallback();
   1724         int netRoleInternal;
   1725 
   1726         if (netRole.equals("ap")) {
   1727             netRoleInternal = WifiManager.EASY_CONNECT_NETWORK_ROLE_AP;
   1728         } else {
   1729             netRoleInternal = WifiManager.EASY_CONNECT_NETWORK_ROLE_STA;
   1730         }
   1731 
   1732         // Start Easy Connect
   1733         mWifi.startEasyConnectAsConfiguratorInitiator(enrolleeUri, selectedNetworkId,
   1734                 netRoleInternal, mService.getMainExecutor(), dppStatusCallback);
   1735     }
   1736 
   1737     /**
   1738      * Start Easy Connect (DPP) in Initiator-Enrollee role: Receive Wi-Fi configuration from a peer
   1739      *
   1740      * @param configuratorUri
   1741      */
   1742     @Rpc(description = "Easy Connect Initiator-Enrollee: Receive Wi-Fi configuration from peer")
   1743     public void startEasyConnectAsEnrolleeInitiator(@RpcParameter(name = "configuratorUri") String
   1744             configuratorUri) {
   1745         EasyConnectCallback dppStatusCallback = new EasyConnectCallback();
   1746 
   1747         // Start Easy Connect
   1748         mWifi.startEasyConnectAsEnrolleeInitiator(configuratorUri, mService.getMainExecutor(),
   1749                 dppStatusCallback);
   1750     }
   1751 
   1752     /**
   1753      * Stop Easy Connect (DPP) session
   1754      *
   1755      */
   1756     @Rpc(description = "Stop Easy Connect session")
   1757     public void stopEasyConnectSession() {
   1758         // Stop Easy Connect
   1759         mWifi.stopEasyConnectSession();
   1760     }
   1761 }
   1762