Detect and report soft resets

Soft reset means that only the Android system/framework resets without
rebooting the Linux kernel. From Android app side, a soft reset looks
like a hard reset, because boot events are triggered again.

Hiccup needs to be able to distinguish between soft and hard resets,
because on a soft reset we need to read the current, not the last kernel
logs to get useful information. Also, other shutdown/boot/reboot reasons
reported by the kernel are not very meaningful on a soft reset.

This change assumes that the Hiccup app queries boot reasons only once
per Android system boot. The native Hiccup daemon persists soft resets,
therefore it can assume that when the app queries the boot reason
multiple times, a soft reset occurred.

Issue: FP2P-490
Change-Id: I77a20dcc91f9c9a7eec5f4a464d186143f6ce81e
diff --git a/native/hiccupd/Hiccupd.cpp b/native/hiccupd/Hiccupd.cpp
index 0b21ef4..915b567 100644
--- a/native/hiccupd/Hiccupd.cpp
+++ b/native/hiccupd/Hiccupd.cpp
@@ -31,6 +31,55 @@
 using namespace android;
 using namespace hiccup;
 
+namespace {
+
+enum class BootState {
+  early,      // Early boot state -- hiccupd just started.
+  hardReset,  // Hiccup app detected one boot event -- a hard reset.
+  softReset,  // Hiccup app detected multiple boots -- a soft reset happened.
+};
+
+BootState boot_state = BootState::early;
+
+void registerHiccupAppBootTrigger() {
+  switch (boot_state) {
+    case BootState::early:
+      boot_state = BootState::hardReset;
+      break;
+    case BootState::hardReset:
+      boot_state = BootState::softReset;
+    case BootState::softReset:
+      break;
+  }
+}
+
+bool softResetOccurred() {
+  return boot_state == BootState::softReset;
+}
+
+std::string copyLastCrashKernelLogIntoCache() {
+  // For now use the same output name in both cases -- so that we don't need to
+  // adjust legacy services to understand the new filename.
+  static const std::string fileName = "/cache/hiccup/last_kmsg";
+  int result = -1;
+  if (softResetOccurred()) {
+    /* Use dmesg to dump the current kernel log */
+    result = system(("dmesg > " + fileName).c_str());
+    ALOGI("Soft reset: Sending current kernel logs (dmesg).");
+  } else {
+    result = system(("cp /proc/last_kmsg " + fileName).c_str());
+    ALOGI("Hard reset: Sending last kernel logs (/proc/last_kmsg).");
+  }
+  if (result != 0) {
+    ALOGE("Failure while copying kernel logs to cache file %s: %i",
+      fileName.c_str(), result);
+  }
+  return fileName;
+}
+
+}
+
+
 class Hiccupd : public BnHiccupd {
   Vector<LogFile> *getLastKmsg();
   String16 getBootReason();
@@ -40,12 +89,18 @@
   int readIntFromFile(const char *fileName);
 };
 
+/** Get kernel logs potentially related to the last crash.
+  *
+  * On hard resets, we want the last kernel logs, because they potentially
+  * contain relevant information about the reason of the crash. On soft resets,
+  * only the Android framework, but not the Linux kernel reset, so we want the
+  * current Linux kernel logs instead.
+  */
 Vector<LogFile> *Hiccupd::getLastKmsg() {
   /* make local copy of logs */
   system("rm -rf /cache/hiccup");
   system("mkdir -p /cache/hiccup");
-  const std::string logFileName = "/cache/hiccup/last_kmsg";
-  system(("cp /proc/last_kmsg " + logFileName).c_str());
+  const std::string logFileName = copyLastCrashKernelLogIntoCache();
 
   auto v = std::make_unique<Vector<LogFile>>();
   const int fd = open(logFileName.c_str(), O_RDONLY);
@@ -70,6 +125,12 @@
 }
 
 String16 Hiccupd::getBootReason() {
+  // NOTE: This assumes that the Hiccup app will only check the boot reason once
+  // it detects a system boot, but never a second time while it is running.
+  registerHiccupAppBootTrigger();
+  if (softResetOccurred()) {
+    return String16("soft reset");
+  }
   String16 ret;
   int boot_reason = readIntFromFile("/proc/sys/kernel/boot_reason");
   switch (boot_reason) {
diff --git a/sepolicy/hiccup.te b/sepolicy/hiccup.te
index 5096315..67c82e5 100644
--- a/sepolicy/hiccup.te
+++ b/sepolicy/hiccup.te
@@ -66,3 +66,7 @@
 #  - /proc/sys/qpnp-power-on/pon_reason
 #  - /proc/sys/qpnp-power-on/poff_reason
 allow hiccupd proc:file r_file_perms;
+
+# Allow dumping of current kernel logs with dmesg
+allow hiccupd kernel:system syslog_read;
+allow hiccupd kmsg_device:chr_file { open read };