Skip to content

Commit

Permalink
[Android] Implement pairing with code API (#26324)
Browse files Browse the repository at this point in the history
* Add pairWithCode API in Android

* restyle

* fix build error

* Add java controller function
  • Loading branch information
joonhaengHeo authored and pull[bot] committed Feb 16, 2024
1 parent 881a621 commit 1178800
Show file tree
Hide file tree
Showing 17 changed files with 642 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ object ChipClient {
if (!this::androidPlatform.isInitialized && context != null) {
//force ChipDeviceController load jni
ChipDeviceController.loadJni()
androidPlatform = AndroidChipPlatform(AndroidBleManager(), PreferencesKeyValueStoreManager(context), PreferencesConfigurationManager(context), NsdManagerServiceResolver(context), NsdManagerServiceBrowser(context), ChipMdnsCallbackImpl(), DiagnosticDataProviderImpl(context))
androidPlatform = AndroidChipPlatform(AndroidBleManager(context), PreferencesKeyValueStoreManager(context), PreferencesConfigurationManager(context), NsdManagerServiceResolver(context), NsdManagerServiceBrowser(context), ChipMdnsCallbackImpl(), DiagnosticDataProviderImpl(context))
}

return androidPlatform
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,23 @@ class Argument {
isValidArgument = numLong.toInt() >= minValue && numLong.toInt() <= maxValue
}

ArgumentType.STRING -> {
val stringBuffer = this.value as StringBuffer
stringBuffer.append(value)
val str = stringBuffer.toString()
isValidArgument = value == str
}

ArgumentType.BOOL -> {
val atomicBoolean = this.value as AtomicBoolean
try {
atomicBoolean.set(value.toBoolean())
isValidArgument = true
} catch (e: Exception) {
isValidArgument = false
}
}

ArgumentType.ADDRESS -> isValidArgument = try {
val ipAddress = this.value as IPAddress
ipAddress.setAddress(InetAddress.getByName(value))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,15 @@ import com.matter.controller.commands.common.CredentialsIssuer

class PairCodeCommand(controller: ChipDeviceController, credsIssue: CredentialsIssuer?) :
PairingCommand(controller, "code", credsIssue, PairingModeType.CODE, PairingNetworkType.NONE) {
override fun runCommand() {}
override fun runCommand() {
currentCommissioner()
.pairDeviceWithCode(
getNodeId(),
getOnboardingPayload(),
null,
getWifiNetworkCredentials(),
)
currentCommissioner().setCompletionListener(this)
waitCompleteMs(getTimeoutMillis())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,15 @@ import com.matter.controller.commands.common.CredentialsIssuer

class PairCodeThreadCommand(controller: ChipDeviceController, credsIssue: CredentialsIssuer?) :
PairingCommand(controller, "code-thread", credsIssue, PairingModeType.CODE, PairingNetworkType.THREAD) {
override fun runCommand() {}
override fun runCommand() {
currentCommissioner()
.pairDeviceWithCode(
getNodeId(),
getOnboardingPayload(),
null,
getThreadNetworkCredentials(),
)
currentCommissioner().setCompletionListener(this)
waitCompleteMs(getTimeoutMillis())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,15 @@ import com.matter.controller.commands.common.CredentialsIssuer

class PairCodeWifiCommand(controller: ChipDeviceController, credsIssue: CredentialsIssuer?) :
PairingCommand(controller, "code-wifi", credsIssue, PairingModeType.CODE, PairingNetworkType.WIFI) {
override fun runCommand() {}
override fun runCommand() {
currentCommissioner()
.pairDeviceWithCode(
getNodeId(),
getOnboardingPayload(),
null,
getWifiNetworkCredentials(),
)
currentCommissioner().setCompletionListener(this)
waitCompleteMs(getTimeoutMillis())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package com.matter.controller.commands.pairing

import chip.devicecontroller.ChipDeviceController
import chip.devicecontroller.NetworkCredentials
import com.matter.controller.commands.common.CredentialsIssuer
import com.matter.controller.commands.common.IPAddress
import com.matter.controller.commands.common.MatterCommand
Expand Down Expand Up @@ -227,6 +228,22 @@ abstract class PairingCommand(
return timeoutMillis.get()
}

fun getOnboardingPayload(): String {
return onboardingPayload.toString()
}

fun getWifiNetworkCredentials(): NetworkCredentials {
return NetworkCredentials.forWiFi(NetworkCredentials.WiFiCredentials(ssid.toString(), password.toString()))
}

fun getThreadNetworkCredentials(): NetworkCredentials {
return NetworkCredentials.forThread(NetworkCredentials.ThreadCredentials(operationalDataset.toString().hexToByteArray()))
}

private fun String.hexToByteArray(): ByteArray {
return chunked(2).map { byteStr -> byteStr.toUByte(16).toByte() }.toByteArray()
}

companion object {
private val logger = Logger.getLogger(PairingCommand::class.java.name)
}
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 @@ -653,6 +653,42 @@ JNI_METHOD(void, pairDeviceWithAddress)
}
}

JNI_METHOD(void, pairDeviceWithCode)
(JNIEnv * env, jobject self, jlong handle, jlong deviceId, jstring setUpCode, jbyteArray csrNonce, jobject networkCredentials)
{
chip::DeviceLayer::StackLock lock;
CHIP_ERROR err = CHIP_NO_ERROR;
AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);

ChipLogProgress(Controller, "pairDeviceWithCode() called");

JniUtfString setUpCodeJniString(env, setUpCode);

CommissioningParameters commissioningParams = wrapper->GetCommissioningParameters();
if (csrNonce != nullptr)
{
JniByteArray jniCsrNonce(env, csrNonce);
commissioningParams.SetCSRNonce(jniCsrNonce.byteSpan());
}

if (networkCredentials != nullptr)
{
wrapper->ApplyNetworkCredentials(commissioningParams, networkCredentials);
}

if (wrapper->GetDeviceAttestationDelegateBridge() != nullptr)
{
commissioningParams.SetDeviceAttestationDelegate(wrapper->GetDeviceAttestationDelegateBridge());
}
err = wrapper->Controller()->PairDevice(deviceId, setUpCodeJniString.c_str(), commissioningParams, DiscoveryType::kAll);

if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Failed to pair the device.");
JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err);
}
}

JNI_METHOD(void, establishPaseConnection)(JNIEnv * env, jobject self, jlong handle, jlong deviceId, jint connObj, jlong pinCode)
{
chip::DeviceLayer::StackLock lock;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,23 @@ public void pairDeviceWithAddress(
deviceControllerPtr, deviceId, address, port, discriminator, pinCode, csrNonce);
}

/**
* Pair a device connected using the scanned QR code or manual entry code.
*
* @param deviceId the node ID to assign to the device
* @param setupCode the scanned QR code or manual entry code
* @param csrNonce the 32-byte CSR nonce to use, or null if we want to use an internally randomly
* generated CSR nonce.
* @param networkCredentials the credentials (Wi-Fi or Thread) to be provisioned
*/
public void pairDeviceWithCode(
long deviceId,
String setupCode,
@Nullable byte[] csrNonce,
@Nullable NetworkCredentials networkCredentials) {
pairDeviceWithCode(deviceControllerPtr, deviceId, setupCode, csrNonce, networkCredentials);
}

public void establishPaseConnection(long deviceId, int connId, long setupPincode) {
if (connectionId == 0) {
connectionId = connId;
Expand Down Expand Up @@ -996,6 +1013,13 @@ private native void pairDeviceWithAddress(
long pinCode,
@Nullable byte[] csrNonce);

private native void pairDeviceWithCode(
long deviceControllerPtr,
long deviceId,
String setupCode,
@Nullable byte[] csrNonce,
@Nullable NetworkCredentials networkCredentials);

private native void establishPaseConnection(
long deviceControllerPtr, long deviceId, int connId, long setupPincode);

Expand Down
5 changes: 5 additions & 0 deletions src/platform/android/AndroidChipPlatform-JNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

#include "AndroidChipPlatform-JNI.h"
#include "BLEManagerImpl.h"
#include "BleConnectCallback-JNI.h"
#include "CommissionableDataProviderImpl.h"
#include "DiagnosticDataProviderImpl.h"
#include "DnssdImpl.h"
Expand Down Expand Up @@ -84,6 +85,9 @@ CHIP_ERROR AndroidChipPlatformJNI_OnLoad(JavaVM * jvm, void * reserved)
SuccessOrExit(err);
ChipLogProgress(DeviceLayer, "Java class references loaded.");

err = BleConnectCallbackJNI_OnLoad(jvm, reserved);
SuccessOrExit(err);

chip::InitializeTracing();

exit:
Expand All @@ -99,6 +103,7 @@ CHIP_ERROR AndroidChipPlatformJNI_OnLoad(JavaVM * jvm, void * reserved)
void AndroidChipPlatformJNI_OnUnload(JavaVM * jvm, void * reserved)
{
ChipLogProgress(DeviceLayer, "AndroidChipPlatform JNI_OnUnload() called");
BleConnectCallbackJNI_OnUnload(jvm, reserved);
chip::Platform::MemoryShutdown();
}

Expand Down
22 changes: 20 additions & 2 deletions src/platform/android/BLEManagerImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ void BLEManagerImpl::InitializeWithObject(jobject manager)
env->ExceptionClear();
}

mOnNewConnectionMethod = env->GetMethodID(BLEManagerClass, "onNewConnection", "(I)V");
mOnNewConnectionMethod = env->GetMethodID(BLEManagerClass, "onNewConnection", "(IZJJ)V");
if (mOnNewConnectionMethod == nullptr)
{
ChipLogError(DeviceLayer, "Failed to access BLEManager 'onNewConnection' method");
Expand Down Expand Up @@ -186,16 +186,19 @@ bool BLEManagerImpl::_IsAdvertising()

CHIP_ERROR BLEManagerImpl::_SetAdvertisingMode(BLEAdvertisingMode mode)
{
ChipLogDetail(DeviceLayer, "%s, %u", __FUNCTION__, static_cast<uint8_t>(mode));
return CHIP_ERROR_NOT_IMPLEMENTED;
}

CHIP_ERROR BLEManagerImpl::_GetDeviceName(char * buf, size_t bufSize)
{
ChipLogDetail(DeviceLayer, "%s, %s", __FUNCTION__, buf);
return CHIP_ERROR_NOT_IMPLEMENTED;
}

CHIP_ERROR BLEManagerImpl::_SetDeviceName(const char * deviceName)
{
ChipLogDetail(DeviceLayer, "%s, %s", __FUNCTION__, deviceName);
return CHIP_ERROR_NOT_IMPLEMENTED;
}

Expand Down Expand Up @@ -449,6 +452,18 @@ void BLEManagerImpl::NotifyChipConnectionClosed(BLE_CONNECTION_OBJECT conId)

// ===== start implement virtual methods on BleConnectionDelegate.

void BLEManagerImpl::OnConnectSuccess(void * appState, BLE_CONNECTION_OBJECT connObj)
{
chip::DeviceLayer::StackLock lock;
BleConnectionDelegate::OnConnectionComplete(appState, connObj);
}

void BLEManagerImpl::OnConnectFailed(void * appState, CHIP_ERROR err)
{
chip::DeviceLayer::StackLock lock;
BleConnectionDelegate::OnConnectionError(appState, err);
}

void BLEManagerImpl::NewConnection(BleLayer * bleLayer, void * appState, const SetupDiscriminator & connDiscriminator)
{
chip::DeviceLayer::StackUnlock unlock;
Expand All @@ -474,7 +489,10 @@ void BLEManagerImpl::NewConnection(BleLayer * bleLayer, void * appState, const S
{
discriminator = connDiscriminator.GetLongValue();
}
env->CallVoidMethod(mBLEManagerObject, mOnNewConnectionMethod, static_cast<jint>(discriminator));

env->CallVoidMethod(mBLEManagerObject, mOnNewConnectionMethod, static_cast<jint>(discriminator),
static_cast<jboolean>(connDiscriminator.IsShortDiscriminator()), reinterpret_cast<jlong>(this),
reinterpret_cast<jlong>(appState));
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);

exit:
Expand Down
3 changes: 3 additions & 0 deletions src/platform/android/BLEManagerImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ class BLEManagerImpl final : public BLEManager,

void InitializeWithObject(jobject managerObject);

void OnConnectSuccess(void * appState, BLE_CONNECTION_OBJECT connObj);
void OnConnectFailed(void * appState, CHIP_ERROR err);

private:
// ===== Members that implement the BLEManager internal interface.

Expand Down
2 changes: 2 additions & 0 deletions src/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ static_library("android") {
"AndroidConfig.h",
"BLEManagerImpl.cpp",
"BLEManagerImpl.h",
"BleConnectCallback-JNI.cpp",
"BlePlatformConfig.h",
"CHIPDevicePlatformEvent.h",
"CHIPP256KeypairBridge.cpp",
Expand Down Expand Up @@ -81,6 +82,7 @@ android_library("java") {
"java/chip/platform/AndroidChipPlatform.java",
"java/chip/platform/AndroidChipPlatformException.java",
"java/chip/platform/BleCallback.java",
"java/chip/platform/BleConnectCallback.java",
"java/chip/platform/BleManager.java",
"java/chip/platform/ChipMdnsCallback.java",
"java/chip/platform/ChipMdnsCallbackImpl.java",
Expand Down
54 changes: 54 additions & 0 deletions src/platform/android/BleConnectCallback-JNI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* 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.
*
*/

#include <jni.h>
#include <lib/core/CHIPError.h>
#include <platform/ConnectivityManager.h>

#include "BLEManagerImpl.h"

using namespace chip;
using namespace chip::DeviceLayer::Internal;

#define JNI_METHOD(RETURN, METHOD_NAME) extern "C" JNIEXPORT RETURN JNICALL Java_chip_platform_BleConnectCallback_##METHOD_NAME

CHIP_ERROR BleConnectCallbackJNI_OnLoad(JavaVM * jvm, void * reserved)
{
ChipLogProgress(DeviceLayer, "BleConnectCallbackJNI_OnLoad");
return CHIP_NO_ERROR;
}

void BleConnectCallbackJNI_OnUnload(JavaVM * jvm, void * reserved) {}

JNI_METHOD(void, onConnectSuccess)(JNIEnv * env, jobject self, jlong managerImplPtr, jlong appStatePtr, jint connId)
{
#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
BLEManagerImpl * impl = reinterpret_cast<BLEManagerImpl *>(managerImplPtr);
void * appState = reinterpret_cast<void *>(appStatePtr);
impl->OnConnectSuccess(appState, reinterpret_cast<BLE_CONNECTION_OBJECT>(connId));
#endif
}

JNI_METHOD(void, onConnectFailed)(JNIEnv * env, jobject self, jlong managerImplPtr, jlong appStatePtr)
{
#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
BLEManagerImpl * impl = reinterpret_cast<BLEManagerImpl *>(managerImplPtr);
void * appState = reinterpret_cast<void *>(appStatePtr);
impl->OnConnectFailed(appState, BLE_ERROR_NO_CONNECTION_RECEIVED_CALLBACK);
#endif
}
29 changes: 29 additions & 0 deletions src/platform/android/BleConnectCallback-JNI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2020-2021 Project CHIP Authors
* All rights reserved.
*
* 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.
*
*/

/**
* @file
* Implementation of JNI bridge for CHIP Device Controller for Android apps
*
*/

#pragma once

CHIP_ERROR BleConnectCallbackJNI_OnLoad(JavaVM * jvm, void * reserved);

void BleConnectCallbackJNI_OnUnload(JavaVM * jvm, void * reserved);
Loading

0 comments on commit 1178800

Please sign in to comment.