Only prevent boot image downgrade if sysprop set.

Only return timestamp error if ro.build.ab_update.gki.prevent_downgrade_version is set.
Otherwise, log warning message and continue the update.

This allows devices that does not support GKI updates to install OTA packages where
the boot image has a lower version than the existing, which is supported before.

Bug: 162623577
Test: apply OTA on devices with and without sysprop set
Change-Id: Ie98fb49ffaae1aa60fc94766f53a6fbbae519a5b
diff --git a/Android.bp b/Android.bp
index 6585ea2..193928b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -260,6 +260,7 @@
     ],
 
     static_libs: [
+        "gkiprops",
         "libkver",
         "libpayload_consumer",
         "libupdate_engine_boot_control",
@@ -383,6 +384,7 @@
         // We add the static versions of the shared libraries that are not installed to
         // recovery image due to size concerns. Need to include all the static library
         // dependencies of these static libraries.
+        "gkiprops",
         "libevent",
         "libmodpb64",
         "libgtest_prod",
diff --git a/hardware_android.cc b/hardware_android.cc
index 4894522..fc6e1dc 100644
--- a/hardware_android.cc
+++ b/hardware_android.cc
@@ -23,6 +23,7 @@
 #include <string>
 #include <string_view>
 
+#include <android/sysprop/GkiProperties.sysprop.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <base/files/file_util.h>
@@ -261,7 +262,10 @@
       PLOG(ERROR) << "Unable to call uname()";
       return ErrorCode::kError;
     }
-    return IsKernelUpdateValid(buf.release, new_version);
+    bool prevent_downgrade =
+        android::sysprop::GkiProperties::prevent_downgrade_version().value_or(
+            false);
+    return IsKernelUpdateValid(buf.release, new_version, prevent_downgrade);
   }
 
   const auto old_version = GetPartitionBuildDate(partition_name);
@@ -278,7 +282,8 @@
 }
 
 ErrorCode HardwareAndroid::IsKernelUpdateValid(const string& old_release,
-                                               const string& new_release) {
+                                               const string& new_release,
+                                               bool prevent_downgrade) {
   // Check that the package either contain an empty version (indicating that the
   // new build does not use GKI), or a valid GKI kernel release.
   std::optional<KernelRelease> new_kernel_release;
@@ -296,10 +301,17 @@
 
   auto old_kernel_release =
       KernelRelease::Parse(old_release, true /* allow_suffix */);
-  return android::kver::IsKernelUpdateValid(old_kernel_release,
-                                            new_kernel_release)
-             ? ErrorCode::kSuccess
-             : ErrorCode::kPayloadTimestampError;
+  bool is_update_valid = android::kver::IsKernelUpdateValid(old_kernel_release,
+                                                            new_kernel_release);
+
+  if (!is_update_valid) {
+    if (prevent_downgrade) {
+      return ErrorCode::kPayloadTimestampError;
+    }
+    LOG(WARNING) << "Boot version downgrade detected, allowing update because "
+                 << "prevent_downgrade_version sysprop is not set.";
+  }
+  return ErrorCode::kSuccess;
 }
 
 }  // namespace chromeos_update_engine
diff --git a/hardware_android.h b/hardware_android.h
index b670447..552cb53 100644
--- a/hardware_android.h
+++ b/hardware_android.h
@@ -70,9 +70,15 @@
  private:
   FRIEND_TEST(HardwareAndroidTest, IsKernelUpdateValid);
 
-  // Helper for IsPartitionUpdateValid.
+  // Helper for IsPartitionUpdateValid. Check an update from |old_release|
+  // to |new_release| is valid or not.
+  // - If |new_release| is invalid, return kDownloadManifestParseError
+  // - If downgrade detected, kPayloadTimestampError if |prevent_downgrade| is
+  //   set to true, or kSuccess if |prevent_downgrade| is set to false
+  // - If update is valid, kSuccess.
   static ErrorCode IsKernelUpdateValid(const std::string& old_release,
-                                       const std::string& new_release);
+                                       const std::string& new_release,
+                                       bool prevent_downgrade);
 
   DISALLOW_COPY_AND_ASSIGN(HardwareAndroid);
 };
diff --git a/hardware_android_unittest.cc b/hardware_android_unittest.cc
index 9a491f3..679356c 100644
--- a/hardware_android_unittest.cc
+++ b/hardware_android_unittest.cc
@@ -14,54 +14,70 @@
 // limitations under the License.
 //
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include "update_engine/common/error_code.h"
-#include "update_engine/common/test_utils.h"
 #include "update_engine/hardware_android.h"
 
+using ::testing::NiceMock;
+using ::testing::Return;
+
 namespace chromeos_update_engine {
 
 TEST(HardwareAndroidTest, IsKernelUpdateValid) {
   EXPECT_EQ(ErrorCode::kSuccess,
-            HardwareAndroid::IsKernelUpdateValid("5.4.42-not-gki", ""))
+            HardwareAndroid::IsKernelUpdateValid(
+                "5.4.42-not-gki", "", true /*prevent_downgrade*/))
       << "Legacy update should be fine";
 
-  EXPECT_EQ(ErrorCode::kSuccess,
-            HardwareAndroid::IsKernelUpdateValid("5.4.42-not-gki",
-                                                 "5.4.42-android12-0"))
+  EXPECT_EQ(
+      ErrorCode::kSuccess,
+      HardwareAndroid::IsKernelUpdateValid(
+          "5.4.42-not-gki", "5.4.42-android12-0", true /*prevent_downgrade*/))
       << "Update to GKI should be fine";
 
-  EXPECT_EQ(
-      ErrorCode::kDownloadManifestParseError,
-      HardwareAndroid::IsKernelUpdateValid("5.4.42-not-gki", "5.4.42-not-gki"))
+  EXPECT_EQ(ErrorCode::kDownloadManifestParseError,
+            HardwareAndroid::IsKernelUpdateValid(
+                "5.4.42-not-gki", "5.4.42-not-gki", true /*prevent_downgrade*/))
       << "Should report parse error for invalid version field";
 
   EXPECT_EQ(ErrorCode::kSuccess,
-            HardwareAndroid::IsKernelUpdateValid(
-                "5.4.42-android12-0-something", "5.4.42-android12-0-something"))
+            HardwareAndroid::IsKernelUpdateValid("5.4.42-android12-0-something",
+                                                 "5.4.42-android12-0-something",
+                                                 true /*prevent_downgrade*/))
       << "Self update should be fine";
 
   EXPECT_EQ(ErrorCode::kSuccess,
-            HardwareAndroid::IsKernelUpdateValid(
-                "5.4.42-android12-0-something", "5.4.43-android12-0-something"))
+            HardwareAndroid::IsKernelUpdateValid("5.4.42-android12-0-something",
+                                                 "5.4.43-android12-0-something",
+                                                 true /*prevent_downgrade*/))
       << "Sub-level update should be fine";
 
   EXPECT_EQ(
       ErrorCode::kSuccess,
       HardwareAndroid::IsKernelUpdateValid("5.4.42-android12-0-something",
-                                           "5.10.10-android12-0-something"))
+                                           "5.10.10-android12-0-something",
+                                           true /*prevent_downgrade*/))
       << "KMI version update should be fine";
 
   EXPECT_EQ(ErrorCode::kPayloadTimestampError,
             HardwareAndroid::IsKernelUpdateValid("5.4.42-android12-0-something",
-                                                 "5.4.5-android12-0-something"))
+                                                 "5.4.5-android12-0-something",
+                                                 true /*prevent_downgrade*/))
       << "Should detect sub-level downgrade";
 
   EXPECT_EQ(ErrorCode::kPayloadTimestampError,
             HardwareAndroid::IsKernelUpdateValid("5.4.42-android12-0-something",
-                                                 "5.1.5-android12-0-something"))
+                                                 "5.1.5-android12-0-something",
+                                                 true /*prevent_downgrade*/))
       << "Should detect KMI version downgrade";
+
+  EXPECT_EQ(ErrorCode::kSuccess,
+            HardwareAndroid::IsKernelUpdateValid("5.4.42-android12-0-something",
+                                                 "5.4.5-android12-0-something",
+                                                 false /*prevent_downgrade*/))
+      << "Should suppress sub-level downgrade";
 }
 
 }  // namespace chromeos_update_engine