-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement kotlin setup-payload phase II (#26963)
* Implement kotlin setuppayload phase II * Address review comments * Remove extra space for null terminator
- Loading branch information
1 parent
7db672a
commit 3425061
Showing
9 changed files
with
1,325 additions
and
107 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
28
src/controller/java/src/chip/onboardingpayload/CommissioningFlow.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* | ||
* | ||
* Copyright (c) 2023 Project CHIP Authors | ||
* | ||
* 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 chip.onboardingpayload | ||
|
||
/** | ||
* Enum values for possible flows for out-of-box commissioning that a Matter device manufacturer | ||
* may select for a given product. | ||
*/ | ||
enum class CommissioningFlow(val value: Int) { | ||
STANDARD(0), // Device automatically enters pairing mode upon power-up | ||
USER_ACTION_REQUIRED(1), // Device requires a user interaction to enter pairing mode | ||
CUSTOM(2) // Commissioning steps should be retrieved from the distributed compliance ledger | ||
} |
152 changes: 152 additions & 0 deletions
152
src/controller/java/src/chip/onboardingpayload/ManualOnboardingPayloadGenerator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
/* | ||
* | ||
* Copyright (c) 2023 Project CHIP Authors | ||
* | ||
* 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 chip.onboardingpayload | ||
|
||
class ManualOnboardingPayloadGenerator(private val payloadContents: OnboardingPayload) { | ||
private var skipPayloadValidation = false | ||
private var forceShortCode = false | ||
|
||
fun setSkipPayloadValidation(allow: Boolean) { | ||
skipPayloadValidation = allow | ||
} | ||
|
||
fun setForceShortCode(useShort: Boolean) { | ||
forceShortCode = useShort | ||
} | ||
|
||
fun payloadDecimalStringRepresentation(): String { | ||
// One extra char for the check digit. | ||
val decimalString = CharArray(kManualSetupLongCodeCharLength + 1) | ||
|
||
if (kManualSetupCodeChunk1CharLength + kManualSetupCodeChunk2CharLength + kManualSetupCodeChunk3CharLength != | ||
kManualSetupShortCodeCharLength) { | ||
throw OnboardingPayloadException("Manual code length mismatch (short)") | ||
} | ||
|
||
if (kManualSetupShortCodeCharLength + kManualSetupVendorIdCharLength + kManualSetupProductIdCharLength != | ||
kManualSetupLongCodeCharLength) { | ||
throw OnboardingPayloadException("Manual code length mismatch (long)") | ||
} | ||
|
||
if (kManualSetupChunk1DiscriminatorMsbitsLength + kManualSetupChunk2DiscriminatorLsbitsLength != | ||
kManualSetupDiscriminatorFieldLengthInBits) { | ||
throw OnboardingPayloadException("Discriminator length is not valid") | ||
} | ||
|
||
if (kManualSetupChunk2PINCodeLsbitsLength + kManualSetupChunk3PINCodeMsbitsLength != | ||
kSetupPINCodeFieldLengthInBits) { | ||
throw OnboardingPayloadException("PIN code length is not valid") | ||
} | ||
|
||
val useLongCode = (payloadContents.commissioningFlow != CommissioningFlow.STANDARD.value) && !forceShortCode | ||
|
||
if (!skipPayloadValidation && !payloadContents.isValidManualCode()) { | ||
throw OnboardingPayloadException("The Manual Pairing code is not valid") | ||
} | ||
|
||
// Add two for the check digit and null terminator. | ||
if ((useLongCode && decimalString.size < kManualSetupLongCodeCharLength + 1) || | ||
(!useLongCode && decimalString.size < kManualSetupShortCodeCharLength + 1)) { | ||
throw OnboardingPayloadException("The decimalString has insufficient size") | ||
} | ||
|
||
val chunk1 = chunk1PayloadRepresentation(payloadContents) | ||
val chunk2 = chunk2PayloadRepresentation(payloadContents) | ||
val chunk3 = chunk3PayloadRepresentation(payloadContents) | ||
|
||
var offset = 0 | ||
|
||
decimalStringWithPadding(decimalString.sliceArray(offset until offset + kManualSetupCodeChunk1CharLength), chunk1) | ||
offset += kManualSetupCodeChunk1CharLength | ||
decimalStringWithPadding(decimalString.sliceArray(offset until offset + kManualSetupCodeChunk2CharLength), chunk2) | ||
offset += kManualSetupCodeChunk2CharLength | ||
decimalStringWithPadding(decimalString.sliceArray(offset until offset + kManualSetupCodeChunk3CharLength), chunk3) | ||
offset += kManualSetupCodeChunk3CharLength | ||
|
||
if (useLongCode) { | ||
decimalStringWithPadding(decimalString.sliceArray(offset until offset + kManualSetupVendorIdCharLength), payloadContents.vendorId) | ||
offset += kManualSetupVendorIdCharLength | ||
decimalStringWithPadding(decimalString.sliceArray(offset until offset + kManualSetupProductIdCharLength), payloadContents.productId) | ||
offset += kManualSetupProductIdCharLength | ||
} | ||
|
||
val checkDigit = Verhoeff10.charToVal(Verhoeff10.computeCheckChar(decimalString.concatToString())) | ||
decimalStringWithPadding(decimalString.sliceArray(offset until offset + 2), checkDigit) | ||
offset += 1 | ||
|
||
// Reduce decimalString size to be the size of written data and to not include null-terminator. In Kotlin, there is no direct | ||
// method to resize an array.We use copyOfRange(0, offset) to create a new CharArray that includes only the elements from index | ||
// 0 to offset-1, effectively reducing the size of the buffer. | ||
decimalString.copyOfRange(0, offset) | ||
|
||
return decimalString.joinToString() | ||
} | ||
|
||
private fun chunk1PayloadRepresentation(payload: OnboardingPayload): Int { | ||
/* <1 digit> Represents: | ||
* - <bits 1..0> Discriminator <bits 11.10> | ||
* - <bit 2> VID/PID present flag | ||
*/ | ||
val discriminatorShift = (kManualSetupDiscriminatorFieldLengthInBits - kManualSetupChunk1DiscriminatorMsbitsLength) | ||
val discriminatorMask: Int = (1 shl kManualSetupChunk1DiscriminatorMsbitsLength) - 1 | ||
|
||
if (kManualSetupChunk1VidPidPresentBitPos < | ||
kManualSetupChunk1DiscriminatorMsbitsPos + kManualSetupChunk1DiscriminatorMsbitsLength) { | ||
throw OnboardingPayloadException("Discriminator won't fit") | ||
} | ||
|
||
val discriminatorChunk: Int = (payload.getShortDiscriminatorValue() shr discriminatorShift) and discriminatorMask | ||
val vidPidPresentFlag: Int = if (payload.commissioningFlow != CommissioningFlow.STANDARD.value) 1 else 0 | ||
|
||
return (discriminatorChunk shl kManualSetupChunk1DiscriminatorMsbitsPos) or | ||
(vidPidPresentFlag shl kManualSetupChunk1VidPidPresentBitPos) | ||
} | ||
|
||
private fun chunk2PayloadRepresentation(payload: OnboardingPayload): Int { | ||
/* <5 digits> Represents: | ||
* - <bits 13..0> PIN Code <bits 13..0> | ||
* - <bits 15..14> Discriminator <bits 9..8> | ||
*/ | ||
val discriminatorMask: Int = (1 shl kManualSetupChunk2DiscriminatorLsbitsLength) - 1 | ||
val pincodeMask: Int = (1 shl kManualSetupChunk2PINCodeLsbitsLength) - 1 | ||
|
||
val discriminatorChunk: Int = payload.getShortDiscriminatorValue() and discriminatorMask | ||
|
||
return ((payload.setupPinCode.toInt() and pincodeMask) shl kManualSetupChunk2PINCodeLsbitsPos) or | ||
(discriminatorChunk shl kManualSetupChunk2DiscriminatorLsbitsPos) | ||
} | ||
|
||
private fun chunk3PayloadRepresentation(payload: OnboardingPayload): Int { | ||
/* <4 digits> Represents: | ||
* - <bits 12..0> PIN Code <bits 26..14> | ||
*/ | ||
val pincodeShift: Int = (kSetupPINCodeFieldLengthInBits - kManualSetupChunk3PINCodeMsbitsLength) | ||
val pincodeMask: Int = (1 shl kManualSetupChunk3PINCodeMsbitsLength) - 1 | ||
|
||
return ((payload.setupPinCode.toInt() shr pincodeShift) and pincodeMask) shl kManualSetupChunk3PINCodeMsbitsPos | ||
} | ||
|
||
private fun decimalStringWithPadding(buffer: CharArray, number: Int): Unit { | ||
val len = buffer.size - 1 | ||
val retval = String.format("%0${len}d", number).toCharArray(buffer, 0, buffer.size) | ||
|
||
if (retval.size >= buffer.size) { | ||
throw OnboardingPayloadException("The outBuffer has insufficient size") | ||
} | ||
} | ||
} |
Oops, something went wrong.