From d0db78009f0a8fe3586676f2f58597cf9886c890 Mon Sep 17 00:00:00 2001
From: Matthias Urhahn <matthias.urhahn@sap.com>
Date: Tue, 25 May 2021 15:17:52 +0200
Subject: [PATCH] Catch debug log FileNotFoundExceptions (DEV) (#3259)

* On low storage, the system may clean caches without asking, if the parent dir of the debuglog is deleted, writing will fail with an exception.
Catch that exception, and try to recreate the path+file, otherwise fail silently.

* Update logsize after retry as log could have been deleted.

Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com>
Co-authored-by: AlexanderAlferov <64849422+AlexanderAlferov@users.noreply.github.com>
---
 .../debuglog/internal/LogWriter.kt            | 32 ++++++++++++++++++-
 .../debuglog/internal/LogWriterTest.kt        | 23 +++++++++++++
 2 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/debuglog/internal/LogWriter.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/debuglog/internal/LogWriter.kt
index a5448ea4d..992f86be6 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/debuglog/internal/LogWriter.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/debuglog/internal/LogWriter.kt
@@ -1,12 +1,17 @@
 package de.rki.coronawarnapp.bugreporting.debuglog.internal
 
+import android.annotation.SuppressLint
+import android.util.Log
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
 import timber.log.Timber
 import java.io.File
+import java.io.FileNotFoundException
 import javax.inject.Inject
 
+@Suppress("BlockingMethodInNonBlockingContext")
+@SuppressLint("LogNotTimber")
 class LogWriter @Inject constructor(val logFile: File) {
     private var ioLimiter = 0
     private val mutex = Mutex()
@@ -34,7 +39,28 @@ class LogWriter @Inject constructor(val logFile: File) {
     }
 
     suspend fun write(formattedLine: String): Unit = mutex.withLock {
-        logFile.appendText(formattedLine + "\n", Charsets.UTF_8)
+        val performWrite = {
+            logFile.appendText(formattedLine + "\n", Charsets.UTF_8)
+        }
+
+        try {
+            performWrite()
+        } catch (e: FileNotFoundException) {
+            Log.e(TAG, "Log file didn't exist when we tried to write, retry.")
+
+            try {
+                logFile.parentFile?.mkdirs()
+                logFile.createNewFile()
+                logFile.writeText("Logfile was deleted.\n")
+
+                performWrite()
+
+                updateLogSize()
+            } catch (e: Exception) {
+                Log.e(TAG, "LogWrite retry failed, something is just wrong...", e)
+                return@withLock
+            }
+        }
 
         if (ioLimiter % 10 == 0) {
             updateLogSize()
@@ -42,4 +68,8 @@ class LogWriter @Inject constructor(val logFile: File) {
         }
         ioLimiter++
     }
+
+    companion object {
+        private const val TAG = "LogWriter"
+    }
 }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/debuglog/internal/LogWriterTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/debuglog/internal/LogWriterTest.kt
index 19120d550..6c261a2c1 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/debuglog/internal/LogWriterTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/debuglog/internal/LogWriterTest.kt
@@ -45,4 +45,27 @@ class LogWriterTest : BaseIOTest() {
             logSize.value shouldBe 0L
         }
     }
+
+    /**
+     * e.g. System cache cleaning interferring
+     */
+    @Test
+    fun `if the file is deleted after setup we try to recreate it and do not crash`() = runBlockingTest {
+        createInstance().apply {
+            setup()
+            write("ABC")
+            logFile.readText() shouldBe "ABC\n"
+
+            logSize.value shouldBe 4L
+
+            logFile.delete()
+            logFile.parentFile!!.delete()
+            logFile.exists() shouldBe false
+
+            write("DEF")
+            logFile.readText() shouldBe "Logfile was deleted.\nDEF\n"
+
+            logSize.value shouldBe 25L
+        }
+    }
 }
-- 
GitLab