// Copyright 2020 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.espressif.provisioning; import android.Manifest; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.wifi.SupplicantState; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkSpecifier; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.text.TextUtils; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.RequiresPermission; import com.espressif.provisioning.listeners.ProvisionListener; import com.espressif.provisioning.listeners.ResponseListener; import com.espressif.provisioning.listeners.WiFiScanListener; import com.espressif.provisioning.security.Security; import com.espressif.provisioning.security.Security0; import com.espressif.provisioning.security.Security1; import com.espressif.provisioning.transport.BLETransport; import com.espressif.provisioning.transport.SoftAPTransport; import com.espressif.provisioning.transport.Transport; import com.espressif.provisioning.utils.MessengeHelper; import com.google.protobuf.InvalidProtocolBufferException; import org.greenrobot.eventbus.EventBus; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.UUID; import espressif.Constants; import espressif.WifiConfig; import espressif.WifiConstants; import espressif.WifiScan; import static java.lang.Thread.sleep; /** * ESPDevice class to hold device information. This will give facility to connect device, send data to device and * do provisioning of it. */ public class ESPDevice { private static final String TAG = "ESP:" + ESPDevice.class.getSimpleName(); private Context context; private Handler handler; private Session session; private Security security; private Transport transport; private WiFiScanListener wifiScanListener; private ProvisionListener provisionListener; private ResponseListener responseListener; private ESPConstants.TransportType transportType; private ESPConstants.SecurityType securityType; private String proofOfPossession; private int totalCount; private int startIndex; private ArrayList<WiFiAccessPoint> wifiApList; private ArrayList<String> deviceCapabilities = new ArrayList<>(); private BluetoothDevice bluetoothDevice; private WiFiAccessPoint wifiDevice; private String primaryServiceUuid; private String deviceName; private WifiManager wifiManager; private ConnectivityManager connectivityManager; private ConnectivityManager.NetworkCallback networkCallback; public ESPDevice(Context context, ESPConstants.TransportType transportType, ESPConstants.SecurityType securityType) { this.context = context; handler = new Handler(Looper.getMainLooper()); this.transportType = transportType; this.securityType = securityType; wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); switch (transportType) { case TRANSPORT_BLE: transport = new BLETransport(context); break; case TRANSPORT_SOFTAP: transport = new SoftAPTransport(); break; } } /** * This method is used to connect ESPDevice. */ @RequiresPermission(allOf = {Manifest.permission.CHANGE_WIFI_STATE, Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.ACCESS_FINE_LOCATION}) public void connectToDevice() { switch (transportType) { case TRANSPORT_BLE: ((BLETransport) transport).connect(bluetoothDevice, UUID.fromString(primaryServiceUuid)); break; case TRANSPORT_SOFTAP: deviceConnectionReqCount = 0; connectWiFiDevice(wifiDevice.getWifiName(), wifiDevice.getPassword()); break; } } /** * This method is used to connect ESPDevice using BLE transport. * * @param bluetoothDevice BluetoothDevice * @param primaryServiceUuid Primary service UUID. */ @RequiresPermission(Manifest.permission.BLUETOOTH) public void connectBLEDevice(BluetoothDevice bluetoothDevice, String primaryServiceUuid) { if (transport instanceof BLETransport) { deviceName = bluetoothDevice.getName(); ((BLETransport) transport).connect(bluetoothDevice, UUID.fromString(primaryServiceUuid)); } else { Log.e(TAG, "Trying to connect device with wrong transport."); EventBus.getDefault().post(new DeviceConnectionEvent(ESPConstants.EVENT_DEVICE_CONNECTION_FAILED)); } } /** * This method is used to connect ESPDevice using Wi-Fi transport. */ @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE) public void connectWiFiDevice() { if (transport instanceof SoftAPTransport) { enableOnlyWifiNetwork(); deviceConnectionReqCount = 0; getCapabilitiesFromDevice(); } else { Log.e(TAG, "Trying to connect device with wrong transport."); EventBus.getDefault().post(new DeviceConnectionEvent(ESPConstants.EVENT_DEVICE_CONNECTION_FAILED)); } } /** * This method is used to connect ESPDevice using Wi-Fi transport. * * @param ssid SSID of the device. * @param password Password of Wi-Fi device. */ @RequiresPermission(allOf = {Manifest.permission.CHANGE_WIFI_STATE, Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.ACCESS_FINE_LOCATION}) public void connectWiFiDevice(String ssid, String password) { Log.e(TAG, "connectWiFiDevice ========== SSID : " + ssid + " and Password : " + password); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { WifiNetworkSpecifier.Builder builder = new WifiNetworkSpecifier.Builder(); builder.setSsid(ssid); builder.setWpa2Passphrase(password); WifiNetworkSpecifier wifiNetworkSpecifier = builder.build(); NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder(); networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); networkRequestBuilder.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); networkRequestBuilder.setNetworkSpecifier(wifiNetworkSpecifier); NetworkRequest networkRequest = networkRequestBuilder.build(); networkCallback = new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(Network network) { Log.e(TAG, "Network is available - 1"); connectivityManager.bindProcessToNetwork(network); getCapabilitiesFromDevice(); } @Override public void onUnavailable() { super.onUnavailable(); Log.e(TAG, "Network is Unavailable - 1"); handler.postDelayed(wifiConnectionFailedTask, 200); } @Override public void onLost(@NonNull Network network) { super.onLost(network); Log.e(TAG, "Lost Network Connection - 1"); } }; connectivityManager.requestNetwork(networkRequest, networkCallback); } else { NetworkRequest.Builder request = new NetworkRequest.Builder(); request.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); request.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);// Internet not required networkCallback = new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(Network network) { Log.e(TAG, "Network is available - 2"); connectivityManager.bindProcessToNetwork(network); } @Override public void onUnavailable() { super.onUnavailable(); Log.e(TAG, "Network is Unavailable - 2"); } @Override public void onLost(@NonNull Network network) { super.onLost(network); Log.e(TAG, "Lost Network Connection - 2"); } }; connectivityManager.registerNetworkCallback(request.build(), networkCallback); if (!wifiManager.isWifiEnabled()) { wifiManager.setWifiEnabled(true); } Log.d(TAG, "Device name : " + ssid); Log.d(TAG, "Device password : " + password); WifiConfiguration config = new WifiConfiguration(); config.SSID = String.format("\"%s\"", ssid); int netId = -1; List<WifiConfiguration> apList = wifiManager.getConfiguredNetworks(); Log.d(TAG, "List Size : " + apList.size()); for (WifiConfiguration i : apList) { if (i.SSID != null && i.SSID.equals("\"" + ssid + "\"")) { netId = i.networkId; } } if (netId == -1) { if (TextUtils.isEmpty(password)) { Log.i(TAG, "Connect to open network"); config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); } else { Log.i(TAG, "Connect to secure network"); config.preSharedKey = String.format("\"%s\"", password); } netId = wifiManager.addNetwork(config); Log.d(TAG, "Network Id : " + netId); } if (netId != -1) { Log.d(TAG, "Connect to network : " + netId); wifiManager.enableNetwork(netId, true); scheduleWiFiConnectionFailure(); checkDeviceConnection(ssid); } else { Log.e(TAG, "Failed to add network"); EventBus.getDefault().post(new DeviceConnectionEvent(ESPConstants.EVENT_DEVICE_CONNECTION_FAILED)); } } } /** * This method is used to disconnect ESPDevice. * Note : It will disconnect only if device is connected thorough BLE transport. */ public void disconnectDevice() { if (transport instanceof BLETransport) { ((BLETransport) transport).disconnect(); } session = null; disableOnlyWifiNetwork(); } /** * This method is used to set Proof Of Possession. * * @param pop Proof Of Possession of the device. */ public void setProofOfPossession(String pop) { this.proofOfPossession = pop; } /** * This method is used to get Proof Of Possession. * * @return Returns Proof Of Possession of the device. */ public String getProofOfPossession() { return proofOfPossession; } /** * This method is used to set device name. * * @param deviceName Device name to be set. */ public void setDeviceName(String deviceName) { this.deviceName = deviceName; } /** * This method is used to get Proof Of Possession. * * @return Returns device name. */ public String getDeviceName() { return deviceName; } /** * This method is used to get device capabilities. * * @return Returns device capabilities. */ public ArrayList<String> getDeviceCapabilities() { if (transport instanceof BLETransport) { return ((BLETransport) transport).deviceCapabilities; } else { return deviceCapabilities; } } /** * This method is used to get transport type. * * @return Returns transport type. */ public ESPConstants.TransportType getTransportType() { return transportType; } /** * This method is used to get security type. * * @return Returns security type. */ public ESPConstants.SecurityType getSecurityType() { return securityType; } /** * This method is used to get Wi-Fi access point. * * @return Returns Wi-Fi access point. */ public WiFiAccessPoint getWifiDevice() { return wifiDevice; } public void setWifiDevice(WiFiAccessPoint wifiDevice) { this.wifiDevice = wifiDevice; } /** * This method is used to get BluetoothDevice object to connect with device using BLE. * * @return Returns BluetoothDevice object of the device. */ public BluetoothDevice getBluetoothDevice() { return bluetoothDevice; } /** * This method is used to set BluetoothDevice. * * @param bluetoothDevice BluetoothDevice */ public void setBluetoothDevice(BluetoothDevice bluetoothDevice) { this.bluetoothDevice = bluetoothDevice; } /** * This method is used to get primary service UUID of the BLE device. * * @return Returns Primary service UUID of the device. */ public String getPrimaryServiceUuid() { return primaryServiceUuid; } /** * This method is used to set primary service UUID. * * @param primaryServiceUuid Primary service UUID of the device. */ public void setPrimaryServiceUuid(String primaryServiceUuid) { this.primaryServiceUuid = primaryServiceUuid; } /** * Send scan command to device to get available Wi-Fi access points. * * @param wifiScanListener WiFiScanListener to get callbacks of scanning networks. */ public void scanNetworks(final WiFiScanListener wifiScanListener) { Log.d(TAG, "Send Wi-Fi scan command to device"); this.wifiScanListener = wifiScanListener; if (session == null || !session.isEstablished()) { initSession(new ResponseListener() { @Override public void onSuccess(byte[] returnData) { startNetworkScan(); } @Override public void onFailure(Exception e) { e.printStackTrace(); if (wifiScanListener != null) { wifiScanListener.onWiFiScanFailed(new RuntimeException("Failed to create session.")); } } }); } else { startNetworkScan(); } } /** * Send data to custom endpoint of the device. * * @param path Endpoint. * @param data Data to be send. * @param listener Listener to get success and failure. */ public void sendDataToCustomEndPoint(final String path, final byte[] data, final ResponseListener listener) { this.responseListener = listener; if (session == null || !session.isEstablished()) { initSession(new ResponseListener() { @Override public void onSuccess(byte[] returnData) { sendData(path, data, listener); } @Override public void onFailure(Exception e) { e.printStackTrace(); if (responseListener != null) { responseListener.onFailure(e); } } }); } else { sendData(path, data, listener); } } /** * Send Wi-Fi credentials to device for provisioning. * * @param ssid SSID of the Wi-Fi which is to be configure in device. * @param passphrase Password of the Wi-Fi which is to be configure in device. * @param provisionListener Listener for provisioning callbacks. */ public void provision(final String ssid, final String passphrase, final ProvisionListener provisionListener) { this.provisionListener = provisionListener; if (session == null || !session.isEstablished()) { initSession(new ResponseListener() { @Override public void onSuccess(byte[] returnData) { sendWiFiConfig(ssid, passphrase, provisionListener); } @Override public void onFailure(Exception e) { e.printStackTrace(); disableOnlyWifiNetwork(); if (provisionListener != null) { provisionListener.createSessionFailed(new RuntimeException("Failed to create session.")); } } }); } else { sendWiFiConfig(ssid, passphrase, provisionListener); } } private void initSession(final ResponseListener listener) { if (securityType.equals(ESPConstants.SecurityType.SECURITY_0)) { security = new Security0(); } else { security = new Security1(proofOfPossession); } session = new Session(transport, security); session.init(null, new Session.SessionListener() { @Override public void OnSessionEstablished() { listener.onSuccess(null); } @Override public void OnSessionEstablishFailed(Exception e) { listener.onFailure(e); } }); } private void sendData(final String path, byte[] data, final ResponseListener listener) { session.sendDataToDevice(path, data, new ResponseListener() { @Override public void onSuccess(byte[] returnData) { if (listener != null) { listener.onSuccess(returnData); } } @Override public void onFailure(Exception e) { e.printStackTrace(); if (listener != null) { listener.onFailure(e); } } }); } private void startNetworkScan() { totalCount = 0; startIndex = 0; wifiApList = new ArrayList<>(); byte[] scanCommand = MessengeHelper.prepareWiFiScanMsg(); session.sendDataToDevice(ESPConstants.HANDLER_PROV_SCAN, scanCommand, new ResponseListener() { @Override public void onSuccess(byte[] returnData) { processStartScanResponse(returnData); byte[] getScanStatusCmd = MessengeHelper.prepareGetWiFiScanStatusMsg(); session.sendDataToDevice(ESPConstants.HANDLER_PROV_SCAN, getScanStatusCmd, new ResponseListener() { @Override public void onSuccess(byte[] returnData) { processWifiStatusResponse(returnData); } @Override public void onFailure(Exception e) { e.printStackTrace(); if (wifiScanListener != null) { wifiScanListener.onWiFiScanFailed(new RuntimeException("Failed to send Wi-Fi scan command.")); } } }); } @Override public void onFailure(Exception e) { e.printStackTrace(); if (wifiScanListener != null) { wifiScanListener.onWiFiScanFailed(new RuntimeException("Failed to send Wi-Fi scan command.")); } } }); } private void getFullWiFiList() { Log.e(TAG, "Total count : " + totalCount + " and start index is : " + startIndex); if (totalCount < 4) { getWiFiScanList(0, totalCount); } else { int temp = totalCount - startIndex; if (temp > 0) { if (temp > 4) { getWiFiScanList(startIndex, 4); } else { getWiFiScanList(startIndex, temp); } } else { Log.d(TAG, "Nothing to do. Wifi list completed."); completeWifiList(); } } } private void getWiFiScanList(int start, int count) { Log.d(TAG, "Getting " + count + " SSIDs"); if (count <= 0) { completeWifiList(); return; } byte[] data = MessengeHelper.prepareGetWiFiScanListMsg(start, count); session.sendDataToDevice(ESPConstants.HANDLER_PROV_SCAN, data, new ResponseListener() { @Override public void onSuccess(byte[] returnData) { Log.d(TAG, "Successfully got SSID list"); processGetSSIDs(returnData); } @Override public void onFailure(Exception e) { e.printStackTrace(); if (wifiScanListener != null) { wifiScanListener.onWiFiScanFailed(new RuntimeException("Failed to get Wi-Fi Networks.")); } } }); } private void completeWifiList() { if (wifiScanListener != null) { wifiScanListener.onWifiListReceived(wifiApList); } } private void sendWiFiConfig(final String ssid, final String passphrase, final ProvisionListener provisionListener) { byte[] scanCommand = MessengeHelper.prepareWiFiConfigMsg(ssid, passphrase); session.sendDataToDevice(ESPConstants.HANDLER_PROV_CONFIG, scanCommand, new ResponseListener() { @Override public void onSuccess(byte[] returnData) { Constants.Status status = processWifiConfigResponse(returnData); if (provisionListener != null) { if (status != Constants.Status.Success) { provisionListener.wifiConfigFailed(new RuntimeException("Failed to send wifi credentials to device")); } else { provisionListener.wifiConfigSent(); } } if (status == Constants.Status.Success) { applyWiFiConfig(); } else { disableOnlyWifiNetwork(); } } @Override public void onFailure(Exception e) { e.printStackTrace(); disableOnlyWifiNetwork(); if (provisionListener != null) { provisionListener.wifiConfigFailed(new RuntimeException("Failed to send wifi credentials to device")); } } }); } private void applyWiFiConfig() { byte[] scanCommand = MessengeHelper.prepareApplyWiFiConfigMsg(); session.sendDataToDevice(ESPConstants.HANDLER_PROV_CONFIG, scanCommand, new ResponseListener() { @Override public void onSuccess(byte[] returnData) { Constants.Status status = processApplyConfigResponse(returnData); if (status == Constants.Status.Success) { if (provisionListener != null) { provisionListener.wifiConfigApplied(); } try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } pollForWifiConnectionStatus(); } else { disableOnlyWifiNetwork(); if (provisionListener != null) { provisionListener.wifiConfigApplyFailed(new RuntimeException("Failed to apply wifi credentials")); } } } @Override public void onFailure(Exception e) { e.printStackTrace(); disableOnlyWifiNetwork(); if (provisionListener != null) { provisionListener.wifiConfigApplyFailed(new RuntimeException("Failed to apply wifi credentials")); } } }); } private void pollForWifiConnectionStatus() { byte[] message = MessengeHelper.prepareGetWiFiConfigStatusMsg(); session.sendDataToDevice(ESPConstants.HANDLER_PROV_CONFIG, message, new ResponseListener() { @Override public void onSuccess(byte[] returnData) { Object[] statuses = processProvisioningStatusResponse(returnData); WifiConstants.WifiStationState wifiStationState = (WifiConstants.WifiStationState) statuses[0]; WifiConstants.WifiConnectFailedReason failedReason = (WifiConstants.WifiConnectFailedReason) statuses[1]; if (wifiStationState == WifiConstants.WifiStationState.Connected) { // Provision success if (provisionListener != null) { provisionListener.deviceProvisioningSuccess(); } session = null; disableOnlyWifiNetwork(); } else if (wifiStationState == WifiConstants.WifiStationState.Disconnected) { // Device disconnected but Provision may got success / failure if (provisionListener != null) { provisionListener.provisioningFailedFromDevice(ESPConstants.ProvisionFailureReason.DEVICE_DISCONNECTED); } session = null; disableOnlyWifiNetwork(); } else if (wifiStationState == WifiConstants.WifiStationState.Connecting) { try { sleep(5000); pollForWifiConnectionStatus(); } catch (InterruptedException e) { e.printStackTrace(); session = null; disableOnlyWifiNetwork(); provisionListener.onProvisioningFailed(new RuntimeException("Provisioning Failed")); } } else { if (failedReason == WifiConstants.WifiConnectFailedReason.AuthError) { provisionListener.provisioningFailedFromDevice(ESPConstants.ProvisionFailureReason.AUTH_FAILED); } else if (failedReason == WifiConstants.WifiConnectFailedReason.NetworkNotFound) { provisionListener.provisioningFailedFromDevice(ESPConstants.ProvisionFailureReason.NETWORK_NOT_FOUND); } else { provisionListener.provisioningFailedFromDevice(ESPConstants.ProvisionFailureReason.UNKNOWN); } session = null; disableOnlyWifiNetwork(); } } @Override public void onFailure(Exception e) { e.printStackTrace(); disableOnlyWifiNetwork(); provisionListener.onProvisioningFailed(new RuntimeException("Provisioning Failed")); } }); } private void processStartScanResponse(byte[] responseData) { Log.d(TAG, "Process Wi-Fi start scan command response"); try { WifiScan.WiFiScanPayload payload = WifiScan.WiFiScanPayload.parseFrom(responseData); WifiScan.RespScanStart response = WifiScan.RespScanStart.parseFrom(payload.toByteArray()); // TODO Proto should send status as ok started or failed } catch (InvalidProtocolBufferException e) { e.printStackTrace(); } } private void processWifiStatusResponse(byte[] responseData) { Log.d(TAG, "Process Wi-Fi scan status command response"); try { WifiScan.WiFiScanPayload payload = WifiScan.WiFiScanPayload.parseFrom(responseData); WifiScan.RespScanStatus response = payload.getRespScanStatus(); boolean scanFinished = response.getScanFinished(); if (scanFinished) { totalCount = response.getResultCount(); getFullWiFiList(); } else { // TODO Error case } } catch (InvalidProtocolBufferException e) { e.printStackTrace(); if (wifiScanListener != null) { wifiScanListener.onWiFiScanFailed(new RuntimeException("Failed to get Wi-Fi status.")); } } } private void processGetSSIDs(byte[] responseData) { try { WifiScan.WiFiScanPayload payload = WifiScan.WiFiScanPayload.parseFrom(responseData); final WifiScan.RespScanResult response = payload.getRespScanResult(); Log.e(TAG, "Response count : " + response.getEntriesCount()); for (int i = 0; i < response.getEntriesCount(); i++) { Log.e(TAG, "SSID : " + response.getEntries(i).getSsid().toStringUtf8()); String ssid = response.getEntries(i).getSsid().toStringUtf8(); int rssi = response.getEntries(i).getRssi(); boolean isAvailable = false; for (int index = 0; index < wifiApList.size(); index++) { if (ssid.equals(wifiApList.get(index).getWifiName())) { isAvailable = true; if (wifiApList.get(index).getRssi() < rssi) { wifiApList.get(index).setRssi(rssi); } break; } } if (!isAvailable) { WiFiAccessPoint wifiAp = new WiFiAccessPoint(); wifiAp.setWifiName(ssid); wifiAp.setRssi(response.getEntries(i).getRssi()); wifiAp.setSecurity(response.getEntries(i).getAuthValue()); wifiApList.add(wifiAp); } Log.e(TAG, "Size of list : " + wifiApList.size()); } startIndex = startIndex + 4; int temp = totalCount - startIndex; if (temp > 0) { getFullWiFiList(); } else { Log.e(TAG, "Wi-Fi LIST Completed"); completeWifiList(); } } catch (InvalidProtocolBufferException e) { e.printStackTrace(); } } private Constants.Status processWifiConfigResponse(byte[] responseData) { Constants.Status status = Constants.Status.InvalidSession; try { WifiConfig.WiFiConfigPayload wiFiConfigPayload = WifiConfig.WiFiConfigPayload.parseFrom(responseData); status = wiFiConfigPayload.getRespSetConfig().getStatus(); } catch (InvalidProtocolBufferException e) { e.printStackTrace(); } return status; } private Constants.Status processApplyConfigResponse(byte[] responseData) { Constants.Status status = Constants.Status.InvalidSession; try { WifiConfig.WiFiConfigPayload wiFiConfigPayload = WifiConfig.WiFiConfigPayload.parseFrom(responseData); status = wiFiConfigPayload.getRespApplyConfig().getStatus(); } catch (InvalidProtocolBufferException e) { e.printStackTrace(); } return status; } private Object[] processProvisioningStatusResponse(byte[] responseData) { WifiConstants.WifiStationState wifiStationState = WifiConstants.WifiStationState.Disconnected; WifiConstants.WifiConnectFailedReason failedReason = WifiConstants.WifiConnectFailedReason.UNRECOGNIZED; if (responseData == null) { return new Object[]{wifiStationState, failedReason}; } try { WifiConfig.WiFiConfigPayload wiFiConfigPayload = WifiConfig.WiFiConfigPayload.parseFrom(responseData); wifiStationState = wiFiConfigPayload.getRespGetStatus().getStaState(); failedReason = wiFiConfigPayload.getRespGetStatus().getFailReason(); } catch (InvalidProtocolBufferException e) { e.printStackTrace(); } return new Object[]{wifiStationState, failedReason}; } private int deviceConnectionReqCount = 0; @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE) private void enableOnlyWifiNetwork() { Log.d(TAG, "enableOnlyWifiNetwork()"); NetworkRequest.Builder request = new NetworkRequest.Builder(); request.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); networkCallback = new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(Network network) { Log.e(TAG, "Network is available - 3"); connectivityManager.bindProcessToNetwork(network); } @Override public void onUnavailable() { super.onUnavailable(); Log.e(TAG, "Network is Unavailable - 3"); } @Override public void onLost(@NonNull Network network) { super.onLost(network); Log.e(TAG, "Lost Network Connection - 3"); } }; connectivityManager.registerNetworkCallback(request.build(), networkCallback); } private void disableOnlyWifiNetwork() { Log.d(TAG, "disableOnlyWifiNetwork()"); if (connectivityManager != null) { try { connectivityManager.bindProcessToNetwork(null); connectivityManager.unregisterNetworkCallback(networkCallback); } catch (Exception e) { Log.e(TAG, "Connectivity Manager is already unregistered"); } } } private Runnable getCapabilitiesTask = new Runnable() { @Override public void run() { Log.d(TAG, "Connecting to device"); deviceConnectionReqCount++; String tempData = "ESP"; transport.sendConfigData(ESPConstants.HANDLER_PROTO_VER, tempData.getBytes(), new ResponseListener() { @RequiresPermission(allOf = {Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.ACCESS_WIFI_STATE}) @Override public void onSuccess(byte[] returnData) { String data = new String(returnData, StandardCharsets.UTF_8); Log.d(TAG, "Value : " + data); deviceCapabilities = new ArrayList<>(); try { JSONObject jsonObject = new JSONObject(data); JSONObject provInfo = jsonObject.getJSONObject("prov"); String versionInfo = provInfo.getString("ver"); Log.d(TAG, "Device Version : " + versionInfo); JSONArray capabilities = provInfo.getJSONArray("cap"); for (int i = 0; i < capabilities.length(); i++) { String cap = capabilities.getString(i); deviceCapabilities.add(cap); } deviceName = fetchWiFiSSID(); Log.d(TAG, "Capabilities : " + deviceCapabilities); } catch (JSONException e) { e.printStackTrace(); Log.d(TAG, "Capabilities JSON not available."); } handler.removeCallbacks(wifiConnectionFailedTask); EventBus.getDefault().post(new DeviceConnectionEvent(ESPConstants.EVENT_DEVICE_CONNECTED)); } @Override public void onFailure(Exception e) { e.printStackTrace(); if (deviceConnectionReqCount == 3) { handler.removeCallbacks(getCapabilitiesTask); sendDeviceConnectionFailure(); } else { getCapabilitiesFromDevice(); } } }); } }; private Runnable deviceConnectionFailedTask = new Runnable() { @Override public void run() { handler.removeCallbacks(getCapabilitiesTask); handler.removeCallbacks(deviceConnectionFailedTask); Log.e(TAG, "deviceConnectionFailedTask"); EventBus.getDefault().post(new DeviceConnectionEvent(ESPConstants.EVENT_DEVICE_CONNECTION_FAILED)); } }; private void sendDeviceConnectionFailure() { handler.postDelayed(deviceConnectionFailedTask, 1000); } private void getCapabilitiesFromDevice() { handler.removeCallbacks(getCapabilitiesTask); handler.postDelayed(getCapabilitiesTask, 100); } private void scheduleWiFiConnectionFailure() { Log.e(TAG, "Schedule wifiConnectionFailedTask"); handler.postDelayed(wifiConnectionFailedTask, 12000); } @RequiresPermission(allOf = {Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.ACCESS_WIFI_STATE}) private String fetchWiFiSSID() { String ssid = null; WifiInfo wifiInfo = wifiManager.getConnectionInfo(); if (wifiInfo.getSupplicantState() == SupplicantState.COMPLETED) { ssid = wifiInfo.getSSID(); ssid = ssid.replace("\"", ""); } Log.e(TAG, "Returning ssid : " + ssid); return ssid; } private Runnable wifiConnectionFailedTask = new Runnable() { @Override public void run() { handler.removeCallbacks(task); Log.e(TAG, "wifiConnectionFailedTask"); EventBus.getDefault().post(new DeviceConnectionEvent(ESPConstants.EVENT_DEVICE_CONNECTION_FAILED)); } }; private class FetchNetworkName implements Runnable { private String ssid; FetchNetworkName(String ssid) { this.ssid = ssid; } @Override @RequiresPermission(allOf = {Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.ACCESS_WIFI_STATE}) public void run() { String networkName = fetchWiFiSSID(); Log.d(TAG, "Fetch SSID : " + networkName); Log.d(TAG, "SSID : " + ssid); if (!TextUtils.isEmpty(ssid) && !TextUtils.isEmpty(networkName) && ssid.equals(networkName)) { try { Thread.sleep(2500); } catch (InterruptedException e) { e.printStackTrace(); } Log.e(TAG, "Removed wifiConnectionFailedTask"); handler.removeCallbacks(wifiConnectionFailedTask); deviceName = ssid; getCapabilitiesFromDevice(); } else { handler.removeCallbacks(task); checkDeviceConnection(ssid); } } } FetchNetworkName task; private void checkDeviceConnection(String ssid) { task = new FetchNetworkName(ssid); handler.postDelayed(task, 2000); } }