pw_software_update: land initial service implementation
Also renames the pw_software_update proto package to instead
use the canonical pw.software_update. As part of this the
Manifest and UpdateBundle C++ classes were renamed to
ManifestAccessor and UpdateBundleAccessor.
Moves the service.proto to bundled_update.proto and renames the source
files accordingly.
No-Docs-Update-Reason: Module still in early development
Requires: pigweed-internal:15503
Change-Id: I6531ee5772c17331e9c5ce7e16f4b72002656834
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/61960
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Reviewed-by: David Rogers <davidrogers@google.com>
Pigweed-Auto-Submit: Ewout van Bekkum <ewout@google.com>
diff --git a/pw_software_update/BUILD.gn b/pw_software_update/BUILD.gn
index d86d01f..fe8a6a6 100644
--- a/pw_software_update/BUILD.gn
+++ b/pw_software_update/BUILD.gn
@@ -16,6 +16,7 @@
import("$dir_pw_docgen/docs.gni")
import("$dir_pw_protobuf_compiler/proto.gni")
+import("$dir_pw_third_party/nanopb/nanopb.gni")
import("$dir_pw_third_party/protobuf/protobuf.gni")
import("$dir_pw_unit_test/test.gni")
@@ -31,7 +32,7 @@
"$dir_pw_third_party/protobuf:wellknown_types",
]
sources = [
- "service.proto",
+ "bundled_update.proto",
"tuf.proto",
"update_bundle.proto",
]
@@ -43,7 +44,7 @@
pw_proto_library("protos") {
deps = [ "$dir_pw_protobuf:common_protos" ]
sources = [
- "service.proto",
+ "bundled_update.proto",
"tuf.proto",
"update_bundle.proto",
]
@@ -69,28 +70,29 @@
public = [
"public/pw_software_update/bundled_update_backend.h",
"public/pw_software_update/config.h",
- "public/pw_software_update/manifest.h",
- "public/pw_software_update/update_bundle.h",
+ "public/pw_software_update/manifest_accessor.h",
+ "public/pw_software_update/update_bundle_accessor.h",
]
deps = [
":protos.pwpb",
dir_pw_log,
]
- sources = [ "update_bundle.cc" ]
+ sources = [ "update_bundle_accessor.cc" ]
}
if (dir_pw_third_party_nanopb != "" && dir_pw_third_party_protobuf != "") {
- pw_source_set("rpc_service") {
+ pw_source_set("bundled_update_service") {
public_configs = [ ":public_include_path" ]
- public_deps = [ ":protos.nanopb_rpc" ]
- deps = [ dir_pw_log ]
- sources = [
- "public/pw_software_update/service.h",
- "service.cc",
+ public_deps = [
+ ":protos.nanopb_rpc",
+ ":update_manager",
]
+ deps = [ dir_pw_log ]
+ public = [ "public/pw_software_update/bundled_update_service.h" ]
+ sources = [ "bundled_update_service.cc" ]
}
} else {
- group("rpc_service") {
+ group("bundled_update_service") {
}
}
@@ -115,8 +117,8 @@
dir_pw_third_party_nanopb != "" && dir_pw_third_party_protobuf != ""
sources = [ "update_bundle_test.cc" ]
public_deps = [
+ ":bundled_update_service",
":generate_test_bundle",
- ":rpc_service",
":update_bundle",
"$dir_pw_kvs:fake_flash",
"$dir_pw_kvs:fake_flash_test_key_value_store",
@@ -125,5 +127,23 @@
}
pw_test_group("tests") {
- tests = [ ":update_bundle_test" ]
+ tests = [
+ ":update_bundle_test",
+ ":update_manager_test",
+ ]
+}
+
+pw_test("update_manager_test") {
+ enable_if =
+ dir_pw_third_party_nanopb != "" && dir_pw_third_party_protobuf != ""
+ sources = [ "update_manager_test.cc" ]
+ public_deps = [ ":update_manager" ]
+}
+
+pw_source_set("update_manager") {
+ public_configs = [ ":public_include_path" ]
+ public_deps = [ ":update_bundle" ]
+ deps = [ ":protos.pwpb" ]
+ public = [ "public/pw_software_update/update_manager.h" ]
+ sources = [ "update_manager.cc" ]
}
diff --git a/pw_software_update/service.proto b/pw_software_update/bundled_update.proto
similarity index 75%
rename from pw_software_update/service.proto
rename to pw_software_update/bundled_update.proto
index 3765fc0..39e042e 100644
--- a/pw_software_update/service.proto
+++ b/pw_software_update/bundled_update.proto
@@ -13,7 +13,7 @@
// the License.
syntax = "proto3";
-package pw_software_update;
+package pw.software_update;
import "pw_software_update/tuf.proto";
import "pw_software_update/update_bundle.proto";
@@ -44,11 +44,11 @@
message PrepareUpdateResult {
OperationResult result = 1;
- optional uint32 transfer_endpoint = 2;
+ optional uint32 transfer_id = 2;
}
// TODO(pwbug/478): add documentation for details of api contract
-service BundledUpdateService {
+service BundledUpdate {
// Abort any current software update in progress.
//
// Safe to call at any point.
@@ -83,17 +83,31 @@
// rejected.
rpc PrepareForUpdate(pw.protobuf.Empty) returns (PrepareUpdateResult) {};
- // Verify the bundle that has been transferred to the staging area. Close the
- // pw_transfer channel used for staging bundle.
+ // Verifies the bundle that has been transferred to the traging area. Closes
+ // the pw_transfer channel used for the staging bundle. If the verification is
+ // successful it immediately triggers the update of the device, which might
+ // result in a device becoming slow to respond and possibly reboot.
//
// Device UpdateState should be READY_FOR_UPDATE when calling, will otherwise
// be rejected.
+ rpc VerifyAndApplyStagedBundle(pw.protobuf.Empty) returns (OperationResult) {
+ };
+
+ // Verify the bundle that has been transferred to the staging area. Closes the
+ // pw_transfer channel used for the staging bundle.
+ //
+ // Device UpdateState should be READY_FOR_UPDATE when calling, will otherwise
+ // be rejected.
+ //
+ // Note: VerifyAndApplyStagedBundle is preferred if possible to minimize the
+ // duration of time between verification and the apply from a data integrity
+ // and security risk point of view.
rpc VerifyStagedBundle(pw.protobuf.Empty) returns (OperationResult) {};
- // Trigger the application of the device, which might result in a device
+ // Trigger the update of the device, which might result in a device
// becoming slow to respond and possibly reboot.
//
// Device UpdateState should be VERIFIED_AND_READY_TO_APPLY when calling, will
// otherwise be rejected.
- rpc ApplyUpdate(pw.protobuf.Empty) returns (OperationResult) {};
+ rpc ApplyStagedBundle(pw.protobuf.Empty) returns (OperationResult) {};
}
diff --git a/pw_software_update/bundled_update_service.cc b/pw_software_update/bundled_update_service.cc
new file mode 100644
index 0000000..4435428
--- /dev/null
+++ b/pw_software_update/bundled_update_service.cc
@@ -0,0 +1,116 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_software_update/bundled_update_service.h"
+
+#include "pw_result/result.h"
+#include "pw_status/status.h"
+#include "pw_status/try.h"
+
+namespace pw::software_update {
+
+Status BundledUpdateService::Abort(ServerContext&,
+ const pw_protobuf_Empty&,
+ pw_software_update_OperationResult&) {
+ return manager_.Abort();
+}
+
+Status BundledUpdateService::SoftwareUpdateState(
+ ServerContext&,
+ const pw_protobuf_Empty&,
+ pw_software_update_OperationResult&) {
+ return Status::Unimplemented();
+}
+
+void BundledUpdateService::GetCurrentManifest(
+ ServerContext&,
+ const pw_protobuf_Empty&,
+ ServerWriter<pw_software_update_Manifest>& writer) {
+ writer.Finish(Status::Unimplemented());
+}
+
+Status BundledUpdateService::VerifyCurrentManifest(
+ ServerContext&,
+ const pw_protobuf_Empty&,
+ pw_software_update_OperationResult&) {
+ return Status::Unimplemented();
+}
+
+Status BundledUpdateService::GetStagedManifest(ServerContext&,
+ const pw_protobuf_Empty&,
+ pw_software_update_Manifest&) {
+ return Status::Unimplemented();
+}
+
+Status BundledUpdateService::PrepareForUpdate(
+ ServerContext&,
+ [[maybe_unused]] const pw_protobuf_Empty& request,
+ pw_software_update_PrepareUpdateResult& response) {
+ pw_software_update_OperationResult result;
+ result.has_extended_status = false;
+ result.has_state = true;
+ pw_software_update_BundledUpdateState state;
+ state.manager_state =
+ pw_software_update_BundledUpdateState_State_READY_FOR_UPDATE;
+ result.state = state;
+ response.has_result = true;
+ response.result = result;
+ response.has_transfer_id = true;
+ const pw::Result<uint32_t> possible_transfer_id = manager_.GetTransferId();
+ PW_TRY(possible_transfer_id.status());
+ response.transfer_id = possible_transfer_id.value();
+ return manager_.BeforeUpdate();
+}
+
+// TODO: dedupe this with VerifyStagedBundle.
+Status BundledUpdateService::VerifyAndApplyStagedBundle(
+ ServerContext&,
+ const pw_protobuf_Empty&,
+ pw_software_update_OperationResult& response) {
+ // TODO: upstream verification logic
+ response.has_extended_status = false;
+ response.has_state = true;
+ pw_software_update_BundledUpdateState state;
+ state.manager_state =
+ pw_software_update_BundledUpdateState_State_VERIFIED_AND_READY_TO_APPLY;
+ response.state = state;
+ // TODO: defer this handling to the work queue so we can respond here.
+ PW_TRY(manager_.VerifyUpdate());
+ return manager_.ApplyUpdate();
+}
+
+Status BundledUpdateService::VerifyStagedBundle(
+ ServerContext&,
+ const pw_protobuf_Empty&,
+ pw_software_update_OperationResult& response) {
+ // TODO: upstream verification logic
+ response.has_extended_status = false;
+ response.has_state = true;
+ pw_software_update_BundledUpdateState state;
+ state.manager_state =
+ pw_software_update_BundledUpdateState_State_VERIFIED_AND_READY_TO_APPLY;
+ response.state = state;
+ // TODO: defer this handling to the work queue so we can respond here.
+ return manager_.VerifyUpdate();
+}
+
+Status BundledUpdateService::ApplyStagedBundle(
+ ServerContext&,
+ const pw_protobuf_Empty&,
+ pw_software_update_OperationResult&) {
+ // TODO: defer this handling to the work queue so we can respond here.
+ return manager_.ApplyUpdate();
+}
+
+} // namespace pw::software_update
diff --git a/pw_software_update/public/pw_software_update/bundled_update_backend.h b/pw_software_update/public/pw_software_update/bundled_update_backend.h
index 7187385..f7abe0c 100644
--- a/pw_software_update/public/pw_software_update/bundled_update_backend.h
+++ b/pw_software_update/public/pw_software_update/bundled_update_backend.h
@@ -14,8 +14,10 @@
#pragma once
+#include <string_view>
+
#include "pw_result/result.h"
-#include "pw_software_update/manifest.h"
+#include "pw_software_update/manifest_accessor.h"
#include "pw_status/status.h"
#include "pw_stream/stream.h"
@@ -31,7 +33,7 @@
// (e.g. by checksum, if failed abort partial update and wipe/mark-invalid
// running manifest)
virtual Status VerifyTargetFile(
- [[maybe_unused]] const Manifest& manifest,
+ [[maybe_unused]] const ManifestAccessor& manifest,
[[maybe_unused]] std::string_view target_file_name) {
return OkStatus();
};
@@ -57,7 +59,8 @@
// Perform any product-specific bundle verification tasks (e.g. hw version
// match check), done after TUF bundle verification process.
- virtual Status VerifyMetadata([[maybe_unused]] const Manifest& manifest) {
+ virtual Status VerifyMetadata(
+ [[maybe_unused]] const ManifestAccessor& manifest) {
return OkStatus();
};
diff --git a/pw_software_update/public/pw_software_update/bundled_update_service.h b/pw_software_update/public/pw_software_update/bundled_update_service.h
new file mode 100644
index 0000000..d7af0fe
--- /dev/null
+++ b/pw_software_update/public/pw_software_update/bundled_update_service.h
@@ -0,0 +1,72 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#pragma once
+
+#include "pw_software_update/bundled_update.rpc.pb.h"
+#include "pw_software_update/update_manager.h"
+#include "pw_status/status.h"
+
+namespace pw::software_update {
+
+// Implementation class for pw_software_update.BundledUpdate.
+class BundledUpdateService
+ : public generated::BundledUpdate<BundledUpdateService> {
+ public:
+ explicit constexpr BundledUpdateService(
+ pw::software_update::BundledUpdateManager& manager)
+ : manager_(manager) {}
+
+ Status Abort(ServerContext&,
+ const pw_protobuf_Empty& request,
+ pw_software_update_OperationResult& response);
+
+ Status SoftwareUpdateState(ServerContext&,
+ const pw_protobuf_Empty& request,
+ pw_software_update_OperationResult& response);
+
+ void GetCurrentManifest(ServerContext&,
+ const pw_protobuf_Empty& request,
+ ServerWriter<pw_software_update_Manifest>& writer);
+
+ Status VerifyCurrentManifest(ServerContext&,
+ const pw_protobuf_Empty& request,
+ pw_software_update_OperationResult& response);
+
+ Status GetStagedManifest(ServerContext&,
+ const pw_protobuf_Empty& request,
+ pw_software_update_Manifest& response);
+
+ Status PrepareForUpdate(ServerContext&,
+ const pw_protobuf_Empty& request,
+ pw_software_update_PrepareUpdateResult& response);
+
+ Status VerifyAndApplyStagedBundle(
+ ServerContext&,
+ const pw_protobuf_Empty& request,
+ pw_software_update_OperationResult& response);
+
+ Status VerifyStagedBundle(ServerContext&,
+ const pw_protobuf_Empty& request,
+ pw_software_update_OperationResult& response);
+
+ Status ApplyStagedBundle(ServerContext&,
+ const pw_protobuf_Empty& request,
+ pw_software_update_OperationResult& response);
+
+ private:
+ pw::software_update::BundledUpdateManager& manager_;
+};
+
+} // namespace pw::software_update
diff --git a/pw_software_update/public/pw_software_update/manifest.h b/pw_software_update/public/pw_software_update/manifest_accessor.h
similarity index 96%
rename from pw_software_update/public/pw_software_update/manifest.h
rename to pw_software_update/public/pw_software_update/manifest_accessor.h
index 3b9cb18..ef7b898 100644
--- a/pw_software_update/public/pw_software_update/manifest.h
+++ b/pw_software_update/public/pw_software_update/manifest_accessor.h
@@ -18,6 +18,6 @@
// TODO(pwbug/456): Place-holder declaration for now. To be implemented
// and moved elsewhere.
-class Manifest {};
+class ManifestAccessor {};
} // namespace pw::software_update
diff --git a/pw_software_update/public/pw_software_update/service.h b/pw_software_update/public/pw_software_update/service.h
deleted file mode 100644
index 4caedea..0000000
--- a/pw_software_update/public/pw_software_update/service.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2021 The Pigweed Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-#pragma once
-
-#include "pw_software_update/service.rpc.pb.h"
diff --git a/pw_software_update/public/pw_software_update/update_bundle.h b/pw_software_update/public/pw_software_update/update_bundle_accessor.h
similarity index 88%
rename from pw_software_update/public/pw_software_update/update_bundle.h
rename to pw_software_update/public/pw_software_update/update_bundle_accessor.h
index b9f85a0..c055852 100644
--- a/pw_software_update/public/pw_software_update/update_bundle.h
+++ b/pw_software_update/public/pw_software_update/update_bundle_accessor.h
@@ -20,12 +20,12 @@
#include "pw_protobuf/map_utils.h"
#include "pw_protobuf/message.h"
#include "pw_software_update/bundled_update_backend.h"
-#include "pw_software_update/manifest.h"
+#include "pw_software_update/manifest_accessor.h"
#include "pw_stream/memory_stream.h"
namespace pw::software_update {
-// UpdateBundle is responsible for parsing, verifying and providing
+// UpdateBundleAccessor is responsible for parsing, verifying and providing
// target payload access of a software update bundle. It takes the following as
// inputs:
//
@@ -39,7 +39,7 @@
//
// Exmple of use:
//
-// UpdateBundle bundle(blob,helper);
+// UpdateBundleAccessor bundle(blob,helper);
// auto status = bundle.OpenAndVerify(current_manifest);
// if (!status.ok()) {
// // handle error
@@ -69,13 +69,13 @@
// // handle error
// ...
// }
-class UpdateBundle {
+class UpdateBundleAccessor {
public:
- // UpdateBundle
+ // UpdateBundleAccessor
// update_bundle - The software update bundle data on storage.
// backend - project-specific BundledUpdateBackend
- UpdateBundle(blob_store::BlobStore& update_bundle,
- BundledUpdateBackend& backend)
+ constexpr UpdateBundleAccessor(blob_store::BlobStore& update_bundle,
+ BundledUpdateBackend& backend)
: bundle_(update_bundle), backend_(backend), bundle_reader_(bundle_) {}
// Opens and verifies the software update bundle, using the TUF process.
@@ -83,7 +83,7 @@
// Returns:
// OK - Bundle was successfully opened and verified.
// TODO(pwbug/456): Add error codes.
- Status OpenAndVerify(const Manifest& current_manifest);
+ Status OpenAndVerify(const ManifestAccessor& current_manifest);
// Closes the bundle by invalidating the verification and closing
// the reader to release the read-only lock
@@ -117,6 +117,8 @@
// TODO(pwbug/456): Figure out a way to propagate error.
stream::IntervalReader GetTargetPayload(std::string_view target_file);
+ protobuf::Message GetDecoder() { return decoder_; }
+
private:
blob_store::BlobStore& bundle_;
BundledUpdateBackend& backend_;
diff --git a/pw_software_update/public/pw_software_update/update_manager.h b/pw_software_update/public/pw_software_update/update_manager.h
new file mode 100644
index 0000000..d45b53b
--- /dev/null
+++ b/pw_software_update/public/pw_software_update/update_manager.h
@@ -0,0 +1,55 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#pragma once
+
+#include "pw_software_update/bundled_update_backend.h"
+#include "pw_software_update/update_bundle_accessor.h"
+
+namespace pw::software_update {
+
+class BundledUpdateManager {
+ public:
+ constexpr BundledUpdateManager(UpdateBundleAccessor& bundle,
+ BundledUpdateBackend& backend)
+
+ : backend_(backend), bundle_(bundle), bundle_open_(false) {}
+
+ pw::Status Abort();
+
+ // Will enable the transfer_id if needed via the BundledUpdateBackend.
+ Result<uint32_t> GetTransferId();
+
+ pw::Status VerifyManifest();
+
+ pw::Status WriteManifest();
+
+ pw::Status ApplyUpdate();
+
+ pw::Status BeforeUpdate();
+
+ pw::Status VerifyUpdate();
+
+ private:
+ BundledUpdateBackend& backend_;
+ UpdateBundleAccessor& bundle_;
+
+ std::optional<uint32_t> transfer_id_;
+ bool bundle_open_;
+
+ // Will disable the transfer_id if needed via the BundledUpdateBackend.
+ void DisableTransferId();
+};
+
+} // namespace pw::software_update
diff --git a/pw_software_update/tuf.proto b/pw_software_update/tuf.proto
index 4081357..45f4b03 100644
--- a/pw_software_update/tuf.proto
+++ b/pw_software_update/tuf.proto
@@ -17,7 +17,7 @@
syntax = "proto3";
-package pw_software_update;
+package pw.software_update;
import "google/protobuf/timestamp.proto";
diff --git a/pw_software_update/update_bundle.proto b/pw_software_update/update_bundle.proto
index 856f575..f5f1757 100644
--- a/pw_software_update/update_bundle.proto
+++ b/pw_software_update/update_bundle.proto
@@ -14,7 +14,7 @@
syntax = "proto3";
-package pw_software_update;
+package pw.software_update;
import "pw_software_update/tuf.proto";
diff --git a/pw_software_update/update_bundle.cc b/pw_software_update/update_bundle_accessor.cc
similarity index 80%
rename from pw_software_update/update_bundle.cc
rename to pw_software_update/update_bundle_accessor.cc
index fd9e894..d989803 100644
--- a/pw_software_update/update_bundle.cc
+++ b/pw_software_update/update_bundle_accessor.cc
@@ -22,8 +22,8 @@
#include "pw_log/log.h"
#include "pw_protobuf/message.h"
#include "pw_result/result.h"
-#include "pw_software_update/update_bundle.h"
#include "pw_software_update/update_bundle.pwpb.h"
+#include "pw_software_update/update_bundle_accessor.h"
#include "pw_stream/interval_reader.h"
#include "pw_stream/memory_stream.h"
@@ -34,7 +34,7 @@
}
-Status UpdateBundle::OpenAndVerify(const Manifest&) {
+Status UpdateBundleAccessor::OpenAndVerify(const ManifestAccessor&) {
PW_TRY(bundle_.Init());
PW_TRY(bundle_reader_.Open());
decoder_ =
@@ -45,25 +45,25 @@
}
// Get the target element corresponding to `target_file`
-stream::IntervalReader UpdateBundle::GetTargetPayload(
+stream::IntervalReader UpdateBundleAccessor::GetTargetPayload(
std::string_view target_file_name) {
protobuf::StringToBytesMap target_payloads =
decoder_.AsStringToBytesMap(static_cast<uint32_t>(
- pw_software_update::UpdateBundle::Fields::TARGET_PAYLOADS));
+ pw::software_update::UpdateBundle::Fields::TARGET_PAYLOADS));
PW_TRY(target_payloads.status());
protobuf::Bytes payload = target_payloads[target_file_name];
PW_TRY(payload.status());
return payload.GetBytesReader();
}
-Result<bool> UpdateBundle::IsTargetPayloadIncluded(
+Result<bool> UpdateBundleAccessor::IsTargetPayloadIncluded(
std::string_view target_file_name) {
// TODO(pwbug/456): Perform personalization check first. If the target
// is personalized out. Don't need to proceed.
protobuf::StringToMessageMap signed_targets_metadata_map =
decoder_.AsStringToMessageMap(static_cast<uint32_t>(
- pw_software_update::UpdateBundle::Fields::TARGETS_METADATA));
+ pw::software_update::UpdateBundle::Fields::TARGETS_METADATA));
PW_TRY(signed_targets_metadata_map.status());
// There should only be one element in the map, which is the top-level
@@ -73,18 +73,18 @@
PW_TRY(signed_targets_metadata.status());
protobuf::Message metadata = signed_targets_metadata.AsMessage(
- static_cast<uint32_t>(pw_software_update::SignedTargetsMetadata::Fields::
+ static_cast<uint32_t>(pw::software_update::SignedTargetsMetadata::Fields::
SERIALIZED_TARGETS_METADATA));
PW_TRY(metadata.status());
protobuf::RepeatedMessages target_files =
metadata.AsRepeatedMessages(static_cast<uint32_t>(
- pw_software_update::TargetsMetadata::Fields::TARGET_FILES));
+ pw::software_update::TargetsMetadata::Fields::TARGET_FILES));
PW_TRY(target_files.status());
for (protobuf::Message target_file : target_files) {
protobuf::String name = target_file.AsString(static_cast<uint32_t>(
- pw_software_update::TargetFile::Fields::FILE_NAME));
+ pw::software_update::TargetFile::Fields::FILE_NAME));
PW_TRY(name.status());
Result<bool> file_name_matches = name.Equal(target_file_name);
PW_TRY(file_name_matches.status());
@@ -96,10 +96,11 @@
return false;
}
-Status UpdateBundle::WriteManifest(stream::Writer& staged_manifest_writer) {
+Status UpdateBundleAccessor::WriteManifest(
+ stream::Writer& staged_manifest_writer) {
protobuf::StringToMessageMap signed_targets_metadata_map =
decoder_.AsStringToMessageMap(static_cast<uint32_t>(
- pw_software_update::UpdateBundle::Fields::TARGETS_METADATA));
+ pw::software_update::UpdateBundle::Fields::TARGETS_METADATA));
PW_TRY(signed_targets_metadata_map.status());
// There should only be one element in the map, which is the top-level
@@ -109,7 +110,7 @@
PW_TRY(signed_targets_metadata.status());
protobuf::Bytes metadata = signed_targets_metadata.AsBytes(
- static_cast<uint32_t>(pw_software_update::SignedTargetsMetadata::Fields::
+ static_cast<uint32_t>(pw::software_update::SignedTargetsMetadata::Fields::
SERIALIZED_TARGETS_METADATA));
PW_TRY(metadata.status());
@@ -120,7 +121,7 @@
std::byte stream_pipe_buffer[WRITE_MANIFEST_STREAM_PIPE_BUFFER_SIZE];
return protobuf::WriteProtoStringToBytesMapEntry(
static_cast<uint32_t>(
- pw_software_update::Manifest::Fields::TARGETS_METADATA),
+ pw::software_update::Manifest::Fields::TARGETS_METADATA),
name_reader,
kTopLevelTargetsName.size(),
metadata_reader,
@@ -129,7 +130,7 @@
staged_manifest_writer);
}
-Status UpdateBundle::Close() {
+Status UpdateBundleAccessor::Close() {
// TODO(pwbug/456): To be implemented.
return bundle_reader_.Close();
}
diff --git a/pw_software_update/update_manager.cc b/pw_software_update/update_manager.cc
new file mode 100644
index 0000000..f5c0860
--- /dev/null
+++ b/pw_software_update/update_manager.cc
@@ -0,0 +1,143 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_software_update/update_manager.h"
+
+#include <string_view>
+
+#include "pw_software_update/manifest_accessor.h"
+#include "pw_software_update/update_bundle.pwpb.h"
+
+namespace pw::software_update {
+
+Status BundledUpdateManager::ApplyUpdate() {
+ PW_LOG_DEBUG("Attempting to apply the update");
+ protobuf::StringToMessageMap signed_targets_metadata_map =
+ bundle_.GetDecoder().AsStringToMessageMap(static_cast<uint32_t>(
+ pw::software_update::UpdateBundle::Fields::TARGETS_METADATA));
+ if (const Status status = signed_targets_metadata_map.status();
+ !status.ok()) {
+ PW_LOG_ERROR("Update bundle does not contain the targets_metadata map: %d",
+ static_cast<int>(status.code()));
+ return status;
+ }
+
+ // There should only be one element in the map, which is the top-level
+ // targets metadata.
+ constexpr std::string_view kTopLevelTargetsName = "targets";
+ protobuf::Message signed_targets_metadata =
+ signed_targets_metadata_map[kTopLevelTargetsName];
+ if (const Status status = signed_targets_metadata.status(); !status.ok()) {
+ PW_LOG_ERROR(
+ "The targets_metadata map does not contain the targets entry: %d",
+ static_cast<int>(status.code()));
+ return status;
+ }
+
+ protobuf::Message targets_metadata = signed_targets_metadata.AsMessage(
+ static_cast<uint32_t>(pw::software_update::SignedTargetsMetadata::Fields::
+ SERIALIZED_TARGETS_METADATA));
+ if (const Status status = targets_metadata.status(); !status.ok()) {
+ PW_LOG_ERROR(
+ "The targets targets_metadata entry does not contain the "
+ "serialized_target_metadata: %d",
+ static_cast<int>(status.code()));
+ return status;
+ }
+
+ protobuf::RepeatedMessages target_files =
+ targets_metadata.AsRepeatedMessages(static_cast<uint32_t>(
+ pw::software_update::TargetsMetadata::Fields::TARGET_FILES));
+ if (const Status status = target_files.status(); !status.ok()) {
+ PW_LOG_ERROR(
+ "The serialized_target_metadata does not contain target_files: %d",
+ static_cast<int>(status.code()));
+ return status;
+ }
+
+ for (pw::protobuf::Message file_name : target_files) {
+ // TODO: Use a config.h parameter for this.
+ constexpr size_t kFileNameMaxSize = 32;
+ std::array<std::byte, kFileNameMaxSize> buf = {};
+ protobuf::String name = file_name.AsString(static_cast<uint32_t>(
+ pw::software_update::TargetFile::Fields::FILE_NAME));
+ PW_TRY(name.status());
+ const Result<ByteSpan> read_result = name.GetBytesReader().Read(buf);
+ PW_TRY(read_result.status());
+ const ConstByteSpan file_name_span = read_result.value();
+ const std::string_view file_name_view(
+ reinterpret_cast<const char*>(file_name_span.data()),
+ file_name_span.size_bytes());
+ stream::IntervalReader file_reader =
+ bundle_.GetTargetPayload(file_name_view);
+ if (const Status status =
+ backend_.ApplyTargetFile(file_name_view, file_reader);
+ !status.ok()) {
+ PW_LOG_ERROR("Failed to apply target file: %d",
+ static_cast<int>(status.code()));
+ return status;
+ }
+ }
+
+ return backend_.FinalizeApply();
+}
+
+Result<uint32_t> BundledUpdateManager::GetTransferId() {
+ if (!transfer_id_.has_value()) {
+ Result<uint32_t> possible_transfer_id =
+ backend_.EnableBundleTransferHandler();
+ PW_TRY(possible_transfer_id.status());
+ transfer_id_ = possible_transfer_id.value();
+ }
+ return transfer_id_.value();
+}
+
+pw::Status BundledUpdateManager::VerifyManifest() {
+ return pw::Status::Unimplemented();
+}
+
+pw::Status BundledUpdateManager::WriteManifest() {
+ return pw::Status::Unimplemented();
+}
+
+pw::Status BundledUpdateManager::BeforeUpdate() {
+ return backend_.BeforeUpdateStart();
+}
+
+void BundledUpdateManager::DisableTransferId() {
+ if (!transfer_id_.has_value()) {
+ return; // Nothing to do, already disabled.
+ }
+ backend_.DisableBundleTransferHandler();
+}
+
+pw::Status BundledUpdateManager::Abort() {
+ DisableTransferId();
+ PW_TRY(backend_.BeforeUpdateAbort());
+ if (bundle_open_) {
+ bundle_.Close();
+ }
+ return OkStatus();
+}
+
+pw::Status BundledUpdateManager::VerifyUpdate() {
+ DisableTransferId();
+ PW_TRY(backend_.BeforeBundleVerify());
+ ManifestAccessor manifest; // TODO(pwbug/456): Place-holder for now.
+ PW_TRY(bundle_.OpenAndVerify(manifest));
+ bundle_open_ = true;
+ return backend_.AfterBundleVerified();
+}
+
+} // namespace pw::software_update
diff --git a/pw_software_update/service.cc b/pw_software_update/update_manager_test.cc
similarity index 84%
rename from pw_software_update/service.cc
rename to pw_software_update/update_manager_test.cc
index 8ad1afd..c53e246 100644
--- a/pw_software_update/service.cc
+++ b/pw_software_update/update_manager_test.cc
@@ -12,6 +12,8 @@
// License for the specific language governing permissions and limitations under
// the License.
-#include "pw_software_update/service.h"
+#include "pw_software_update/update_manager.h"
-namespace pw_software_update {}
\ No newline at end of file
+#include "gtest/gtest.h"
+
+TEST(UpdateManager, Compiles) {}