update_engine: Store fingerprint value from Omaha response.

Store the unique fp value from response into prefs. Value is later sent
to Omaha to determine if there is a subsequent update available
while the system is waiting to be rebooted.

BUG=b:161259884
TEST=cros_workon_make --board=hatch --test update_engine

Change-Id: Ie37aa5da3cd8a0820e633f5ef426fb50e8a02838
Reviewed-on: https://chromium-review.googlesource.com/c/aosp/platform/system/update_engine/+/2491618
Tested-by: Vyshu Khota <vyshu@google.com>
Commit-Queue: Vyshu Khota <vyshu@google.com>
Reviewed-by: Amin Hassani <ahassani@chromium.org>
diff --git a/cros/omaha_request_action.cc b/cros/omaha_request_action.cc
index faa7dde..cad0c67 100644
--- a/cros/omaha_request_action.cc
+++ b/cros/omaha_request_action.cc
@@ -98,6 +98,7 @@
 constexpr char kAttrElapsedDays[] = "elapsed_days";
 constexpr char kAttrElapsedSeconds[] = "elapsed_seconds";
 constexpr char kAttrEvent[] = "event";
+constexpr char kAttrFp[] = "fp";
 constexpr char kAttrHashSha256[] = "hash_sha256";
 // Deprecated: "hash"; Although we still need to pass it from the server for
 // backward compatibility.
@@ -150,6 +151,7 @@
       string name;
       string size;
       string hash;
+      string fp;
     };
     vector<Package> packages;
   };
@@ -214,7 +216,8 @@
     if (!data->apps.empty())
       data->apps.back().packages.push_back({.name = attrs[kAttrName],
                                             .size = attrs[kAttrSize],
-                                            .hash = attrs[kAttrHashSha256]});
+                                            .hash = attrs[kAttrHashSha256],
+                                            .fp = attrs[kAttrFp]});
   } else if (data->current_path == "/response/app/updatecheck/manifest") {
     // Get the version.
     if (!data->apps.empty())
@@ -612,6 +615,8 @@
       return false;
     }
 
+    out_package.fp = package.fp;
+
     if (i < is_delta_payloads.size())
       out_package.is_delta = ParseBool(is_delta_payloads[i]);
 
diff --git a/cros/omaha_request_action_unittest.cc b/cros/omaha_request_action_unittest.cc
index 8d94195..9f9c75f 100644
--- a/cros/omaha_request_action_unittest.cc
+++ b/cros/omaha_request_action_unittest.cc
@@ -158,10 +158,10 @@
            version +
            "\">"
            "<packages><package hash=\"not-used\" name=\"" +
-           filename + "\" size=\"" + base::NumberToString(size) +
-           "\" hash_sha256=\"" + hash + "\"/>" +
-           (multi_package ? "<package name=\"package2\" size=\"222\" "
-                            "hash_sha256=\"hash2\"/>"
+           filename + "\" size=\"" + base::NumberToString(size) + "\" fp=\"" +
+           fp + "\" hash_sha256=\"" + hash + "\"/>" +
+           (multi_package ? "<package name=\"package2\" size=\"222\" fp=\"" +
+                                fp2 + "\" hash_sha256=\"hash2\"/>"
                           : "") +
            "</packages>"
            "<actions><action event=\"postinstall\" MetadataSize=\"11" +
@@ -187,8 +187,9 @@
                       "><updatecheck status=\"ok\"><urls><url codebase=\"" +
                       codebase2 + "\"/></urls><manifest version=\"" + version2 +
                       "\"><packages>"
-                      "<package name=\"package3\" size=\"333\" "
-                      "hash_sha256=\"hash3\"/></packages>"
+                      "<package name=\"package3\" size=\"333\" fp=\"" +
+                      fp2 +
+                      "\" hash_sha256=\"hash3\"/></packages>"
                       "<actions><action event=\"postinstall\" " +
                       (multi_app_self_update
                            ? "noupdate=\"true\" IsDeltaPayload=\"true\" "
@@ -215,8 +216,10 @@
                       codebase + "\"/><url codebase=\"" + codebase2 +
                       "\"/></urls><manifest version=\"" + version +
                       "\"><packages><package name=\"package3\" size=\"333\" "
-                      "hash_sha256=\"hash3\"/></packages><actions>"
-                      "<action event=\"install\" run=\".signed\"/>"
+                      "fp=\"" +
+                      fp2 +
+                      "\" hash_sha256=\"hash3\"/></packages>"
+                      "<actions><action event=\"install\" run=\".signed\"/>"
                       "<action event=\"postinstall\" MetadataSize=\"33\"/>"
                       "</actions></manifest></updatecheck></app>"
                 : "") +
@@ -248,6 +251,8 @@
   string codebase2 = "http://code/base/2/";
   string filename = "file.signed";
   string hash = "4841534831323334";
+  string fp = "3.98ba213e";
+  string fp2 = "3.755aff78e";
   uint64_t size = 123;
   string deadline = "";
   string max_days_to_scatter = "7";
@@ -670,6 +675,7 @@
   EXPECT_EQ(fake_update_response_.more_info_url, response.more_info_url);
   EXPECT_EQ(fake_update_response_.hash, response.packages[0].hash);
   EXPECT_EQ(fake_update_response_.size, response.packages[0].size);
+  EXPECT_EQ(fake_update_response_.fp, response.packages[0].fp);
   EXPECT_EQ(true, response.packages[0].is_delta);
   EXPECT_EQ(fake_update_response_.prompt == "true", response.prompt);
   EXPECT_EQ(fake_update_response_.deadline, response.deadline);
@@ -695,11 +701,13 @@
             response.packages[1].payload_urls[0]);
   EXPECT_EQ(fake_update_response_.hash, response.packages[0].hash);
   EXPECT_EQ(fake_update_response_.size, response.packages[0].size);
+  EXPECT_EQ(fake_update_response_.fp, response.packages[0].fp);
   EXPECT_EQ(true, response.packages[0].is_delta);
   EXPECT_EQ(11u, response.packages[0].metadata_size);
   ASSERT_EQ(2u, response.packages.size());
   EXPECT_EQ(string("hash2"), response.packages[1].hash);
   EXPECT_EQ(222u, response.packages[1].size);
+  EXPECT_EQ(fake_update_response_.fp2, response.packages[1].fp);
   EXPECT_EQ(22u, response.packages[1].metadata_size);
   EXPECT_EQ(false, response.packages[1].is_delta);
 }
@@ -718,11 +726,13 @@
             response.packages[1].payload_urls[0]);
   EXPECT_EQ(fake_update_response_.hash, response.packages[0].hash);
   EXPECT_EQ(fake_update_response_.size, response.packages[0].size);
+  EXPECT_EQ(fake_update_response_.fp, response.packages[0].fp);
   EXPECT_EQ(11u, response.packages[0].metadata_size);
   EXPECT_EQ(true, response.packages[0].is_delta);
   ASSERT_EQ(2u, response.packages.size());
   EXPECT_EQ(string("hash3"), response.packages[1].hash);
   EXPECT_EQ(333u, response.packages[1].size);
+  EXPECT_EQ(fake_update_response_.fp2, response.packages[1].fp);
   EXPECT_EQ(33u, response.packages[1].metadata_size);
   EXPECT_EQ(false, response.packages[1].is_delta);
 }
@@ -740,10 +750,12 @@
             response.packages[0].payload_urls[0]);
   EXPECT_EQ(fake_update_response_.hash, response.packages[0].hash);
   EXPECT_EQ(fake_update_response_.size, response.packages[0].size);
+  EXPECT_EQ(fake_update_response_.fp, response.packages[0].fp);
   EXPECT_EQ(11u, response.packages[0].metadata_size);
   ASSERT_EQ(2u, response.packages.size());
   EXPECT_EQ(string("hash3"), response.packages[1].hash);
   EXPECT_EQ(333u, response.packages[1].size);
+  EXPECT_EQ(fake_update_response_.fp2, response.packages[1].fp);
   EXPECT_EQ(33u, response.packages[1].metadata_size);
   EXPECT_EQ(true, response.packages[1].is_delta);
 }
@@ -765,15 +777,18 @@
             response.packages[2].payload_urls[0]);
   EXPECT_EQ(fake_update_response_.hash, response.packages[0].hash);
   EXPECT_EQ(fake_update_response_.size, response.packages[0].size);
+  EXPECT_EQ(fake_update_response_.fp, response.packages[0].fp);
   EXPECT_EQ(11u, response.packages[0].metadata_size);
   EXPECT_EQ(true, response.packages[0].is_delta);
   ASSERT_EQ(3u, response.packages.size());
   EXPECT_EQ(string("hash2"), response.packages[1].hash);
   EXPECT_EQ(222u, response.packages[1].size);
