Check how long the system has been forced into big image

Bug: 110939839
Test: sanity check CHRE
Change-Id: Ief5a212e673fdc0cb261d24e48fea88656e30e22
diff --git a/platform/slpi/include/chre/platform/slpi/see/island_vote_client.h b/platform/slpi/include/chre/platform/slpi/see/island_vote_client.h
index 283cef8..f112e99 100644
--- a/platform/slpi/include/chre/platform/slpi/see/island_vote_client.h
+++ b/platform/slpi/include/chre/platform/slpi/see/island_vote_client.h
@@ -67,6 +67,10 @@
   void decrementBigImageRefCount();
 
  private:
+  //! The maximum allowed duration to be voted into big image by
+  //! incrementBigImageRefCount before a FATAL_ERROR is triggered.
+  static constexpr Seconds kSeeMaxBigImageDuration = Seconds(300);
+
   //! Last big image request made through voteBigImage().
   bool mLastBigImageRequest = false;
 
@@ -93,6 +97,16 @@
    * @return true if the vote returned success.
    */
   bool voteSnsPowerMode(bool bigImage);
+
+  /**
+   * Check how long the system has been voted into big image due to
+   * incrementBigImageRefCount. If longer than kSeeMaxBigImageDuration, trigger
+   * a crash.
+   *
+   * @return the duration in milliseconds since the system has been voted into
+   *         big image due to incrementBigImageRefCount.
+   */
+  uint64_t checkBigImageDuration() const;
 #endif  // CHRE_SLPI_UIMG_ENABLED
 };
 
diff --git a/platform/slpi/see/island_vote_client.cc b/platform/slpi/see/island_vote_client.cc
index 02fe701..e7a81d2 100644
--- a/platform/slpi/see/island_vote_client.cc
+++ b/platform/slpi/see/island_vote_client.cc
@@ -26,6 +26,8 @@
 
 namespace chre {
 
+constexpr Seconds IslandVoteClient::kSeeMaxBigImageDuration;
+
 IslandVoteClient::IslandVoteClient(const char *clientName) {
 #ifdef CHRE_SLPI_UIMG_ENABLED
   mClientHandle = sns_island_aggregator_register_client(clientName);
@@ -75,6 +77,8 @@
       voteSnsPowerMode(true /* bigImage */);
       mLastBigImageVote = true;
     }
+  } else {
+    checkBigImageDuration();
   }
 }
 
@@ -83,10 +87,8 @@
   CHRE_ASSERT_LOG(mBigImageRefCount > 0,
                   "Tried to decrement big image ref count when it's 0");
 
+  uint64_t duration = checkBigImageDuration();
   if (--mBigImageRefCount == 0) {
-    uint64_t duration =
-        Milliseconds(SystemTime::getMonotonicTime()).getMilliseconds()
-        - mRefCountStart.getMilliseconds();
     LOGW("Big image ref count ends: %" PRIu64 " ms", duration);
 
     // There's no big image activity now, restore the intended uimg power state.
@@ -110,6 +112,22 @@
   }
   return success;
 }
+
+uint64_t IslandVoteClient::checkBigImageDuration() const {
+  uint64_t duration = 0;
+  if (mBigImageRefCount > 0) {
+    duration = Milliseconds(SystemTime::getMonotonicTime()).getMilliseconds()
+               - mRefCountStart.getMilliseconds();
+  }
+
+  // Bimg memory fallback only intends to handle a surge of uimg memory
+  // requests. If there's a prolonged period of memory fallback, this might
+  // indicate a memory leak or inadequate uimg heap size.
+  if (duration > kSeeMaxBigImageDuration.getMilliseconds()) {
+    FATAL_ERROR("Forced into big image for %" PRIu64 " msec", duration);
+  }
+  return duration;
+}
 #endif  // CHRE_SLPI_UIMG_ENABLED
 
 //! Explicitly instantiate the IslandVoteClient singleton to reduce code size.
diff --git a/util/include/chre/util/time.h b/util/include/chre/util/time.h
index 9a2c065..0d97231 100644
--- a/util/include/chre/util/time.h
+++ b/util/include/chre/util/time.h
@@ -22,10 +22,10 @@
 namespace chre {
 
 //! The number of milliseconds in one min.
-constexpr uint64_t kOneMinuteInMillisecods(60000);
+constexpr uint64_t kOneMinuteInMilliseconds(60000);
 
 //! The number of milliseconds in one second.
-constexpr uint64_t kOneSecondInMillisecods(1000);
+constexpr uint64_t kOneSecondInMilliseconds(1000);
 
 //! The number of nanoseconds in one second.
 constexpr uint64_t kOneSecondInNanoseconds(1000000000);
@@ -60,6 +60,13 @@
    */
   constexpr uint64_t toRawNanoseconds() const;
 
+  /**
+   * Obtains the number of Milliseconds stored by this time duration.
+   *
+   * @return the value of milliseconds.
+   */
+  constexpr uint64_t getMilliseconds() const;
+
  private:
   uint64_t mSeconds;
 };
