[IncrementalService] add last read error in dumpsys

BUG: 184844615
Test: dumpsys
Change-Id: I3e8178c8a62dc5d09c718b140bb48bda85a3bae5
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index de8c8bc..68b5bd9 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -395,6 +395,15 @@
     return std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
 }
 
+static uint64_t elapsedUsSinceMonoTs(uint64_t monoTsUs) {
+    timespec now;
+    if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
+        return 0;
+    }
+    uint64_t nowUs = now.tv_sec * 1000000LL + now.tv_nsec / 1000;
+    return nowUs - monoTsUs;
+}
+
 void IncrementalService::onDump(int fd) {
     dprintf(fd, "Incremental is %s\n", incfs::enabled() ? "ENABLED" : "DISABLED");
     dprintf(fd, "Incremental dir: %s\n", mIncrementalDir.c_str());
@@ -462,6 +471,25 @@
                 dprintf(fd, "      Metrics not available. Errno: %d\n", errno);
             }
             dprintf(fd, "    }\n");
+
+            const auto lastReadError = mIncFs->getLastReadError(ifs->control);
+            const auto errorNo = errno;
+            dprintf(fd, "    lastReadError: {\n");
+            if (lastReadError) {
+                if (lastReadError->timestampUs == 0) {
+                    dprintf(fd, "      No read errors.\n");
+                } else {
+                    dprintf(fd, "      fileId: %s\n",
+                            IncFsWrapper::toString(lastReadError->id).c_str());
+                    dprintf(fd, "      time: %llu microseconds ago\n",
+                            (unsigned long long)elapsedUsSinceMonoTs(lastReadError->timestampUs));
+                    dprintf(fd, "      blockIndex: %d\n", lastReadError->block);
+                    dprintf(fd, "      errno: %d\n", lastReadError->errorNo);
+                }
+            } else {
+                dprintf(fd, "      Info not available. Errno: %d\n", errorNo);
+            }
+            dprintf(fd, "    }\n");
         }
         dprintf(fd, "  }\n");
     }
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index c8e5169..68a28b2 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -265,6 +265,9 @@
     std::optional<Metrics> getMetrics(std::string_view sysfsName) const final {
         return incfs::getMetrics(sysfsName);
     }
+    std::optional<LastReadError> getLastReadError(const Control& control) const final {
+        return incfs::getLastReadError(control);
+    }
 };
 
 static JNIEnv* getOrAttachJniEnv(JavaVM* jvm);
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 3c969e5..c0ef7ba 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -81,6 +81,7 @@
     using WaitResult = incfs::WaitResult;
     using Features = incfs::Features;
     using Metrics = incfs::Metrics;
+    using LastReadError = incfs::LastReadError;
 
     using ExistingMountCallback = android::base::function_ref<
             void(std::string_view root, std::string_view backingDir,
@@ -127,6 +128,7 @@
     virtual ErrorCode forEachFile(const Control& control, FileCallback cb) const = 0;
     virtual ErrorCode forEachIncompleteFile(const Control& control, FileCallback cb) const = 0;
     virtual std::optional<Metrics> getMetrics(std::string_view sysfsName) const = 0;
+    virtual std::optional<LastReadError> getLastReadError(const Control& control) const = 0;
 };
 
 class AppOpsManagerWrapper {
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 766f713..da7f0db 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -417,6 +417,7 @@
     MOCK_CONST_METHOD2(forEachFile, ErrorCode(const Control& control, FileCallback cb));
     MOCK_CONST_METHOD2(forEachIncompleteFile, ErrorCode(const Control& control, FileCallback cb));
     MOCK_CONST_METHOD1(getMetrics, std::optional<Metrics>(std::string_view path));
+    MOCK_CONST_METHOD1(getLastReadError, std::optional<LastReadError>(const Control& control));
 
     MockIncFs() {
         ON_CALL(*this, listExistingMounts(_)).WillByDefault(Return());