From b915dbdfa22651eca5f8404cf182a77ab40a32f8 Mon Sep 17 00:00:00 2001 From: Joonhaeng Heo Date: Thu, 21 Nov 2024 18:44:07 +0900 Subject: [PATCH 01/11] Implement Android DiagnosticLog --- .../CHIPTool/app/src/main/AndroidManifest.xml | 9 + .../chip/chiptool/SelectActionFragment.kt | 5 + .../clusterclient/DiagnosticLogFragment.kt | 139 ++++++++++ .../res/layout/diagnostic_log_fragment.xml | 75 +++++ .../res/layout/select_action_fragment.xml | 8 + .../app/src/main/res/values/strings.xml | 4 + .../app/src/main/res/xml/file_paths.xml | 4 + examples/java-matter-controller/BUILD.gn | 1 + .../java/src/com/matter/controller/Main.kt | 11 + .../commands/bdx/DownloadLogCommand.kt | 68 +++++ .../java/AndroidLogDownloadFromNode.cpp | 260 ++++++++++++++++++ .../java/AndroidLogDownloadFromNode.h | 87 ++++++ src/controller/java/BUILD.gn | 6 + .../java/BdxDiagnosticLogsReceiver.cpp | 102 +++++++ .../java/BdxDiagnosticLogsReceiver.h | 60 ++++ .../java/CHIPDeviceController-JNI.cpp | 18 ++ .../ChipDeviceController.java | 6 + .../devicecontroller/DiagnosticLogType.java | 52 ++++ .../devicecontroller/DownloadLogCallback.java | 26 ++ .../bdx/BdxTransferDiagnosticLog.cpp | 2 +- src/protocols/bdx/BdxTransferServerDelegate.h | 12 +- src/protocols/bdx/BdxUri.h | 2 + 22 files changed, 955 insertions(+), 2 deletions(-) create mode 100644 examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/DiagnosticLogFragment.kt create mode 100644 examples/android/CHIPTool/app/src/main/res/layout/diagnostic_log_fragment.xml create mode 100644 examples/android/CHIPTool/app/src/main/res/xml/file_paths.xml create mode 100644 examples/java-matter-controller/java/src/com/matter/controller/commands/bdx/DownloadLogCommand.kt create mode 100644 src/controller/java/AndroidLogDownloadFromNode.cpp create mode 100644 src/controller/java/AndroidLogDownloadFromNode.h create mode 100644 src/controller/java/BdxDiagnosticLogsReceiver.cpp create mode 100644 src/controller/java/BdxDiagnosticLogsReceiver.h create mode 100644 src/controller/java/src/chip/devicecontroller/DiagnosticLogType.java create mode 100644 src/controller/java/src/chip/devicecontroller/DownloadLogCallback.java diff --git a/examples/android/CHIPTool/app/src/main/AndroidManifest.xml b/examples/android/CHIPTool/app/src/main/AndroidManifest.xml index 0526dc5a78160d..efd089817efe68 100644 --- a/examples/android/CHIPTool/app/src/main/AndroidManifest.xml +++ b/examples/android/CHIPTool/app/src/main/AndroidManifest.xml @@ -46,6 +46,15 @@ + + + diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt index 69bfdc109912c9..fb8141e72d33a8 100644 --- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt @@ -72,6 +72,7 @@ class SelectActionFragment : Fragment() { binding.provisionCustomFlowBtn.setOnClickListener { handleProvisionCustomFlowClicked() } binding.wildcardBtn.setOnClickListener { handleWildcardClicked() } binding.unpairDeviceBtn.setOnClickListener { handleUnpairDeviceClicked() } + binding.diagnosticLogBtn.setOnClickListener { handleDiagnosticLogClicked() } binding.groupSettingBtn.setOnClickListener { handleGroupSettingClicked() } binding.otaProviderBtn.setOnClickListener { handleOTAProviderClicked() } binding.icdBtn.setOnClickListener { handleICDClicked() } @@ -225,6 +226,10 @@ class SelectActionFragment : Fragment() { showFragment(OtaProviderClientFragment.newInstance()) } + private fun handleDiagnosticLogClicked() { + showFragment(DiagnosticLogFragment.newInstance()) + } + /** Notifies listener of provision-WiFi-credentials button click. */ private fun handleProvisionWiFiCredentialsClicked() { getCallback()?.setNetworkType(ProvisionNetworkType.WIFI) diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/DiagnosticLogFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/DiagnosticLogFragment.kt new file mode 100644 index 00000000000000..8f0622e4a76424 --- /dev/null +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/DiagnosticLogFragment.kt @@ -0,0 +1,139 @@ +package com.google.chip.chiptool.clusterclient + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.os.Environment +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import androidx.core.content.FileProvider +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import chip.devicecontroller.ChipDeviceController +import chip.devicecontroller.DiagnosticLogType +import chip.devicecontroller.DownloadLogCallback +import com.google.chip.chiptool.ChipClient +import com.google.chip.chiptool.R +import com.google.chip.chiptool.databinding.DiagnosticLogFragmentBinding +import kotlinx.coroutines.* +import java.io.File +import java.io.FileOutputStream +import java.io.IOException + +class DiagnosticLogFragment : Fragment() { + private val deviceController: ChipDeviceController + get() = ChipClient.getDeviceController(requireContext()) + + private lateinit var scope: CoroutineScope + + private lateinit var addressUpdateFragment: AddressUpdateFragment + + private var _binding: DiagnosticLogFragmentBinding? = null + private val binding + get() = _binding!! + + private val timeout: Int + get() = binding.timeoutEd.text.toString().toUIntOrNull()?.toInt() ?: 0 + + private val diagnosticLogTypeList = DiagnosticLogType.values() + private val diagnosticLogType: DiagnosticLogType + get() = diagnosticLogTypeList[binding.diagnosticTypeSp.selectedItemPosition] + + private var mDownloadFile: File? = null + private var mDownloadFileOutputStream: FileOutputStream? = null + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = DiagnosticLogFragmentBinding.inflate(inflater, container, false) + scope = viewLifecycleOwner.lifecycleScope + + addressUpdateFragment = + childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment + + binding.getDiagnosticLogBtn.setOnClickListener { scope.launch { getDiagnosticLogClick() } } + + binding.diagnosticTypeSp.adapter = + ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, diagnosticLogTypeList) + + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + inner class ChipDownloadLogCallback : DownloadLogCallback { + override fun onError(fabricIndex: Int, nodeId: Long, errorCode: Long) { + Log.d(TAG, "onError: $fabricIndex, ${nodeId.toULong()}, $errorCode") + } + + override fun onTransferData( + fabricIndex: Int, + nodeId: Long, + data: ByteArray, + isEof: Boolean + ): Boolean { + Log.d(TAG, "onTransferData : ${data.size}, $isEof") + if (mDownloadFileOutputStream == null || mDownloadFile == null) { + Log.d(TAG, "mDownloadFileOutputStream or mDownloadFile is null") + return false + } + try { + mDownloadFileOutputStream!!.write(data) + if (isEof) { + mDownloadFileOutputStream!!.flush() + showNotification(mDownloadFile!!) + } + } catch (e: IOException) { + Log.d(TAG, "IOException", e) + return false + } + return true + } + } + + private fun getDiagnosticLogClick() { + mDownloadFile = createLogFile(deviceController.fabricIndex.toUInt(), addressUpdateFragment.deviceId.toULong(), diagnosticLogType) + mDownloadFileOutputStream = FileOutputStream(mDownloadFile) + deviceController.downloadLogFromNode(addressUpdateFragment.deviceId, diagnosticLogType, timeout, ChipDownloadLogCallback()) + } + + private fun isExternalStorageWritable(): Boolean { + return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED + } + + private fun createLogFile(fabricIndex: UInt, nodeId: ULong, type:DiagnosticLogType) : File? { + if (!isExternalStorageWritable()) { + return null + } + val now = System.currentTimeMillis() + val fileName = "${type}_${fabricIndex}_${nodeId}_$now.txt" + return File(requireContext().getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), fileName) + } + + private fun showNotification(file: File) { + val intent = Intent(Intent.ACTION_VIEW).apply { + setDataAndType(getFileUri(file), "text/plain") + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + + requireActivity().startActivity(intent) + } + + private fun getFileUri(file: File): Uri { + return FileProvider.getUriForFile(requireContext(), "${requireContext().packageName}.provider", file) + } + + companion object { + private const val TAG = "DiagnosticLogFragment" + + fun newInstance(): DiagnosticLogFragment = DiagnosticLogFragment() + } +} diff --git a/examples/android/CHIPTool/app/src/main/res/layout/diagnostic_log_fragment.xml b/examples/android/CHIPTool/app/src/main/res/layout/diagnostic_log_fragment.xml new file mode 100644 index 00000000000000..6233bee067b992 --- /dev/null +++ b/examples/android/CHIPTool/app/src/main/res/layout/diagnostic_log_fragment.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + +