Skip to content

Commit

Permalink
Implemented Android CHIPTool support for ESF
Browse files Browse the repository at this point in the history
  • Loading branch information
swan-amazon committed Jul 30, 2024
1 parent dd26f66 commit 96c0095
Show file tree
Hide file tree
Showing 14 changed files with 196 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ class CHIPToolActivity :
startActivity(redirectIntent)
}

override fun handleEnhancedSetupFlowClicked() {
Toast.makeText(this, "Enhanced Setup Flow", Toast.LENGTH_SHORT).show()
showFragment(DeviceProvisioningFragment.newInstance(deviceInfo!!, null))
}

override fun setNetworkType(type: ProvisionNetworkType?) {
networkType = type
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,12 @@ open class GenericChipDeviceListener : ChipDeviceController.CompletionListener {
override fun onICDRegistrationComplete(errorCode: Long, icdDeviceInfo: ICDDeviceInfo) {
// No op
}

override fun onTermsAndConditionsRequired() {
// No op
}

override fun onTermsAndConditionsRequiredComplete(termsAndConditionsAcknowledgements: Int, termsAndConditionsVersion: Int) {
// No op
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class SelectActionFragment : Fragment() {
binding.basicClusterBtn.setOnClickListener { handleBasicClicked() }
binding.attestationTestBtn.setOnClickListener { handleAttestationTestClicked() }
binding.clusterInteractionBtn.setOnClickListener { handleClusterInteractionClicked() }
binding.provisionCustomFlowBtn.setOnClickListener { handleProvisionCustomFlowClicked() }
binding.provisionCustomFlowReadFromLedgerBtn.setOnClickListener { handleProvisionCustomFlowClicked() }
binding.wildcardBtn.setOnClickListener { handleWildcardClicked() }
binding.unpairDeviceBtn.setOnClickListener { handleUnpairDeviceClicked() }
binding.groupSettingBtn.setOnClickListener { handleGroupSettingClicked() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,14 @@ class DeviceProvisioningFragment : Fragment() {
.show()
}
}

override fun onTermsAndConditionsRequired() {
}

override fun onTermsAndConditionsRequiredComplete(termsAndConditionsAcknowledgements: Int, termsAndConditionsVersion: Int) {
Log.d(TAG, "onTermsAndConditionsRequiredComplete: $termsAndConditionsAcknowledgements, $termsAndConditionsVersion")
deviceController.updateTermsAndConditionsAcknowledgements(termsAndConditionsAcknowledgements, termsAndConditionsVersion)
}
}

private fun ByteArray.toHex(): String =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ class CHIPDeviceDetailsFragment : Fragment() {

// commissioningFlow = 2 (Custom), read device info from Ledger
if (deviceInfo.commissioningFlow == 2) {
binding.customFlowBtn.visibility = View.VISIBLE
binding.customFlowBtn.setOnClickListener {
binding.customFlowReadFromLedgerBtn.visibility = View.VISIBLE
binding.customFlowReadFromLedgerBtn.setOnClickListener {
FragmentUtil.getHost(this@CHIPDeviceDetailsFragment, Callback::class.java)
?.handleReadFromLedgerClicked(deviceInfo)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,19 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.android.volley.Request
import com.android.volley.Response.Listener
import com.android.volley.toolbox.JsonObjectRequest
import com.android.volley.toolbox.Volley
import com.google.chip.chiptool.R
import com.google.chip.chiptool.databinding.ChipLedgerInfoFragmentBinding
import com.google.chip.chiptool.util.FragmentUtil
import com.google.gson.Gson
import java.io.BufferedReader
import java.io.InputStreamReader
import org.json.JSONObject

/** Show the [CHIPDeviceInfo] from Ledger */
class CHIPLedgerDetailsFragment : Fragment() {
Expand All @@ -55,61 +60,50 @@ class CHIPLedgerDetailsFragment : Fragment() {

// Ledger api url
val url =
Uri.parse(context!!.getString(R.string.dcl_api_root_url))
Uri.parse(DCL_API_ROOT_URL)
.buildUpon()
.appendPath("${deviceInfo.vendorId}")
.appendPath("${deviceInfo.productId}")
.build()
.toString()
Log.d(TAG, "Dcl request Url: $url")

// Ledger API call
val jsonObjectRequest =
JsonObjectRequest(
Request.Method.GET,
url,
null,
{ response ->
Log.d(TAG, "Response from dcl $response")

// parse redirect Url
val responseJson = response.getJSONObject(context!!.getString(R.string.dcl_response_key))
val redirectUrl =
responseJson.getString(context!!.getString(R.string.dcl_custom_flow_url_key))
Log.d(TAG, "Redirect Url from Ledger: $redirectUrl")
binding.commissioningFlowUrlTv.text = redirectUrl

// generate redirect payload
val gson = Gson()
val payloadJson = gson.toJson(deviceInfo)
val payloadBase64 = Base64.encodeToString(payloadJson.toByteArray(), Base64.DEFAULT)
val redirectUrlWithPayload =
Uri.parse(redirectUrl)
.buildUpon()
.appendQueryParameter("payload", payloadBase64)
.appendQueryParameter(
"returnUrl",
context!!.getString(R.string.custom_flow_return_url)
)
.build()
.toString()

Log.d(TAG, "Redirect Url with Payload: $redirectUrlWithPayload")
binding.redirectBtn.setOnClickListener {
FragmentUtil.getHost(this@CHIPLedgerDetailsFragment, Callback::class.java)
?.handleCustomFlowRedirectClicked(redirectUrlWithPayload)
}
val context = getContext()!!
val mockDclRequestsResponsesRaw = readRawResourceFile(R.raw.mock_dcl_responses_json)
val mockDclRequestsResponses: org.json.JSONArray = org.json.JSONArray(mockDclRequestsResponsesRaw)

val mockDclRequestsResponseMap = HashMap<String, org.json.JSONObject>()
for (i in 0 until mockDclRequestsResponses.length()) {
val mockDclRequestsResponse = mockDclRequestsResponses.getJSONObject(i)

val mockDclRequest: org.json.JSONObject = mockDclRequestsResponse.getJSONObject("request")
val mockDclResponse: org.json.JSONObject = mockDclRequestsResponse.getJSONObject("response")

// enable redirect button
binding.redirectBtn.visibility = View.VISIBLE
},
{ error ->
Log.e(TAG, "Dcl request failed: $error")
binding.commissioningFlowUrlTv.text =
context!!.getString(R.string.chip_ledger_info_commissioning_flow_url_not_available)
}
)
queue.add(jsonObjectRequest)
val mockDclRequestURL = mockDclRequest.getString("url")
val mockDclResponseBody = mockDclResponse.getJSONObject("body")

mockDclRequestsResponseMap.put(mockDclRequestURL, mockDclResponseBody)
Log.d(TAG, "mockDclRequestURL= $mockDclRequestURL")
Log.d(TAG, "mockDclResponseBody= $mockDclResponseBody")
}

Log.d(TAG, "Request url= $url")
val response: org.json.JSONObject = mockDclRequestsResponseMap.get(url.toString())!!
Log.d(TAG, "Response from dcl $response")
Toast.makeText(context, "Enhanced Setup Flow: $response", Toast.LENGTH_LONG).show()

val enhancedSetupFlowOptions = response.optInt("enhancedSetupFlowOptions", 0)

if (0 != (enhancedSetupFlowOptions and 0b00000001)) {
Toast.makeText(context, "Enhanced Setup Flow supported!", Toast.LENGTH_SHORT).show()

binding.commissionBtn.setOnClickListener {
FragmentUtil.getHost(this@CHIPLedgerDetailsFragment, Callback::class.java)
?.handleEnhancedSetupFlowClicked()
}

binding.commissionBtn.visibility = View.VISIBLE
}

return binding.root
}
Expand All @@ -119,15 +113,40 @@ class CHIPLedgerDetailsFragment : Fragment() {
_binding = null
}

private fun readRawResourceFile(resourceId: Int): String? {
return try {
val inputStream = resources.openRawResource(resourceId)
val bufferedReader = BufferedReader(InputStreamReader(inputStream))
val stringBuilder = StringBuilder()
var line: String? = bufferedReader.readLine()
while (line != null) {
stringBuilder.append(line)
line = bufferedReader.readLine()
}
bufferedReader.close()
inputStream.close()
stringBuilder.toString()
} catch (e: Exception) {
Log.e("MainActivity", "Error reading raw resource file", e)
null
}
}

/** Interface for notifying the host. */
interface Callback {
/** Notifies listener of Custom flow redirect button click. */
fun handleCustomFlowRedirectClicked(redirectUrl: String)

/** Notifies listener of Enhanced Setup flow button click. */
fun handleEnhancedSetupFlowClicked()
}

companion object {
private const val TAG = "CUSTOM_FLOW"
private const val ARG_DEVICE_INFO = "device_info"
private const val DCL_CUSTOM_FLOW_URL_KEY = "commissioningCustomFlowUrl";
private const val DCL_API_ROOT_URL = "https://on.dcl.csa-iot.org/dcl/model/models";
private const val CUSTOM_FLOW_RETURN_URL = "mt://modelinfo";

@JvmStatic
fun newInstance(deviceInfo: CHIPDeviceInfo): CHIPLedgerDetailsFragment {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@
tools:ignore="LabelFor" />

<Button
android:id="@+id/customFlowBtn"
android:id="@+id/customFlowReadFromLedgerBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/serialNumberLabelTv"
Expand All @@ -234,7 +234,7 @@
android:id="@+id/showQRCodeBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/customFlowBtn"
android:layout_below="@id/customFlowReadFromLedgerBtn"
android:layout_alignParentRight="true"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,20 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_below="@id/commissioningFlowUrlTv"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:visibility="invisible"
android:text="@string/chip_ledge_info_redirect_btn_text" />
<Button
android:id="@+id/commissionBtn"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
android:layout_below="@id/redirectBtn"
android:layout_alignParentEnd="true"
android:visibility="invisible"
android:text="@string/chip_ledge_info_commission_btn_text" />
</RelativeLayout>
</ScrollView>
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
android:text="@string/provision_thread_credentials_btn_text" />

<Button
android:id="@+id/provisionCustomFlowBtn"
android:id="@+id/provisionCustomFlowReadFromLedgerBtn"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginStart="8dp"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[
{
"request": {
"url": "https://on.dcl.csa-iot.org/dcl/model/models/65521/32769",
"method": "GET"
},
"response": {
"statusCode": 200,
"body": {
"vid": 65521,
"pid": 32769,
"deviceTypeId": 1,
"productName": "Harmony Home",
"productLabel": "Purifier",
"partNumber": "1",
"commissioningCustomFlow": 2,
"commissioningCustomFlowUrl": "https://example.com/matter/65521/32769/setup/1/index.html",
"commissioningModeInitialStepsHint": 0,
"commissioningModeInitialStepsInstruction": "",
"commissioningModeSecondaryStepsHint": 0,
"commissioningModeSecondaryStepsInstruction": "",
"userManualUrl": "https://example.com/matter/65521/32769/manual/1/index.html",
"supportUrl": "https://example.com/matter/65521/32769/support/1/index.html",
"productUrl": "https://example.com/matter/65521/32769/product/1/index.html",
"lsfUrl": "",
"lsfRevision": 0,
"creator": "cosmos1cj3f9jue3s389n2u6492nvyn86apey0x6hx03u",
"schemaVersion": 1,
"enhancedSetupFlowOptions": 1,
"enhancedSetupFlowTCUrl": "https://example.com/matter/65521/32769/tc/1/content.json",
"enhancedSetupFlowTCRevision": 1,
"enhancedSetupFlowTCDigest": "MTZlYjIwYWViYzU2ZWI0Nzc4Zjg0NTRiMmJjMjVlYWFlOTY5MjMzNGRiMzRhMjUxZjMwYmQ1Y2MzYzI1NGI2OA==",
"enhancedSetupFlowTCFileSize": 1024,
"maintenanceUrl": "https://example.com/matter/65521/32769/maintenance/1/index.html"
}
}
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,10 @@
<string name="chip_ledger_info_commissioning_flow_url_label">Commissioning Url:</string>
<string name="chip_ledger_info_commissioning_flow_url_text">Loading...</string>
<string name="chip_ledge_info_redirect_btn_text">Redirect</string>
<string name="chip_ledge_info_commission_btn_text">Commission</string>
<string name="chip_ledger_info_commissioning_flow_url_not_available">Not available</string>
<string name="provision_custom_flow_alert_title">Commissioning flow Completed.</string>

<string name="dcl_api_root_url">https://dcl.dev.dsr-corporation.com/api/modelinfo/models</string>
<string name="dcl_response_key">result</string>
<string name="dcl_custom_flow_url_key">custom</string>
<string name="custom_flow_return_url">mt://modelinfo</string>
<string name="retrieve_endpoint_list">Retrieve Endpoint List</string>
<string name="invoke">Invoke</string>
<string name="select_a_command">Select a command</string>
Expand Down
36 changes: 36 additions & 0 deletions src/controller/java/CHIPDeviceController-JNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,9 @@ JNI_METHOD(void, pairDeviceWithAddress)
{
commissioningParams.SetDeviceAttestationDelegate(wrapper->GetDeviceAttestationDelegateBridge());
}

commissioningParams.SetTermsAndConditionsAcknowledgement((const TermsAndConditionsAcknowledgement){ 1, 1 });

err = wrapper->Controller()->PairDevice(static_cast<chip::NodeId>(deviceId), rendezvousParams, commissioningParams);

if (err != CHIP_NO_ERROR)
Expand Down Expand Up @@ -933,6 +936,39 @@ JNI_METHOD(void, updateCommissioningNetworkCredentials)
}
}

JNI_METHOD(void, updateTermsAndConditionsAcknowledgements)
(JNIEnv * env, jobject self, jlong handle, jint acceptedTermsAndConditions, jint acceptedTermsAndConditionsVersion)
{
ChipLogProgress(Controller, "updateTermsAndConditionsAcknowledgements() called");

// Check if the input values are within the range of uint16_t
if (acceptedTermsAndConditions < 0 || acceptedTermsAndConditions > 65535 || acceptedTermsAndConditionsVersion < 0 ||
acceptedTermsAndConditionsVersion > 65535)
{
jclass illegalArgumentExceptionClass = env->FindClass("java/lang/IllegalArgumentException");
if (illegalArgumentExceptionClass != nullptr)
{
env->ThrowNew(illegalArgumentExceptionClass, "Input values must be in the range 0 to 65535.");
}
return;
}

AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);

// Retrieve the commissioning parameters
CommissioningParameters commissioningParams = wrapper->GetAutoCommissioner()->GetCommissioningParameters();

TermsAndConditionsAcknowledgement termsAndConditionsAcknowledgement;
termsAndConditionsAcknowledgement.acceptedTermsAndConditions = static_cast<uint16_t>(acceptedTermsAndConditions);
termsAndConditionsAcknowledgement.acceptedTermsAndConditionsVersion = static_cast<uint16_t>(acceptedTermsAndConditionsVersion);

// Update the commissioning parameters with the new terms and conditions
commissioningParams.SetTermsAndConditionsAcknowledgement(termsAndConditionsAcknowledgement);

// Set the updated commissioning parameters back to the wrapper
wrapper->GetAutoCommissioner()->SetCommissioningParameters(commissioningParams);
}

JNI_METHOD(void, updateCommissioningICDRegistrationInfo)
(JNIEnv * env, jobject self, jlong handle, jobject icdRegistrationInfo)
{
Expand Down
Loading

0 comments on commit 96c0095

Please sign in to comment.