diff --git a/examples/tv-app/android/App/app/src/main/java/com/tcl/chip/chiptvserver/service/MatterServant.java b/examples/tv-app/android/App/app/src/main/java/com/tcl/chip/chiptvserver/service/MatterServant.java index d6798354058b93..a5033a724f272c 100644 --- a/examples/tv-app/android/App/app/src/main/java/com/tcl/chip/chiptvserver/service/MatterServant.java +++ b/examples/tv-app/android/App/app/src/main/java/com/tcl/chip/chiptvserver/service/MatterServant.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2021-2022 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. + * + */ package com.tcl.chip.chiptvserver.service; import android.content.Context; @@ -6,6 +23,7 @@ import chip.platform.AndroidBleManager; import chip.platform.AndroidChipPlatform; import chip.platform.ChipMdnsCallbackImpl; +import chip.platform.DiagnosticDataProviderImpl; import chip.platform.NsdManagerServiceResolver; import chip.platform.PreferencesConfigurationManager; import chip.platform.PreferencesKeyValueStoreManager; @@ -69,7 +87,8 @@ public void init(@NonNull Context context) { new PreferencesKeyValueStoreManager(applicationContext), new PreferencesConfigurationManager(applicationContext), new NsdManagerServiceResolver(applicationContext), - new ChipMdnsCallbackImpl()); + new ChipMdnsCallbackImpl(), + new DiagnosticDataProviderImpl(applicationContext)); chipAppServer = new ChipAppServer(); chipAppServer.startApp(); diff --git a/src/android/CHIPTest/app/src/main/java/com/tcl/chip/chiptest/MainActivity.kt b/src/android/CHIPTest/app/src/main/java/com/tcl/chip/chiptest/MainActivity.kt index d1f4fa14ec8ff7..b969cb9fce1eac 100644 --- a/src/android/CHIPTest/app/src/main/java/com/tcl/chip/chiptest/MainActivity.kt +++ b/src/android/CHIPTest/app/src/main/java/com/tcl/chip/chiptest/MainActivity.kt @@ -53,6 +53,6 @@ class MainActivity : AppCompatActivity() { }.start() } - AndroidChipPlatform(AndroidBleManager(), PreferencesKeyValueStoreManager(this), PreferencesConfigurationManager(this), NsdManagerServiceResolver(this), ChipMdnsCallbackImpl()) + AndroidChipPlatform(AndroidBleManager(), PreferencesKeyValueStoreManager(this), PreferencesConfigurationManager(this), NsdManagerServiceResolver(this), ChipMdnsCallbackImpl(), DiagnosticDataProviderImpl(this)) } } diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt index 7fcdfeb46f706f..d4ed41a5347f66 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt @@ -24,6 +24,7 @@ import chip.devicecontroller.GetConnectedDeviceCallbackJni.GetConnectedDeviceCal import chip.platform.AndroidBleManager import chip.platform.AndroidChipPlatform import chip.platform.ChipMdnsCallbackImpl +import chip.platform.DiagnosticDataProviderImpl import chip.platform.NsdManagerServiceResolver import chip.platform.PreferencesConfigurationManager import chip.platform.PreferencesKeyValueStoreManager @@ -50,7 +51,7 @@ object ChipClient { if (!this::androidPlatform.isInitialized && context != null) { //force ChipDeviceController load jni ChipDeviceController.loadJni() - androidPlatform = AndroidChipPlatform(AndroidBleManager(), PreferencesKeyValueStoreManager(context), PreferencesConfigurationManager(context), NsdManagerServiceResolver(context), ChipMdnsCallbackImpl()) + androidPlatform = AndroidChipPlatform(AndroidBleManager(), PreferencesKeyValueStoreManager(context), PreferencesConfigurationManager(context), NsdManagerServiceResolver(context), ChipMdnsCallbackImpl(), DiagnosticDataProviderImpl(context)) } return androidPlatform } diff --git a/src/platform/android/AndroidChipPlatform-JNI.cpp b/src/platform/android/AndroidChipPlatform-JNI.cpp index dc10f5d1c5c932..0848201a981ad6 100644 --- a/src/platform/android/AndroidChipPlatform-JNI.cpp +++ b/src/platform/android/AndroidChipPlatform-JNI.cpp @@ -35,6 +35,7 @@ #include "AndroidChipPlatform-JNI.h" #include "BLEManagerImpl.h" +#include "DiagnosticDataProviderImpl.h" #include "DnssdImpl.h" using namespace chip; @@ -221,6 +222,13 @@ JNI_METHOD(void, setConfigurationManager)(JNIEnv * env, jclass self, jobject man } } +// for DiagnosticDataProviderManager +JNI_METHOD(void, setDiagnosticDataProviderManager)(JNIEnv * env, jclass self, jobject manager) +{ + chip::DeviceLayer::StackLock lock; + chip::DeviceLayer::DiagnosticDataProviderImpl::GetDefaultInstance().InitializeWithObject(manager); +} + // for ServiceResolver JNI_METHOD(void, nativeSetServiceResolver)(JNIEnv * env, jclass self, jobject resolver, jobject chipMdnsCallback) { diff --git a/src/platform/android/BUILD.gn b/src/platform/android/BUILD.gn index dcdb3690ab048c..c6ab8699b96cf0 100644 --- a/src/platform/android/BUILD.gn +++ b/src/platform/android/BUILD.gn @@ -65,7 +65,10 @@ static_library("android") { android_library("java") { output_name = "AndroidPlatform.jar" - deps = [ ":android_sdk" ] + deps = [ + ":android_sdk", + "${chip_root}/third_party/android_deps:annotation", + ] data_deps = [ ":android" ] sources = [ @@ -77,7 +80,10 @@ android_library("java") { "java/chip/platform/ChipMdnsCallback.java", "java/chip/platform/ChipMdnsCallbackImpl.java", "java/chip/platform/ConfigurationManager.java", + "java/chip/platform/DiagnosticDataProvider.java", + "java/chip/platform/DiagnosticDataProviderImpl.java", "java/chip/platform/KeyValueStoreManager.java", + "java/chip/platform/NetworkInterface.java", "java/chip/platform/NsdManagerServiceResolver.java", "java/chip/platform/PreferencesConfigurationManager.java", "java/chip/platform/PreferencesKeyValueStoreManager.java", diff --git a/src/platform/android/DiagnosticDataProviderImpl.cpp b/src/platform/android/DiagnosticDataProviderImpl.cpp index a7015f00895253..09a580062d71e0 100644 --- a/src/platform/android/DiagnosticDataProviderImpl.cpp +++ b/src/platform/android/DiagnosticDataProviderImpl.cpp @@ -21,12 +21,21 @@ * for android platform. */ +#include +#include +#include #include +#include "DiagnosticDataProviderImpl.h" +#include #include +#include +#include #include #include -#include +#include + +using namespace ::chip::app::Clusters::GeneralDiagnostics; namespace chip { namespace DeviceLayer { @@ -37,5 +46,153 @@ DiagnosticDataProviderImpl & DiagnosticDataProviderImpl::GetDefaultInstance() return sInstance; } +void DiagnosticDataProviderImpl::InitializeWithObject(jobject manager) +{ + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + VerifyOrReturn(env != nullptr, + ChipLogError(DeviceLayer, "Failed to GetEnvForCurrentThread for DiagnosticDataProviderManagerImpl")); + + mDiagnosticDataProviderManagerObject = env->NewGlobalRef(manager); + VerifyOrReturn(mDiagnosticDataProviderManagerObject != nullptr, + ChipLogError(DeviceLayer, "Failed to NewGlobalRef DiagnosticDataProviderManager")); + + jclass DiagnosticDataProviderManagerClass = env->GetObjectClass(manager); + VerifyOrReturn(DiagnosticDataProviderManagerClass != nullptr, + ChipLogError(DeviceLayer, "Failed to get DiagnosticDataProviderManager Java class")); + + mGetRebootCountMethod = env->GetMethodID(DiagnosticDataProviderManagerClass, "getRebootCount", "()I"); + if (mGetRebootCountMethod == nullptr) + { + ChipLogError(DeviceLayer, "Failed to access DiagnosticDataProviderManager 'getRebootCount' method"); + env->ExceptionClear(); + } + + mGetNifMethod = + env->GetMethodID(DiagnosticDataProviderManagerClass, "getNetworkInterfaces", "()[Lchip/platform/NetworkInterface;"); + if (mGetNifMethod == nullptr) + { + ChipLogError(DeviceLayer, "Failed to access DiagnosticDataProviderManager 'getNetworkInterfaces' method"); + env->ExceptionClear(); + } +} + +CHIP_ERROR DiagnosticDataProviderImpl::GetRebootCount(uint16_t & rebootCount) +{ + chip::DeviceLayer::StackUnlock unlock; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + VerifyOrReturnLogError(mDiagnosticDataProviderManagerObject != nullptr, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnLogError(mGetRebootCountMethod != nullptr, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnLogError(env != nullptr, CHIP_JNI_ERROR_NO_ENV); + ChipLogProgress(DeviceLayer, "Received GetRebootCount"); + + jint count = env->CallIntMethod(mDiagnosticDataProviderManagerObject, mGetRebootCountMethod); + VerifyOrReturnLogError(count < UINT16_MAX, CHIP_ERROR_INVALID_INTEGER_VALUE); + rebootCount = static_cast(count); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR DiagnosticDataProviderImpl::GetNetworkInterfaces(NetworkInterface ** netifpp) +{ + chip::DeviceLayer::StackUnlock unlock; + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + VerifyOrExit(mDiagnosticDataProviderManagerObject != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(mGetNifMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV); + { + ChipLogProgress(DeviceLayer, "Received GetNetworkInterfaces"); + jobjectArray nifList = (jobjectArray) env->CallObjectMethod(mDiagnosticDataProviderManagerObject, mGetNifMethod); + if (env->ExceptionCheck()) + { + ChipLogError(DeviceLayer, "Java exception in DiagnosticDataProviderImpl::GetNetworkInterfaces"); + env->ExceptionDescribe(); + env->ExceptionClear(); + return CHIP_ERROR_INCORRECT_STATE; + } + jint length = env->GetArrayLength(nifList); + + NetworkInterface * head = nullptr; + for (jint i = 0; i < length; i++) + { + NetworkInterface * ifp = new NetworkInterface(); + + jobject nifObject = env->GetObjectArrayElement(nifList, i); + jclass nifClass = env->GetObjectClass(nifObject); + + jfieldID getNameField = env->GetFieldID(nifClass, "name", "Ljava/lang/String;"); + jstring jname = static_cast(env->GetObjectField(nifObject, getNameField)); + if (jname != nullptr) + { + JniUtfString name(env, jname); + Platform::CopyString(ifp->Name, name.c_str()); + ifp->Name[Inet::InterfaceId::kMaxIfNameLength - 1] = '\0'; + ifp->name = CharSpan(ifp->Name, strlen(ifp->Name)); + } + + jfieldID isOperationalField = env->GetFieldID(nifClass, "isOperational", "Z"); + ifp->isOperational = static_cast(env->GetBooleanField(nifObject, isOperationalField)); + + jfieldID getOpsrIPV4Field = env->GetFieldID(nifClass, "offPremiseServicesReachableIPv4", "Ljava/lang/Boolean;"); + jobject opsrIPV4Obj = env->GetObjectField(nifObject, getOpsrIPV4Field); + if (opsrIPV4Obj == nullptr) + { + ifp->offPremiseServicesReachableIPv4.SetNull(); + } + else + { + jboolean opsrIPV4 = JniReferences::GetInstance().BooleanToPrimitive(opsrIPV4Obj); + ifp->offPremiseServicesReachableIPv4.SetNonNull(static_cast(opsrIPV4)); + } + + jfieldID getOpsrIPV6Field = env->GetFieldID(nifClass, "offPremiseServicesReachableIPv6", "Ljava/lang/Boolean;"); + jobject opsrIPV6Obj = env->GetObjectField(nifObject, getOpsrIPV6Field); + if (opsrIPV6Obj == nullptr) + { + ifp->offPremiseServicesReachableIPv6.SetNull(); + } + else + { + jboolean opsrIPV6 = JniReferences::GetInstance().BooleanToPrimitive(opsrIPV6Obj); + ifp->offPremiseServicesReachableIPv6.SetNonNull(static_cast(opsrIPV6)); + } + + jfieldID gethardwareAddressField = env->GetFieldID(nifClass, "hardwareAddress", "[B"); + jbyteArray jHardwareAddressObj = static_cast(env->GetObjectField(nifObject, gethardwareAddressField)); + if (jHardwareAddressObj != nullptr) + { + size_t len = env->GetArrayLength(jHardwareAddressObj); + len = (len > kMaxHardwareAddrSize) ? kMaxHardwareAddrSize : len; + env->GetByteArrayRegion(jHardwareAddressObj, 0, len, reinterpret_cast(ifp->MacAddress)); + ifp->hardwareAddress = ByteSpan(ifp->MacAddress, 6); + } + + jfieldID getTypeField = env->GetFieldID(nifClass, "type", "I"); + ifp->type = static_cast(env->GetIntField(nifObject, getTypeField)); + + ifp->Next = head; + head = ifp; + } + *netifpp = head; + } + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "ChannelManager::getChannelList status error: %s", err.AsString()); + } + return err; +} + +void DiagnosticDataProviderImpl::ReleaseNetworkInterfaces(NetworkInterface * netifp) +{ + while (netifp) + { + NetworkInterface * del = netifp; + netifp = netifp->Next; + delete del; + } +} + } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/android/DiagnosticDataProviderImpl.h b/src/platform/android/DiagnosticDataProviderImpl.h index 731f445fec8671..ddfb9be57b294d 100644 --- a/src/platform/android/DiagnosticDataProviderImpl.h +++ b/src/platform/android/DiagnosticDataProviderImpl.h @@ -22,8 +22,15 @@ #pragma once +#include #include +namespace { +constexpr int offPremiseServicesReachableUnknown = 0; +constexpr int offPremiseServicesReachableYes = 1; +constexpr int offPremiseServicesReachableNo = 2; +} // namespace + namespace chip { namespace DeviceLayer { @@ -32,8 +39,18 @@ namespace DeviceLayer { */ class DiagnosticDataProviderImpl : public DiagnosticDataProvider { +public: + void InitializeWithObject(jobject managerObject); + public: static DiagnosticDataProviderImpl & GetDefaultInstance(); + CHIP_ERROR GetRebootCount(uint16_t & rebootCount) override; + CHIP_ERROR GetNetworkInterfaces(NetworkInterface ** netifpp) override; + void ReleaseNetworkInterfaces(NetworkInterface * netifp) override; + + jobject mDiagnosticDataProviderManagerObject = nullptr; + jmethodID mGetRebootCountMethod = nullptr; + jmethodID mGetNifMethod = nullptr; }; } // namespace DeviceLayer diff --git a/src/platform/android/java/chip/platform/AndroidChipPlatform.java b/src/platform/android/java/chip/platform/AndroidChipPlatform.java index 88754ee5d9e1b0..f3f1dbbd27f461 100644 --- a/src/platform/android/java/chip/platform/AndroidChipPlatform.java +++ b/src/platform/android/java/chip/platform/AndroidChipPlatform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2021-2022 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,7 +25,8 @@ public AndroidChipPlatform( KeyValueStoreManager kvm, ConfigurationManager cfg, ServiceResolver resolver, - ChipMdnsCallback chipMdnsCallback) { + ChipMdnsCallback chipMdnsCallback, + DiagnosticDataProvider dataProvider) { // Order is important here: initChipStack() initializes the BLEManagerImpl, which depends on the // BLEManager being set. setConfigurationManager() depends on the CHIP stack being initialized. setBLEManager(ble); @@ -33,6 +34,7 @@ public AndroidChipPlatform( setKeyValueStoreManager(kvm); setConfigurationManager(cfg); setServiceResolver(resolver, chipMdnsCallback); + setDiagnosticDataProviderManager(dataProvider); } // for BLEManager @@ -87,4 +89,6 @@ private void setServiceResolver(ServiceResolver resolver, ChipMdnsCallback chipM private native void nativeSetServiceResolver( ServiceResolver resolver, ChipMdnsCallback chipMdnsCallback); + + private native void setDiagnosticDataProviderManager(DiagnosticDataProvider dataProviderCallback); } diff --git a/src/platform/android/java/chip/platform/DiagnosticDataProvider.java b/src/platform/android/java/chip/platform/DiagnosticDataProvider.java new file mode 100644 index 00000000000000..10ee86a9e4310f --- /dev/null +++ b/src/platform/android/java/chip/platform/DiagnosticDataProvider.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 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. + * + */ +package chip.platform; + +public interface DiagnosticDataProvider { + int getRebootCount(); + + NetworkInterface[] getNetworkInterfaces(); +} diff --git a/src/platform/android/java/chip/platform/DiagnosticDataProviderImpl.java b/src/platform/android/java/chip/platform/DiagnosticDataProviderImpl.java new file mode 100644 index 00000000000000..769b9070679c27 --- /dev/null +++ b/src/platform/android/java/chip/platform/DiagnosticDataProviderImpl.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022 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. + * + */ +package chip.platform; + +import android.content.Context; +import android.util.Log; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class DiagnosticDataProviderImpl implements DiagnosticDataProvider { + private static final String TAG = DiagnosticDataProviderImpl.class.getSimpleName(); + private Context mContext; + + public DiagnosticDataProviderImpl(Context context) { + mContext = context; + } + + @Override + public int getRebootCount() { + // TODO: boot count is not supported until api 24, please do it at factory app + // try { + // return Settings.Global.getInt(mContext.getContentResolver(), + // Settings.Global.BOOT_COUNT); + // } catch (Settings.SettingNotFoundException e) { + // Log.e(TAG, "getRebootCount: " + e.getMessage()); + // } + return 100; + } + + @Override + public NetworkInterface[] getNetworkInterfaces() { + try { + ArrayList networkInterfaces = + Collections.list(java.net.NetworkInterface.getNetworkInterfaces()); + int size = networkInterfaces.size(); + List destInterfaces = new ArrayList<>(); + for (int i = 0; i < size; i++) { + java.net.NetworkInterface nif = networkInterfaces.get(i); + String name = nif.getName(); + + if (name != null && (name.startsWith("eth") || name.startsWith("wlan"))) { + NetworkInterface anInterface = new NetworkInterface(); + anInterface.isOperational = nif.isUp(); + anInterface.hardwareAddress = nif.getHardwareAddress(); + anInterface.name = nif.getName(); + anInterface.type = + name.startsWith("wlan") + ? NetworkInterface.INTERFACE_TYPE_WI_FI + : NetworkInterface.INTERFACE_TYPE_ETHERNET; + destInterfaces.add(anInterface); + } + } + + NetworkInterface[] inetArray = new NetworkInterface[destInterfaces.size()]; + destInterfaces.toArray(inetArray); + return inetArray; + } catch (SocketException e) { + Log.e(TAG, "getNetworkInterfaces: " + e.getMessage()); + } + + return new NetworkInterface[0]; + } +} diff --git a/src/platform/android/java/chip/platform/NetworkInterface.java b/src/platform/android/java/chip/platform/NetworkInterface.java new file mode 100644 index 00000000000000..faa33cf9a91143 --- /dev/null +++ b/src/platform/android/java/chip/platform/NetworkInterface.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 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. + * + */ +package chip.platform; + +import androidx.annotation.Nullable; + +public class NetworkInterface { + + public static final int INTERFACE_TYPE_UNSPECIFIED = 0x00; + public static final int INTERFACE_TYPE_WI_FI = 0x01; + public static final int INTERFACE_TYPE_ETHERNET = 0x02; + public static final int INTERFACE_TYPE_CELLULAR = 0x03; + public static final int INTERFACE_TYPE_THREAD = 0x04; + + public String name; + public boolean isOperational; + + @Nullable public Boolean offPremiseServicesReachableIPv4; + @Nullable public Boolean offPremiseServicesReachableIPv6; + public byte[] hardwareAddress; + public int type; +}