+  EXPECT_EQ(fake_update_response_.fp2, response.packages[1].fp);
   EXPECT_EQ(22u, response.packages[1].metadata_size);
   EXPECT_EQ(false, response.packages[1].is_delta);
   EXPECT_EQ(string("hash3"), response.packages[2].hash);
   EXPECT_EQ(333u, response.packages[2].size);
+  EXPECT_EQ(fake_update_response_.fp2, response.packages[2].fp);
   EXPECT_EQ(33u, response.packages[2].metadata_size);
   EXPECT_EQ(false, response.packages[2].is_delta);
 }
@@ -1557,7 +1572,7 @@
       "<urls><url codebase=\"http://missing/field/test/\"/></urls>"
       "<manifest version=\"10.2.3.4\">"
       "<packages><package hash=\"not-used\" name=\"f\" "
-      "size=\"587\" hash_sha256=\"lkq34j5345\"/></packages>"
+      "size=\"587\" fp=\"3.789\" hash_sha256=\"lkq34j5345\"/></packages>"
       "<actions><action event=\"postinstall\" "
       "Prompt=\"false\" "
       "IsDeltaPayload=\"false\" "
@@ -1572,6 +1587,7 @@
             response.packages[0].payload_urls[0]);
   EXPECT_EQ("", response.more_info_url);
   EXPECT_EQ("lkq34j5345", response.packages[0].hash);
+  EXPECT_EQ(string("3.789"), response.packages[0].fp);
   EXPECT_EQ(587u, response.packages[0].size);
   EXPECT_FALSE(response.prompt);
   EXPECT_TRUE(response.deadline.empty());
diff --git a/cros/omaha_response.h b/cros/omaha_response.h
index 43783d6..3b07745 100644
--- a/cros/omaha_response.h
+++ b/cros/omaha_response.h
@@ -55,6 +55,8 @@
     bool can_exclude = false;
     // The App ID associated with the package.
     std::string app_id;
+    // The unique fingerprint value associated with the package.
+    std::string fp;
   };
   std::vector<Package> packages;
 
diff --git a/cros/omaha_response_handler_action.cc b/cros/omaha_response_handler_action.cc
index b6c223f..52142a3 100644
--- a/cros/omaha_response_handler_action.cc
+++ b/cros/omaha_response_handler_action.cc
@@ -106,7 +106,9 @@
          .metadata_signature = package.metadata_signature,
          .hash = raw_hash,
          .type = package.is_delta ? InstallPayloadType::kDelta
-                                  : InstallPayloadType::kFull});
+                                  : InstallPayloadType::kFull,
+         .fp = package.fp,
+         .app_id = package.app_id});
     update_check_response_hash += package.hash + ":";
   }
   install_plan_.public_key_rsa = response.public_key_rsa;
diff --git a/cros/omaha_response_handler_action_unittest.cc b/cros/omaha_response_handler_action_unittest.cc
index b05660c..74f4d04 100644
--- a/cros/omaha_response_handler_action_unittest.cc
+++ b/cros/omaha_response_handler_action_unittest.cc
@@ -117,6 +117,9 @@
     "-the_update_a.b.c.d_DELTA_.tgz";
 const char* const kBadVersion = "don't update me";
 const char* const kPayloadHashHex = "486173682b";
+const char* const kPayloadFp1 = "1.755aff78ec73dfc7f590893ac";
+const char* const kPayloadFp2 = "1.98ba213e0ccec0d0e8cdc74a5";
+const char* const kPayloadAppId = "test_app_id";
 }  // namespace
 
 bool OmahaResponseHandlerActionTest::DoTest(const OmahaResponse& in,
@@ -185,7 +188,9 @@
     in.packages.push_back(
         {.payload_urls = {"http://foo/the_update_a.b.c.d.tgz"},
          .size = 12,
-         .hash = kPayloadHashHex});
+         .hash = kPayloadHashHex,
+         .app_id = kPayloadAppId,
+         .fp = kPayloadFp1});
     in.more_info_url = "http://more/info";
     in.prompt = false;
     in.deadline = "20101020";
@@ -193,6 +198,8 @@
     EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
     EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
     EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+    EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+    EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
     EXPECT_EQ(1U, install_plan.target_slot);
     string deadline;
     EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline));
