From 9a96a991c347efb4d647363dc39fa3da75f006d7 Mon Sep 17 00:00:00 2001 From: Kevin Coppock Date: Thu, 20 Jan 2022 19:12:14 -0600 Subject: [PATCH] Surface a NetworkLocation on Android for ChipDeviceController Currently there is a method to getIpAddress(), but we also need to know the port. This adds a Java wrapper class that combines both properties, and the JNI bindings to resolve. Tested: - Commissioned an m5stack, and verified that the resolved NetworkLocation on the Java side matched Addr 0 logged from DIS after mDNS discovery completed. --- .../java/CHIPDeviceController-JNI.cpp | 57 +++++++++++++++++++ .../ChipDeviceController.java | 13 +++++ .../devicecontroller/NetworkLocation.java | 28 +++++++++ 3 files changed, 98 insertions(+) create mode 100644 src/controller/java/src/chip/devicecontroller/NetworkLocation.java diff --git a/src/controller/java/CHIPDeviceController-JNI.cpp b/src/controller/java/CHIPDeviceController-JNI.cpp index 5adb06dd43f17c..3f433f82c8f323 100644 --- a/src/controller/java/CHIPDeviceController-JNI.cpp +++ b/src/controller/java/CHIPDeviceController-JNI.cpp @@ -62,6 +62,7 @@ using namespace chip::Controller; static void * IOThreadMain(void * arg); static CHIP_ERROR N2J_PaseVerifierParams(JNIEnv * env, jlong setupPincode, jint passcodeId, jbyteArray pakeVerifier, jobject & outParams); +static CHIP_ERROR N2J_NetworkLocation(JNIEnv * env, jstring ipAddress, jint port, jobject & outLocation); namespace { @@ -450,6 +451,40 @@ JNI_METHOD(jstring, getIpAddress)(JNIEnv * env, jobject self, jlong handle, jlon return env->NewStringUTF(addrStr); } +JNI_METHOD(jobject, getNetworkLocation)(JNIEnv * env, jobject self, jlong handle, jlong deviceId) +{ + chip::DeviceLayer::StackLock lock; + AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); + + chip::Inet::IPAddress addr; + uint16_t port; + jobject networkLocation; + char addrStr[50]; + + CHIP_ERROR err = + wrapper->Controller()->GetPeerAddressAndPort(PeerId() + .SetCompressedFabricId(wrapper->Controller()->GetCompressedFabricId()) + .SetNodeId(static_cast(deviceId)), + addr, port); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(Controller, "Failed to get device address."); + JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); + } + + addr.ToString(addrStr); + + err = N2J_NetworkLocation(env, env->NewStringUTF(addrStr), static_cast(port), networkLocation); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Controller, "Failed to create NetworkLocation"); + JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); + } + + return networkLocation; +} + JNI_METHOD(jlong, getCompressedFabricId)(JNIEnv * env, jobject self, jlong handle) { chip::DeviceLayer::StackLock lock; @@ -621,3 +656,25 @@ CHIP_ERROR N2J_PaseVerifierParams(JNIEnv * env, jlong setupPincode, jint passcod exit: return err; } + +CHIP_ERROR N2J_NetworkLocation(JNIEnv * env, jstring ipAddress, jint port, jobject & outLocation) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + jmethodID constructor; + jclass locationClass; + + err = JniReferences::GetInstance().GetClassRef(env, "chip/devicecontroller/NetworkLocation", locationClass); + JniClass networkLocationClass(locationClass); + SuccessOrExit(err); + + env->ExceptionClear(); + constructor = env->GetMethodID(locationClass, "", "(Ljava/lang/String;I)V"); + VerifyOrExit(constructor != nullptr, err = CHIP_JNI_ERROR_METHOD_NOT_FOUND); + + outLocation = (jobject) env->NewObject(locationClass, constructor, ipAddress, port); + + VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); +exit: + return err; +} + diff --git a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java index 5f85dede15a222..e9829c373b687b 100644 --- a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java +++ b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java @@ -21,6 +21,7 @@ import android.util.Log; import androidx.annotation.Nullable; import chip.devicecontroller.GetConnectedDeviceCallbackJni.GetConnectedDeviceCallback; +import controller.java.src.chip.devicecontroller.NetworkLocation; /** Controller to interact with the CHIP device. */ public class ChipDeviceController { @@ -259,6 +260,16 @@ public String getIpAddress(long deviceId) { return getIpAddress(deviceControllerPtr, deviceId); } + /** + * Returns the {@link NetworkLocation} at which the given {@code deviceId} has been found. + * + * @param deviceId the 64-bit node ID of the device + * @throws ChipDeviceControllerException if the device location could not be resolved + */ + public NetworkLocation getNetworkLocation(long deviceId) { + return getNetworkLocation(deviceControllerPtr, deviceId); + } + public long getCompressedFabricId() { return getCompressedFabricId(deviceControllerPtr); } @@ -346,6 +357,8 @@ private native void getConnectedDevicePointer( private native String getIpAddress(long deviceControllerPtr, long deviceId); + private native NetworkLocation getNetworkLocation(long deviceControllerPtr, long deviceId); + private native long getCompressedFabricId(long deviceControllerPtr); private native void updateDevice(long deviceControllerPtr, long fabricId, long deviceId); diff --git a/src/controller/java/src/chip/devicecontroller/NetworkLocation.java b/src/controller/java/src/chip/devicecontroller/NetworkLocation.java new file mode 100644 index 00000000000000..2f1380f98d8e96 --- /dev/null +++ b/src/controller/java/src/chip/devicecontroller/NetworkLocation.java @@ -0,0 +1,28 @@ +package chip.devicecontroller; + +import java.util.Locale; + +/** Represents a location on an operational network. */ +public final class NetworkLocation { + private final String ipAddress; + private final int port; + + public NetworkLocation(String ipAddress, int port) { + this.ipAddress = ipAddress; + this.port = port; + } + + /** Returns the IP address (e.g. fe80::3e61:5ff:fe0c:89f8). */ + public String getIpAddress() { + return ipAddress; + } + + public int getPort() { + return port; + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "%s[%d]", ipAddress, port); + } +}