Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] Implement DiagnosticLog #36591

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/java-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,17 @@ jobs:
--tool-args "onnetwork-long --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 1000" \
--factoryreset \
'
- name: Run Pairing Onnetwork and get diagnostic log Test
run: |
scripts/run_in_python_env.sh out/venv \
'./scripts/tests/run_java_test.py \
--app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app \
--app-args "--discriminator 3840 --interface-id -1 --crash_log ./crashLog.log --end_user_support_log ./enduser.log --network_diagnostics_log ./network.log" \
--tool-path out/linux-x64-java-matter-controller \
--tool-cluster "bdx" \
--tool-args "onnetwork-long-downloadLog --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 3000 --logType CrashLogs --fileName ./crashLog.log" \
--factoryreset \
'
- name: Run Pairing Onnetwork Test
run: |
scripts/run_in_python_env.sh out/venv \
Expand Down
9 changes: 9 additions & 0 deletions examples/android/CHIPTool/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@
<data android:scheme="mt" android:host="modelinfo" /> <!-- Process Redirect URIs -->
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>

<queries>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() }
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
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 java.io.File
import java.io.FileOutputStream
import java.io.IOException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

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: Long
get() = binding.timeoutEd.text.toString().toULongOrNull()?.toLong() ?: 0L

private val diagnosticLogTypeList = DiagnosticLogType.values()
private val diagnosticLogType: DiagnosticLogType
get() = diagnosticLogTypeList[binding.diagnosticTypeSp.selectedItemPosition]

private var mDownloadFile: File? = null
private var mDownloadFileOutputStream: FileOutputStream? = null

private var mReceiveFileLen = 0U

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")
showMessage("Downloading Failed")
mDownloadFileOutputStream?.flush() ?: return
}

override fun onSuccess(fabricIndex: Int, nodeId: Long) {
Log.d(TAG, "onSuccess: $fabricIndex, ${nodeId.toULong()}")
mDownloadFileOutputStream?.flush() ?: return
showMessage("Downloading Completed")
mDownloadFile?.let { showNotification(it) } ?: return
}

override fun onTransferData(fabricIndex: Int, nodeId: Long, data: ByteArray): Boolean {
Log.d(TAG, "onTransferData : ${data.size}")
if (mDownloadFileOutputStream == null) {
Log.d(TAG, "mDownloadFileOutputStream or mDownloadFile is null")
return false
}
return addData(mDownloadFileOutputStream!!, data)
}

private fun addData(outputStream: FileOutputStream, data: ByteArray): Boolean {
try {
outputStream.write(data)
} catch (e: IOException) {
Log.d(TAG, "IOException", e)
return false
}
mReceiveFileLen += data.size.toUInt()
showMessage("Receive Data Size : $mReceiveFileLen")
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"
mReceiveFileLen = 0U
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
)
}

private fun showMessage(msg: String) {
requireActivity().runOnUiThread { binding.diagnosticLogTv.text = msg }
}

companion object {
private const val TAG = "DiagnosticLogFragment"

fun newInstance(): DiagnosticLogFragment = DiagnosticLogFragment()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">

<androidx.fragment.app.FragmentContainerView
android:id="@+id/addressUpdateFragment"
android:name="com.google.chip.chiptool.clusterclient.AddressUpdateFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>

<TextView
android:id="@+id/titleDiagnosticType"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/diagnostic_log_type_title_text"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/addressUpdateFragment" />

<Spinner
android:id="@+id/diagnosticTypeSp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="text"
android:spinnerMode="dropdown"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/titleDiagnosticType" />

<EditText
android:id="@+id/timeoutTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:enabled="false"
android:padding="8dp"
android:text="@string/diagnostic_log_timeout_title_text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/timeoutEd"
app:layout_constraintTop_toBottomOf="@id/diagnosticTypeSp"
android:textSize="16sp" />

<EditText
android:id="@+id/timeoutEd"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:autofillHints="@string/diagnostic_log_timeout_title_text"
android:inputType="numberDecimal"
android:padding="8dp"
app:layout_constraintStart_toEndOf="@id/timeoutTv"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/diagnosticTypeSp"
android:textSize="16sp" />

<Button
android:id="@+id/getDiagnosticLogBtn"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="@string/diagnostic_log_btn_text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/timeoutTv"/>

<TextView
android:id="@+id/diagnosticLogTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minLines="4"
android:padding="16dp"
android:singleLine="false"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/getDiagnosticLogBtn" />

</androidx.constraintlayout.widget.ConstraintLayout>
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,14 @@
android:layout_marginTop="8dp"
android:text="@string/unpair_device_btn_text" />

<Button
android:id="@+id/diagnosticLogBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="@string/diagnostic_log_btn_text" />

<Button
android:id="@+id/groupSettingBtn"
android:layout_width="wrap_content"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@

<string name="unpair_device_btn_text">Unpair</string>

<string name="diagnostic_log_btn_text">Get Diagnostic Log</string>
<string name="diagnostic_log_type_title_text">Log Type</string>
<string name="diagnostic_log_timeout_title_text">Timeout(Sec)</string>

<string name="group_setting_btn_text">Group Setting</string>
<string name="group_setting_group_text">Group :</string>
<string name="group_setting_key_text">Key :</string>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="external_files" path="." />
</paths>
2 changes: 2 additions & 0 deletions examples/java-matter-controller/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ kotlin_binary("java-matter-controller") {

sources = [
"java/src/com/matter/controller/Main.kt",
"java/src/com/matter/controller/commands/bdx/DownloadLogCommand.kt",
"java/src/com/matter/controller/commands/bdx/PairOnNetworkLongDownloadLogCommand.kt",
"java/src/com/matter/controller/commands/common/Argument.kt",
"java/src/com/matter/controller/commands/common/ArgumentType.kt",
"java/src/com/matter/controller/commands/common/Command.kt",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package com.matter.controller

import chip.devicecontroller.ChipDeviceController
import chip.devicecontroller.ControllerParams
import com.matter.controller.commands.bdx.*
import com.matter.controller.commands.common.*
import com.matter.controller.commands.discover.*
import com.matter.controller.commands.icd.*
Expand Down Expand Up @@ -80,6 +81,16 @@ private fun getICDCommands(
)
}

private fun getBdxCommands(
controller: ChipDeviceController,
credentialsIssuer: CredentialsIssuer
): List<Command> {
return listOf(
DownloadLogCommand(controller, credentialsIssuer),
PairOnNetworkLongDownloadLogCommand(controller, credentialsIssuer),
)
}

fun main(args: Array<String>) {
val controller =
ChipDeviceController(
Expand All @@ -96,6 +107,7 @@ fun main(args: Array<String>) {
commandManager.register("pairing", getPairingCommands(controller, credentialsIssuer))
commandManager.register("im", getImCommands(controller, credentialsIssuer))
commandManager.register("icd", getICDCommands(controller, credentialsIssuer))
commandManager.register("bdx", getBdxCommands(controller, credentialsIssuer))

try {
commandManager.run(args)
Expand Down
Loading
Loading