@@ -211,7 +218,9 @@
     in.packages.push_back(
         {.payload_urls = {"http://foo/the_update_a.b.c.d.tgz"},
          .size = 12,
-         .hash = kPayloadHashHex});
+         .hash = kPayloadHashHex,
+         .app_id = kPayloadAppId,
+         .fp = kPayloadFp1});
     in.more_info_url = "http://more/info";
     in.prompt = true;
     InstallPlan install_plan;
@@ -220,6 +229,8 @@
     EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
     EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
     EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+    EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+    EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
     EXPECT_EQ(0U, install_plan.target_slot);
     string deadline;
     EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline) &&
@@ -230,8 +241,11 @@
     OmahaResponse in;
     in.update_exists = true;
     in.version = "a.b.c.d";
-    in.packages.push_back(
-        {.payload_urls = {kLongName}, .size = 12, .hash = kPayloadHashHex});
+    in.packages.push_back({.payload_urls = {kLongName},
+                           .size = 12,
+                           .hash = kPayloadHashHex,
+                           .app_id = kPayloadAppId,
+                           .fp = kPayloadFp1});
     in.more_info_url = "http://more/info";
     in.prompt = true;
     in.deadline = "some-deadline";
@@ -245,6 +259,8 @@
     EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
     EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
     EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+    EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+    EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
     EXPECT_EQ(1U, install_plan.target_slot);
     string deadline;
     EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline));
@@ -255,8 +271,11 @@
     OmahaResponse in;
     in.update_exists = true;
     in.version = "a.b.c.d";
-    in.packages.push_back(
-        {.payload_urls = {kLongName}, .size = 12, .hash = kPayloadHashHex});
+    in.packages.push_back({.payload_urls = {kLongName},
+                           .size = 12,
+                           .hash = kPayloadHashHex,
+                           .app_id = kPayloadAppId,
+                           .fp = kPayloadFp1});
     in.more_info_url = "http://more/info";
     in.prompt = true;
     in.deadline = "some-deadline";
@@ -268,6 +287,8 @@
     EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
     EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
     EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+    EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+    EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
     EXPECT_EQ(1U, install_plan.target_slot);
     string deadline;
     EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline));
@@ -309,10 +330,14 @@
   in.version = "a.b.c.d";
   in.packages.push_back({.payload_urls = {"http://package/1"},
                          .size = 1,
-                         .hash = kPayloadHashHex});
+                         .hash = kPayloadHashHex,
+                         .app_id = kPayloadAppId,
+                         .fp = kPayloadFp1});
   in.packages.push_back({.payload_urls = {"http://package/2"},
                          .size = 2,
-                         .hash = kPayloadHashHex});
+                         .hash = kPayloadHashHex,
+                         .app_id = kPayloadAppId,
+                         .fp = kPayloadFp2});
   in.more_info_url = "http://more/info";
   InstallPlan install_plan;
   EXPECT_TRUE(DoTest(in, "", &install_plan));
@@ -322,6 +347,10 @@
   EXPECT_EQ(in.packages[1].size, install_plan.payloads[1].size);
   EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
   EXPECT_EQ(expected_hash_, install_plan.payloads[1].hash);
+  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+  EXPECT_EQ(in.packages[1].app_id, install_plan.payloads[1].app_id);
+  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
+  EXPECT_EQ(in.packages[1].fp, install_plan.payloads[1].fp);
   EXPECT_EQ(in.version, install_plan.version);
 }
 
@@ -332,7 +361,9 @@
   in.packages.push_back(
       {.payload_urls = {"http://test.should/need/hash.checks.signed"},
        .size = 12,
-       .hash = kPayloadHashHex});
+       .hash = kPayloadHashHex,
+       .app_id = kPayloadAppId,
+       .fp = kPayloadFp1});
   in.more_info_url = "http://more/info";
   // Hash checks are always skipped for non-official update URLs.
   EXPECT_CALL(*(fake_system_state_.mock_request_params()),
@@ -342,6 +373,8 @@
   EXPECT_TRUE(DoTest(in, "", &install_plan));
   EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
   EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
   EXPECT_TRUE(install_plan.hash_checks_mandatory);
   EXPECT_EQ(in.version, install_plan.version);
 }
@@ -353,7 +386,9 @@
   in.packages.push_back(
       {.payload_urls = {"http://url.normally/needs/hash.checks.signed"},
        .size = 12,
-       .hash = kPayloadHashHex});
+       .hash = kPayloadHashHex,
+       .app_id = kPayloadAppId,
+       .fp = kPayloadFp1});
   in.more_info_url = "http://more/info";
   EXPECT_CALL(*(fake_system_state_.mock_request_params()),
               IsUpdateUrlOfficial())
