From 94055b52c83816052fdd1670fe77ace2202cd8fe Mon Sep 17 00:00:00 2001 From: Khushbu Shah Date: Fri, 21 Jun 2024 11:48:33 +0530 Subject: [PATCH 1/2] Added thread provisioning support in library. --- provisioning/build.gradle | 5 +- .../com/espressif/provisioning/ESPDevice.java | 499 +++++++++++++++++- .../provisioning/utils/MessengeHelper.java | 158 +++++- .../src/main/proto/network_config.proto | 96 ++++ ...onstants.proto => network_constants.proto} | 21 +- .../src/main/proto/network_ctrl.proto | 57 ++ .../src/main/proto/network_scan.proto | 110 ++++ provisioning/src/main/proto/wifi_config.proto | 58 -- provisioning/src/main/proto/wifi_scan.proto | 62 --- 9 files changed, 889 insertions(+), 177 deletions(-) create mode 100644 provisioning/src/main/proto/network_config.proto rename provisioning/src/main/proto/{wifi_constants.proto => network_constants.proto} (60%) create mode 100644 provisioning/src/main/proto/network_ctrl.proto create mode 100644 provisioning/src/main/proto/network_scan.proto delete mode 100644 provisioning/src/main/proto/wifi_config.proto delete mode 100644 provisioning/src/main/proto/wifi_scan.proto diff --git a/provisioning/build.gradle b/provisioning/build.gradle index b02eff9..c25f18a 100644 --- a/provisioning/build.gradle +++ b/provisioning/build.gradle @@ -18,12 +18,11 @@ allprojects { } android { - compileSdkVersion 33 - buildToolsVersion "30.0.3" + compileSdkVersion 34 defaultConfig { minSdkVersion 23 - targetSdkVersion 33 + targetSdkVersion 34 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles 'consumer-rules.pro' diff --git a/provisioning/src/main/java/com/espressif/provisioning/ESPDevice.java b/provisioning/src/main/java/com/espressif/provisioning/ESPDevice.java index ed6217e..f7d9436 100644 --- a/provisioning/src/main/java/com/espressif/provisioning/ESPDevice.java +++ b/provisioning/src/main/java/com/espressif/provisioning/ESPDevice.java @@ -61,9 +61,9 @@ import java.util.UUID; import espressif.Constants; -import espressif.WifiConfig; -import espressif.WifiConstants; -import espressif.WifiScan; +import espressif.NetworkConfig; +import espressif.NetworkConstants; +import espressif.NetworkScan; /** * ESPDevice class to hold device information. This will give facility to connect device, send data to device and @@ -520,6 +520,38 @@ public void onFailure(Exception e) { } } + /** + * Send scan command to device to get available Thread networks. + * + * @param wifiScanListener WiFiScanListener to get callbacks of scanning networks. + */ + public void scanThreadNetworks(final WiFiScanListener wifiScanListener) { + + Log.d(TAG, "Send Thread scan command to device"); + this.wifiScanListener = wifiScanListener; + + if (session == null || !session.isEstablished()) { + + initSession(new ResponseListener() { + + @Override + public void onSuccess(byte[] returnData) { + startThreadNetworkScan(); + } + + @Override + public void onFailure(Exception e) { + e.printStackTrace(); + if (wifiScanListener != null) { + wifiScanListener.onWiFiScanFailed(new RuntimeException("Failed to create session.")); + } + } + }); + } else { + startThreadNetworkScan(); + } + } + /** * Send data to custom endpoint of the device. * @@ -587,6 +619,39 @@ public void onFailure(Exception e) { } } + /** + * Send Thread credentials to device for provisioning. + * + * @param activeDataset Thread dataset of a thread network which is to be configure in device. + * @param provisionListener Listener for provisioning callbacks. + */ + public void provision(final String activeDataset, final ProvisionListener provisionListener) { + + this.provisionListener = provisionListener; + + if (session == null || !session.isEstablished()) { + + initSession(new ResponseListener() { + + @Override + public void onSuccess(byte[] returnData) { + sendThreadConfig(activeDataset, provisionListener); + } + + @Override + public void onFailure(Exception e) { + e.printStackTrace(); + disableOnlyWifiNetwork(); + if (provisionListener != null) { + provisionListener.createSessionFailed(new RuntimeException("Failed to create session.")); + } + } + }); + } else { + sendThreadConfig(activeDataset, provisionListener); + } + } + public void initSession(final ResponseListener listener) { try { @@ -724,6 +789,49 @@ public void onFailure(Exception e) { }); } + private void startThreadNetworkScan() { + + Log.d(TAG, "Start thread network scan"); + totalCount = 0; + startIndex = 0; + wifiApList = new ArrayList<>(); + byte[] scanCommand = MessengeHelper.prepareThreadScanMsg(); + + session.sendDataToDevice(ESPConstants.HANDLER_PROV_SCAN, scanCommand, new ResponseListener() { + + @Override + public void onSuccess(byte[] returnData) { + + processStartThreadScanResponse(returnData); + + byte[] getScanStatusCmd = MessengeHelper.prepareGetThreadScanStatusMsg(); + session.sendDataToDevice(ESPConstants.HANDLER_PROV_SCAN, getScanStatusCmd, new ResponseListener() { + + @Override + public void onSuccess(byte[] returnData) { + processThreadStatusResponse(returnData); + } + + @Override + public void onFailure(Exception e) { + e.printStackTrace(); + if (wifiScanListener != null) { + wifiScanListener.onWiFiScanFailed(new RuntimeException("Failed to send thread scan command.")); + } + } + }); + } + + @Override + public void onFailure(Exception e) { + e.printStackTrace(); + if (wifiScanListener != null) { + wifiScanListener.onWiFiScanFailed(new RuntimeException("Failed to send thread scan command.")); + } + } + }); + } + private void getFullWiFiList() { Log.d(TAG, "Total count : " + totalCount + " and start index is : " + startIndex); @@ -751,6 +859,33 @@ private void getFullWiFiList() { } } + private void getFullThreadList() { + + Log.d(TAG, "Total count : " + totalCount + " and start index is : " + startIndex); + + if (totalCount < 4) { + + getThreadScanList(0, totalCount); + + } else { + + int temp = totalCount - startIndex; + + if (temp > 0) { + + if (temp > 4) { + getThreadScanList(startIndex, 4); + } else { + getThreadScanList(startIndex, temp); + } + + } else { + Log.d(TAG, "Nothing to do. Thread list completed."); + completeWifiList(); + } + } + } + private void getWiFiScanList(int start, int count) { Log.d(TAG, "Getting " + count + " SSIDs"); @@ -779,6 +914,34 @@ public void onFailure(Exception e) { }); } + private void getThreadScanList(int start, int count) { + + Log.d(TAG, "Getting " + count + " Thread networks"); + + if (count <= 0) { + completeWifiList(); + return; + } + + byte[] data = MessengeHelper.prepareGetThreadScanListMsg(start, count); + session.sendDataToDevice(ESPConstants.HANDLER_PROV_SCAN, data, new ResponseListener() { + + @Override + public void onSuccess(byte[] returnData) { + Log.d(TAG, "Successfully got thread networks"); + processGetThreadNetworks(returnData); + } + + @Override + public void onFailure(Exception e) { + e.printStackTrace(); + if (wifiScanListener != null) { + wifiScanListener.onWiFiScanFailed(new RuntimeException("Failed to get Thread Networks.")); + } + } + }); + } + private void completeWifiList() { if (wifiScanListener != null) { @@ -822,6 +985,42 @@ public void onFailure(Exception e) { }); } + private void sendThreadConfig(final String activeDataset, final ProvisionListener provisionListener) { + + byte[] scanCommand = MessengeHelper.prepareThreadConfigMsg(activeDataset); + + session.sendDataToDevice(ESPConstants.HANDLER_PROV_CONFIG, scanCommand, new ResponseListener() { + + @Override + public void onSuccess(byte[] returnData) { + + Constants.Status status = processThreadConfigResponse(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) { + applyThreadConfig(); + } else { + disableOnlyWifiNetwork(); + } + } + + @Override + public void onFailure(Exception e) { + e.printStackTrace(); + disableOnlyWifiNetwork(); + if (provisionListener != null) { + provisionListener.wifiConfigFailed(new RuntimeException("Failed to send thread credentials to device")); + } + } + }); + } + private void applyWiFiConfig() { byte[] scanCommand = MessengeHelper.prepareApplyWiFiConfigMsg(); @@ -863,6 +1062,47 @@ public void onFailure(Exception e) { }); } + private void applyThreadConfig() { + + byte[] scanCommand = MessengeHelper.prepareApplyThreadConfigMsg(); + + session.sendDataToDevice(ESPConstants.HANDLER_PROV_CONFIG, scanCommand, new ResponseListener() { + + @Override + public void onSuccess(byte[] returnData) { + + Constants.Status status = processApplyThreadConfigResponse(returnData); + + if (status == Constants.Status.Success) { + if (provisionListener != null) { + provisionListener.wifiConfigApplied(); + } + + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + pollForThreadConnectionStatus(); + } else { + disableOnlyWifiNetwork(); + if (provisionListener != null) { + provisionListener.wifiConfigApplyFailed(new RuntimeException("Failed to apply thread credentials")); + } + } + } + + @Override + public void onFailure(Exception e) { + e.printStackTrace(); + disableOnlyWifiNetwork(); + if (provisionListener != null) { + provisionListener.wifiConfigApplyFailed(new RuntimeException("Failed to apply thread credentials")); + } + } + }); + } + private void pollForWifiConnectionStatus() { byte[] message = MessengeHelper.prepareGetWiFiConfigStatusMsg(); @@ -872,10 +1112,10 @@ private void pollForWifiConnectionStatus() { public void onSuccess(byte[] returnData) { Object[] statuses = processProvisioningStatusResponse(returnData); - WifiConstants.WifiStationState wifiStationState = (WifiConstants.WifiStationState) statuses[0]; - WifiConstants.WifiConnectFailedReason failedReason = (WifiConstants.WifiConnectFailedReason) statuses[1]; + NetworkConstants.WifiStationState wifiStationState = (NetworkConstants.WifiStationState) statuses[0]; + NetworkConstants.WifiConnectFailedReason failedReason = (NetworkConstants.WifiConnectFailedReason) statuses[1]; - if (wifiStationState == WifiConstants.WifiStationState.Connected) { + if (wifiStationState == NetworkConstants.WifiStationState.Connected) { // Provision success if (provisionListener != null) { @@ -884,7 +1124,7 @@ public void onSuccess(byte[] returnData) { session = null; disableOnlyWifiNetwork(); - } else if (wifiStationState == WifiConstants.WifiStationState.Disconnected) { + } else if (wifiStationState == NetworkConstants.WifiStationState.Disconnected) { // Device disconnected but Provision may got success / failure if (provisionListener != null) { @@ -893,7 +1133,7 @@ public void onSuccess(byte[] returnData) { session = null; disableOnlyWifiNetwork(); - } else if (wifiStationState == WifiConstants.WifiStationState.Connecting) { + } else if (wifiStationState == NetworkConstants.WifiStationState.Connecting) { try { sleep(5000); @@ -906,11 +1146,79 @@ public void onSuccess(byte[] returnData) { } } else { - if (failedReason == WifiConstants.WifiConnectFailedReason.AuthError) { + if (failedReason == NetworkConstants.WifiConnectFailedReason.AuthError) { provisionListener.provisioningFailedFromDevice(ESPConstants.ProvisionFailureReason.AUTH_FAILED); - } else if (failedReason == WifiConstants.WifiConnectFailedReason.NetworkNotFound) { + } else if (failedReason == NetworkConstants.WifiConnectFailedReason.WifiNetworkNotFound) { + + 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 pollForThreadConnectionStatus() { + + byte[] message = MessengeHelper.prepareGetThreadConfigStatusMsg(); + session.sendDataToDevice(ESPConstants.HANDLER_PROV_CONFIG, message, new ResponseListener() { + + @Override + public void onSuccess(byte[] returnData) { + + Object[] statuses = processThreadProvisioningStatusResponse(returnData); + NetworkConstants.ThreadNetworkState threadNetworkState = (NetworkConstants.ThreadNetworkState) statuses[0]; + NetworkConstants.ThreadAttachFailedReason failedReason = (NetworkConstants.ThreadAttachFailedReason) statuses[1]; + + if (threadNetworkState == NetworkConstants.ThreadNetworkState.Attached) { + + // Provision success + if (provisionListener != null) { + provisionListener.deviceProvisioningSuccess(); + } + session = null; + disableOnlyWifiNetwork(); + + } else if (threadNetworkState == NetworkConstants.ThreadNetworkState.Dettached) { + + // Device disconnected but Provision may got success / failure + if (provisionListener != null) { + provisionListener.provisioningFailedFromDevice(ESPConstants.ProvisionFailureReason.DEVICE_DISCONNECTED); + } + session = null; + disableOnlyWifiNetwork(); + + } else if (threadNetworkState == NetworkConstants.ThreadNetworkState.Attaching) { + + try { + sleep(5000); + pollForThreadConnectionStatus(); + } catch (InterruptedException e) { + e.printStackTrace(); + session = null; + disableOnlyWifiNetwork(); + provisionListener.onProvisioningFailed(new RuntimeException("Provisioning Failed")); + } + } else { + + if (failedReason == NetworkConstants.ThreadAttachFailedReason.DatasetInvalid) { + + provisionListener.provisioningFailedFromDevice(ESPConstants.ProvisionFailureReason.AUTH_FAILED); + + } else if (failedReason == NetworkConstants.ThreadAttachFailedReason.ThreadNetworkNotFound) { provisionListener.provisioningFailedFromDevice(ESPConstants.ProvisionFailureReason.NETWORK_NOT_FOUND); @@ -936,8 +1244,21 @@ 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()); + NetworkScan.NetworkScanPayload payload = NetworkScan.NetworkScanPayload.parseFrom(responseData); + NetworkScan.RespScanWifiStart response = NetworkScan.RespScanWifiStart.parseFrom(payload.toByteArray()); + // TODO Proto should send status as ok started or failed + } catch (InvalidProtocolBufferException e) { + e.printStackTrace(); + } + } + + private void processStartThreadScanResponse(byte[] responseData) { + + Log.d(TAG, "Process Thread start scan command response"); + + try { + NetworkScan.NetworkScanPayload payload = NetworkScan.NetworkScanPayload.parseFrom(responseData); + NetworkScan.RespScanThreadStart response = NetworkScan.RespScanThreadStart.parseFrom(payload.toByteArray()); // TODO Proto should send status as ok started or failed } catch (InvalidProtocolBufferException e) { e.printStackTrace(); @@ -948,9 +1269,10 @@ 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(); + NetworkScan.NetworkScanPayload payload = NetworkScan.NetworkScanPayload.parseFrom(responseData); + NetworkScan.RespScanWifiStatus response = payload.getRespScanWifiStatus(); boolean scanFinished = response.getScanFinished(); + Log.d(TAG, "scanFinished : " + scanFinished); if (scanFinished) { totalCount = response.getResultCount(); @@ -968,11 +1290,37 @@ private void processWifiStatusResponse(byte[] responseData) { } } + private void processThreadStatusResponse(byte[] responseData) { + + Log.d(TAG, "Process Thread scan status command response"); + try { + NetworkScan.NetworkScanPayload payload = NetworkScan.NetworkScanPayload.parseFrom(responseData); + NetworkScan.RespScanThreadStatus response = payload.getRespScanThreadStatus(); + boolean scanFinished = response.getScanFinished(); + Log.d(TAG, "scanFinished : " + scanFinished); + + if (scanFinished) { + totalCount = response.getResultCount(); + getFullThreadList(); + ; + } 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(); + NetworkScan.NetworkScanPayload payload = NetworkScan.NetworkScanPayload.parseFrom(responseData); + final NetworkScan.RespScanWifiResult response = payload.getRespScanWifiResult(); Log.d(TAG, "Response count : " + response.getEntriesCount()); @@ -1028,12 +1376,83 @@ private void processGetSSIDs(byte[] responseData) { } } + private void processGetThreadNetworks(byte[] responseData) { + + try { + NetworkScan.NetworkScanPayload payload = NetworkScan.NetworkScanPayload.parseFrom(responseData); + final NetworkScan.RespScanThreadResult response = payload.getRespScanThreadResult(); + + Log.d(TAG, "Response count : " + response.getEntriesCount()); + + for (int i = 0; i < response.getEntriesCount(); i++) { + + Log.d(TAG, "Network Name : " + response.getEntries(i).getNetworkName()); + String ssid = response.getEntries(i).getNetworkName(); + 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(0); + wifiApList.add(wifiAp); + } + + Log.d(TAG, "Size of list : " + wifiApList.size()); + } + + startIndex = startIndex + 4; + + int temp = totalCount - startIndex; + + if (temp > 0) { + + getFullThreadList(); + + } else { + + Log.e(TAG, "Thread 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(); + NetworkConfig.NetworkConfigPayload wiFiConfigPayload = NetworkConfig.NetworkConfigPayload.parseFrom(responseData); + status = wiFiConfigPayload.getRespSetWifiConfig().getStatus(); + } catch (InvalidProtocolBufferException e) { + e.printStackTrace(); + } + return status; + } + + private Constants.Status processThreadConfigResponse(byte[] responseData) { + + Constants.Status status = Constants.Status.InvalidSession; + try { + NetworkConfig.NetworkConfigPayload wiFiConfigPayload = NetworkConfig.NetworkConfigPayload.parseFrom(responseData); + status = wiFiConfigPayload.getRespSetThreadConfig().getStatus(); } catch (InvalidProtocolBufferException e) { e.printStackTrace(); } @@ -1043,8 +1462,19 @@ private Constants.Status processWifiConfigResponse(byte[] responseData) { private Constants.Status processApplyConfigResponse(byte[] responseData) { Constants.Status status = Constants.Status.InvalidSession; try { - WifiConfig.WiFiConfigPayload wiFiConfigPayload = WifiConfig.WiFiConfigPayload.parseFrom(responseData); - status = wiFiConfigPayload.getRespApplyConfig().getStatus(); + NetworkConfig.NetworkConfigPayload wiFiConfigPayload = NetworkConfig.NetworkConfigPayload.parseFrom(responseData); + status = wiFiConfigPayload.getRespApplyWifiConfig().getStatus(); + } catch (InvalidProtocolBufferException e) { + e.printStackTrace(); + } + return status; + } + + private Constants.Status processApplyThreadConfigResponse(byte[] responseData) { + Constants.Status status = Constants.Status.InvalidSession; + try { + NetworkConfig.NetworkConfigPayload threadConfigPayload = NetworkConfig.NetworkConfigPayload.parseFrom(responseData); + status = threadConfigPayload.getRespApplyThreadConfig().getStatus(); } catch (InvalidProtocolBufferException e) { e.printStackTrace(); } @@ -1053,23 +1483,42 @@ private Constants.Status processApplyConfigResponse(byte[] responseData) { private Object[] processProvisioningStatusResponse(byte[] responseData) { - WifiConstants.WifiStationState wifiStationState = WifiConstants.WifiStationState.Disconnected; - WifiConstants.WifiConnectFailedReason failedReason = WifiConstants.WifiConnectFailedReason.UNRECOGNIZED; + NetworkConstants.WifiStationState wifiStationState = NetworkConstants.WifiStationState.Disconnected; + NetworkConstants.WifiConnectFailedReason failedReason = NetworkConstants.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(); + NetworkConfig.NetworkConfigPayload wiFiConfigPayload = NetworkConfig.NetworkConfigPayload.parseFrom(responseData); + wifiStationState = wiFiConfigPayload.getRespGetWifiStatus().getWifiStaState(); + failedReason = wiFiConfigPayload.getRespGetWifiStatus().getWifiFailReason(); } catch (InvalidProtocolBufferException e) { e.printStackTrace(); } return new Object[]{wifiStationState, failedReason}; } + private Object[] processThreadProvisioningStatusResponse(byte[] responseData) { + + NetworkConstants.ThreadNetworkState threadNetworkState = NetworkConstants.ThreadNetworkState.Dettached; + NetworkConstants.ThreadAttachFailedReason failedReason = NetworkConstants.ThreadAttachFailedReason.UNRECOGNIZED; + + if (responseData == null) { + return new Object[]{threadNetworkState, failedReason}; + } + + try { + NetworkConfig.NetworkConfigPayload threadConfigPayload = NetworkConfig.NetworkConfigPayload.parseFrom(responseData); + threadNetworkState = threadConfigPayload.getRespGetThreadStatus().getThreadState(); + failedReason = threadConfigPayload.getRespGetThreadStatus().getThreadFailReason(); + } catch (InvalidProtocolBufferException e) { + e.printStackTrace(); + } + return new Object[]{threadNetworkState, failedReason}; + } + private int deviceConnectionReqCount = 0; @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE) diff --git a/provisioning/src/main/java/com/espressif/provisioning/utils/MessengeHelper.java b/provisioning/src/main/java/com/espressif/provisioning/utils/MessengeHelper.java index 6f2f88d..0911bbe 100644 --- a/provisioning/src/main/java/com/espressif/provisioning/utils/MessengeHelper.java +++ b/provisioning/src/main/java/com/espressif/provisioning/utils/MessengeHelper.java @@ -17,24 +17,42 @@ import com.google.protobuf.ByteString; -import espressif.WifiConfig; -import espressif.WifiScan; +import java.util.ArrayList; +import java.util.List; + +import espressif.NetworkConfig; +import espressif.NetworkScan; public class MessengeHelper { // Send Wi-Fi Scan command public static byte[] prepareWiFiScanMsg() { - WifiScan.CmdScanStart configRequest = WifiScan.CmdScanStart.newBuilder() + NetworkScan.CmdScanWifiStart configRequest = NetworkScan.CmdScanWifiStart.newBuilder() .setBlocking(true) .setPassive(false) .setGroupChannels(0) .setPeriodMs(120) .build(); - WifiScan.WiFiScanMsgType msgType = WifiScan.WiFiScanMsgType.TypeCmdScanStart; - WifiScan.WiFiScanPayload payload = WifiScan.WiFiScanPayload.newBuilder() + NetworkScan.NetworkScanMsgType msgType = NetworkScan.NetworkScanMsgType.TypeCmdScanWifiStart; + NetworkScan.NetworkScanPayload payload = NetworkScan.NetworkScanPayload.newBuilder() + .setMsg(msgType) + .setCmdScanWifiStart(configRequest) + .build(); + + return payload.toByteArray(); + } + + public static byte[] prepareThreadScanMsg() { + + NetworkScan.CmdScanThreadStart configRequest = NetworkScan.CmdScanThreadStart.newBuilder() + .setBlocking(true) + .setChannelMask(0) + .build(); + NetworkScan.NetworkScanMsgType msgType = NetworkScan.NetworkScanMsgType.TypeCmdScanThreadStart; + NetworkScan.NetworkScanPayload payload = NetworkScan.NetworkScanPayload.newBuilder() .setMsg(msgType) - .setCmdScanStart(configRequest) + .setCmdScanThreadStart(configRequest) .build(); return payload.toByteArray(); @@ -42,12 +60,24 @@ public static byte[] prepareWiFiScanMsg() { public static byte[] prepareGetWiFiScanStatusMsg() { - WifiScan.CmdScanStatus configRequest = WifiScan.CmdScanStatus.newBuilder() + NetworkScan.CmdScanWifiStatus configRequest = NetworkScan.CmdScanWifiStatus.newBuilder() + .build(); + NetworkScan.NetworkScanMsgType msgType = NetworkScan.NetworkScanMsgType.TypeCmdScanWifiStatus; + NetworkScan.NetworkScanPayload payload = NetworkScan.NetworkScanPayload.newBuilder() + .setMsg(msgType) + .setCmdScanWifiStatus(configRequest) + .build(); + return payload.toByteArray(); + } + + public static byte[] prepareGetThreadScanStatusMsg() { + + NetworkScan.CmdScanThreadStatus configRequest = NetworkScan.CmdScanThreadStatus.newBuilder() .build(); - WifiScan.WiFiScanMsgType msgType = WifiScan.WiFiScanMsgType.TypeCmdScanStatus; - WifiScan.WiFiScanPayload payload = WifiScan.WiFiScanPayload.newBuilder() + NetworkScan.NetworkScanMsgType msgType = NetworkScan.NetworkScanMsgType.TypeCmdScanThreadStatus; + NetworkScan.NetworkScanPayload payload = NetworkScan.NetworkScanPayload.newBuilder() .setMsg(msgType) - .setCmdScanStatus(configRequest) + .setCmdScanThreadStatus(configRequest) .build(); return payload.toByteArray(); } @@ -55,14 +85,29 @@ public static byte[] prepareGetWiFiScanStatusMsg() { // Get Wi-Fi scan list public static byte[] prepareGetWiFiScanListMsg(int start, int count) { - WifiScan.CmdScanResult configRequest = WifiScan.CmdScanResult.newBuilder() + NetworkScan.CmdScanWifiResult configRequest = NetworkScan.CmdScanWifiResult.newBuilder() + .setStartIndex(start) + .setCount(count) + .build(); + NetworkScan.NetworkScanMsgType msgType = NetworkScan.NetworkScanMsgType.TypeCmdScanWifiResult; + NetworkScan.NetworkScanPayload payload = NetworkScan.NetworkScanPayload.newBuilder() + .setMsg(msgType) + .setCmdScanWifiResult(configRequest) + .build(); + + return payload.toByteArray(); + } + + public static byte[] prepareGetThreadScanListMsg(int start, int count) { + + NetworkScan.CmdScanThreadResult configRequest = NetworkScan.CmdScanThreadResult.newBuilder() .setStartIndex(start) .setCount(count) .build(); - WifiScan.WiFiScanMsgType msgType = WifiScan.WiFiScanMsgType.TypeCmdScanResult; - WifiScan.WiFiScanPayload payload = WifiScan.WiFiScanPayload.newBuilder() + NetworkScan.NetworkScanMsgType msgType = NetworkScan.NetworkScanMsgType.TypeCmdScanThreadResult; + NetworkScan.NetworkScanPayload payload = NetworkScan.NetworkScanPayload.newBuilder() .setMsg(msgType) - .setCmdScanResult(configRequest) + .setCmdScanThreadResult(configRequest) .build(); return payload.toByteArray(); @@ -71,55 +116,112 @@ public static byte[] prepareGetWiFiScanListMsg(int start, int count) { // Send Wi-Fi Config public static byte[] prepareWiFiConfigMsg(String ssid, String passphrase) { - WifiConfig.CmdSetConfig cmdSetConfig; + NetworkConfig.CmdSetWifiConfig cmdSetConfig; if (passphrase != null) { - cmdSetConfig = WifiConfig.CmdSetConfig + cmdSetConfig = NetworkConfig.CmdSetWifiConfig .newBuilder() .setSsid(ByteString.copyFrom(ssid.getBytes())) .setPassphrase(ByteString.copyFrom(passphrase.getBytes())) .build(); } else { - cmdSetConfig = WifiConfig.CmdSetConfig + cmdSetConfig = NetworkConfig.CmdSetWifiConfig .newBuilder() .setSsid(ByteString.copyFrom(ssid.getBytes())) .build(); } - WifiConfig.WiFiConfigPayload wiFiConfigPayload = WifiConfig.WiFiConfigPayload + NetworkConfig.NetworkConfigPayload wiFiConfigPayload = NetworkConfig.NetworkConfigPayload .newBuilder() - .setCmdSetConfig(cmdSetConfig) - .setMsg(WifiConfig.WiFiConfigMsgType.TypeCmdSetConfig) + .setMsg(NetworkConfig.NetworkConfigMsgType.TypeCmdSetWifiConfig) + .setCmdSetWifiConfig(cmdSetConfig) .build(); return wiFiConfigPayload.toByteArray(); } + public static byte[] prepareThreadConfigMsg(String activeDataset) { + + byte[] dataset = dsToByteArray(activeDataset); + + NetworkConfig.CmdSetThreadConfig cmdSetConfig = NetworkConfig.CmdSetThreadConfig + .newBuilder() + .setDataset(ByteString.copyFrom(dataset)) + .build(); + NetworkConfig.NetworkConfigPayload threadConfigPayload = NetworkConfig.NetworkConfigPayload + .newBuilder() + .setMsg(NetworkConfig.NetworkConfigMsgType.TypeCmdSetThreadConfig) + .setCmdSetThreadConfig(cmdSetConfig) + .build(); + + return threadConfigPayload.toByteArray(); + } + + private static byte[] dsToByteArray(String input) { + List bytes = new ArrayList<>(); + for (int i = 0; i < input.length(); i += 2) { + String hex = input.substring(i, i + 2); + bytes.add((byte) Integer.parseInt(hex, 16)); + } + byte[] result = new byte[bytes.size()]; + for (int i = 0; i < bytes.size(); i++) { + result[i] = bytes.get(i); + } + return result; + } + // Apply Wi-Fi config public static byte[] prepareApplyWiFiConfigMsg() { - WifiConfig.CmdApplyConfig cmdApplyConfig = WifiConfig.CmdApplyConfig + NetworkConfig.CmdApplyWifiConfig cmdApplyConfig = NetworkConfig.CmdApplyWifiConfig .newBuilder() .build(); - WifiConfig.WiFiConfigPayload wiFiConfigPayload = WifiConfig.WiFiConfigPayload + NetworkConfig.NetworkConfigPayload wiFiConfigPayload = NetworkConfig.NetworkConfigPayload .newBuilder() - .setCmdApplyConfig(cmdApplyConfig) - .setMsg(WifiConfig.WiFiConfigMsgType.TypeCmdApplyConfig) + .setMsg(NetworkConfig.NetworkConfigMsgType.TypeCmdApplyWifiConfig) + .setCmdApplyWifiConfig(cmdApplyConfig) .build(); return wiFiConfigPayload.toByteArray(); } + public static byte[] prepareApplyThreadConfigMsg() { + + NetworkConfig.CmdApplyThreadConfig cmdApplyConfig = NetworkConfig.CmdApplyThreadConfig + .newBuilder() + .build(); + NetworkConfig.NetworkConfigPayload threadConfigPayload = NetworkConfig.NetworkConfigPayload + .newBuilder() + .setMsg(NetworkConfig.NetworkConfigMsgType.TypeCmdApplyThreadConfig) + .setCmdApplyThreadConfig(cmdApplyConfig) + .build(); + return threadConfigPayload.toByteArray(); + } + // Get Wi-Fi Config status public static byte[] prepareGetWiFiConfigStatusMsg() { - WifiConfig.CmdGetStatus cmdGetStatus = WifiConfig.CmdGetStatus + NetworkConfig.CmdGetWifiStatus cmdGetStatus = NetworkConfig.CmdGetWifiStatus .newBuilder() .build(); - WifiConfig.WiFiConfigPayload wiFiConfigPayload = WifiConfig.WiFiConfigPayload + NetworkConfig.NetworkConfigPayload wiFiConfigPayload = NetworkConfig.NetworkConfigPayload .newBuilder() - .setCmdGetStatus(cmdGetStatus) - .setMsg(WifiConfig.WiFiConfigMsgType.TypeCmdGetStatus) + .setMsg(NetworkConfig.NetworkConfigMsgType.TypeCmdGetWifiStatus) + .setCmdGetWifiStatus(cmdGetStatus) .build(); return wiFiConfigPayload.toByteArray(); } + + // Get Wi-Fi Config status + public static byte[] prepareGetThreadConfigStatusMsg() { + + NetworkConfig.CmdGetThreadStatus cmdGetStatus = NetworkConfig.CmdGetThreadStatus + .newBuilder() + .build(); + NetworkConfig.NetworkConfigPayload threadConfigPayload = NetworkConfig.NetworkConfigPayload + .newBuilder() + .setMsg(NetworkConfig.NetworkConfigMsgType.TypeCmdGetThreadStatus) + .setCmdGetThreadStatus(cmdGetStatus) + .build(); + return threadConfigPayload.toByteArray(); + } } diff --git a/provisioning/src/main/proto/network_config.proto b/provisioning/src/main/proto/network_config.proto new file mode 100644 index 0000000..946440e --- /dev/null +++ b/provisioning/src/main/proto/network_config.proto @@ -0,0 +1,96 @@ +syntax = "proto3"; +package espressif; + +import "constants.proto"; +import "network_constants.proto"; + +message CmdGetWifiStatus { +} + +message RespGetWifiStatus { + Status status = 1; + WifiStationState wifi_sta_state = 2; + oneof state { + WifiConnectFailedReason wifi_fail_reason = 10; + WifiConnectedState wifi_connected = 11; + } +} + +message CmdGetThreadStatus { +} + +message RespGetThreadStatus { + Status status = 1; + ThreadNetworkState thread_state = 2; + oneof state { + ThreadAttachFailedReason thread_fail_reason = 10; + ThreadAttachState thread_attached = 11; + } +} + +message CmdSetWifiConfig { + bytes ssid = 1; + bytes passphrase = 2; + bytes bssid = 3; + int32 channel = 4; +} + +message CmdSetThreadConfig { + bytes dataset = 1; +} + +message RespSetWifiConfig { + Status status = 1; +} + +message RespSetThreadConfig { + Status status = 1; +} + +message CmdApplyWifiConfig { +} + +message CmdApplyThreadConfig { +} + +message RespApplyWifiConfig { + Status status = 1; +} + +message RespApplyThreadConfig { + Status status = 1; +} + +enum NetworkConfigMsgType { + TypeCmdGetWifiStatus = 0; + TypeRespGetWifiStatus = 1; + TypeCmdSetWifiConfig = 2; + TypeRespSetWifiConfig = 3; + TypeCmdApplyWifiConfig = 4; + TypeRespApplyWifiConfig = 5; + TypeCmdGetThreadStatus = 6; + TypeRespGetThreadStatus = 7; + TypeCmdSetThreadConfig = 8; + TypeRespSetThreadConfig = 9; + TypeCmdApplyThreadConfig = 10; + TypeRespApplyThreadConfig = 11; + +} + +message NetworkConfigPayload { + NetworkConfigMsgType msg = 1; + oneof payload { + CmdGetWifiStatus cmd_get_wifi_status = 10; + RespGetWifiStatus resp_get_wifi_status = 11; + CmdSetWifiConfig cmd_set_wifi_config = 12; + RespSetWifiConfig resp_set_wifi_config = 13; + CmdApplyWifiConfig cmd_apply_wifi_config = 14; + RespApplyWifiConfig resp_apply_wifi_config = 15; + CmdGetThreadStatus cmd_get_thread_status = 16; + RespGetThreadStatus resp_get_thread_status = 17; + CmdSetThreadConfig cmd_set_thread_config = 18; + RespSetThreadConfig resp_set_thread_config = 19; + CmdApplyThreadConfig cmd_apply_thread_config = 20; + RespApplyThreadConfig resp_apply_thread_config = 21; + } +} diff --git a/provisioning/src/main/proto/wifi_constants.proto b/provisioning/src/main/proto/network_constants.proto similarity index 60% rename from provisioning/src/main/proto/wifi_constants.proto rename to provisioning/src/main/proto/network_constants.proto index ce1d305..3e3df72 100644 --- a/provisioning/src/main/proto/wifi_constants.proto +++ b/provisioning/src/main/proto/network_constants.proto @@ -10,7 +10,7 @@ enum WifiStationState { enum WifiConnectFailedReason { AuthError = 0; - NetworkNotFound = 1; + WifiNetworkNotFound = 1; } enum WifiAuthMode { @@ -31,3 +31,22 @@ message WifiConnectedState { bytes bssid = 4; int32 channel = 5; } + +enum ThreadNetworkState { + Attached = 0; + Attaching = 1; + Dettached = 2; + AttachingFailed = 3; +} + +enum ThreadAttachFailedReason { + DatasetInvalid = 0; + ThreadNetworkNotFound = 1; +} + +message ThreadAttachState { + uint32 pan_id = 1; + bytes ext_pan_id = 2; + uint32 channel = 3; + string name = 4; +} diff --git a/provisioning/src/main/proto/network_ctrl.proto b/provisioning/src/main/proto/network_ctrl.proto new file mode 100644 index 0000000..2578768 --- /dev/null +++ b/provisioning/src/main/proto/network_ctrl.proto @@ -0,0 +1,57 @@ +syntax = "proto3"; +package espressif; + +import "constants.proto"; +import "network_constants.proto"; + +message CmdCtrlWifiReset { +} + +message RespCtrlWifiReset { +} + +message CmdCtrlWifiReprov { +} + +message RespCtrlWifiReprov{ +} + +message CmdCtrlThreadReset { +} + +message RespCtrlThreadReset { +} + +message CmdCtrlThreadReprov { +} + +message RespCtrlThreadReprov{ +} + +enum NetworkCtrlMsgType { + TypeCtrlReserved = 0; + TypeCmdCtrlWifiReset = 1; + TypeRespCtrlWifiReset = 2; + TypeCmdCtrlWifiReprov = 3; + TypeRespCtrlWifiReprov = 4; + TypeCmdCtrlThreadReset = 5; + TypeRespCtrlThreadReset = 6; + TypeCmdCtrlThreadReprov = 7; + TypeRespCtrlThreadReprov = 8; + +} + +message NetworkCtrlPayload { + NetworkCtrlMsgType msg = 1; + Status status = 2; + oneof payload { + CmdCtrlWifiReset cmd_ctrl_wifi_reset = 11; + RespCtrlWifiReset resp_ctrl_wifi_reset = 12; + CmdCtrlWifiReprov cmd_ctrl_wifi_reprov = 13; + RespCtrlWifiReprov resp_ctrl_wifi_reprov = 14; + CmdCtrlThreadReset cmd_ctrl_thread_reset = 15; + RespCtrlThreadReset resp_ctrl_thread_reset = 16; + CmdCtrlThreadReprov cmd_ctrl_thread_reprov = 17; + RespCtrlThreadReprov resp_ctrl_thread_reprov = 18; + } +} diff --git a/provisioning/src/main/proto/network_scan.proto b/provisioning/src/main/proto/network_scan.proto new file mode 100644 index 0000000..7907a53 --- /dev/null +++ b/provisioning/src/main/proto/network_scan.proto @@ -0,0 +1,110 @@ +syntax = "proto3"; +package espressif; + +import "constants.proto"; +import "network_constants.proto"; + +message CmdScanWifiStart { + bool blocking = 1; + bool passive = 2; + uint32 group_channels = 3; + uint32 period_ms = 4; +} + +message CmdScanThreadStart { + bool blocking = 1; + uint32 channel_mask = 2; +} + +message RespScanWifiStart { +} + +message RespScanThreadStart { +} + +message CmdScanWifiStatus { +} + +message CmdScanThreadStatus { +} + +message RespScanWifiStatus { + bool scan_finished = 1; + uint32 result_count = 2; +} + +message RespScanThreadStatus { + bool scan_finished = 1; + uint32 result_count = 2; +} + +message CmdScanWifiResult { + uint32 start_index = 1; + uint32 count = 2; +} + +message CmdScanThreadResult { + uint32 start_index = 1; + uint32 count = 2; +} + +message WiFiScanResult { + bytes ssid = 1; + uint32 channel = 2; + int32 rssi = 3; + bytes bssid = 4; + WifiAuthMode auth = 5; +} + +message ThreadScanResult { + uint32 pan_id = 1; + uint32 channel = 2; + int32 rssi = 3; + uint32 lqi = 4; + bytes ext_addr = 5; + string network_name = 6; + bytes ext_pan_id = 7; +} + +message RespScanWifiResult { + repeated WiFiScanResult entries = 1; +} + +message RespScanThreadResult { + repeated ThreadScanResult entries = 1; +} + + +enum NetworkScanMsgType { + TypeCmdScanWifiStart = 0; + TypeRespScanWifiStart = 1; + TypeCmdScanWifiStatus = 2; + TypeRespScanWifiStatus = 3; + TypeCmdScanWifiResult = 4; + TypeRespScanWifiResult = 5; + TypeCmdScanThreadStart = 6; + TypeRespScanThreadStart = 7; + TypeCmdScanThreadStatus = 8; + TypeRespScanThreadStatus = 9; + TypeCmdScanThreadResult = 10; + TypeRespScanThreadResult = 11; +} + +message NetworkScanPayload { + NetworkScanMsgType msg = 1; + Status status = 2; + oneof payload { + CmdScanWifiStart cmd_scan_wifi_start = 10; + RespScanWifiStart resp_scan_wifi_start = 11; + CmdScanWifiStatus cmd_scan_wifi_status = 12; + RespScanWifiStatus resp_scan_wifi_status = 13; + CmdScanWifiResult cmd_scan_wifi_result = 14; + RespScanWifiResult resp_scan_wifi_result = 15; + CmdScanThreadStart cmd_scan_thread_start = 16; + RespScanThreadStart resp_scan_thread_start = 17; + CmdScanThreadStatus cmd_scan_thread_status = 18; + RespScanThreadStatus resp_scan_thread_status = 19; + CmdScanThreadResult cmd_scan_thread_result = 20; + RespScanThreadResult resp_scan_thread_result = 21; + } +} diff --git a/provisioning/src/main/proto/wifi_config.proto b/provisioning/src/main/proto/wifi_config.proto deleted file mode 100644 index dddfa5c..0000000 --- a/provisioning/src/main/proto/wifi_config.proto +++ /dev/null @@ -1,58 +0,0 @@ -syntax = "proto3"; -package espressif; - -import "constants.proto"; -import "wifi_constants.proto"; - -message CmdGetStatus { - -} - -message RespGetStatus { - Status status = 1; - WifiStationState sta_state = 2; - oneof state { - WifiConnectFailedReason fail_reason = 10; - WifiConnectedState connected = 11; - } -} - -message CmdSetConfig { - bytes ssid = 1; - bytes passphrase = 2; - bytes bssid = 3; - int32 channel = 4; -} - -message RespSetConfig { - Status status = 1; -} - -message CmdApplyConfig { - -} - -message RespApplyConfig { - Status status = 1; -} - -enum WiFiConfigMsgType { - TypeCmdGetStatus = 0; - TypeRespGetStatus = 1; - TypeCmdSetConfig = 2; - TypeRespSetConfig = 3; - TypeCmdApplyConfig = 4; - TypeRespApplyConfig = 5; -} - -message WiFiConfigPayload { - WiFiConfigMsgType msg = 1; - oneof payload { - CmdGetStatus cmd_get_status = 10; - RespGetStatus resp_get_status = 11; - CmdSetConfig cmd_set_config = 12; - RespSetConfig resp_set_config = 13; - CmdApplyConfig cmd_apply_config = 14; - RespApplyConfig resp_apply_config = 15; - } -} diff --git a/provisioning/src/main/proto/wifi_scan.proto b/provisioning/src/main/proto/wifi_scan.proto deleted file mode 100644 index d42b1a0..0000000 --- a/provisioning/src/main/proto/wifi_scan.proto +++ /dev/null @@ -1,62 +0,0 @@ -syntax = "proto3"; -package espressif; - -import "constants.proto"; -import "wifi_constants.proto"; - -message CmdScanStart { - bool blocking = 1; - bool passive = 2; - uint32 group_channels = 3; - uint32 period_ms = 4; -} - -message RespScanStart { -} - -message CmdScanStatus { -} - -message RespScanStatus { - bool scan_finished = 1; - uint32 result_count = 2; -} - -message CmdScanResult { - uint32 start_index = 1; - uint32 count = 2; -} - -message WiFiScanResult { - bytes ssid = 1; - uint32 channel = 2; - int32 rssi = 3; - bytes bssid = 4; - WifiAuthMode auth = 5; -} - -message RespScanResult { - repeated WiFiScanResult entries = 1; -} - -enum WiFiScanMsgType { - TypeCmdScanStart = 0; - TypeRespScanStart = 1; - TypeCmdScanStatus = 2; - TypeRespScanStatus = 3; - TypeCmdScanResult = 4; - TypeRespScanResult = 5; -} - -message WiFiScanPayload { - WiFiScanMsgType msg = 1; - Status status = 2; - oneof payload { - CmdScanStart cmd_scan_start = 10; - RespScanStart resp_scan_start = 11; - CmdScanStatus cmd_scan_status = 12; - RespScanStatus resp_scan_status = 13; - CmdScanResult cmd_scan_result = 14; - RespScanResult resp_scan_result = 15; - } -} From 6a2d30c192ff005223283c2b172d7f8d0e738b0a Mon Sep 17 00:00:00 2001 From: Khushbu Shah Date: Fri, 21 Jun 2024 14:06:37 +0530 Subject: [PATCH 2/2] Feature: Thread Provisioning Added support for thread device provisioning. --- README.md | 4 +- app/build.gradle | 12 +- .../espressif/ExampleInstrumentedTest.java | 11 +- app/src/main/AndroidManifest.xml | 5 + .../main/java/com/espressif/AppConstants.java | 12 +- .../ui/activities/AddDeviceActivity.java | 29 +- .../ui/activities/BLEProvisionLanding.java | 30 +- .../ui/activities/ManualProvBaseActivity.java | 13 +- .../activities/ProofOfPossessionActivity.java | 16 +- .../ui/activities/ProvisionActivity.java | 442 ++++++++++++------ .../ui/activities/ProvisionLanding.java | 46 +- .../ui/activities/SettingsActivity.java | 31 +- .../ui/activities/ThreadConfigActivity.java | 345 ++++++++++++++ .../java/com/espressif/ui/utils/Utils.java | 8 + .../res/layout/activity_thread_scan_list.xml | 78 ++++ app/src/main/res/values/strings.xml | 12 +- app/src/main/res/xml/preferences.xml | 12 +- 17 files changed, 910 insertions(+), 196 deletions(-) create mode 100644 app/src/main/java/com/espressif/ui/activities/ThreadConfigActivity.java create mode 100644 app/src/main/res/layout/activity_thread_scan_list.xml diff --git a/README.md b/README.md index 6a97cb9..6f016d7 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ To get this app please clone this repository using the below command: ## Requirements -- Supports Android 6.0 (API level 23) and above. +- Supports Android 8.0 (API level 26) and above. ## How to include @@ -48,7 +48,7 @@ To get this app please clone this repository using the below command: ``` And add a dependency code to your app module's `build.gradle` file. ``` - implementation 'com.github.espressif:esp-idf-provisioning-android:lib-2.1.4' + implementation 'com.github.espressif:esp-idf-provisioning-android:lib-2.2.0' ``` ## Using Provisioning Library diff --git a/app/build.gradle b/app/build.gradle index 080aa3d..89eae9d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,14 +11,14 @@ def getGitHash = { -> android { - compileSdkVersion 33 + compileSdkVersion 34 defaultConfig { applicationId "com.espressif.wifi_provisioning" - minSdkVersion 23 - targetSdkVersion 33 - versionCode 19 - versionName "2.1.4 - ${getGitHash()}" + minSdkVersion 26 + targetSdkVersion 34 + versionCode 20 + versionName "2.2.0 - ${getGitHash()}" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } @@ -65,6 +65,8 @@ dependencies { implementation 'com.github.yuriy-budiyev:code-scanner:2.1.2' implementation 'com.github.firdausmaulan:AVLoadingIndicatorView:2.3.0' + implementation 'com.google.android.gms:play-services-threadnetwork:16.0.1' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.4' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' diff --git a/app/src/androidTest/java/com/espressif/espressif/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/espressif/espressif/ExampleInstrumentedTest.java index 14a0fab..36a7bb9 100644 --- a/app/src/androidTest/java/com/espressif/espressif/ExampleInstrumentedTest.java +++ b/app/src/androidTest/java/com/espressif/espressif/ExampleInstrumentedTest.java @@ -13,15 +13,16 @@ // limitations under the License. package com.espressif.espressif; +import static org.junit.Assert.assertEquals; + import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; import org.junit.Test; import org.junit.runner.RunWith; -import static org.junit.Assert.*; - /** * Instrumented test, which will execute on an Android device. * @@ -32,7 +33,7 @@ public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); assertEquals("com.espressif.espressif", appContext.getPackageName()); } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b848ef7..b057cfc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -77,6 +77,11 @@ android:label="@string/title_activity_wifi_scan_list" android:screenOrientation="portrait" android:theme="@style/AppTheme.NoActionBar" /> + deviceCaps = provisionManager.getEspDevice().getDeviceCapabilities(); + + if (deviceCaps != null && deviceCaps.size() > 0 + && (deviceCaps.contains(AppConstants.CAPABILITY_THREAD_SCAN) || deviceCaps.contains(AppConstants.CAPABILITY_THREAD_PROV)) + ) { + String userName = sharedPreferences.getString(AppConstants.KEY_USER_NAME_THREAD, AppConstants.DEFAULT_USER_NAME_THREAD); + provisionManager.getEspDevice().setUserName(userName); + } else if (TextUtils.isEmpty(provisionManager.getEspDevice().getUserName())) { + String userName = sharedPreferences.getString(AppConstants.KEY_USER_NAME_WIFI, AppConstants.DEFAULT_USER_NAME_WIFI); + provisionManager.getEspDevice().setUserName(userName); + } break; } } else { @@ -754,8 +769,12 @@ private void setSecurityTypeFromVersionInfo() { private void processDeviceCapabilities() { ArrayList deviceCaps = espDevice.getDeviceCapabilities(); - if (deviceCaps.contains("wifi_scan")) { + if (deviceCaps.contains(AppConstants.CAPABILITY_WIFI_SCAN)) { goToWiFiScanActivity(); + } else if (deviceCaps.contains(AppConstants.CAPABILITY_THREAD_SCAN)) { + goToThreadScanActivity(true); + } else if (deviceCaps.contains(AppConstants.CAPABILITY_THREAD_PROV)) { + goToThreadScanActivity(false); } else { goToWiFiConfigActivity(); } diff --git a/app/src/main/java/com/espressif/ui/activities/BLEProvisionLanding.java b/app/src/main/java/com/espressif/ui/activities/BLEProvisionLanding.java index 904a552..1c372b0 100644 --- a/app/src/main/java/com/espressif/ui/activities/BLEProvisionLanding.java +++ b/app/src/main/java/com/espressif/ui/activities/BLEProvisionLanding.java @@ -325,16 +325,19 @@ private void requestLocationAndBtPermission() { private void processDeviceCapabilities() { ArrayList deviceCaps = provisionManager.getEspDevice().getDeviceCapabilities(); - if (deviceCaps != null && !deviceCaps.contains("no_pop") && securityType != AppConstants.SEC_TYPE_0) { - - goToPopActivity(); - - } else if (deviceCaps.contains("wifi_scan")) { - - goToWifiScanListActivity(); - + if (deviceCaps != null) { + if (!deviceCaps.contains("no_pop") && securityType != AppConstants.SEC_TYPE_0) { + goToPopActivity(); + } else if (deviceCaps.contains(AppConstants.CAPABILITY_WIFI_SCAN)) { + goToWifiScanListActivity(); + } else if (deviceCaps.contains(AppConstants.CAPABILITY_THREAD_SCAN)) { + goToThreadScanActivity(true); + } else if (deviceCaps.contains(AppConstants.CAPABILITY_THREAD_PROV)) { + goToThreadScanActivity(false); + } else { + goToWiFiConfigActivity(); + } } else { - goToWiFiConfigActivity(); } } @@ -568,6 +571,15 @@ private void goToWifiScanListActivity() { startActivity(wifiListIntent); } + private void goToThreadScanActivity(boolean scanCapAvailable) { + + finish(); + Intent threadConfigIntent = new Intent(getApplicationContext(), ThreadConfigActivity.class); + threadConfigIntent.putExtra(AppConstants.KEY_DEVICE_NAME, deviceList.get(position).getName()); + threadConfigIntent.putExtra(AppConstants.KEY_THREAD_SCAN_AVAILABLE, scanCapAvailable); + startActivity(threadConfigIntent); + } + private void goToWiFiConfigActivity() { finish(); diff --git a/app/src/main/java/com/espressif/ui/activities/ManualProvBaseActivity.java b/app/src/main/java/com/espressif/ui/activities/ManualProvBaseActivity.java index fc941e9..68a24a2 100644 --- a/app/src/main/java/com/espressif/ui/activities/ManualProvBaseActivity.java +++ b/app/src/main/java/com/espressif/ui/activities/ManualProvBaseActivity.java @@ -31,6 +31,8 @@ import org.json.JSONException; import org.json.JSONObject; +import java.util.ArrayList; + public class ManualProvBaseActivity extends AppCompatActivity { private static final String TAG = ManualProvBaseActivity.class.getSimpleName(); @@ -92,8 +94,15 @@ void setSecurityTypeFromVersionInfo() { default: securityType = AppConstants.SEC_TYPE_2; provisionManager.getEspDevice().setSecurityType(ESPConstants.SecurityType.SECURITY_2); - if (TextUtils.isEmpty(provisionManager.getEspDevice().getUserName())) { - String userName = sharedPreferences.getString(AppConstants.KEY_USER_NAME, AppConstants.DEFAULT_USER_NAME); + ArrayList deviceCaps = provisionManager.getEspDevice().getDeviceCapabilities(); + + if (deviceCaps != null && deviceCaps.size() > 0 + && (deviceCaps.contains(AppConstants.CAPABILITY_THREAD_SCAN) || deviceCaps.contains(AppConstants.CAPABILITY_THREAD_PROV)) + ) { + String userName = sharedPreferences.getString(AppConstants.KEY_USER_NAME_THREAD, AppConstants.DEFAULT_USER_NAME_THREAD); + provisionManager.getEspDevice().setUserName(userName); + } else if (TextUtils.isEmpty(provisionManager.getEspDevice().getUserName())) { + String userName = sharedPreferences.getString(AppConstants.KEY_USER_NAME_WIFI, AppConstants.DEFAULT_USER_NAME_WIFI); provisionManager.getEspDevice().setUserName(userName); } break; diff --git a/app/src/main/java/com/espressif/ui/activities/ProofOfPossessionActivity.java b/app/src/main/java/com/espressif/ui/activities/ProofOfPossessionActivity.java index eac7de0..5020742 100644 --- a/app/src/main/java/com/espressif/ui/activities/ProofOfPossessionActivity.java +++ b/app/src/main/java/com/espressif/ui/activities/ProofOfPossessionActivity.java @@ -28,6 +28,7 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.cardview.widget.CardView; +import com.espressif.AppConstants; import com.espressif.provisioning.DeviceConnectionEvent; import com.espressif.provisioning.ESPConstants; import com.espressif.provisioning.ESPProvisionManager; @@ -131,8 +132,12 @@ public void run() { btnNext.setAlpha(1f); progressBar.setVisibility(View.GONE); ArrayList deviceCaps = provisionManager.getEspDevice().getDeviceCapabilities(); - if (deviceCaps.contains("wifi_scan")) { + if (deviceCaps.contains(AppConstants.CAPABILITY_WIFI_SCAN)) { goToWiFiScanListActivity(); + } else if (deviceCaps.contains(AppConstants.CAPABILITY_THREAD_SCAN)) { + goToThreadScanActivity(true); + } else if (deviceCaps.contains(AppConstants.CAPABILITY_THREAD_PROV)) { + goToThreadScanActivity(false); } else { goToWiFiConfigActivity(); } @@ -197,6 +202,15 @@ private void goToWiFiScanListActivity() { finish(); } + private void goToThreadScanActivity(boolean scanCapAvailable) { + + Intent threadConfigIntent = new Intent(getApplicationContext(), ThreadConfigActivity.class); + threadConfigIntent.putExtras(getIntent()); + threadConfigIntent.putExtra(AppConstants.KEY_THREAD_SCAN_AVAILABLE, scanCapAvailable); + startActivity(threadConfigIntent); + finish(); + } + private void goToWiFiConfigActivity() { Intent wifiConfigIntent = new Intent(getApplicationContext(), WiFiConfigActivity.class); diff --git a/app/src/main/java/com/espressif/ui/activities/ProvisionActivity.java b/app/src/main/java/com/espressif/ui/activities/ProvisionActivity.java index b0f1b53..9c1667d 100644 --- a/app/src/main/java/com/espressif/ui/activities/ProvisionActivity.java +++ b/app/src/main/java/com/espressif/ui/activities/ProvisionActivity.java @@ -17,6 +17,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; +import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.ImageView; @@ -50,7 +51,7 @@ public class ProvisionActivity extends AppCompatActivity { private CardView btnOk; private TextView txtOkBtn; - private String ssidValue, passphraseValue = ""; + private String ssidValue, passphraseValue = "", dataset; private ESPProvisionManager provisionManager; private boolean isProvisioningCompleted = false; @@ -63,6 +64,7 @@ protected void onCreate(Bundle savedInstanceState) { Intent intent = getIntent(); ssidValue = intent.getStringExtra(AppConstants.KEY_WIFI_SSID); passphraseValue = intent.getStringExtra(AppConstants.KEY_WIFI_PASSWORD); + dataset = intent.getStringExtra(AppConstants.KEY_THREAD_DATASET); provisionManager = ESPProvisionManager.getInstance(getApplicationContext()); initViews(); EventBus.getDefault().register(this); @@ -144,157 +146,311 @@ private void doProvisioning() { tick1.setVisibility(View.GONE); progress1.setVisibility(View.VISIBLE); - provisionManager.getEspDevice().provision(ssidValue, passphraseValue, new ProvisionListener() { + if (!TextUtils.isEmpty(dataset)) { + provisionManager.getEspDevice().provision(dataset, new ProvisionListener() { - @Override - public void createSessionFailed(Exception e) { - - runOnUiThread(new Runnable() { - - @Override - public void run() { - tick1.setImageResource(R.drawable.ic_error); - tick1.setVisibility(View.VISIBLE); - progress1.setVisibility(View.GONE); - tvErrAtStep1.setVisibility(View.VISIBLE); - tvErrAtStep1.setText(R.string.error_session_creation); - tvProvError.setVisibility(View.VISIBLE); - hideLoading(); - } - }); - } + @Override + public void createSessionFailed(Exception e) { - @Override - public void wifiConfigSent() { - - runOnUiThread(new Runnable() { - - @Override - public void run() { - tick1.setImageResource(R.drawable.ic_checkbox_on); - tick1.setVisibility(View.VISIBLE); - progress1.setVisibility(View.GONE); - tick2.setVisibility(View.GONE); - progress2.setVisibility(View.VISIBLE); - } - }); - } + runOnUiThread(new Runnable() { - @Override - public void wifiConfigFailed(Exception e) { - - runOnUiThread(new Runnable() { - - @Override - public void run() { - tick1.setImageResource(R.drawable.ic_error); - tick1.setVisibility(View.VISIBLE); - progress1.setVisibility(View.GONE); - tvErrAtStep1.setVisibility(View.VISIBLE); - tvErrAtStep1.setText(R.string.error_prov_step_1); - tvProvError.setVisibility(View.VISIBLE); - hideLoading(); - } - }); - } + @Override + public void run() { + tick1.setImageResource(R.drawable.ic_error); + tick1.setVisibility(View.VISIBLE); + progress1.setVisibility(View.GONE); + tvErrAtStep1.setVisibility(View.VISIBLE); + tvErrAtStep1.setText(R.string.error_session_creation); + tvProvError.setVisibility(View.VISIBLE); + hideLoading(); + } + }); + } - @Override - public void wifiConfigApplied() { - - runOnUiThread(new Runnable() { - - @Override - public void run() { - tick2.setImageResource(R.drawable.ic_checkbox_on); - tick2.setVisibility(View.VISIBLE); - progress2.setVisibility(View.GONE); - tick3.setVisibility(View.GONE); - progress3.setVisibility(View.VISIBLE); - } - }); - } + @Override + public void wifiConfigSent() { - @Override - public void wifiConfigApplyFailed(Exception e) { - - runOnUiThread(new Runnable() { - - @Override - public void run() { - tick2.setImageResource(R.drawable.ic_error); - tick2.setVisibility(View.VISIBLE); - progress2.setVisibility(View.GONE); - tvErrAtStep2.setVisibility(View.VISIBLE); - tvErrAtStep2.setText(R.string.error_prov_step_2); - tvProvError.setVisibility(View.VISIBLE); - hideLoading(); - } - }); - } + runOnUiThread(new Runnable() { - @Override - public void provisioningFailedFromDevice(final ESPConstants.ProvisionFailureReason failureReason) { - - runOnUiThread(new Runnable() { - @Override - public void run() { - - switch (failureReason) { - case AUTH_FAILED: - tvErrAtStep3.setText(R.string.error_authentication_failed); - break; - case NETWORK_NOT_FOUND: - tvErrAtStep3.setText(R.string.error_network_not_found); - break; - case DEVICE_DISCONNECTED: - case UNKNOWN: - tvErrAtStep3.setText(R.string.error_prov_step_3); - break; + @Override + public void run() { + tick1.setImageResource(R.drawable.ic_checkbox_on); + tick1.setVisibility(View.VISIBLE); + progress1.setVisibility(View.GONE); + tick2.setVisibility(View.GONE); + progress2.setVisibility(View.VISIBLE); } - tick3.setImageResource(R.drawable.ic_error); - tick3.setVisibility(View.VISIBLE); - progress3.setVisibility(View.GONE); - tvErrAtStep3.setVisibility(View.VISIBLE); - tvProvError.setVisibility(View.VISIBLE); - hideLoading(); - } - }); - } + }); + } - @Override - public void deviceProvisioningSuccess() { - - runOnUiThread(new Runnable() { - - @Override - public void run() { - isProvisioningCompleted = true; - tick3.setImageResource(R.drawable.ic_checkbox_on); - tick3.setVisibility(View.VISIBLE); - progress3.setVisibility(View.GONE); - hideLoading(); - } - }); - } + @Override + public void wifiConfigFailed(Exception e) { - @Override - public void onProvisioningFailed(Exception e) { - - runOnUiThread(new Runnable() { - - @Override - public void run() { - tick3.setImageResource(R.drawable.ic_error); - tick3.setVisibility(View.VISIBLE); - progress3.setVisibility(View.GONE); - tvErrAtStep3.setVisibility(View.VISIBLE); - tvErrAtStep3.setText(R.string.error_prov_step_3); - tvProvError.setVisibility(View.VISIBLE); - hideLoading(); - } - }); - } - }); + runOnUiThread(new Runnable() { + + @Override + public void run() { + tick1.setImageResource(R.drawable.ic_error); + tick1.setVisibility(View.VISIBLE); + progress1.setVisibility(View.GONE); + tvErrAtStep1.setVisibility(View.VISIBLE); + tvErrAtStep1.setText(R.string.error_prov_thread_step_1); + tvProvError.setVisibility(View.VISIBLE); + hideLoading(); + } + }); + } + + @Override + public void wifiConfigApplied() { + + runOnUiThread(new Runnable() { + + @Override + public void run() { + tick2.setImageResource(R.drawable.ic_checkbox_on); + tick2.setVisibility(View.VISIBLE); + progress2.setVisibility(View.GONE); + tick3.setVisibility(View.GONE); + progress3.setVisibility(View.VISIBLE); + } + }); + } + + @Override + public void wifiConfigApplyFailed(Exception e) { + + runOnUiThread(new Runnable() { + + @Override + public void run() { + tick2.setImageResource(R.drawable.ic_error); + tick2.setVisibility(View.VISIBLE); + progress2.setVisibility(View.GONE); + tvErrAtStep2.setVisibility(View.VISIBLE); + tvErrAtStep2.setText(R.string.error_prov_thread_step_2); + tvProvError.setVisibility(View.VISIBLE); + hideLoading(); + } + }); + } + + @Override + public void provisioningFailedFromDevice(final ESPConstants.ProvisionFailureReason failureReason) { + + runOnUiThread(new Runnable() { + @Override + public void run() { + + switch (failureReason) { + case AUTH_FAILED: + tvErrAtStep3.setText(R.string.error_dataset_invalid); + break; + case NETWORK_NOT_FOUND: + tvErrAtStep3.setText(R.string.error_network_not_found); + break; + case DEVICE_DISCONNECTED: + case UNKNOWN: + tvErrAtStep3.setText(R.string.error_prov_step_3); + break; + } + tick3.setImageResource(R.drawable.ic_error); + tick3.setVisibility(View.VISIBLE); + progress3.setVisibility(View.GONE); + tvErrAtStep3.setVisibility(View.VISIBLE); + tvProvError.setVisibility(View.VISIBLE); + hideLoading(); + } + }); + } + + @Override + public void deviceProvisioningSuccess() { + + runOnUiThread(new Runnable() { + + @Override + public void run() { + isProvisioningCompleted = true; + tick3.setImageResource(R.drawable.ic_checkbox_on); + tick3.setVisibility(View.VISIBLE); + progress3.setVisibility(View.GONE); + hideLoading(); + } + }); + } + + @Override + public void onProvisioningFailed(Exception e) { + + runOnUiThread(new Runnable() { + + @Override + public void run() { + tick3.setImageResource(R.drawable.ic_error); + tick3.setVisibility(View.VISIBLE); + progress3.setVisibility(View.GONE); + tvErrAtStep3.setVisibility(View.VISIBLE); + tvErrAtStep3.setText(R.string.error_prov_step_3); + tvProvError.setVisibility(View.VISIBLE); + hideLoading(); + } + }); + } + }); + } else { + provisionManager.getEspDevice().provision(ssidValue, passphraseValue, new ProvisionListener() { + + @Override + public void createSessionFailed(Exception e) { + + runOnUiThread(new Runnable() { + + @Override + public void run() { + tick1.setImageResource(R.drawable.ic_error); + tick1.setVisibility(View.VISIBLE); + progress1.setVisibility(View.GONE); + tvErrAtStep1.setVisibility(View.VISIBLE); + tvErrAtStep1.setText(R.string.error_session_creation); + tvProvError.setVisibility(View.VISIBLE); + hideLoading(); + } + }); + } + + @Override + public void wifiConfigSent() { + + runOnUiThread(new Runnable() { + + @Override + public void run() { + tick1.setImageResource(R.drawable.ic_checkbox_on); + tick1.setVisibility(View.VISIBLE); + progress1.setVisibility(View.GONE); + tick2.setVisibility(View.GONE); + progress2.setVisibility(View.VISIBLE); + } + }); + } + + @Override + public void wifiConfigFailed(Exception e) { + + runOnUiThread(new Runnable() { + + @Override + public void run() { + tick1.setImageResource(R.drawable.ic_error); + tick1.setVisibility(View.VISIBLE); + progress1.setVisibility(View.GONE); + tvErrAtStep1.setVisibility(View.VISIBLE); + tvErrAtStep1.setText(R.string.error_prov_step_1); + tvProvError.setVisibility(View.VISIBLE); + hideLoading(); + } + }); + } + + @Override + public void wifiConfigApplied() { + + runOnUiThread(new Runnable() { + + @Override + public void run() { + tick2.setImageResource(R.drawable.ic_checkbox_on); + tick2.setVisibility(View.VISIBLE); + progress2.setVisibility(View.GONE); + tick3.setVisibility(View.GONE); + progress3.setVisibility(View.VISIBLE); + } + }); + } + + @Override + public void wifiConfigApplyFailed(Exception e) { + + runOnUiThread(new Runnable() { + + @Override + public void run() { + tick2.setImageResource(R.drawable.ic_error); + tick2.setVisibility(View.VISIBLE); + progress2.setVisibility(View.GONE); + tvErrAtStep2.setVisibility(View.VISIBLE); + tvErrAtStep2.setText(R.string.error_prov_step_2); + tvProvError.setVisibility(View.VISIBLE); + hideLoading(); + } + }); + } + + @Override + public void provisioningFailedFromDevice(final ESPConstants.ProvisionFailureReason failureReason) { + + runOnUiThread(new Runnable() { + @Override + public void run() { + + switch (failureReason) { + case AUTH_FAILED: + tvErrAtStep3.setText(R.string.error_authentication_failed); + break; + case NETWORK_NOT_FOUND: + tvErrAtStep3.setText(R.string.error_network_not_found); + break; + case DEVICE_DISCONNECTED: + case UNKNOWN: + tvErrAtStep3.setText(R.string.error_prov_step_3); + break; + } + tick3.setImageResource(R.drawable.ic_error); + tick3.setVisibility(View.VISIBLE); + progress3.setVisibility(View.GONE); + tvErrAtStep3.setVisibility(View.VISIBLE); + tvProvError.setVisibility(View.VISIBLE); + hideLoading(); + } + }); + } + + @Override + public void deviceProvisioningSuccess() { + + runOnUiThread(new Runnable() { + + @Override + public void run() { + isProvisioningCompleted = true; + tick3.setImageResource(R.drawable.ic_checkbox_on); + tick3.setVisibility(View.VISIBLE); + progress3.setVisibility(View.GONE); + hideLoading(); + } + }); + } + + @Override + public void onProvisioningFailed(Exception e) { + + runOnUiThread(new Runnable() { + + @Override + public void run() { + tick3.setImageResource(R.drawable.ic_error); + tick3.setVisibility(View.VISIBLE); + progress3.setVisibility(View.GONE); + tvErrAtStep3.setVisibility(View.VISIBLE); + tvErrAtStep3.setText(R.string.error_prov_step_3); + tvProvError.setVisibility(View.VISIBLE); + hideLoading(); + } + }); + } + }); + } } private void showLoading() { diff --git a/app/src/main/java/com/espressif/ui/activities/ProvisionLanding.java b/app/src/main/java/com/espressif/ui/activities/ProvisionLanding.java index 6098573..198ae22 100644 --- a/app/src/main/java/com/espressif/ui/activities/ProvisionLanding.java +++ b/app/src/main/java/com/espressif/ui/activities/ProvisionLanding.java @@ -213,24 +213,36 @@ private void processDeviceCapabilities() { ArrayList deviceCaps = provisionManager.getEspDevice().getDeviceCapabilities(); - if (!TextUtils.isEmpty(pop)) { + if (deviceCaps != null) { - provisionManager.getEspDevice().setProofOfPossession(pop); + if (!TextUtils.isEmpty(pop)) { - if (deviceCaps != null && deviceCaps.contains("wifi_scan")) { - goToWifiScanListActivity(); - } else { - goToWiFiConfigActivity(); - } - } else { + provisionManager.getEspDevice().setProofOfPossession(pop); - if (deviceCaps != null && !deviceCaps.contains("no_pop") && securityType != AppConstants.SEC_TYPE_0) { - goToPopActivity(); - } else if (deviceCaps != null && deviceCaps.contains("wifi_scan")) { - goToWifiScanListActivity(); + if (deviceCaps.contains(AppConstants.CAPABILITY_WIFI_SCAN)) { + goToWifiScanListActivity(); + } else if (deviceCaps.contains(AppConstants.CAPABILITY_THREAD_SCAN)) { + goToThreadScanActivity(true); + } else if (deviceCaps.contains(AppConstants.CAPABILITY_THREAD_PROV)) { + goToThreadScanActivity(false); + } else { + goToWiFiConfigActivity(); + } } else { - goToWiFiConfigActivity(); + if (!deviceCaps.contains("no_pop") && securityType != AppConstants.SEC_TYPE_0) { + goToPopActivity(); + } else if (deviceCaps.contains(AppConstants.CAPABILITY_WIFI_SCAN)) { + goToWifiScanListActivity(); + } else if (deviceCaps.contains(AppConstants.CAPABILITY_THREAD_SCAN)) { + goToThreadScanActivity(true); + } else if (deviceCaps.contains(AppConstants.CAPABILITY_THREAD_PROV)) { + goToThreadScanActivity(false); + } else { + goToWiFiConfigActivity(); + } } + } else { + goToWiFiConfigActivity(); } } @@ -248,6 +260,14 @@ private void goToWifiScanListActivity() { startActivity(wifiListIntent); } + private void goToThreadScanActivity(boolean scanCapAvailable) { + + finish(); + Intent threadConfigIntent = new Intent(getApplicationContext(), ThreadConfigActivity.class); + threadConfigIntent.putExtra(AppConstants.KEY_THREAD_SCAN_AVAILABLE, scanCapAvailable); + startActivity(threadConfigIntent); + } + private void goToWiFiConfigActivity() { finish(); diff --git a/app/src/main/java/com/espressif/ui/activities/SettingsActivity.java b/app/src/main/java/com/espressif/ui/activities/SettingsActivity.java index 4de1dcc..adfd84f 100644 --- a/app/src/main/java/com/espressif/ui/activities/SettingsActivity.java +++ b/app/src/main/java/com/espressif/ui/activities/SettingsActivity.java @@ -1,3 +1,17 @@ +// Copyright 2024 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.ui.activities; import android.content.SharedPreferences; @@ -61,7 +75,7 @@ public boolean onOptionsItemSelected(MenuItem item) { public static class SettingsFragment extends PreferenceFragmentCompat { SwitchPreferenceCompat securityPref; - EditTextPreference userNamePref; + EditTextPreference userNamePrefWifi, userNamePrefThread; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { @@ -72,15 +86,18 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { SharedPreferences sharedPreferences = prefMgr.getSharedPreferences(); securityPref = prefMgr.findPreference(AppConstants.KEY_SECURITY_TYPE); - userNamePref = prefMgr.findPreference(AppConstants.KEY_USER_NAME); + userNamePrefWifi = prefMgr.findPreference(AppConstants.KEY_USER_NAME_WIFI); + userNamePrefThread = prefMgr.findPreference(AppConstants.KEY_USER_NAME_THREAD); boolean isSecure = sharedPreferences.getBoolean(AppConstants.KEY_SECURITY_TYPE, true); if (isSecure) { securityPref.setSummary(R.string.summary_secured); - userNamePref.setVisible(true); + userNamePrefWifi.setVisible(true); + userNamePrefThread.setVisible(true); } else { securityPref.setSummary(R.string.summary_unsecured); - userNamePref.setVisible(false); + userNamePrefWifi.setVisible(false); + userNamePrefThread.setVisible(false); } securityPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @@ -91,10 +108,12 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { if (isSecure) { preference.setSummary(R.string.summary_secured); - userNamePref.setVisible(true); + userNamePrefWifi.setVisible(true); + userNamePrefThread.setVisible(true); } else { preference.setSummary(R.string.summary_unsecured); - userNamePref.setVisible(false); + userNamePrefWifi.setVisible(false); + userNamePrefThread.setVisible(false); } return true; } diff --git a/app/src/main/java/com/espressif/ui/activities/ThreadConfigActivity.java b/app/src/main/java/com/espressif/ui/activities/ThreadConfigActivity.java new file mode 100644 index 0000000..22c6905 --- /dev/null +++ b/app/src/main/java/com/espressif/ui/activities/ThreadConfigActivity.java @@ -0,0 +1,345 @@ +// Copyright 2024 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.ui.activities; + +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentSender; +import android.os.Bundle; +import android.os.Handler; +import android.util.Log; +import android.view.View; +import android.widget.ProgressBar; +import android.widget.TextView; + +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.IntentSenderRequest; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.cardview.widget.CardView; + +import com.espressif.AppConstants; +import com.espressif.provisioning.DeviceConnectionEvent; +import com.espressif.provisioning.ESPConstants; +import com.espressif.provisioning.ESPProvisionManager; +import com.espressif.provisioning.WiFiAccessPoint; +import com.espressif.provisioning.listeners.WiFiScanListener; +import com.espressif.ui.utils.Utils; +import com.espressif.wifi_provisioning.R; +import com.google.android.gms.threadnetwork.ThreadNetwork; +import com.google.android.gms.threadnetwork.ThreadNetworkCredentials; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + +import java.util.ArrayList; + +public class ThreadConfigActivity extends AppCompatActivity { + + private static final String TAG = ThreadConfigActivity.class.getSimpleName(); + + private Handler handler; + private ProgressBar progressBar; + private CardView btnNext; + private TextView txtNextBtn, tvProgress, tvError; + private ArrayList threadNetworkList; + private ESPProvisionManager provisionManager; + + private ActivityResultLauncher preferredCredentialsLauncher; + private ThreadNetworkCredentials preferredCredentials; + private boolean scanCapAvailable = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_thread_scan_list); + + handler = new Handler(); + threadNetworkList = new ArrayList<>(); + provisionManager = ESPProvisionManager.getInstance(getApplicationContext()); + scanCapAvailable = getIntent().getBooleanExtra(AppConstants.KEY_THREAD_SCAN_AVAILABLE, false); + + initViews(); + EventBus.getDefault().register(this); + getThreadPreferredCredentials(); + } + + @Override + protected void onDestroy() { + EventBus.getDefault().unregister(this); + super.onDestroy(); + } + + @Override + public void onBackPressed() { + provisionManager.getEspDevice().disconnectDevice(); + super.onBackPressed(); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onEvent(DeviceConnectionEvent event) { + + Log.d(TAG, "On Device Connection Event RECEIVED : " + event.getEventType()); + + if (event.getEventType() == ESPConstants.EVENT_DEVICE_DISCONNECTED) { + if (!isFinishing()) { + showAlertForDeviceDisconnected(); + } + } + } + + private View.OnClickListener nextBtnClickListener = new View.OnClickListener() { + + @Override + public void onClick(View v) { + + String btnText = txtNextBtn.getText().toString(); + + if (btnText.equals(getString(R.string.btn_next))) { + + showLoading(getString(R.string.progress_thread_networks)); + byte[] activeDataset = preferredCredentials.getActiveOperationalDataset(); + sendActiveDataset(Utils.byteArrayToDs(activeDataset)); + + } else if (btnText.equals(getString(R.string.btn_try_again))) { + + showLoading(getString(R.string.progress_thread_networks)); + getThreadPreferredCredentials(); + + } else if (btnText.equals(getString(R.string.btn_ok))) { + finish(); + } + } + }; + + private void initViews() { + Toolbar toolbar = findViewById(R.id.toolbar); + toolbar.setTitle(R.string.title_activity_thread_config); + setSupportActionBar(toolbar); + + tvProgress = findViewById(R.id.tv_thread_message); + tvError = findViewById(R.id.tv_prov_error); + progressBar = findViewById(R.id.network_search_loading); + progressBar.setVisibility(View.VISIBLE); + + btnNext = findViewById(R.id.btn_next); + txtNextBtn = findViewById(R.id.text_btn); + txtNextBtn.setText(R.string.btn_next); + btnNext.setEnabled(false); + btnNext.setAlpha(0.5f); + btnNext.setOnClickListener(nextBtnClickListener); + + preferredCredentialsLauncher = registerForActivityResult(new ActivityResultContracts.StartIntentSenderForResult(), + result -> { + if (result.getResultCode() == RESULT_OK) { + + preferredCredentials = ThreadNetworkCredentials.fromIntentSenderResultData(result.getData()); + Log.d(TAG, "Preferred Credentials Network Name : " + preferredCredentials.getNetworkName()); + + if (scanCapAvailable) { + startThreadScan(); + } else { + // If "thread_scan" capability is not available and "thread_prov" is available + hideLoading(); + txtNextBtn.setText(R.string.btn_next); + String str = "Available Thread Network : " + preferredCredentials.getNetworkName() + "\n" + + "Do you want to proceed ?"; + tvProgress.setText(str); + } + + } else { + // The user denied to share! + Log.e(TAG, "User denied request."); + hideLoading(); + showError(getString(R.string.error_title), getString(R.string.error_read_preferred_creds_request_denied), true); + } + }); + } + + private void getThreadPreferredCredentials() { + + Log.d(TAG, "ThreadClient: getPreferredCredentials intent sent"); + ThreadNetwork.getClient(this) + .getPreferredCredentials() + .addOnSuccessListener(intentSenderResult -> { + IntentSender intentSender = intentSenderResult.getIntentSender(); + if (intentSender != null) { + preferredCredentialsLauncher.launch( + new IntentSenderRequest.Builder(intentSender).build()); + } else { + // No preferred credentials found! + Log.e(TAG, "No preferred credentials found!"); + hideLoading(); + showError(getString(R.string.error_title), getString(R.string.error_no_preferred_creds), false); + } + }) + .addOnFailureListener(e -> { + e.printStackTrace(); + hideLoading(); + showError(getString(R.string.error_title), getString(R.string.error_no_preferred_creds), false); + }); + } + + private void startThreadScan() { + + Log.d(TAG, "Start Thread Scan"); + threadNetworkList.clear(); + + runOnUiThread(new Runnable() { + + @Override + public void run() { + showLoading(getString(R.string.progress_thread_networks)); + } + }); + + handler.postDelayed(stopScanningTask, 15000); + + provisionManager.getEspDevice().scanThreadNetworks(new WiFiScanListener() { + + @Override + public void onWifiListReceived(final ArrayList wifiList) { + + runOnUiThread(new Runnable() { + @Override + public void run() { + + threadNetworkList.addAll(wifiList); + handler.removeCallbacks(stopScanningTask); + boolean isNetworkAvailable = false; + + if (!threadNetworkList.isEmpty()) { + + for (WiFiAccessPoint network : threadNetworkList) { + + if (preferredCredentials.getNetworkName().equals(network.getWifiName())) { + + Log.d(TAG, "Thread Network available : " + network.getWifiName()); + isNetworkAvailable = true; + break; + } + } + + if (isNetworkAvailable) { + + hideLoading(); + txtNextBtn.setText(R.string.btn_next); + String str = "Available Thread Network : " + preferredCredentials.getNetworkName() + "\n" + + "Do you want to proceed ?"; + tvProgress.setText(str); + + } else { + hideLoading(); + showError(getString(R.string.error_title), getString(R.string.error_no_thread_network), false); + } + } else { + hideLoading(); + showError(getString(R.string.error_title), getString(R.string.error_no_thread_network), false); + } + } + }); + } + + @Override + public void onWiFiScanFailed(Exception e) { + + Log.e(TAG, "onWiFiScanFailed"); + e.printStackTrace(); + runOnUiThread(new Runnable() { + @Override + public void run() { + hideLoading(); + showError(getString(R.string.error_title), "Failed to get thread scan list", false); + } + }); + } + }); + } + + private void sendActiveDataset(String activeDataset) { + + hideLoading(); + goToProvisioningActivity(activeDataset); + } + + private void goToProvisioningActivity(String activeDataset) { + + finish(); + Intent provisionIntent = new Intent(getApplicationContext(), ProvisionActivity.class); + provisionIntent.putExtras(getIntent()); + provisionIntent.putExtra(AppConstants.KEY_THREAD_DATASET, activeDataset); + startActivity(provisionIntent); + } + + private Runnable stopScanningTask = new Runnable() { + + @Override + public void run() { + hideLoading(); + } + }; + + private void showAlertForDeviceDisconnected() { + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setCancelable(false); + builder.setTitle(R.string.error_title); + builder.setMessage(R.string.dialog_msg_ble_device_disconnection); + + // Set up the buttons + builder.setPositiveButton(R.string.btn_ok, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + finish(); + } + }); + + builder.show(); + } + + private void showLoading(String msg) { + btnNext.setEnabled(false); + btnNext.setAlpha(0.5f); + tvProgress.setText(msg); + progressBar.setVisibility(View.VISIBLE); + tvError.setVisibility(View.GONE); + } + + private void hideLoading() { + btnNext.setEnabled(true); + btnNext.setAlpha(1f); + progressBar.setVisibility(View.GONE); + } + + private void showError(String title, String msg, boolean canReadAgain) { + + findViewById(R.id.iv_arrow).setVisibility(View.GONE); + + if (canReadAgain) { + txtNextBtn.setText(R.string.btn_try_again); + } else { + txtNextBtn.setText(R.string.btn_ok); + } + + tvProgress.setText(title); + tvError.setText(msg); + tvError.setVisibility(View.VISIBLE); + } +} diff --git a/app/src/main/java/com/espressif/ui/utils/Utils.java b/app/src/main/java/com/espressif/ui/utils/Utils.java index 17552f3..d44ac18 100644 --- a/app/src/main/java/com/espressif/ui/utils/Utils.java +++ b/app/src/main/java/com/espressif/ui/utils/Utils.java @@ -24,6 +24,14 @@ public class Utils { + public static String byteArrayToDs(byte[] byteArray) { + StringBuilder sb = new StringBuilder(); + for (byte b : byteArray) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } + public static void displayDeviceConnectionError(Activity aContext, String msg) { AlertDialog.Builder builder = new AlertDialog.Builder(aContext); diff --git a/app/src/main/res/layout/activity_thread_scan_list.xml b/app/src/main/res/layout/activity_thread_scan_list.xml new file mode 100644 index 0000000..6f06241 --- /dev/null +++ b/app/src/main/res/layout/activity_thread_scan_list.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 132f36d..37a12ea 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -9,6 +9,7 @@ Proof of Possession Select Wi-Fi Network Wi-Fi Configuration + Thread Network Configuration Provisioning Settings Add Device @@ -30,6 +31,7 @@ Change Yes Get Permission + Try Again Proof of Possession @@ -51,6 +53,8 @@ Failed to send Wi-Fi credentials Failed to apply Wi-Fi credentials Failed to provisioning device + Failed to send Thread credentials + Failed to apply Thread credentials Settings Connect to device @@ -76,6 +80,7 @@ Scanning Devices... Getting Status Loading... + Searching thread\nnetworks... Error! @@ -96,6 +101,10 @@ Security applied for communicating with device does not match configuration setting Camera permission is not given. App will not be able to scan QR code to add device. Location permission is not given. It will be required to add BLE or SoftAP devices. + No thread network added + You have denied request for reading thread credentials + No Active Thread Border Router + Invalid Dataset Prefix : Enter Prefix @@ -122,7 +131,8 @@ Supported Device Types Encrypted Communication - Set Username + Sec2 Username for Wi-Fi + Sec2 Username for Thread Username Secured Unsecured diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 0cbcf67..72823e3 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -24,8 +24,16 @@ + +