diff --git a/util/include/chre/util/time_impl.h b/util/include/chre/util/time_impl.h
index 9d8adba..7e79cf8 100644
--- a/util/include/chre/util/time_impl.h
+++ b/util/include/chre/util/time_impl.h
@@ -28,10 +28,18 @@
   // Perform the simple unit conversion. Warning: overflow is caught and
   // handled by returning UINT64_MAX. A ternary expression is used because
   // constexpr requires it.
-  return mSeconds > (UINT64_MAX / kOneSecondInNanoseconds) ? UINT64_MAX
+  return (mSeconds > (UINT64_MAX / kOneSecondInNanoseconds)) ? UINT64_MAX
       : mSeconds * kOneSecondInNanoseconds;
 }
 
+constexpr uint64_t Seconds::getMilliseconds() const {
+  // Perform the simple unit conversion. Warning: overflow is caught and
+  // handled by returning UINT64_MAX. A ternary expression is used because
+  // constexpr requires it.
+  return (mSeconds > (UINT64_MAX / kOneSecondInMilliseconds)) ? UINT64_MAX
+      : mSeconds * kOneSecondInMilliseconds;
+}
+
 constexpr Milliseconds::Milliseconds()
     : mMilliseconds(0) {}
 
@@ -51,7 +59,11 @@
 }
 
 constexpr uint64_t Milliseconds::getMicroseconds() const {
-  return mMilliseconds * kOneMillisecondInMicroseconds;
+  // Perform the simple unit conversion. Warning: overflow is caught and
+  // handled by returning UINT64_MAX. A ternary expression is used because
+  // constexpr requires it.
+  return (mMilliseconds > (UINT64_MAX / kOneMillisecondInMicroseconds))
+      ? UINT64_MAX : mMilliseconds * kOneMillisecondInMicroseconds ;
 }
 
 constexpr uint64_t Milliseconds::getMilliseconds() const {
diff --git a/util/tests/time_test.cc b/util/tests/time_test.cc
index 94d1032..96616e4 100644
--- a/util/tests/time_test.cc
+++ b/util/tests/time_test.cc
@@ -22,10 +22,11 @@
 using chre::Milliseconds;
 using chre::Microseconds;
 using chre::Nanoseconds;
+using chre::kOneSecondInMilliseconds;
 using chre::kOneSecondInNanoseconds;
+using chre::kOneMillisecondInMicroseconds;
 using chre::kOneMillisecondInNanoseconds;
 using chre::kOneMicrosecondInNanoseconds;
-using chre::kOneMillisecondInMicroseconds;
 
 // Tests for Time constants
 TEST(Time, CheckTimeConversionConstants) {
@@ -46,6 +47,16 @@
   EXPECT_EQ(t.toRawNanoseconds(), UINT64_MAX);
 }
 
+TEST(Time, ConvertSecToMillisec) {
+  Seconds t(5);
+  EXPECT_EQ(t.getMilliseconds(), 5 * kOneSecondInMilliseconds);
+}
+
+TEST(Time, ConvertSecToMillisecOverflowIsUint64Max) {
+  Seconds t(UINT64_MAX / kOneSecondInMilliseconds + 1);
+  EXPECT_EQ(t.getMilliseconds(), UINT64_MAX);
+}
+
 // Tests for Milliseconds
 TEST(Time, DefaultMillisecIsZero) {
   Milliseconds t;
@@ -68,6 +79,11 @@
   EXPECT_EQ(t.getMicroseconds(), 5 * kOneMillisecondInMicroseconds);
 }
 
+TEST(Time, ConvertMillisecToMicrosecOverflowIsUint64Max) {
+  Milliseconds t(UINT64_MAX / kOneMillisecondInMicroseconds + 1);
+  EXPECT_EQ(t.getMicroseconds(), UINT64_MAX);
+}
+
 TEST(Time, ConvertMillisecToNanosec) {
   Milliseconds t(5);
   EXPECT_EQ(t.toRawNanoseconds(), 5 * kOneMillisecondInNanoseconds);