@@ -362,6 +397,8 @@
   EXPECT_TRUE(DoTest(in, "", &install_plan));
   EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
   EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
   EXPECT_FALSE(install_plan.hash_checks_mandatory);
   EXPECT_EQ(in.version, install_plan.version);
 }
@@ -375,7 +412,9 @@
   in.packages.push_back(
       {.payload_urls = {"http://url.normally/needs/hash.checks.signed"},
        .size = 12,
-       .hash = kPayloadHashHex});
+       .hash = kPayloadHashHex,
+       .app_id = kPayloadAppId,
+       .fp = kPayloadFp1});
   in.more_info_url = "http://more/info";
   EXPECT_CALL(*(fake_system_state_.mock_request_params()),
               IsUpdateUrlOfficial())
@@ -385,6 +424,8 @@
   EXPECT_TRUE(DoTest(in, "", &install_plan));
   EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
   EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
   EXPECT_FALSE(install_plan.hash_checks_mandatory);
   EXPECT_EQ(in.version, install_plan.version);
 }
@@ -396,7 +437,9 @@
   in.packages.push_back(
       {.payload_urls = {"https://test.should/need/hash.checks.signed"},
        .size = 12,
-       .hash = kPayloadHashHex});
+       .hash = kPayloadHashHex,
+       .app_id = kPayloadAppId,
+       .fp = kPayloadFp1});
   in.more_info_url = "http://more/info";
   EXPECT_CALL(*(fake_system_state_.mock_request_params()),
               IsUpdateUrlOfficial())
@@ -405,6 +448,8 @@
   EXPECT_TRUE(DoTest(in, "", &install_plan));
   EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
   EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
   EXPECT_TRUE(install_plan.hash_checks_mandatory);
   EXPECT_EQ(in.version, install_plan.version);
 }
@@ -417,7 +462,9 @@
       {.payload_urls = {"http://test.should.still/need/hash.checks",
                         "https://test.should.still/need/hash.checks"},
        .size = 12,
-       .hash = kPayloadHashHex});
+       .hash = kPayloadHashHex,
+       .app_id = kPayloadAppId,
+       .fp = kPayloadFp1});
   in.more_info_url = "http://more/info";
   EXPECT_CALL(*(fake_system_state_.mock_request_params()),
               IsUpdateUrlOfficial())
@@ -426,6 +473,8 @@
   EXPECT_TRUE(DoTest(in, "", &install_plan));
   EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
   EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
   EXPECT_TRUE(install_plan.hash_checks_mandatory);
   EXPECT_EQ(in.version, install_plan.version);
 }
@@ -675,7 +724,9 @@
   in.packages.push_back(
       {.payload_urls = {"https://would.not/cause/hash/checks"},
        .size = 12,
-       .hash = kPayloadHashHex});
+       .hash = kPayloadHashHex,
+       .app_id = kPayloadAppId,
+       .fp = kPayloadFp1});
   in.more_info_url = "http://more/info";
 
   OmahaRequestParams params(&fake_system_state_);
@@ -698,6 +749,8 @@
   InstallPlan install_plan;
   EXPECT_TRUE(DoTest(in, "", &install_plan));
   EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
   EXPECT_EQ(p2p_url, install_plan.download_url);
   EXPECT_TRUE(install_plan.hash_checks_mandatory);
 }
@@ -899,10 +952,14 @@
   in.version = "a.b.c.d";
   in.packages.push_back({.payload_urls = {"http://package/1"},
                          .size = 1,
-                         .hash = kPayloadHashHex});
+                         .hash = kPayloadHashHex,
+                         .app_id = kPayloadAppId,
+                         .fp = kPayloadFp1});
   in.packages.push_back({.payload_urls = {"http://package/2"},
                          .size = 2,
-                         .hash = kPayloadHashHex});
+                         .hash = kPayloadHashHex,
+                         .app_id = kPayloadAppId,
+                         .fp = kPayloadFp2});
   in.more_info_url = "http://more/info";
   InstallPlan install_plan;
   EXPECT_TRUE(DoTest(in, "", &install_plan));
@@ -912,6 +969,10 @@
   EXPECT_EQ(in.packages[1].size, install_plan.payloads[1].size);
   EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
   EXPECT_EQ(expected_hash_, install_plan.payloads[1].hash);
+  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+  EXPECT_EQ(in.packages[1].app_id, install_plan.payloads[1].app_id);
+  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
+  EXPECT_EQ(in.packages[1].fp, install_plan.payloads[1].fp);
   EXPECT_EQ(in.version, install_plan.version);
 }
 
@@ -921,7 +982,9 @@
   in.version = "a.b.c.d";
   in.packages.push_back({.payload_urls = {"http://foo/the_update_a.b.c.d.tgz"},
                          .size = 12,
-                         .hash = kPayloadHashHex});
+                         .hash = kPayloadHashHex,
+                         .app_id = kPayloadAppId,
+                         .fp = kPayloadFp1});
   // Setup the UpdateManager to disallow the update.
   FakeClock fake_clock;
   MockPolicy* mock_policy = new MockPolicy(&fake_clock);
@@ -942,6 +1005,8 @@
   install_plan = *delegate_.response_handler_action_install_plan_;
   EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
   EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+  EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+  EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
   EXPECT_EQ(1U, install_plan.target_slot);
   EXPECT_EQ(in.version, install_plan.version);
 }
diff --git a/cros/update_attempter.cc b/cros/update_attempter.cc
index e417457..5c21d04 100644
--- a/cros/update_attempter.cc
+++ b/cros/update_attempter.cc
@@ -158,10 +158,12 @@
 
   // In case of update_engine restart without a reboot we need to restore the
   // reboot needed state.
-  if (GetBootTimeAtUpdate(nullptr))
+  if (GetBootTimeAtUpdate(nullptr)) {
     status_ = UpdateStatus::UPDATED_NEED_REBOOT;
-  else
+  } else {
     status_ = UpdateStatus::IDLE;
+    prefs_->Delete(kPrefsLastFp, {kDlcPrefsSubDir});
+  }
 }
 
 bool UpdateAttempter::ScheduleUpdates() {
@@ -646,6 +648,20 @@
   return failures.size() == 0;
 }
 
+void UpdateAttempter::SetPref(const string& pref_key,
+                              const string& pref_value,
+                              const string& payload_id) {
+  string dlc_id;
+  if (!omaha_request_params_->GetDlcId(payload_id, &dlc_id)) {
+    // Not a DLC ID, set fingerprint in perf for platform ID.
+    prefs_->SetString(pref_key, pref_value);
+  } else {
+    // Set fingerprint in pref for DLC ID.
+    auto key = prefs_->CreateSubKey({kDlcPrefsSubDir, dlc_id, pref_key});
+    prefs_->SetString(key, pref_value);
+  }
+}
+
 bool UpdateAttempter::SetDlcActiveValue(bool is_active, const string& dlc_id) {
   if (dlc_id.empty()) {
     LOG(ERROR) << "Empty DLC ID passed.";
@@ -1198,6 +1214,9 @@
     for (const auto& payload : install_plan_->payloads) {
       target_version_uid += brillo::data_encoding::Base64Encode(payload.hash) +
                             ":" + payload.metadata_signature + ":";
+      // Set fingerprint value for updates only.
+      if (!is_install_)
+        SetPref(kPrefsLastFp, payload.fp, payload.app_id);
     }
 
     // If we just downloaded a rollback image, we should preserve this fact
@@ -1419,6 +1438,7 @@
       // UpdateStatus::UPDATED_NEED_REBOOT state.
       ret_value = prefs_->Delete(kPrefsUpdateCompletedOnBootId) && ret_value;
       ret_value = prefs_->Delete(kPrefsUpdateCompletedBootTime) && ret_value;
+      ret_value = prefs_->Delete(kPrefsLastFp, {kDlcPrefsSubDir}) && ret_value;
 
       // Update the boot flags so the current slot has higher priority.
       BootControlInterface* boot_control = system_state_->boot_control();
diff --git a/cros/update_attempter.h b/cros/update_attempter.h
index bd0aef6..a201acf 100644
--- a/cros/update_attempter.h
+++ b/cros/update_attempter.h
@@ -433,6 +433,11 @@
   // Resets all the DLC prefs.
   bool ResetDlcPrefs(const std::string& dlc_id);
 
+  // Sets given pref key for DLC and platform.
+  void SetPref(const std::string& pref_key,
+               const std::string& pref_value,
+               const std::string& payload_id);
+
   // Get the integer values from the DLC metadata for |kPrefsPingLastActive|
   // or |kPrefsPingLastRollcall|.
   // The value is equal to -2 when the value cannot be read or is not numeric.