Merge remote-tracking branch 'goog/upstream-master'.
The following commits were reverted:
840703a Fix update over cellular network on guest account
eaad5d0 Do not merge to AOSP: Fixes the link to brillo-clang-format in CrOS
740efad Reboot even if a system update is not available.
Fixed a few sign compare warnings.
Had to ifdef out 2 SquashfsFilesystemTest because it depends on unsquashfs -m.
Test: update_engine_unittests
Change-Id: I6f4ca5003e78c76064ec60d0797505d8c18d00bf
Merged-In: I6f4ca5003e78c76064ec60d0797505d8c18d00bf
diff --git a/.clang-format b/.clang-format
index 73f786c..f412743 120000
--- a/.clang-format
+++ b/.clang-format
@@ -1 +1 @@
-../../../platform2/common-mk/brillo-clang-format
\ No newline at end of file
+../../build/tools/brillo-clang-format
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..c3d164b
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,8 @@
+// AIDL interface between libupdate_engine and framework.jar
+filegroup {
+ name: "libupdate_engine_aidl",
+ srcs: [
+ "binder_bindings/android/os/IUpdateEngine.aidl",
+ "binder_bindings/android/os/IUpdateEngineCallback.aidl",
+ ],
+}
diff --git a/Android.mk b/Android.mk
index 523ff6f..ddf633d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -14,6 +14,8 @@
# limitations under the License.
#
+ifneq ($(TARGET_BUILD_PDK),true)
+
LOCAL_PATH := $(my-dir)
# Default values for the USE flags. Override these USE flags from your product
@@ -82,6 +84,7 @@
generated_sources_dir := $(call local-generated-sources-dir)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(generated_sources_dir)/proto/system
LOCAL_SRC_FILES := $(ue_update_metadata_protos_src_files)
+LOCAL_CFLAGS := -Wall -Werror
include $(BUILD_HOST_STATIC_LIBRARY)
# Build for the target.
@@ -91,6 +94,7 @@
generated_sources_dir := $(call local-generated-sources-dir)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(generated_sources_dir)/proto/system
LOCAL_SRC_FILES := $(ue_update_metadata_protos_src_files)
+LOCAL_CFLAGS := -Wall -Werror
include $(BUILD_STATIC_LIBRARY)
# libpayload_consumer (type: static_library)
@@ -101,6 +105,7 @@
libxz \
libbz \
libbspatch \
+ libbrotli \
libpuffpatch \
$(ue_update_metadata_protos_exported_static_libraries)
ue_libpayload_consumer_exported_shared_libraries := \
@@ -147,7 +152,6 @@
LOCAL_MODULE := libpayload_consumer
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CLANG := true
LOCAL_CFLAGS := $(ue_common_cflags)
LOCAL_CPPFLAGS := $(ue_common_cppflags)
LOCAL_LDFLAGS := $(ue_common_ldflags)
@@ -171,7 +175,6 @@
LOCAL_MODULE := libpayload_consumer
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CLANG := true
LOCAL_CFLAGS := $(ue_common_cflags)
LOCAL_CPPFLAGS := $(ue_common_cppflags)
LOCAL_LDFLAGS := $(ue_common_ldflags)
@@ -207,7 +210,6 @@
LOCAL_MODULE := libupdate_engine_boot_control
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CLANG := true
LOCAL_CFLAGS := $(ue_common_cflags)
LOCAL_CPPFLAGS := $(ue_common_cppflags)
LOCAL_LDFLAGS := $(ue_common_ldflags)
@@ -265,7 +267,6 @@
LOCAL_MODULE := libupdate_engine
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CLANG := true
LOCAL_EXPORT_C_INCLUDE_DIRS := $(ue_libupdate_engine_exported_c_includes)
LOCAL_CFLAGS := $(ue_common_cflags)
LOCAL_CPPFLAGS := $(ue_common_cppflags)
@@ -295,7 +296,7 @@
hardware_android.cc \
image_properties_android.cc \
libcurl_http_fetcher.cc \
- metrics.cc \
+ metrics_reporter_omaha.cc \
metrics_utils.cc \
omaha_request_action.cc \
omaha_request_params.cc \
@@ -307,10 +308,15 @@
proxy_resolver.cc \
real_system_state.cc \
update_attempter.cc \
+ update_manager/android_things_policy.cc \
+ update_manager/api_restricted_downloads_policy_impl.cc \
update_manager/boxed_value.cc \
- update_manager/chromeos_policy.cc \
update_manager/default_policy.cc \
+ update_manager/enough_slots_ab_updates_policy_impl.cc \
update_manager/evaluation_context.cc \
+ update_manager/interactive_update_policy_impl.cc \
+ update_manager/next_update_check_policy_impl.cc \
+ update_manager/official_build_check_policy_impl.cc \
update_manager/policy.cc \
update_manager/real_config_provider.cc \
update_manager/real_device_policy_provider.cc \
@@ -363,6 +369,7 @@
libbrillo-binder \
libcutils \
libcurl \
+ libmetricslogger \
libssl \
libutils
@@ -370,7 +377,6 @@
LOCAL_MODULE := libupdate_engine_android
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CLANG := true
LOCAL_CFLAGS := $(ue_common_cflags)
LOCAL_CPPFLAGS := $(ue_common_cppflags)
LOCAL_LDFLAGS := $(ue_common_ldflags)
@@ -397,6 +403,8 @@
daemon_state_android.cc \
hardware_android.cc \
libcurl_http_fetcher.cc \
+ metrics_reporter_android.cc \
+ metrics_utils.cc \
network_selector_android.cc \
proxy_resolver.cc \
update_attempter_android.cc \
@@ -415,7 +423,6 @@
LOCAL_REQUIRED_MODULES := \
cacerts_google
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CLANG := true
LOCAL_CFLAGS := $(ue_common_cflags)
LOCAL_CPPFLAGS := $(ue_common_cppflags)
LOCAL_LDFLAGS := $(ue_common_ldflags)
@@ -457,7 +464,6 @@
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CLANG := true
LOCAL_CFLAGS := \
$(ue_common_cflags) \
-D_UE_SIDELOAD
@@ -473,6 +479,8 @@
LOCAL_SRC_FILES := \
boot_control_recovery.cc \
hardware_android.cc \
+ metrics_reporter_stub.cc \
+ metrics_utils.cc \
network_selector_stub.cc \
proxy_resolver.cc \
sideload_main.cc \
@@ -493,21 +501,13 @@
# library dependencies of these static libraries.
LOCAL_STATIC_LIBRARIES += \
$(ue_common_shared_libraries) \
- libcutils \
+ libbase \
liblog \
$(ue_libpayload_consumer_exported_shared_libraries:-host=) \
$(ue_update_metadata_protos_exported_shared_libraries) \
libevent \
libmodpb64 \
libgtest_prod
-# libchrome requires these extra LDFLAGS which are not propagated through the
-# build system.
-LOCAL_LDFLAGS += \
- -Wl,-wrap,calloc \
- -Wl,-wrap,free \
- -Wl,-wrap,malloc \
- -Wl,-wrap,memalign \
- -Wl,-wrap,realloc
ifeq ($(strip $(PRODUCT_STATIC_BOOT_CONTROL_HAL)),)
# No static boot_control HAL defined, so no sideload support. We use a fake
@@ -534,7 +534,6 @@
-Werror \
-Wno-unused-parameter \
-DUSE_BINDER=$(local_use_binder)
-LOCAL_CLANG := true
LOCAL_CPP_EXTENSION := .cc
# TODO(deymo): Remove "external/cros/system_api/dbus" when dbus is not used.
LOCAL_C_INCLUDES := \
@@ -572,7 +571,6 @@
LOCAL_MODULE := update_engine_client
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CLANG := true
LOCAL_CFLAGS := $(ue_common_cflags)
LOCAL_CPPFLAGS := $(ue_common_cppflags)
LOCAL_LDFLAGS := $(ue_common_ldflags)
@@ -612,6 +610,9 @@
# for any client code.
ue_libpayload_generator_exported_static_libraries := \
libbsdiff \
+ libdivsufsort \
+ libdivsufsort64 \
+ libbrotli \
liblzma \
libpayload_consumer \
libpuffdiff \
@@ -656,13 +657,14 @@
LOCAL_MODULE := libpayload_generator
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CLANG := true
LOCAL_CFLAGS := $(ue_common_cflags)
LOCAL_CPPFLAGS := $(ue_common_cppflags)
LOCAL_LDFLAGS := $(ue_common_ldflags)
LOCAL_C_INCLUDES := $(ue_common_c_includes)
LOCAL_STATIC_LIBRARIES := \
libbsdiff \
+ libdivsufsort \
+ libdivsufsort64 \
liblzma \
libpayload_consumer \
libpuffdiff \
@@ -684,12 +686,14 @@
LOCAL_MODULE := libpayload_generator
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CLANG := true
LOCAL_CFLAGS := $(ue_common_cflags)
LOCAL_CPPFLAGS := $(ue_common_cppflags)
LOCAL_LDFLAGS := $(ue_common_ldflags)
LOCAL_C_INCLUDES := $(ue_common_c_includes)
LOCAL_STATIC_LIBRARIES := \
+ libbsdiff \
+ libdivsufsort \
+ libdivsufsort64 \
libpayload_consumer \
update_metadata-protos \
liblzma \
@@ -716,7 +720,6 @@
LOCAL_MODULE := delta_generator
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CLANG := true
LOCAL_CFLAGS := $(ue_common_cflags)
LOCAL_CPPFLAGS := $(ue_common_cppflags)
LOCAL_LDFLAGS := $(ue_common_ldflags)
@@ -742,7 +745,6 @@
LOCAL_MODULE_STEM := delta_generator
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CLANG := true
LOCAL_CFLAGS := $(ue_common_cflags)
LOCAL_CPPFLAGS := $(ue_common_cppflags)
LOCAL_LDFLAGS := $(ue_common_ldflags)
@@ -817,15 +819,6 @@
$(call ue-unittest-sample-image,disk_ext2_4k_empty.img)
$(call ue-unittest-sample-image,disk_ext2_unittest.img)
-# Zlib Fingerprint
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := zlib_fingerprint
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_NATIVE_TESTS)/update_engine_unittests
-LOCAL_PREBUILT_MODULE_FILE := $(TARGET_OUT_COMMON_GEN)/zlib_fingerprint
-include $(BUILD_PREBUILT)
-
# update_engine.conf
# ========================================================
include $(CLEAR_VARS)
@@ -844,7 +837,6 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_NATIVE_TESTS)/update_engine_unittests
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CLANG := true
LOCAL_CFLAGS := $(ue_common_cflags)
LOCAL_CPPFLAGS := $(ue_common_cppflags)
LOCAL_LDFLAGS := $(ue_common_ldflags)
@@ -864,7 +856,6 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_NATIVE_TESTS)/update_engine_unittests
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CLANG := true
LOCAL_CFLAGS := $(ue_common_cflags)
LOCAL_CPPFLAGS := $(ue_common_cppflags)
LOCAL_LDFLAGS := $(ue_common_ldflags)
@@ -891,10 +882,8 @@
ue_unittest_key.pub.pem \
ue_unittest_key2.pem \
ue_unittest_key2.pub.pem \
- ue_unittest_update_engine.conf \
- zlib_fingerprint
+ ue_unittest_update_engine.conf
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CLANG := true
LOCAL_CFLAGS := $(ue_common_cflags)
LOCAL_CPPFLAGS := $(ue_common_cppflags)
LOCAL_LDFLAGS := $(ue_common_ldflags)
@@ -974,6 +963,8 @@
LOCAL_SRC_FILES += \
common_service_unittest.cc \
fake_system_state.cc \
+ image_properties_android_unittest.cc \
+ metrics_reporter_omaha_unittest.cc \
metrics_utils_unittest.cc \
omaha_request_action_unittest.cc \
omaha_request_params_unittest.cc \
@@ -982,12 +973,16 @@
p2p_manager_unittest.cc \
payload_consumer/download_action_unittest.cc \
payload_state_unittest.cc \
- proxy_resolver_unittest.cc \
+ parcelable_update_engine_status_unittest.cc \
update_attempter_unittest.cc \
+ update_manager/android_things_policy_unittest.cc \
update_manager/boxed_value_unittest.cc \
+ update_manager/chromeos_policy.cc \
update_manager/chromeos_policy_unittest.cc \
update_manager/evaluation_context_unittest.cc \
update_manager/generic_variables_unittest.cc \
+ update_manager/next_update_check_policy_impl_unittest.cc \
+ update_manager/policy_test_utils.cc \
update_manager/prng_unittest.cc \
update_manager/real_device_policy_provider_unittest.cc \
update_manager/real_random_provider_unittest.cc \
@@ -1003,12 +998,14 @@
$(ue_libupdate_engine_android_exported_static_libraries:-host=)
LOCAL_SHARED_LIBRARIES += \
$(ue_libupdate_engine_android_exported_shared_libraries:-host=)
+LOCAL_SRC_FILES += \
+ update_attempter_android_unittest.cc
endif # local_use_omaha == 1
include $(BUILD_NATIVE_TEST)
# Update payload signing public key.
# ========================================================
-ifdef BRILLO
+ifeq ($(PRODUCT_IOT),true)
include $(CLEAR_VARS)
LOCAL_MODULE := brillo-update-payload-key
LOCAL_MODULE_CLASS := ETC
@@ -1017,7 +1014,7 @@
LOCAL_SRC_FILES := update_payload_key/brillo-update-payload-key.pub.pem
LOCAL_BUILT_MODULE_STEM := update_payload_key/brillo-update-payload-key.pub.pem
include $(BUILD_PREBUILT)
-endif # BRILLO
+endif # PRODUCT_IOT
# Brillo update payload generation script
# ========================================================
@@ -1030,6 +1027,9 @@
LOCAL_MODULE_TAGS := optional
LOCAL_REQUIRED_MODULES := \
delta_generator \
- shflags
+ shflags \
+ simg2img
include $(BUILD_PREBUILT)
endif # HOST_OS == linux
+
+endif # ifneq ($(TARGET_BUILD_PDK),true)
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 706dfa8..40ddcd1 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,3 +1,4 @@
[Builtin Hooks]
clang_format = true
+cpplint = true
pylint = true
diff --git a/UpdateEngine.conf b/UpdateEngine.conf
index 192e6ab..58cca09 100644
--- a/UpdateEngine.conf
+++ b/UpdateEngine.conf
@@ -53,9 +53,6 @@
send_member="SetUpdateOverCellularPermission"/>
<allow send_destination="org.chromium.UpdateEngine"
send_interface="org.chromium.UpdateEngineInterface"
- send_member="SetUpdateOverCellularTarget"/>
- <allow send_destination="org.chromium.UpdateEngine"
- send_interface="org.chromium.UpdateEngineInterface"
send_member="GetUpdateOverCellularPermission"/>
<allow send_destination="org.chromium.UpdateEngine"
send_interface="org.chromium.UpdateEngineInterface"
diff --git a/binder_bindings/android/brillo/IUpdateEngine.aidl b/binder_bindings/android/brillo/IUpdateEngine.aidl
index 7893548..e549a4d 100644
--- a/binder_bindings/android/brillo/IUpdateEngine.aidl
+++ b/binder_bindings/android/brillo/IUpdateEngine.aidl
@@ -20,7 +20,8 @@
import android.brillo.ParcelableUpdateEngineStatus;
interface IUpdateEngine {
- void AttemptUpdate(in String app_version, in String omaha_url, in int flags);
+ void SetUpdateAttemptFlags(in int flags);
+ boolean AttemptUpdate(in String app_version, in String omaha_url, in int flags);
void AttemptRollback(in boolean powerwash);
boolean CanRollback();
void ResetStatus();
@@ -33,8 +34,6 @@
void SetP2PUpdatePermission(in boolean enabled);
boolean GetP2PUpdatePermission();
void SetUpdateOverCellularPermission(in boolean enabled);
- void SetUpdateOverCellularTarget(in String target_version,
- in long target_size);
boolean GetUpdateOverCellularPermission();
long GetDurationSinceUpdate();
String GetPrevVersion();
diff --git a/binder_bindings/android/brillo/IUpdateEngineStatusCallback.aidl b/binder_bindings/android/brillo/IUpdateEngineStatusCallback.aidl
index 42b6438..837d44d 100644
--- a/binder_bindings/android/brillo/IUpdateEngineStatusCallback.aidl
+++ b/binder_bindings/android/brillo/IUpdateEngineStatusCallback.aidl
@@ -16,8 +16,9 @@
package android.brillo;
+import android.brillo.ParcelableUpdateEngineStatus;
+
interface IUpdateEngineStatusCallback {
oneway
- void HandleStatusUpdate(in long last_checked_time, in double progress,
- in String current_operation, in String new_version, in long new_size);
+ void HandleStatusUpdate(in ParcelableUpdateEngineStatus status);
}
diff --git a/binder_bindings/android/os/IUpdateEngine.aidl b/binder_bindings/android/os/IUpdateEngine.aidl
index 67f828a..7e26752 100644
--- a/binder_bindings/android/os/IUpdateEngine.aidl
+++ b/binder_bindings/android/os/IUpdateEngine.aidl
@@ -28,6 +28,8 @@
/** @hide */
boolean bind(IUpdateEngineCallback callback);
/** @hide */
+ boolean unbind(IUpdateEngineCallback callback);
+ /** @hide */
void suspend();
/** @hide */
void resume();
diff --git a/binder_service_android.cc b/binder_service_android.cc
index 872f64c..0305727 100644
--- a/binder_service_android.cc
+++ b/binder_service_android.cc
@@ -24,6 +24,7 @@
using android::binder::Status;
using android::os::IUpdateEngineCallback;
+using update_engine::UpdateEngineStatus;
namespace {
Status ErrorPtrToStatus(const brillo::ErrorPtr& error) {
@@ -40,13 +41,9 @@
}
void BinderUpdateEngineAndroidService::SendStatusUpdate(
- int64_t /* last_checked_time */,
- double progress,
- update_engine::UpdateStatus status,
- const std::string& /* new_version */,
- int64_t /* new_size */) {
- last_status_ = static_cast<int>(status);
- last_progress_ = progress;
+ const UpdateEngineStatus& update_engine_status) {
+ last_status_ = static_cast<int>(update_engine_status.status);
+ last_progress_ = update_engine_status.progress;
for (auto& callback : callbacks_) {
callback->onStatusUpdate(last_status_, last_progress_);
}
@@ -63,12 +60,15 @@
const android::sp<IUpdateEngineCallback>& callback, bool* return_value) {
callbacks_.emplace_back(callback);
+ const android::sp<IBinder>& callback_binder =
+ IUpdateEngineCallback::asBinder(callback);
auto binder_wrapper = android::BinderWrapper::Get();
binder_wrapper->RegisterForDeathNotifications(
- IUpdateEngineCallback::asBinder(callback),
- base::Bind(&BinderUpdateEngineAndroidService::UnbindCallback,
- base::Unretained(this),
- base::Unretained(callback.get())));
+ callback_binder,
+ base::Bind(
+ base::IgnoreResult(&BinderUpdateEngineAndroidService::UnbindCallback),
+ base::Unretained(this),
+ base::Unretained(callback_binder.get())));
// Send an status update on connection (except when no update sent so far),
// since the status update is oneway and we don't need to wait for the
@@ -80,6 +80,17 @@
return Status::ok();
}
+Status BinderUpdateEngineAndroidService::unbind(
+ const android::sp<IUpdateEngineCallback>& callback, bool* return_value) {
+ const android::sp<IBinder>& callback_binder =
+ IUpdateEngineCallback::asBinder(callback);
+ auto binder_wrapper = android::BinderWrapper::Get();
+ binder_wrapper->UnregisterForDeathNotifications(callback_binder);
+
+ *return_value = UnbindCallback(callback_binder.get());
+ return Status::ok();
+}
+
Status BinderUpdateEngineAndroidService::applyPayload(
const android::String16& url,
int64_t payload_offset,
@@ -128,19 +139,19 @@
return Status::ok();
}
-void BinderUpdateEngineAndroidService::UnbindCallback(
- IUpdateEngineCallback* callback) {
- auto it =
- std::find_if(callbacks_.begin(),
- callbacks_.end(),
- [&callback](const android::sp<IUpdateEngineCallback>& elem) {
- return elem.get() == callback;
- });
+bool BinderUpdateEngineAndroidService::UnbindCallback(const IBinder* callback) {
+ auto it = std::find_if(
+ callbacks_.begin(),
+ callbacks_.end(),
+ [&callback](const android::sp<IUpdateEngineCallback>& elem) {
+ return IUpdateEngineCallback::asBinder(elem).get() == callback;
+ });
if (it == callbacks_.end()) {
- LOG(ERROR) << "Got death notification for unknown callback.";
- return;
+ LOG(ERROR) << "Unable to unbind unknown callback.";
+ return false;
}
callbacks_.erase(it);
+ return true;
}
} // namespace chromeos_update_engine
diff --git a/binder_service_android.h b/binder_service_android.h
index 3293c0e..eb36e4c 100644
--- a/binder_service_android.h
+++ b/binder_service_android.h
@@ -45,11 +45,8 @@
}
// ServiceObserverInterface overrides.
- void SendStatusUpdate(int64_t last_checked_time,
- double progress,
- update_engine::UpdateStatus status,
- const std::string& new_version,
- int64_t new_size) override;
+ void SendStatusUpdate(
+ const update_engine::UpdateEngineStatus& update_engine_status) override;
void SendPayloadApplicationComplete(ErrorCode error_code) override;
// android::os::BnUpdateEngine overrides.
@@ -61,6 +58,9 @@
android::binder::Status bind(
const android::sp<android::os::IUpdateEngineCallback>& callback,
bool* return_value) override;
+ android::binder::Status unbind(
+ const android::sp<android::os::IUpdateEngineCallback>& callback,
+ bool* return_value) override;
android::binder::Status suspend() override;
android::binder::Status resume() override;
android::binder::Status cancel() override;
@@ -68,8 +68,9 @@
private:
// Remove the passed |callback| from the list of registered callbacks. Called
- // whenever the callback object is destroyed.
- void UnbindCallback(android::os::IUpdateEngineCallback* callback);
+ // on unbind() or whenever the callback object is destroyed.
+ // Returns true on success.
+ bool UnbindCallback(const IBinder* callback);
// List of currently bound callbacks.
std::vector<android::sp<android::os::IUpdateEngineCallback>> callbacks_;
diff --git a/binder_service_brillo.cc b/binder_service_brillo.cc
index 14df725..3f01e42 100644
--- a/binder_service_brillo.cc
+++ b/binder_service_brillo.cc
@@ -33,6 +33,7 @@
using android::sp;
using brillo::ErrorPtr;
using std::string;
+using update_engine::UpdateEngineStatus;
namespace chromeos_update_engine {
@@ -57,12 +58,20 @@
return ToStatus(&error);
}
+Status BinderUpdateEngineBrilloService::SetUpdateAttemptFlags(int flags) {
+ return CallCommonHandler(&UpdateEngineService::SetUpdateAttemptFlags, flags);
+}
+
Status BinderUpdateEngineBrilloService::AttemptUpdate(
- const String16& app_version, const String16& omaha_url, int flags) {
+ const String16& app_version,
+ const String16& omaha_url,
+ int flags,
+ bool* out_result) {
return CallCommonHandler(&UpdateEngineService::AttemptUpdate,
NormalString(app_version),
NormalString(omaha_url),
- flags);
+ flags,
+ out_result);
}
Status BinderUpdateEngineBrilloService::AttemptRollback(bool powerwash) {
@@ -79,19 +88,12 @@
Status BinderUpdateEngineBrilloService::GetStatus(
ParcelableUpdateEngineStatus* status) {
- string current_op;
- string new_version;
-
- auto ret = CallCommonHandler(&UpdateEngineService::GetStatus,
- &status->last_checked_time_,
- &status->progress_,
- ¤t_op,
- &new_version,
- &status->new_size_);
+ UpdateEngineStatus update_engine_status;
+ auto ret =
+ CallCommonHandler(&UpdateEngineService::GetStatus, &update_engine_status);
if (ret.isOk()) {
- status->current_operation_ = String16{current_op.c_str()};
- status->new_version_ = String16{new_version.c_str()};
+ *status = ParcelableUpdateEngineStatus(update_engine_status);
}
return ret;
@@ -151,14 +153,6 @@
&UpdateEngineService::SetUpdateOverCellularPermission, enabled);
}
-Status BinderUpdateEngineBrilloService::SetUpdateOverCellularTarget(
- const String16& target_version,
- int64_t target_size) {
- return CallCommonHandler(
- &UpdateEngineService::SetUpdateOverCellularTarget,
- NormalString(target_version), target_size);
-}
-
Status BinderUpdateEngineBrilloService::GetUpdateOverCellularPermission(
bool* out_cellular_permission) {
return CallCommonHandler(
@@ -236,18 +230,10 @@
}
void BinderUpdateEngineBrilloService::SendStatusUpdate(
- int64_t last_checked_time,
- double progress,
- update_engine::UpdateStatus status,
- const string& new_version,
- int64_t new_size) {
- const string str_status = UpdateStatusToString(status);
+ const UpdateEngineStatus& update_engine_status) {
+ ParcelableUpdateEngineStatus parcelable_status(update_engine_status);
for (auto& callback : callbacks_) {
- callback->HandleStatusUpdate(last_checked_time,
- progress,
- String16{str_status.c_str()},
- String16{new_version.c_str()},
- new_size);
+ callback->HandleStatusUpdate(parcelable_status);
}
}
diff --git a/binder_service_brillo.h b/binder_service_brillo.h
index 4cc007b..c802fca 100644
--- a/binder_service_brillo.h
+++ b/binder_service_brillo.h
@@ -46,17 +46,16 @@
}
// ServiceObserverInterface overrides.
- void SendStatusUpdate(int64_t last_checked_time,
- double progress,
- update_engine::UpdateStatus status,
- const std::string& new_version,
- int64_t new_size) override;
+ void SendStatusUpdate(
+ const update_engine::UpdateEngineStatus& update_engine_status) override;
void SendPayloadApplicationComplete(ErrorCode error_code) override {}
// android::brillo::BnUpdateEngine overrides.
+ android::binder::Status SetUpdateAttemptFlags(int flags) override;
android::binder::Status AttemptUpdate(const android::String16& app_version,
const android::String16& omaha_url,
- int flags) override;
+ int flags,
+ bool* out_result) override;
android::binder::Status AttemptRollback(bool powerwash) override;
android::binder::Status CanRollback(bool* out_can_rollback) override;
android::binder::Status ResetStatus() override;
@@ -76,9 +75,6 @@
bool* out_p2p_permission) override;
android::binder::Status SetUpdateOverCellularPermission(
bool enabled) override;
- android::binder::Status SetUpdateOverCellularTarget(
- const android::String16& target_version,
- int64_t target_size) override;
android::binder::Status GetUpdateOverCellularPermission(
bool* out_cellular_permission) override;
android::binder::Status GetDurationSinceUpdate(
diff --git a/client_library/client_binder.cc b/client_library/client_binder.cc
index e98c225..54b33ed 100644
--- a/client_library/client_binder.cc
+++ b/client_library/client_binder.cc
@@ -25,15 +25,15 @@
#include "update_engine/parcelable_update_engine_status.h"
#include "update_engine/update_status_utils.h"
-using android::OK;
-using android::String16;
-using android::String8;
using android::binder::Status;
using android::brillo::ParcelableUpdateEngineStatus;
using android::getService;
+using android::OK;
+using android::String16;
+using android::String8;
using chromeos_update_engine::StringToUpdateStatus;
-using chromeos_update_engine::UpdateEngineService;
using std::string;
+using update_engine::UpdateAttemptFlags;
namespace update_engine {
namespace internal {
@@ -48,10 +48,14 @@
bool BinderUpdateEngineClient::AttemptUpdate(const string& in_app_version,
const string& in_omaha_url,
bool at_user_request) {
- return service_->AttemptUpdate(String16{in_app_version.c_str()},
- String16{in_omaha_url.c_str()},
- at_user_request ? 0 :
- UpdateEngineService::kAttemptUpdateFlagNonInteractive).isOk();
+ bool started;
+ return service_
+ ->AttemptUpdate(
+ String16{in_app_version.c_str()},
+ String16{in_omaha_url.c_str()},
+ at_user_request ? 0 : UpdateAttemptFlags::kFlagNonInteractive,
+ &started)
+ .isOk();
}
bool BinderUpdateEngineClient::GetStatus(int64_t* out_last_checked_time,
@@ -143,18 +147,18 @@
}
Status BinderUpdateEngineClient::StatusUpdateCallback::HandleStatusUpdate(
- int64_t last_checked_time,
- double progress,
- const String16& current_operation,
- const String16& new_version,
- int64_t new_size) {
+ const ParcelableUpdateEngineStatus& status) {
UpdateStatus update_status;
- StringToUpdateStatus(String8{current_operation}.string(), &update_status);
+ StringToUpdateStatus(String8{status.current_operation_}.string(),
+ &update_status);
for (auto& handler : client_->handlers_) {
- handler->HandleStatusUpdate(last_checked_time, progress, update_status,
- String8{new_version}.string(), new_size);
+ handler->HandleStatusUpdate(status.last_checked_time_,
+ status.progress_,
+ update_status,
+ String8{status.new_version_}.string(),
+ status.new_size_);
}
return Status::ok();
diff --git a/client_library/client_binder.h b/client_library/client_binder.h
index b1b34da..17f2beb 100644
--- a/client_library/client_binder.h
+++ b/client_library/client_binder.h
@@ -94,11 +94,7 @@
: client_(client) {}
android::binder::Status HandleStatusUpdate(
- int64_t last_checked_time,
- double progress,
- const android::String16& current_operation,
- const android::String16& new_version,
- int64_t new_size) override;
+ const android::brillo::ParcelableUpdateEngineStatus& status) override;
private:
BinderUpdateEngineClient* client_;
diff --git a/client_library/include/update_engine/update_status.h b/client_library/include/update_engine/update_status.h
index 2f6322a..41fab48 100644
--- a/client_library/include/update_engine/update_status.h
+++ b/client_library/include/update_engine/update_status.h
@@ -17,15 +17,16 @@
#ifndef UPDATE_ENGINE_CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_UPDATE_STATUS_H_
#define UPDATE_ENGINE_CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_UPDATE_STATUS_H_
+#include <string>
+
+#include <brillo/enum_flags.h>
+
namespace update_engine {
enum class UpdateStatus {
IDLE = 0,
CHECKING_FOR_UPDATE,
UPDATE_AVAILABLE,
- // Broadcast this state when an update aborts because user preferences does
- // not allow update over cellular.
- NEED_PERMISSION_TO_UPDATE,
DOWNLOADING,
VERIFYING,
FINALIZING,
@@ -35,6 +36,39 @@
DISABLED,
};
+// Enum of bit-wise flags for controlling how updates are attempted.
+enum UpdateAttemptFlags : int32_t {
+ kNone = 0,
+ // Treat the update like a non-interactive update, even when being triggered
+ // by the interactive APIs.
+ kFlagNonInteractive = (1 << 0),
+ // Restrict (disallow) downloading of updates.
+ kFlagRestrictDownload = (1 << 1),
+};
+
+// Enable bit-wise operators for the above enumeration of flag values.
+DECLARE_FLAGS_ENUM(UpdateAttemptFlags);
+
+struct UpdateEngineStatus {
+ // When the update_engine last checked for updates (time_t: seconds from unix
+ // epoch)
+ int64_t last_checked_time;
+ // the current status/operation of the update_engine
+ UpdateStatus status;
+ // the current product version (oem bundle id)
+ std::string current_version;
+ // the current system version
+ std::string current_system_version;
+ // The current progress (0.0f-1.0f).
+ double progress;
+ // the size of the update (bytes)
+ uint64_t new_size_bytes;
+ // the new product version
+ std::string new_version;
+ // the new system version, if there is one (empty, otherwise)
+ std::string new_system_version;
+};
+
} // namespace update_engine
#endif // UPDATE_ENGINE_CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_UPDATE_STATUS_H_
diff --git a/common/constants.cc b/common/constants.cc
index d6d1c6d..5941c93 100644
--- a/common/constants.cc
+++ b/common/constants.cc
@@ -57,6 +57,7 @@
const char kPrefsP2PFirstAttemptTimestamp[] = "p2p-first-attempt-timestamp";
const char kPrefsP2PNumAttempts[] = "p2p-num-attempts";
const char kPrefsPayloadAttemptNumber[] = "payload-attempt-number";
+const char kPrefsPostInstallSucceeded[] = "post-install-succeeded";
const char kPrefsPreviousVersion[] = "previous-version";
const char kPrefsResumedUpdateFailures[] = "resumed-update-failures";
const char kPrefsRollbackVersion[] = "rollback-version";
@@ -74,14 +75,11 @@
const char kPrefsUpdateFirstSeenAt[] = "update-first-seen-at";
const char kPrefsUpdateOverCellularPermission[] =
"update-over-cellular-permission";
-const char kPrefsUpdateOverCellularTargetVersion[] =
- "update-over-cellular-target-version";
-const char kPrefsUpdateOverCellularTargetSize[] =
- "update-over-cellular-target-size";
const char kPrefsUpdateServerCertificate[] = "update-server-cert";
const char kPrefsUpdateStateNextDataLength[] = "update-state-next-data-length";
const char kPrefsUpdateStateNextDataOffset[] = "update-state-next-data-offset";
const char kPrefsUpdateStateNextOperation[] = "update-state-next-operation";
+const char kPrefsUpdateStatePayloadIndex[] = "update-state-payload-index";
const char kPrefsUpdateStateSHA256Context[] = "update-state-sha-256-context";
const char kPrefsUpdateStateSignatureBlob[] = "update-state-signature-blob";
const char kPrefsUpdateStateSignedSHA256Context[] =
@@ -106,5 +104,12 @@
// This can be used to zero-rate OTA traffic by sending it over the correct
// network.
const char kPayloadPropertyNetworkId[] = "NETWORK_ID";
+// Set "SWITCH_SLOT_ON_REBOOT=0" to skip marking the updated partitions active.
+// The default is 1 (always switch slot if update succeeded).
+const char kPayloadPropertySwitchSlotOnReboot[] = "SWITCH_SLOT_ON_REBOOT";
+// Set "RUN_POST_INSTALL=0" to skip running post install, this will only be
+// honored if we're resuming an update and post install has already succeeded.
+// The default is 1 (always run post install).
+const char kPayloadPropertyRunPostInstall[] = "RUN_POST_INSTALL";
} // namespace chromeos_update_engine
diff --git a/common/constants.h b/common/constants.h
index c571403..26773cf 100644
--- a/common/constants.h
+++ b/common/constants.h
@@ -59,6 +59,7 @@
extern const char kPrefsP2PFirstAttemptTimestamp[];
extern const char kPrefsP2PNumAttempts[];
extern const char kPrefsPayloadAttemptNumber[];
+extern const char kPrefsPostInstallSucceeded[];
extern const char kPrefsPreviousVersion[];
extern const char kPrefsResumedUpdateFailures[];
extern const char kPrefsRollbackVersion[];
@@ -75,12 +76,11 @@
extern const char kPrefsUpdateDurationUptime[];
extern const char kPrefsUpdateFirstSeenAt[];
extern const char kPrefsUpdateOverCellularPermission[];
-extern const char kPrefsUpdateOverCellularTargetVersion[];
-extern const char kPrefsUpdateOverCellularTargetSize[];
extern const char kPrefsUpdateServerCertificate[];
extern const char kPrefsUpdateStateNextDataLength[];
extern const char kPrefsUpdateStateNextDataOffset[];
extern const char kPrefsUpdateStateNextOperation[];
+extern const char kPrefsUpdateStatePayloadIndex[];
extern const char kPrefsUpdateStateSHA256Context[];
extern const char kPrefsUpdateStateSignatureBlob[];
extern const char kPrefsUpdateStateSignedSHA256Context[];
@@ -97,6 +97,8 @@
extern const char kPayloadPropertyUserAgent[];
extern const char kPayloadPropertyPowerwash[];
extern const char kPayloadPropertyNetworkId[];
+extern const char kPayloadPropertySwitchSlotOnReboot[];
+extern const char kPayloadPropertyRunPostInstall[];
// A download source is any combination of protocol and server (that's of
// interest to us when looking at UMA metrics) using which we may download
@@ -174,6 +176,7 @@
// succeeding. When using p2p, this is low in order to fail fast.
const int kDownloadMaxRetryCount = 20;
const int kDownloadMaxRetryCountOobeNotComplete = 3;
+const int kDownloadMaxRetryCountInteractive = 3;
const int kDownloadP2PMaxRetryCount = 5;
// The connect timeout, in seconds.
diff --git a/common/error_code.h b/common/error_code.h
index bcf541c..0b08005 100644
--- a/common/error_code.h
+++ b/common/error_code.h
@@ -73,7 +73,9 @@
kFilesystemVerifierError = 47,
kUserCanceled = 48,
kNonCriticalUpdateInOOBE = 49,
- kOmahaUpdateIgnoredOverCellular = 50,
+ // kOmahaUpdateIgnoredOverCellular = 50,
+ kPayloadTimestampError = 51,
+ kUpdatedButNotActive = 52,
// VERY IMPORTANT! When adding new error codes:
//
diff --git a/common/error_code_utils.cc b/common/error_code_utils.cc
index 0169ab8..313a15f 100644
--- a/common/error_code_utils.cc
+++ b/common/error_code_utils.cc
@@ -144,10 +144,12 @@
return "ErrorCode::kUserCanceled";
case ErrorCode::kNonCriticalUpdateInOOBE:
return "ErrorCode::kNonCriticalUpdateInOOBE";
- case ErrorCode::kOmahaUpdateIgnoredOverCellular:
- return "ErrorCode::kOmahaUpdateIgnoredOverCellular";
- // Don't add a default case to let the compiler warn about newly added
- // error codes which should be added here.
+ case ErrorCode::kPayloadTimestampError:
+ return "ErrorCode::kPayloadTimestampError";
+ case ErrorCode::kUpdatedButNotActive:
+ return "ErrorCode::kUpdatedButNotActive";
+ // Don't add a default case to let the compiler warn about newly added
+ // error codes which should be added here.
}
return "Unknown error: " + base::UintToString(static_cast<unsigned>(code));
diff --git a/common/fake_hardware.h b/common/fake_hardware.h
index 01d23d0..f2b2c9d 100644
--- a/common/fake_hardware.h
+++ b/common/fake_hardware.h
@@ -81,6 +81,8 @@
return false;
}
+ int64_t GetBuildTimestamp() const override { return build_timestamp_; }
+
bool GetFirstActiveOmahaPingSent() const override {
return first_active_omaha_ping_sent_;
}
@@ -133,6 +135,10 @@
powerwash_count_ = powerwash_count;
}
+ void SetBuildTimestamp(int64_t build_timestamp) {
+ build_timestamp_ = build_timestamp;
+ }
+
private:
bool is_official_build_{true};
bool is_normal_boot_mode_{true};
@@ -145,6 +151,7 @@
std::string ec_version_{"Fake EC v1.0a"};
int powerwash_count_{kPowerwashCountNotSet};
bool powerwash_scheduled_{false};
+ int64_t build_timestamp_{0};
bool first_active_omaha_ping_sent_{false};
DISALLOW_COPY_AND_ASSIGN(FakeHardware);
diff --git a/common/hardware_interface.h b/common/hardware_interface.h
index 541a68a..94442d1 100644
--- a/common/hardware_interface.h
+++ b/common/hardware_interface.h
@@ -17,6 +17,8 @@
#ifndef UPDATE_ENGINE_COMMON_HARDWARE_INTERFACE_H_
#define UPDATE_ENGINE_COMMON_HARDWARE_INTERFACE_H_
+#include <stdint.h>
+
#include <string>
#include <vector>
@@ -90,6 +92,9 @@
// returns false.
virtual bool GetPowerwashSafeDirectory(base::FilePath* path) const = 0;
+ // Returns the timestamp of the current OS build.
+ virtual int64_t GetBuildTimestamp() const = 0;
+
// Returns whether the first active ping was sent to Omaha at some point, and
// that the value is persisted across recovery (and powerwash) once set with
// |SetFirstActiveOmahaPingSent()|.
diff --git a/common/hash_calculator.cc b/common/hash_calculator.cc
index de6e0f9..ebfdb6e 100644
--- a/common/hash_calculator.cc
+++ b/common/hash_calculator.cc
@@ -20,7 +20,6 @@
#include <base/logging.h>
#include <base/posix/eintr_wrapper.h>
-#include <brillo/data_encoding.h>
#include "update_engine/common/utils.h"
@@ -37,7 +36,7 @@
// Mostly just passes the data through to OpenSSL's SHA256_Update()
bool HashCalculator::Update(const void* data, size_t length) {
TEST_AND_RETURN_FALSE(valid_);
- TEST_AND_RETURN_FALSE(hash_.empty());
+ TEST_AND_RETURN_FALSE(raw_hash_.empty());
static_assert(sizeof(size_t) <= sizeof(unsigned long), // NOLINT(runtime/int)
"length param may be truncated in SHA256_Update");
TEST_AND_RETURN_FALSE(SHA256_Update(&ctx_, data, length) == 1);
@@ -73,16 +72,11 @@
}
// Call Finalize() when all data has been passed in. This mostly just
-// calls OpenSSL's SHA256_Final() and then base64 encodes the hash.
+// calls OpenSSL's SHA256_Final().
bool HashCalculator::Finalize() {
- TEST_AND_RETURN_FALSE(hash_.empty());
TEST_AND_RETURN_FALSE(raw_hash_.empty());
raw_hash_.resize(SHA256_DIGEST_LENGTH);
TEST_AND_RETURN_FALSE(SHA256_Final(raw_hash_.data(), &ctx_) == 1);
-
- // Convert raw_hash_ to base64 encoding and store it in hash_.
- hash_ = brillo::data_encoding::Base64Encode(raw_hash_.data(),
- raw_hash_.size());
return true;
}
@@ -115,21 +109,6 @@
return res;
}
-string HashCalculator::HashOfBytes(const void* data, size_t length) {
- HashCalculator calc;
- calc.Update(data, length);
- calc.Finalize();
- return calc.hash();
-}
-
-string HashCalculator::HashOfString(const string& str) {
- return HashOfBytes(str.data(), str.size());
-}
-
-string HashCalculator::HashOfData(const brillo::Blob& data) {
- return HashOfBytes(data.data(), data.size());
-}
-
string HashCalculator::GetContext() const {
return string(reinterpret_cast<const char*>(&ctx_), sizeof(ctx_));
}
diff --git a/common/hash_calculator.h b/common/hash_calculator.h
index f749585..06d2cfb 100644
--- a/common/hash_calculator.h
+++ b/common/hash_calculator.h
@@ -27,11 +27,11 @@
#include <base/macros.h>
#include <brillo/secure_blob.h>
-// Omaha uses base64 encoded SHA-256 as the hash. This class provides a simple
-// wrapper around OpenSSL providing such a formatted hash of data passed in.
+// This class provides a simple wrapper around OpenSSL providing a hash of data
+// passed in.
// The methods of this class must be called in a very specific order: First the
// ctor (of course), then 0 or more calls to Update(), then Finalize(), then 0
-// or more calls to hash().
+// or more calls to raw_hash().
namespace chromeos_update_engine {
@@ -50,17 +50,10 @@
off_t UpdateFile(const std::string& name, off_t length);
// Call Finalize() when all data has been passed in. This method tells
- // OpenSSl that no more data will come in and base64 encodes the resulting
- // hash.
+ // OpenSSL that no more data will come in.
// Returns true on success.
bool Finalize();
- // Gets the hash. Finalize() must have been called.
- const std::string& hash() const {
- DCHECK(!hash_.empty()) << "Call Finalize() first";
- return hash_;
- }
-
const brillo::Blob& raw_hash() const {
DCHECK(!raw_hash_.empty()) << "Call Finalize() first";
return raw_hash_;
@@ -83,15 +76,9 @@
static off_t RawHashOfFile(const std::string& name, off_t length,
brillo::Blob* out_hash);
- // Used by tests
- static std::string HashOfBytes(const void* data, size_t length);
- static std::string HashOfString(const std::string& str);
- static std::string HashOfData(const brillo::Blob& data);
-
private:
- // If non-empty, the final base64 encoded hash and the raw hash. Will only be
- // set to non-empty when Finalize is called.
- std::string hash_;
+ // If non-empty, the final raw hash. Will only be set to non-empty when
+ // Finalize is called.
brillo::Blob raw_hash_;
// Init success
diff --git a/common/hash_calculator_unittest.cc b/common/hash_calculator_unittest.cc
index 436e6a7..233237b 100644
--- a/common/hash_calculator_unittest.cc
+++ b/common/hash_calculator_unittest.cc
@@ -22,6 +22,7 @@
#include <string>
#include <vector>
+#include <brillo/data_encoding.h>
#include <brillo/secure_blob.h>
#include <gtest/gtest.h>
@@ -33,9 +34,8 @@
namespace chromeos_update_engine {
// Generated by running this on a linux shell:
-// $ echo -n hi | openssl dgst -sha256 -binary | openssl base64
-static const char kExpectedHash[] =
- "j0NDRmSPa5bfid2pAcUXaxCm2Dlh3TwayItZstwyeqQ=";
+// $ echo -n hi | openssl dgst -sha256 -binary |
+// hexdump -v -e '" " 12/1 "0x%02x, " "\n"'
static const uint8_t kExpectedRawHash[] = {
0x8f, 0x43, 0x43, 0x46, 0x64, 0x8f, 0x6b, 0x96,
0xdf, 0x89, 0xdd, 0xa9, 0x01, 0xc5, 0x17, 0x6b,
@@ -52,7 +52,6 @@
HashCalculator calc;
calc.Update("hi", 2);
calc.Finalize();
- EXPECT_EQ(kExpectedHash, calc.hash());
brillo::Blob raw_hash(std::begin(kExpectedRawHash),
std::end(kExpectedRawHash));
EXPECT_TRUE(raw_hash == calc.raw_hash());
@@ -63,7 +62,6 @@
calc.Update("h", 1);
calc.Update("i", 1);
calc.Finalize();
- EXPECT_EQ(kExpectedHash, calc.hash());
brillo::Blob raw_hash(std::begin(kExpectedRawHash),
std::end(kExpectedRawHash));
EXPECT_TRUE(raw_hash == calc.raw_hash());
@@ -78,7 +76,6 @@
calc_next.SetContext(calc_context);
calc_next.Update("i", 1);
calc_next.Finalize();
- EXPECT_EQ(kExpectedHash, calc_next.hash());
brillo::Blob raw_hash(std::begin(kExpectedRawHash),
std::end(kExpectedRawHash));
EXPECT_TRUE(raw_hash == calc_next.raw_hash());
@@ -106,7 +103,8 @@
// echo -n $C
// let C=C+1
// done | openssl dgst -sha256 -binary | openssl base64
- EXPECT_EQ("NZf8k6SPBkYMvhaX8YgzuMgbkLP1XZ+neM8K5wcSsf8=", calc.hash());
+ EXPECT_EQ("NZf8k6SPBkYMvhaX8YgzuMgbkLP1XZ+neM8K5wcSsf8=",
+ brillo::data_encoding::Base64Encode(calc.raw_hash()));
}
TEST_F(HashCalculatorTest, UpdateFileSimpleTest) {
@@ -121,7 +119,6 @@
HashCalculator calc;
EXPECT_EQ(2, calc.UpdateFile(data_path, kLengths[i]));
EXPECT_TRUE(calc.Finalize());
- EXPECT_EQ(kExpectedHash, calc.hash());
brillo::Blob raw_hash(std::begin(kExpectedRawHash),
std::end(kExpectedRawHash));
EXPECT_TRUE(raw_hash == calc.raw_hash());
@@ -132,7 +129,8 @@
EXPECT_EQ(1, calc.UpdateFile(data_path, 1));
EXPECT_TRUE(calc.Finalize());
// echo -n h | openssl dgst -sha256 -binary | openssl base64
- EXPECT_EQ("qqlAJmTxpB9A67xSyZk+tmrrNmYClY/fqig7ceZNsSM=", calc.hash());
+ EXPECT_EQ("qqlAJmTxpB9A67xSyZk+tmrrNmYClY/fqig7ceZNsSM=",
+ brillo::data_encoding::Base64Encode(calc.raw_hash()));
}
TEST_F(HashCalculatorTest, RawHashOfFileSimpleTest) {
diff --git a/common/http_fetcher_unittest.cc b/common/http_fetcher_unittest.cc
index 73110e9..867216e 100644
--- a/common/http_fetcher_unittest.cc
+++ b/common/http_fetcher_unittest.cc
@@ -32,7 +32,6 @@
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <base/time/time.h>
-#include <brillo/bind_lambda.h>
#include <brillo/message_loops/base_message_loop.h>
#include <brillo/message_loops/message_loop.h>
#include <brillo/message_loops/message_loop_utils.h>
diff --git a/common/hwid_override_unittest.cc b/common/hwid_override_unittest.cc
index 26ef30a..35e6438 100644
--- a/common/hwid_override_unittest.cc
+++ b/common/hwid_override_unittest.cc
@@ -32,7 +32,7 @@
void SetUp() override {
ASSERT_TRUE(tempdir_.CreateUniqueTempDir());
- ASSERT_TRUE(base::CreateDirectory(tempdir_.path().Append("etc")));
+ ASSERT_TRUE(base::CreateDirectory(tempdir_.GetPath().Append("etc")));
}
protected:
@@ -46,22 +46,24 @@
std::string expected_hwid("expected");
std::string keyval(HwidOverride::kHwidOverrideKey);
keyval += ("=" + expected_hwid);
- ASSERT_EQ(base::WriteFile(tempdir_.path().Append("etc/lsb-release"),
- keyval.c_str(), keyval.length()),
+ ASSERT_EQ(base::WriteFile(tempdir_.GetPath().Append("etc/lsb-release"),
+ keyval.c_str(),
+ keyval.length()),
static_cast<int>(keyval.length()));
- EXPECT_EQ(expected_hwid, HwidOverride::Read(tempdir_.path()));
+ EXPECT_EQ(expected_hwid, HwidOverride::Read(tempdir_.GetPath()));
}
TEST_F(HwidOverrideTest, ReadNothing) {
std::string keyval("SOMETHING_ELSE=UNINTERESTING");
- ASSERT_EQ(base::WriteFile(tempdir_.path().Append("etc/lsb-release"),
- keyval.c_str(), keyval.length()),
+ ASSERT_EQ(base::WriteFile(tempdir_.GetPath().Append("etc/lsb-release"),
+ keyval.c_str(),
+ keyval.length()),
static_cast<int>(keyval.length()));
- EXPECT_EQ(std::string(), HwidOverride::Read(tempdir_.path()));
+ EXPECT_EQ(std::string(), HwidOverride::Read(tempdir_.GetPath()));
}
TEST_F(HwidOverrideTest, ReadFailure) {
- EXPECT_EQ(std::string(), HwidOverride::Read(tempdir_.path()));
+ EXPECT_EQ(std::string(), HwidOverride::Read(tempdir_.GetPath()));
}
} // namespace chromeos_update_engine
diff --git a/common/prefs_unittest.cc b/common/prefs_unittest.cc
index 73ceb00..aa2eb04 100644
--- a/common/prefs_unittest.cc
+++ b/common/prefs_unittest.cc
@@ -44,7 +44,7 @@
protected:
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- prefs_dir_ = temp_dir_.path();
+ prefs_dir_ = temp_dir_.GetPath();
ASSERT_TRUE(prefs_.Init(prefs_dir_));
}
diff --git a/common/subprocess_unittest.cc b/common/subprocess_unittest.cc
index cbc9a85..c8996db 100644
--- a/common/subprocess_unittest.cc
+++ b/common/subprocess_unittest.cc
@@ -32,7 +32,6 @@
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <base/time/time.h>
-#include <brillo/bind_lambda.h>
#include <brillo/message_loops/base_message_loop.h>
#include <brillo/message_loops/message_loop.h>
#include <brillo/message_loops/message_loop_utils.h>
@@ -225,7 +224,7 @@
TEST_F(SubprocessTest, CancelTest) {
base::ScopedTempDir tempdir;
ASSERT_TRUE(tempdir.CreateUniqueTempDir());
- string fifo_path = tempdir.path().Append("fifo").value();
+ string fifo_path = tempdir.GetPath().Append("fifo").value();
EXPECT_EQ(0, mkfifo(fifo_path.c_str(), 0666));
// Start a process, make sure it is running and try to cancel it. We write
diff --git a/common/test_utils.cc b/common/test_utils.cc
index fb22c80..85f78f9 100644
--- a/common/test_utils.cc
+++ b/common/test_utils.cc
@@ -24,6 +24,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
+#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
@@ -201,6 +202,13 @@
device_info.lo_file_name[LO_NAME_SIZE - 1] = '\0';
TEST_AND_RETURN_FALSE_ERRNO(
ioctl(loop_device_fd, LOOP_SET_STATUS64, &device_info) == 0);
+ if (writable) {
+ // Make sure loop device isn't read only.
+ int ro = 0;
+ if (ioctl(loop_device_fd, BLKROSET, &ro) != 0) {
+ PLOG(WARNING) << "Failed to mark loop device writable.";
+ }
+ }
return true;
}
@@ -248,7 +256,7 @@
string* mnt_path,
unsigned long flags) { // NOLINT - long
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
- *mnt_path = temp_dir_.path().value();
+ *mnt_path = temp_dir_.GetPath().value();
string loop_dev;
loop_binder_.reset(
diff --git a/common/test_utils.h b/common/test_utils.h
index ba9f5f2..ddb3d34 100644
--- a/common/test_utils.h
+++ b/common/test_utils.h
@@ -31,6 +31,7 @@
#include <base/callback.h>
#include <base/files/file_path.h>
#include <base/files/scoped_temp_dir.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "update_engine/common/action.h"
@@ -101,6 +102,11 @@
void FillWithData(brillo::Blob* buffer);
+// Compare the value of native array for download source parameter.
+MATCHER_P(DownloadSourceMatcher, source_array, "") {
+ return std::equal(source_array, source_array + kNumDownloadSources, arg);
+}
+
// Class to unmount FS when object goes out of scope
class ScopedFilesystemUnmounter {
public:
diff --git a/common/utils.cc b/common/utils.cc
index b63b867..f651823 100644
--- a/common/utils.cc
+++ b/common/utils.cc
@@ -57,7 +57,6 @@
#include "update_engine/common/prefs_interface.h"
#include "update_engine/common/subprocess.h"
#include "update_engine/payload_consumer/file_descriptor.h"
-#include "update_engine/payload_consumer/file_writer.h"
using base::Time;
using base::TimeDelta;
@@ -944,8 +943,16 @@
return str;
}
-string CalculateP2PFileId(const string& payload_hash, size_t payload_size) {
- string encoded_hash = brillo::data_encoding::Base64Encode(payload_hash);
+// The P2P file id should be the same for devices running new version and old
+// version so that they can share it with each other. The hash in the response
+// was base64 encoded, but now that we switched to use "hash_sha256" field which
+// is hex encoded, we have to convert them back to base64 for P2P. However, the
+// base64 encoded hash was base64 encoded here again historically for some
+// reason, so we keep the same behavior here.
+string CalculateP2PFileId(const brillo::Blob& payload_hash,
+ size_t payload_size) {
+ string encoded_hash = brillo::data_encoding::Base64Encode(
+ brillo::data_encoding::Base64Encode(payload_hash));
return base::StringPrintf("cros_update_size_%" PRIuS "_hash_%s",
payload_size,
encoded_hash.c_str());
diff --git a/common/utils.h b/common/utils.h
index 262a403..e4ffcf8 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -53,7 +53,7 @@
std::string StringVectorToString(const std::vector<std::string> &vec_str);
// Calculates the p2p file id from payload hash and size
-std::string CalculateP2PFileId(const std::string& payload_hash,
+std::string CalculateP2PFileId(const brillo::Blob& payload_hash,
size_t payload_size);
// Parse the firmware version from one line of output from the
diff --git a/common/utils_unittest.cc b/common/utils_unittest.cc
index 6e9a911..62f9f6c 100644
--- a/common/utils_unittest.cc
+++ b/common/utils_unittest.cc
@@ -100,11 +100,11 @@
TEST(UtilsTest, IsSymlinkTest) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- string temp_file = temp_dir.path().Append("temp-file").value();
+ string temp_file = temp_dir.GetPath().Append("temp-file").value();
EXPECT_TRUE(utils::WriteFile(temp_file.c_str(), "", 0));
- string temp_symlink = temp_dir.path().Append("temp-symlink").value();
+ string temp_symlink = temp_dir.GetPath().Append("temp-symlink").value();
EXPECT_EQ(0, symlink(temp_file.c_str(), temp_symlink.c_str()));
- EXPECT_FALSE(utils::IsSymlink(temp_dir.path().value().c_str()));
+ EXPECT_FALSE(utils::IsSymlink(temp_dir.GetPath().value().c_str()));
EXPECT_FALSE(utils::IsSymlink(temp_file.c_str()));
EXPECT_TRUE(utils::IsSymlink(temp_symlink.c_str()));
EXPECT_FALSE(utils::IsSymlink("/non/existent/path"));
@@ -303,8 +303,9 @@
base::Time::Exploded exploded = (base::Time::Exploded) {
.year = 2001, .month = 9, .day_of_week = 0, .day_of_month = 9,
.hour = 1, .minute = 46, .second = 40, .millisecond = 42};
- EXPECT_EQ(base::Time::FromUTCExploded(exploded),
- utils::TimeFromStructTimespec(&ts));
+ base::Time time;
+ EXPECT_TRUE(base::Time::FromUTCExploded(exploded, &time));
+ EXPECT_EQ(time, utils::TimeFromStructTimespec(&ts));
}
TEST(UtilsTest, DecodeAndStoreBase64String) {
@@ -478,23 +479,23 @@
test_utils::ScopedLoopbackDeviceBinder loop_binder(
tmp_image, true, &loop_dev);
- EXPECT_FALSE(utils::IsMountpoint(mnt_dir.path().value()));
+ EXPECT_FALSE(utils::IsMountpoint(mnt_dir.GetPath().value()));
// This is the actual test part. While we hold a file descriptor open for the
// mounted filesystem, umount should still succeed.
EXPECT_TRUE(utils::MountFilesystem(
- loop_dev, mnt_dir.path().value(), MS_RDONLY, "ext4", ""));
+ loop_dev, mnt_dir.GetPath().value(), MS_RDONLY, "ext4", ""));
// Verify the directory is a mount point now.
- EXPECT_TRUE(utils::IsMountpoint(mnt_dir.path().value()));
+ EXPECT_TRUE(utils::IsMountpoint(mnt_dir.GetPath().value()));
- string target_file = mnt_dir.path().Append("empty-file").value();
+ string target_file = mnt_dir.GetPath().Append("empty-file").value();
int fd = HANDLE_EINTR(open(target_file.c_str(), O_RDONLY));
EXPECT_GE(fd, 0);
- EXPECT_TRUE(utils::UnmountFilesystem(mnt_dir.path().value()));
+ EXPECT_TRUE(utils::UnmountFilesystem(mnt_dir.GetPath().value()));
// The filesystem should be already unmounted at this point.
- EXPECT_FALSE(utils::IsMountpoint(mnt_dir.path().value()));
+ EXPECT_FALSE(utils::IsMountpoint(mnt_dir.GetPath().value()));
IGNORE_EINTR(close(fd));
// The filesystem was already unmounted so this call should fail.
- EXPECT_FALSE(utils::UnmountFilesystem(mnt_dir.path().value()));
+ EXPECT_FALSE(utils::UnmountFilesystem(mnt_dir.GetPath().value()));
}
TEST(UtilsTest, IsMountpointTest) {
@@ -503,7 +504,7 @@
base::ScopedTempDir mnt_dir;
EXPECT_TRUE(mnt_dir.CreateUniqueTempDir());
- EXPECT_FALSE(utils::IsMountpoint(mnt_dir.path().value()));
+ EXPECT_FALSE(utils::IsMountpoint(mnt_dir.GetPath().value()));
base::FilePath file;
EXPECT_TRUE(base::CreateTemporaryFile(&file));
diff --git a/common_service.cc b/common_service.cc
index ee890e1..9f3b862 100644
--- a/common_service.cc
+++ b/common_service.cc
@@ -16,12 +16,13 @@
#include "update_engine/common_service.h"
+#include <set>
#include <string>
+#include <base/bind.h>
#include <base/location.h>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
-#include <brillo/bind_lambda.h>
#include <brillo/message_loops/message_loop.h>
#include <brillo/strings/string_utils.h>
#include <policy/device_policy.h>
@@ -40,7 +41,10 @@
using base::StringPrintf;
using brillo::ErrorPtr;
using brillo::string_utils::ToString;
+using std::set;
using std::string;
+using update_engine::UpdateAttemptFlags;
+using update_engine::UpdateEngineStatus;
namespace chromeos_update_engine {
@@ -69,19 +73,35 @@
// org::chromium::UpdateEngineInterfaceInterface methods implementation.
+bool UpdateEngineService::SetUpdateAttemptFlags(ErrorPtr* /* error */,
+ int32_t in_flags_as_int) {
+ auto flags = static_cast<UpdateAttemptFlags>(in_flags_as_int);
+ LOG(INFO) << "Setting Update Attempt Flags: "
+ << "flags=0x" << std::hex << flags << " "
+ << "RestrictDownload="
+ << ((flags & UpdateAttemptFlags::kFlagRestrictDownload) ? "yes"
+ : "no");
+ system_state_->update_attempter()->SetUpdateAttemptFlags(flags);
+ return true;
+}
+
bool UpdateEngineService::AttemptUpdate(ErrorPtr* /* error */,
const string& in_app_version,
const string& in_omaha_url,
- int32_t in_flags_as_int) {
- AttemptUpdateFlags flags = static_cast<AttemptUpdateFlags>(in_flags_as_int);
- bool interactive = !(flags & kAttemptUpdateFlagNonInteractive);
+ int32_t in_flags_as_int,
+ bool* out_result) {
+ auto flags = static_cast<UpdateAttemptFlags>(in_flags_as_int);
+ bool interactive = !(flags & UpdateAttemptFlags::kFlagNonInteractive);
+ bool restrict_downloads = (flags & UpdateAttemptFlags::kFlagRestrictDownload);
LOG(INFO) << "Attempt update: app_version=\"" << in_app_version << "\" "
<< "omaha_url=\"" << in_omaha_url << "\" "
<< "flags=0x" << std::hex << flags << " "
- << "interactive=" << (interactive ? "yes" : "no");
- system_state_->update_attempter()->CheckForUpdate(
- in_app_version, in_omaha_url, interactive);
+ << "interactive=" << (interactive ? "yes " : "no ")
+ << "RestrictDownload=" << (restrict_downloads ? "yes " : "no ");
+
+ *out_result = system_state_->update_attempter()->CheckForUpdate(
+ in_app_version, in_omaha_url, flags);
return true;
}
@@ -114,16 +134,8 @@
}
bool UpdateEngineService::GetStatus(ErrorPtr* error,
- int64_t* out_last_checked_time,
- double* out_progress,
- string* out_current_operation,
- string* out_new_version,
- int64_t* out_new_size) {
- if (!system_state_->update_attempter()->GetStatus(out_last_checked_time,
- out_progress,
- out_current_operation,
- out_new_version,
- out_new_size)) {
+ UpdateEngineStatus* out_status) {
+ if (!system_state_->update_attempter()->GetStatus(out_status)) {
LogAndSetError(error, FROM_HERE, "GetStatus failed.");
return false;
}
@@ -246,12 +258,24 @@
bool UpdateEngineService::SetUpdateOverCellularPermission(ErrorPtr* error,
bool in_allowed) {
- ConnectionManagerInterface* connection_manager =
- system_state_->connection_manager();
+ set<string> allowed_types;
+ const policy::DevicePolicy* device_policy = system_state_->device_policy();
+
+ // The device_policy is loaded in a lazy way before an update check. Load it
+ // now from the libbrillo cache if it wasn't already loaded.
+ if (!device_policy) {
+ UpdateAttempter* update_attempter = system_state_->update_attempter();
+ if (update_attempter) {
+ update_attempter->RefreshDevicePolicy();
+ device_policy = system_state_->device_policy();
+ }
+ }
// Check if this setting is allowed by the device policy.
- if (connection_manager->IsAllowedConnectionTypesForUpdateSet()) {
- LogAndSetError(error, FROM_HERE,
+ if (device_policy &&
+ device_policy->GetAllowedConnectionTypesForUpdate(&allowed_types)) {
+ LogAndSetError(error,
+ FROM_HERE,
"Ignoring the update over cellular setting since there's "
"a device policy enforcing this setting.");
return false;
@@ -262,9 +286,9 @@
PrefsInterface* prefs = system_state_->prefs();
- if (!prefs ||
- !prefs->SetBoolean(kPrefsUpdateOverCellularPermission, in_allowed)) {
- LogAndSetError(error, FROM_HERE,
+ if (!prefs->SetBoolean(kPrefsUpdateOverCellularPermission, in_allowed)) {
+ LogAndSetError(error,
+ FROM_HERE,
string("Error setting the update over cellular to ") +
(in_allowed ? "true" : "false"));
return false;
@@ -272,63 +296,24 @@
return true;
}
-bool UpdateEngineService::SetUpdateOverCellularTarget(
- brillo::ErrorPtr* error, const std::string &target_version,
- int64_t target_size) {
- ConnectionManagerInterface* connection_manager =
- system_state_->connection_manager();
-
- // Check if this setting is allowed by the device policy.
- if (connection_manager->IsAllowedConnectionTypesForUpdateSet()) {
- LogAndSetError(error, FROM_HERE,
- "Ignoring the update over cellular setting since there's "
- "a device policy enforcing this setting.");
- return false;
- }
-
- // If the policy wasn't loaded yet, then it is still OK to change the local
- // setting because the policy will be checked again during the update check.
-
- PrefsInterface* prefs = system_state_->prefs();
-
- if (!prefs ||
- !prefs->SetString(kPrefsUpdateOverCellularTargetVersion,
- target_version) ||
- !prefs->SetInt64(kPrefsUpdateOverCellularTargetSize, target_size)) {
- LogAndSetError(error, FROM_HERE,
- "Error setting the target for update over cellular.");
- return false;
- }
- return true;
-}
-
-bool UpdateEngineService::GetUpdateOverCellularPermission(ErrorPtr* error,
+bool UpdateEngineService::GetUpdateOverCellularPermission(ErrorPtr* /* error */,
bool* out_allowed) {
- ConnectionManagerInterface* connection_manager =
- system_state_->connection_manager();
+ ConnectionManagerInterface* cm = system_state_->connection_manager();
- if (connection_manager->IsAllowedConnectionTypesForUpdateSet()) {
- // We have device policy, so ignore the user preferences.
- *out_allowed = connection_manager->IsUpdateAllowedOver(
- ConnectionType::kCellular, ConnectionTethering::kUnknown);
- } else {
- PrefsInterface* prefs = system_state_->prefs();
-
- if (!prefs || !prefs->Exists(kPrefsUpdateOverCellularPermission)) {
- // Update is not allowed as user preference is not set or not available.
- *out_allowed = false;
- return true;
- }
-
- bool is_allowed;
-
- if (!prefs->GetBoolean(kPrefsUpdateOverCellularPermission, &is_allowed)) {
- LogAndSetError(error, FROM_HERE,
- "Error getting the update over cellular preference.");
- return false;
- }
- *out_allowed = is_allowed;
+ // The device_policy is loaded in a lazy way before an update check and is
+ // used to determine if an update is allowed over cellular. Load the device
+ // policy now from the libbrillo cache if it wasn't already loaded.
+ if (!system_state_->device_policy()) {
+ UpdateAttempter* update_attempter = system_state_->update_attempter();
+ if (update_attempter)
+ update_attempter->RefreshDevicePolicy();
}
+
+ // Return the current setting based on the same logic used while checking for
+ // updates. A log message could be printed as the result of this test.
+ LOG(INFO) << "Checking if updates over cellular networks are allowed:";
+ *out_allowed = cm->IsUpdateAllowedOver(ConnectionType::kCellular,
+ ConnectionTethering::kUnknown);
return true;
}
diff --git a/common_service.h b/common_service.h
index 4320d09..544dd93 100644
--- a/common_service.h
+++ b/common_service.h
@@ -24,17 +24,13 @@
#include <base/memory/ref_counted.h>
#include <brillo/errors/error.h>
+#include "update_engine/client_library/include/update_engine/update_status.h"
#include "update_engine/system_state.h"
namespace chromeos_update_engine {
class UpdateEngineService {
public:
- // Flags used in the AttemptUpdateWithFlags() D-Bus method.
- typedef enum {
- kAttemptUpdateFlagNonInteractive = (1<<0)
- } AttemptUpdateFlags;
-
// Error domain for all the service errors.
static const char* const kErrorDomain;
@@ -44,10 +40,17 @@
explicit UpdateEngineService(SystemState* system_state);
virtual ~UpdateEngineService() = default;
+ // Set flags that influence how updates and checks are performed. These
+ // influence all future checks and updates until changed or the device
+ // reboots. The |in_flags_as_int| values are a union of values from
+ // |UpdateAttemptFlags|
+ bool SetUpdateAttemptFlags(brillo::ErrorPtr* error, int32_t in_flags_as_int);
+
bool AttemptUpdate(brillo::ErrorPtr* error,
const std::string& in_app_version,
const std::string& in_omaha_url,
- int32_t in_flags_as_int);
+ int32_t in_flags_as_int,
+ bool* out_result);
bool AttemptRollback(brillo::ErrorPtr* error, bool in_powerwash);
@@ -63,11 +66,7 @@
// progress, the number of operations, size to download and overall progress
// is reported.
bool GetStatus(brillo::ErrorPtr* error,
- int64_t* out_last_checked_time,
- double* out_progress,
- std::string* out_current_operation,
- std::string* out_new_version,
- int64_t* out_new_size);
+ update_engine::UpdateEngineStatus* out_status);
// Reboots the device if an update is applied and a reboot is required.
bool RebootIfNeeded(brillo::ErrorPtr* error);
@@ -115,12 +114,6 @@
bool SetUpdateOverCellularPermission(brillo::ErrorPtr* error,
bool in_allowed);
- // If there's no device policy installed, sets the update over cellular
- // target. Otherwise, this method returns with an error.
- bool SetUpdateOverCellularTarget(brillo::ErrorPtr* error,
- const std::string& target_version,
- int64_t target_size);
-
// Returns the current value of the update over cellular network setting,
// either forced by the device policy if the device is enrolled or the current
// user preference otherwise.
diff --git a/common_service_unittest.cc b/common_service_unittest.cc
index 17265b9..d9ef567 100644
--- a/common_service_unittest.cc
+++ b/common_service_unittest.cc
@@ -28,9 +28,10 @@
#include "update_engine/omaha_utils.h"
using std::string;
+using testing::_;
using testing::Return;
using testing::SetArgPointee;
-using testing::_;
+using update_engine::UpdateAttemptFlags;
namespace chromeos_update_engine {
@@ -56,13 +57,32 @@
};
TEST_F(UpdateEngineServiceTest, AttemptUpdate) {
- EXPECT_CALL(*mock_update_attempter_, CheckForUpdate(
- "app_ver", "url", false /* interactive */));
- // The update is non-interactive when we pass the non-interactive flag.
- EXPECT_TRUE(common_service_.AttemptUpdate(
- &error_, "app_ver", "url",
- UpdateEngineService::kAttemptUpdateFlagNonInteractive));
+ EXPECT_CALL(
+ *mock_update_attempter_,
+ CheckForUpdate("app_ver", "url", UpdateAttemptFlags::kFlagNonInteractive))
+ .WillOnce(Return(true));
+
+ // The non-interactive flag needs to be passed through to CheckForUpdate.
+ bool result = false;
+ EXPECT_TRUE(
+ common_service_.AttemptUpdate(&error_,
+ "app_ver",
+ "url",
+ UpdateAttemptFlags::kFlagNonInteractive,
+ &result));
EXPECT_EQ(nullptr, error_);
+ EXPECT_TRUE(result);
+}
+
+TEST_F(UpdateEngineServiceTest, AttemptUpdateReturnsFalse) {
+ EXPECT_CALL(*mock_update_attempter_,
+ CheckForUpdate("app_ver", "url", UpdateAttemptFlags::kNone))
+ .WillOnce(Return(false));
+ bool result = true;
+ EXPECT_TRUE(common_service_.AttemptUpdate(
+ &error_, "app_ver", "url", UpdateAttemptFlags::kNone, &result));
+ EXPECT_EQ(nullptr, error_);
+ EXPECT_FALSE(result);
}
// SetChannel is allowed when there's no device policy (the device is not
diff --git a/connection_manager.cc b/connection_manager.cc
index 5085842..d15faf0 100644
--- a/connection_manager.cc
+++ b/connection_manager.cc
@@ -30,7 +30,6 @@
#include "update_engine/connection_utils.h"
#include "update_engine/shill_proxy.h"
#include "update_engine/system_state.h"
-#include "update_engine/update_attempter.h"
using org::chromium::flimflam::ManagerProxyInterface;
using org::chromium::flimflam::ServiceProxyInterface;
@@ -59,47 +58,54 @@
case ConnectionType::kCellular: {
set<string> allowed_types;
-
const policy::DevicePolicy* device_policy =
system_state_->device_policy();
- // The device_policy is loaded in a lazy way before an update check. Load
- // it now from the libbrillo cache if it wasn't already loaded.
+ // A device_policy is loaded in a lazy way right before an update check,
+ // so the device_policy should be already loaded at this point. If it's
+ // not, return a safe value for this setting.
if (!device_policy) {
- UpdateAttempter* update_attempter = system_state_->update_attempter();
- if (update_attempter) {
- update_attempter->RefreshDevicePolicy();
- device_policy = system_state_->device_policy();
- }
- }
-
- if (!device_policy) {
- // Device policy fails to be loaded (possibly due to guest account). We
- // do not check the local user setting here, which should be checked by
- // |OmahaRequestAction| during checking for update.
- LOG(INFO) << "Allowing updates over cellular as device policy "
- "fails to be loaded.";
- return true;
+ LOG(INFO) << "Disabling updates over cellular networks as there's no "
+ "device policy loaded yet.";
+ return false;
}
if (device_policy->GetAllowedConnectionTypesForUpdate(&allowed_types)) {
// The update setting is enforced by the device policy.
- if (!ContainsKey(allowed_types, shill::kTypeCellular)) {
- LOG(INFO) << "Disabling updates over cellular per device policy.";
+ if (!base::ContainsKey(allowed_types, shill::kTypeCellular)) {
+ LOG(INFO) << "Disabling updates over cellular connection as it's not "
+ "allowed in the device policy.";
return false;
}
LOG(INFO) << "Allowing updates over cellular per device policy.";
return true;
- }
+ } else {
+ // There's no update setting in the device policy, using the local user
+ // setting.
+ PrefsInterface* prefs = system_state_->prefs();
- // If there's no update setting in the device policy, we do not check
- // the local user setting here, which should be checked by
- // |OmahaRequestAction| during checking for update.
- LOG(INFO) << "Allowing updates over cellular as device policy does "
- "not include update setting.";
- return true;
+ if (!prefs || !prefs->Exists(kPrefsUpdateOverCellularPermission)) {
+ LOG(INFO) << "Disabling updates over cellular connection as there's "
+ "no device policy setting nor user preference present.";
+ return false;
+ }
+
+ bool stored_value;
+ if (!prefs->GetBoolean(kPrefsUpdateOverCellularPermission,
+ &stored_value)) {
+ return false;
+ }
+
+ if (!stored_value) {
+ LOG(INFO) << "Disabling updates over cellular connection per user "
+ "setting.";
+ return false;
+ }
+ LOG(INFO) << "Allowing updates over cellular per user setting.";
+ return true;
+ }
}
default:
@@ -114,22 +120,6 @@
}
}
-bool ConnectionManager::IsAllowedConnectionTypesForUpdateSet() const {
- const policy::DevicePolicy* device_policy = system_state_->device_policy();
- if (!device_policy) {
- LOG(INFO) << "There's no device policy loaded yet.";
- return false;
- }
-
- set<string> allowed_types;
- if (!device_policy->GetAllowedConnectionTypesForUpdate(
- &allowed_types)) {
- return false;
- }
-
- return true;
-}
-
bool ConnectionManager::GetConnectionProperties(
ConnectionType* out_type, ConnectionTethering* out_tethering) {
dbus::ObjectPath default_service_path;
diff --git a/connection_manager.h b/connection_manager.h
index 864985e..e5a9d49 100644
--- a/connection_manager.h
+++ b/connection_manager.h
@@ -43,7 +43,6 @@
ConnectionTethering* out_tethering) override;
bool IsUpdateAllowedOver(ConnectionType type,
ConnectionTethering tethering) const override;
- bool IsAllowedConnectionTypesForUpdateSet() const override;
private:
// Returns (via out_path) the default network path, or empty string if
diff --git a/connection_manager_android.cc b/connection_manager_android.cc
index c594b53..2dd824a 100644
--- a/connection_manager_android.cc
+++ b/connection_manager_android.cc
@@ -34,9 +34,5 @@
ConnectionType type, ConnectionTethering tethering) const {
return true;
}
-bool ConnectionManagerAndroid::IsAllowedConnectionTypesForUpdateSet() const {
- return false;
-}
-
} // namespace chromeos_update_engine
diff --git a/connection_manager_android.h b/connection_manager_android.h
index 006f4ea..0cd5e73 100644
--- a/connection_manager_android.h
+++ b/connection_manager_android.h
@@ -34,7 +34,6 @@
ConnectionTethering* out_tethering) override;
bool IsUpdateAllowedOver(ConnectionType type,
ConnectionTethering tethering) const override;
- bool IsAllowedConnectionTypesForUpdateSet() const override;
DISALLOW_COPY_AND_ASSIGN(ConnectionManagerAndroid);
};
diff --git a/connection_manager_interface.h b/connection_manager_interface.h
index 2faeb80..df8eb4b 100644
--- a/connection_manager_interface.h
+++ b/connection_manager_interface.h
@@ -46,10 +46,6 @@
virtual bool IsUpdateAllowedOver(ConnectionType type,
ConnectionTethering tethering) const = 0;
- // Returns true if the allowed connection types for update is set in the
- // device policy. Otherwise, returns false.
- virtual bool IsAllowedConnectionTypesForUpdateSet() const = 0;
-
protected:
ConnectionManagerInterface() = default;
diff --git a/connection_manager_unittest.cc b/connection_manager_unittest.cc
index 0005b2e..e26a686 100644
--- a/connection_manager_unittest.cc
+++ b/connection_manager_unittest.cc
@@ -276,24 +276,16 @@
ConnectionTethering::kConfirmed));
}
-TEST_F(ConnectionManagerTest, AllowUpdatesOverCellularByDefaultTest) {
- policy::MockDevicePolicy device_policy;
- // Set an empty device policy.
- fake_system_state_.set_device_policy(&device_policy);
-
- EXPECT_TRUE(cmut_.IsUpdateAllowedOver(ConnectionType::kCellular,
- ConnectionTethering::kUnknown));
+TEST_F(ConnectionManagerTest, BlockUpdatesOverCellularByDefaultTest) {
+ EXPECT_FALSE(cmut_.IsUpdateAllowedOver(ConnectionType::kCellular,
+ ConnectionTethering::kUnknown));
}
-TEST_F(ConnectionManagerTest, AllowUpdatesOverTetheredNetworkByDefaultTest) {
- policy::MockDevicePolicy device_policy;
- // Set an empty device policy.
- fake_system_state_.set_device_policy(&device_policy);
-
- EXPECT_TRUE(cmut_.IsUpdateAllowedOver(ConnectionType::kWifi,
- ConnectionTethering::kConfirmed));
- EXPECT_TRUE(cmut_.IsUpdateAllowedOver(ConnectionType::kEthernet,
- ConnectionTethering::kConfirmed));
+TEST_F(ConnectionManagerTest, BlockUpdatesOverTetheredNetworkByDefaultTest) {
+ EXPECT_FALSE(cmut_.IsUpdateAllowedOver(ConnectionType::kWifi,
+ ConnectionTethering::kConfirmed));
+ EXPECT_FALSE(cmut_.IsUpdateAllowedOver(ConnectionType::kEthernet,
+ ConnectionTethering::kConfirmed));
EXPECT_TRUE(cmut_.IsUpdateAllowedOver(ConnectionType::kWifi,
ConnectionTethering::kSuspected));
}
@@ -318,28 +310,62 @@
ConnectionTethering::kUnknown));
}
-TEST_F(ConnectionManagerTest, AllowUpdatesOver3GIfPolicyIsNotSet) {
- policy::MockDevicePolicy device_policy;
+TEST_F(ConnectionManagerTest, BlockUpdatesOver3GIfErrorInPolicyFetchTest) {
+ policy::MockDevicePolicy allow_3g_policy;
- fake_system_state_.set_device_policy(&device_policy);
+ fake_system_state_.set_device_policy(&allow_3g_policy);
+
+ set<string> allowed_set;
+ allowed_set.insert(StringForConnectionType(ConnectionType::kCellular));
// Return false for GetAllowedConnectionTypesForUpdate and see
- // that updates are allowed as device policy is not set. Further
- // check is left to |OmahaRequestAction|.
- EXPECT_CALL(device_policy, GetAllowedConnectionTypesForUpdate(_))
+ // that updates are still blocked for 3G despite the value being in
+ // the string set above.
+ EXPECT_CALL(allow_3g_policy, GetAllowedConnectionTypesForUpdate(_))
.Times(1)
- .WillOnce(Return(false));
+ .WillOnce(DoAll(SetArgPointee<0>(allowed_set), Return(false)));
- EXPECT_TRUE(cmut_.IsUpdateAllowedOver(ConnectionType::kCellular,
- ConnectionTethering::kUnknown));
+ EXPECT_FALSE(cmut_.IsUpdateAllowedOver(ConnectionType::kCellular,
+ ConnectionTethering::kUnknown));
}
-TEST_F(ConnectionManagerTest,
- AllowUpdatesOverCellularIfPolicyFailsToBeLoaded) {
- fake_system_state_.set_device_policy(nullptr);
+TEST_F(ConnectionManagerTest, UseUserPrefForUpdatesOverCellularIfNoPolicyTest) {
+ policy::MockDevicePolicy no_policy;
+ testing::NiceMock<MockPrefs>* prefs = fake_system_state_.mock_prefs();
+ fake_system_state_.set_device_policy(&no_policy);
+
+ // No setting enforced by the device policy, user prefs should be used.
+ EXPECT_CALL(no_policy, GetAllowedConnectionTypesForUpdate(_))
+ .Times(3)
+ .WillRepeatedly(Return(false));
+
+ // No user pref: block.
+ EXPECT_CALL(*prefs, Exists(kPrefsUpdateOverCellularPermission))
+ .Times(1)
+ .WillOnce(Return(false));
+ EXPECT_FALSE(cmut_.IsUpdateAllowedOver(ConnectionType::kCellular,
+ ConnectionTethering::kUnknown));
+
+ // Allow per user pref.
+ EXPECT_CALL(*prefs, Exists(kPrefsUpdateOverCellularPermission))
+ .Times(1)
+ .WillOnce(Return(true));
+ EXPECT_CALL(*prefs, GetBoolean(kPrefsUpdateOverCellularPermission, _))
+ .Times(1)
+ .WillOnce(DoAll(SetArgPointee<1>(true), Return(true)));
EXPECT_TRUE(cmut_.IsUpdateAllowedOver(ConnectionType::kCellular,
ConnectionTethering::kUnknown));
+
+ // Block per user pref.
+ EXPECT_CALL(*prefs, Exists(kPrefsUpdateOverCellularPermission))
+ .Times(1)
+ .WillOnce(Return(true));
+ EXPECT_CALL(*prefs, GetBoolean(kPrefsUpdateOverCellularPermission, _))
+ .Times(1)
+ .WillOnce(DoAll(SetArgPointee<1>(false), Return(true)));
+ EXPECT_FALSE(cmut_.IsUpdateAllowedOver(ConnectionType::kCellular,
+ ConnectionTethering::kUnknown));
}
TEST_F(ConnectionManagerTest, StringForConnectionTypeTest) {
diff --git a/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml b/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
index a20f33f..848f775 100644
--- a/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
+++ b/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
@@ -67,10 +67,6 @@
<method name="SetUpdateOverCellularPermission">
<arg type="b" name="allowed" direction="in" />
</method>
- <method name="SetUpdateOverCellularTarget">
- <arg type="s" name="target_version" direction="in" />
- <arg type="x" name="target_size" direction="in" />
- </method>
<method name="GetUpdateOverCellularPermission">
<arg type="b" name="allowed" direction="out" />
</method>
diff --git a/dbus_service.cc b/dbus_service.cc
index 731d489..3a35c71 100644
--- a/dbus_service.cc
+++ b/dbus_service.cc
@@ -25,6 +25,7 @@
using brillo::ErrorPtr;
using chromeos_update_engine::UpdateEngineService;
using std::string;
+using update_engine::UpdateEngineStatus;
DBusUpdateEngineService::DBusUpdateEngineService(SystemState* system_state)
: common_(new UpdateEngineService{system_state}) {
@@ -123,14 +124,6 @@
return common_->SetUpdateOverCellularPermission(error, in_allowed);
}
-bool DBusUpdateEngineService::SetUpdateOverCellularTarget(
- brillo::ErrorPtr* error,
- const std::string& target_version,
- int64_t target_size) {
- return common_->SetUpdateOverCellularTarget(error, target_version,
- target_size);
-}
-
bool DBusUpdateEngineService::GetUpdateOverCellularPermission(
ErrorPtr* error, bool* out_allowed) {
return common_->GetUpdateOverCellularPermission(error, out_allowed);
@@ -180,14 +173,13 @@
dbus::Bus::REQUIRE_PRIMARY);
}
-void UpdateEngineAdaptor::SendStatusUpdate(int64_t last_checked_time,
- double progress,
- update_engine::UpdateStatus status,
- const string& new_version,
- int64_t new_size) {
- const string str_status = UpdateStatusToString(status);
- SendStatusUpdateSignal(
- last_checked_time, progress, str_status, new_version, new_size);
+void UpdateEngineAdaptor::SendStatusUpdate(
+ const UpdateEngineStatus& update_engine_status) {
+ SendStatusUpdateSignal(update_engine_status.last_checked_time,
+ update_engine_status.progress,
+ UpdateStatusToString(update_engine_status.status),
+ update_engine_status.new_version,
+ update_engine_status.new_size);
}
} // namespace chromeos_update_engine
diff --git a/dbus_service.h b/dbus_service.h
index 644dcee..b754661 100644
--- a/dbus_service.h
+++ b/dbus_service.h
@@ -114,12 +114,6 @@
bool SetUpdateOverCellularPermission(brillo::ErrorPtr* error,
bool in_allowed) override;
- // If there's no device policy installed, sets the update over cellular
- // target. Otherwise, this method returns with an error.
- bool SetUpdateOverCellularTarget(brillo::ErrorPtr* error,
- const std::string& target_version,
- int64_t target_size) override;
-
// Returns the current value of the update over cellular network setting,
// either forced by the device policy if the device is enrolled or the current
// user preference otherwise.
@@ -173,11 +167,8 @@
bool RequestOwnership();
// ServiceObserverInterface overrides.
- void SendStatusUpdate(int64_t last_checked_time,
- double progress,
- update_engine::UpdateStatus status,
- const std::string& new_version,
- int64_t new_size) override;
+ void SendStatusUpdate(
+ const update_engine::UpdateEngineStatus& update_engine_status) override;
void SendPayloadApplicationComplete(ErrorCode error_code) override {}
diff --git a/fake_p2p_manager_configuration.h b/fake_p2p_manager_configuration.h
index 1bc1dc8..c1cf4f2 100644
--- a/fake_p2p_manager_configuration.h
+++ b/fake_p2p_manager_configuration.h
@@ -37,9 +37,7 @@
}
// P2PManager::Configuration override
- base::FilePath GetP2PDir() override {
- return p2p_dir_.path();
- }
+ base::FilePath GetP2PDir() override { return p2p_dir_.GetPath(); }
// P2PManager::Configuration override
std::vector<std::string> GetInitctlArgs(bool is_start) override {
diff --git a/fake_system_state.cc b/fake_system_state.cc
index 9403f8b..1bfcafa 100644
--- a/fake_system_state.cc
+++ b/fake_system_state.cc
@@ -27,7 +27,7 @@
clock_(&fake_clock_),
connection_manager_(&mock_connection_manager_),
hardware_(&fake_hardware_),
- metrics_lib_(&mock_metrics_lib_),
+ metrics_reporter_(&mock_metrics_reporter_),
prefs_(&mock_prefs_),
powerwash_safe_prefs_(&mock_powerwash_safe_prefs_),
payload_state_(&mock_payload_state_),
diff --git a/fake_system_state.h b/fake_system_state.h
index 2225933..67ad3aa 100644
--- a/fake_system_state.h
+++ b/fake_system_state.h
@@ -27,6 +27,7 @@
#include "update_engine/common/fake_hardware.h"
#include "update_engine/common/mock_prefs.h"
#include "update_engine/mock_connection_manager.h"
+#include "update_engine/mock_metrics_reporter.h"
#include "update_engine/mock_omaha_request_params.h"
#include "update_engine/mock_p2p_manager.h"
#include "update_engine/mock_payload_state.h"
@@ -66,8 +67,9 @@
inline HardwareInterface* hardware() override { return hardware_; }
- inline MetricsLibraryInterface* metrics_lib() override {
- return metrics_lib_;
+ inline MetricsReporterInterface* metrics_reporter() override {
+ CHECK(metrics_reporter_ != nullptr);
+ return metrics_reporter_;
}
inline PrefsInterface* prefs() override { return prefs_; }
@@ -122,8 +124,9 @@
hardware_ = hardware ? hardware : &fake_hardware_;
}
- inline void set_metrics_lib(MetricsLibraryInterface* metrics_lib) {
- metrics_lib_ = metrics_lib ? metrics_lib : &mock_metrics_lib_;
+ inline void set_metrics_reporter(MetricsReporterInterface* metrics_reporter) {
+ metrics_reporter_ =
+ metrics_reporter ? metrics_reporter : &mock_metrics_reporter_;
}
inline void set_prefs(PrefsInterface* prefs) {
@@ -187,9 +190,9 @@
return &fake_hardware_;
}
- inline testing::NiceMock<MetricsLibraryMock>* mock_metrics_lib() {
- CHECK(metrics_lib_ == &mock_metrics_lib_);
- return &mock_metrics_lib_;
+ inline testing::NiceMock<MockMetricsReporter>* mock_metrics_reporter() {
+ CHECK(metrics_reporter_ == &mock_metrics_reporter_);
+ return &mock_metrics_reporter_;
}
inline testing::NiceMock<MockPrefs> *mock_prefs() {
@@ -233,7 +236,7 @@
FakeClock fake_clock_;
testing::NiceMock<MockConnectionManager> mock_connection_manager_;
FakeHardware fake_hardware_;
- testing::NiceMock<MetricsLibraryMock> mock_metrics_lib_;
+ testing::NiceMock<MockMetricsReporter> mock_metrics_reporter_;
testing::NiceMock<MockPrefs> mock_prefs_;
testing::NiceMock<MockPrefs> mock_powerwash_safe_prefs_;
testing::NiceMock<MockPayloadState> mock_payload_state_;
@@ -249,7 +252,7 @@
ClockInterface* clock_;
ConnectionManagerInterface* connection_manager_;
HardwareInterface* hardware_;
- MetricsLibraryInterface* metrics_lib_;
+ MetricsReporterInterface* metrics_reporter_;
PrefsInterface* prefs_;
PrefsInterface* powerwash_safe_prefs_;
PayloadStateInterface* payload_state_;
diff --git a/hardware_android.cc b/hardware_android.cc
index c388b82..947b13a 100644
--- a/hardware_android.cc
+++ b/hardware_android.cc
@@ -25,15 +25,18 @@
#include <bootloader.h>
+#include <android-base/properties.h>
#include <base/files/file_util.h>
#include <base/strings/stringprintf.h>
-#include <cutils/properties.h>
#include "update_engine/common/hardware.h"
#include "update_engine/common/platform_constants.h"
#include "update_engine/common/utils.h"
#include "update_engine/utils_android.h"
+using android::base::GetBoolProperty;
+using android::base::GetIntProperty;
+using android::base::GetProperty;
using std::string;
namespace chromeos_update_engine {
@@ -54,6 +57,7 @@
const char kPropProductManufacturer[] = "ro.product.manufacturer";
const char kPropBootHardwareSKU[] = "ro.boot.hardware.sku";
const char kPropBootRevision[] = "ro.boot.revision";
+const char kPropBuildDateUTC[] = "ro.build.date.utc";
// Write a recovery command line |message| to the BCB. The arguments to recovery
// must be separated by '\n'. An empty string will erase the BCB.
@@ -71,8 +75,7 @@
std::min(message.size(), sizeof(boot.recovery) - 1));
}
- int fd =
- HANDLE_EINTR(open(misc_device.value().c_str(), O_WRONLY | O_SYNC, 0600));
+ int fd = HANDLE_EINTR(open(misc_device.value().c_str(), O_WRONLY | O_SYNC));
if (fd < 0) {
PLOG(ERROR) << "Opening misc";
return false;
@@ -121,7 +124,7 @@
//
// In case of a non-bool value, we take the most restrictive option and
// assume we are in an official-build.
- return property_get_bool("ro.secure", 1) != 0;
+ return GetBoolProperty("ro.secure", true);
}
bool HardwareAndroid::IsNormalBootMode() const {
@@ -129,7 +132,7 @@
// update_engine will allow extra developers options, such as providing a
// different update URL. In case of error, we assume the build is in
// normal-mode.
- return property_get_bool("ro.debuggable", 0) != 1;
+ return !GetBoolProperty("ro.debuggable", false);
}
bool HardwareAndroid::AreDevFeaturesEnabled() const {
@@ -149,26 +152,19 @@
}
string HardwareAndroid::GetHardwareClass() const {
- char manufacturer[PROPERTY_VALUE_MAX];
- char sku[PROPERTY_VALUE_MAX];
- char revision[PROPERTY_VALUE_MAX];
- property_get(kPropBootHardwareSKU, sku, "");
- property_get(kPropProductManufacturer, manufacturer, "");
- property_get(kPropBootRevision, revision, "");
+ auto manufacturer = GetProperty(kPropProductManufacturer, "");
+ auto sku = GetProperty(kPropBootHardwareSKU, "");
+ auto revision = GetProperty(kPropBootRevision, "");
- return base::StringPrintf("%s:%s:%s", manufacturer, sku, revision);
+ return manufacturer + ":" + sku + ":" + revision;
}
string HardwareAndroid::GetFirmwareVersion() const {
- char bootloader[PROPERTY_VALUE_MAX];
- property_get(kPropBootBootloader, bootloader, "");
- return bootloader;
+ return GetProperty(kPropBootBootloader, "");
}
string HardwareAndroid::GetECVersion() const {
- char baseband[PROPERTY_VALUE_MAX];
- property_get(kPropBootBaseband, baseband, "");
- return baseband;
+ return GetProperty(kPropBootBaseband, "");
}
int HardwareAndroid::GetPowerwashCount() const {
@@ -200,12 +196,16 @@
return false;
}
+int64_t HardwareAndroid::GetBuildTimestamp() const {
+ return GetIntProperty<int64_t>(kPropBuildDateUTC, 0);
+}
+
bool HardwareAndroid::GetFirstActiveOmahaPingSent() const {
LOG(WARNING) << "STUB: Assuming first active omaha was never set.";
return false;
}
-void HardwareChromeOS::SetFirstActiveOmahaPingSent() {
+void HardwareAndroid::SetFirstActiveOmahaPingSent() {
LOG(WARNING) << "STUB: Assuming first active omaha is never set.";
return;
}
diff --git a/hardware_android.h b/hardware_android.h
index 37393ce..ca90b62 100644
--- a/hardware_android.h
+++ b/hardware_android.h
@@ -47,6 +47,7 @@
bool CancelPowerwash() override;
bool GetNonVolatileDirectory(base::FilePath* path) const override;
bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
+ int64_t GetBuildTimestamp() const override;
bool GetFirstActiveOmahaPingSent() const override;
void SetFirstActiveOmahaPingSent() override;
diff --git a/hardware_chromeos.cc b/hardware_chromeos.cc
index 8c19aa7..f2bb28a 100644
--- a/hardware_chromeos.cc
+++ b/hardware_chromeos.cc
@@ -231,6 +231,11 @@
return true;
}
+int64_t HardwareChromeOS::GetBuildTimestamp() const {
+ // TODO(senj): implement this in Chrome OS.
+ return 0;
+}
+
void HardwareChromeOS::LoadConfig(const string& root_prefix, bool normal_mode) {
brillo::KeyValueStore store;
diff --git a/hardware_chromeos.h b/hardware_chromeos.h
index 3a0bba2..0cf1214 100644
--- a/hardware_chromeos.h
+++ b/hardware_chromeos.h
@@ -51,6 +51,7 @@
bool CancelPowerwash() override;
bool GetNonVolatileDirectory(base::FilePath* path) const override;
bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
+ int64_t GetBuildTimestamp() const override;
bool GetFirstActiveOmahaPingSent() const override;
void SetFirstActiveOmahaPingSent() override;
diff --git a/hardware_chromeos_unittest.cc b/hardware_chromeos_unittest.cc
index a6bad54..162dec4 100644
--- a/hardware_chromeos_unittest.cc
+++ b/hardware_chromeos_unittest.cc
@@ -37,21 +37,22 @@
void SetUp() override { ASSERT_TRUE(root_dir_.CreateUniqueTempDir()); }
void WriteStatefulConfig(const string& config) {
- base::FilePath kFile(root_dir_.path().value() + kStatefulPartition +
+ base::FilePath kFile(root_dir_.GetPath().value() + kStatefulPartition +
"/etc/update_manager.conf");
ASSERT_TRUE(base::CreateDirectory(kFile.DirName()));
ASSERT_TRUE(WriteFileString(kFile.value(), config));
}
void WriteRootfsConfig(const string& config) {
- base::FilePath kFile(root_dir_.path().value() + "/etc/update_manager.conf");
+ base::FilePath kFile(root_dir_.GetPath().value() +
+ "/etc/update_manager.conf");
ASSERT_TRUE(base::CreateDirectory(kFile.DirName()));
ASSERT_TRUE(WriteFileString(kFile.value(), config));
}
// Helper method to call HardwareChromeOS::LoadConfig with the test directory.
void CallLoadConfig(bool normal_mode) {
- hardware_.LoadConfig(root_dir_.path().value(), normal_mode);
+ hardware_.LoadConfig(root_dir_.GetPath().value(), normal_mode);
}
HardwareChromeOS hardware_;
diff --git a/image_properties.h b/image_properties.h
index e03b8dc..f8fe095 100644
--- a/image_properties.h
+++ b/image_properties.h
@@ -33,14 +33,25 @@
std::string product_id;
// The canary-channel product id.
std::string canary_product_id;
+ // The system id for the Android Things SoM, empty for Chrome OS.
+ std::string system_id;
// The product version of this image.
std::string version;
+ // The system version of this image.
+ std::string system_version;
+
+ // The version of all product components in key values pairs.
+ std::string product_components;
// A unique string that identifies this build. Normally a combination of the
// the version, signing keys and build target.
std::string build_fingerprint;
+ // The Android build type, should be either 'user', 'userdebug' or 'eng'.
+ // It's empty string on other platform.
+ std::string build_type;
+
// The board name this image was built for.
std::string board;
diff --git a/image_properties_android.cc b/image_properties_android.cc
index 765b4ba..1ec425d 100644
--- a/image_properties_android.cc
+++ b/image_properties_android.cc
@@ -18,25 +18,35 @@
#include <string>
+#include <android-base/properties.h>
#include <base/logging.h>
#include <brillo/osrelease_reader.h>
-#include <cutils/properties.h>
+#include <brillo/strings/string_utils.h>
#include "update_engine/common/boot_control_interface.h"
#include "update_engine/common/constants.h"
#include "update_engine/common/platform_constants.h"
#include "update_engine/common/prefs_interface.h"
+#include "update_engine/common/utils.h"
#include "update_engine/system_state.h"
+using android::base::GetProperty;
+using std::string;
+
namespace chromeos_update_engine {
namespace {
-// Build time properties name used in Brillo.
+// Build time properties name used in Android Things.
const char kProductId[] = "product_id";
const char kProductVersion[] = "product_version";
+const char kSystemId[] = "system_id";
const char kSystemVersion[] = "system_version";
+// The path to the product_components file which stores the version of each
+// components in OEM partition.
+const char kProductComponentsPath[] = "/oem/os-release.d/product_components";
+
// Prefs used to store the target channel and powerwash settings.
const char kPrefsImgPropChannelName[] = "img-prop-channel-name";
const char kPrefsImgPropPowerwashAllowed[] = "img-prop-powerwash-allowed";
@@ -44,11 +54,15 @@
// System properties that identifies the "board".
const char kPropProductName[] = "ro.product.name";
const char kPropBuildFingerprint[] = "ro.build.fingerprint";
+const char kPropBuildType[] = "ro.build.type";
-std::string GetStringWithDefault(const brillo::OsReleaseReader& osrelease,
- const std::string& key,
- const std::string& default_value) {
- std::string result;
+// A prefix added to the path, used for testing.
+const char* root_prefix = nullptr;
+
+string GetStringWithDefault(const brillo::OsReleaseReader& osrelease,
+ const string& key,
+ const string& default_value) {
+ string result;
if (osrelease.GetString(key, &result))
return result;
LOG(INFO) << "Cannot load ImageProperty " << key << ", using default value "
@@ -59,38 +73,50 @@
} // namespace
namespace test {
-void SetImagePropertiesRootPrefix(const char* /* test_root_prefix */) {}
+void SetImagePropertiesRootPrefix(const char* test_root_prefix) {
+ root_prefix = test_root_prefix;
+}
} // namespace test
ImageProperties LoadImageProperties(SystemState* system_state) {
ImageProperties result;
brillo::OsReleaseReader osrelease;
- osrelease.Load();
- result.product_id = GetStringWithDefault(
- osrelease, kProductId, "developer-boards:brillo-starter-board");
+ if (root_prefix)
+ osrelease.LoadTestingOnly(base::FilePath(root_prefix));
+ else
+ osrelease.Load();
+ result.product_id =
+ GetStringWithDefault(osrelease, kProductId, "invalid-product");
+ result.system_id = GetStringWithDefault(
+ osrelease, kSystemId, "developer-boards:brillo-starter-board");
+ // Update the system id to match the prefix of product id for testing.
+ string prefix, not_used, system_id;
+ if (brillo::string_utils::SplitAtFirst(
+ result.product_id, ":", &prefix, ¬_used, false) &&
+ brillo::string_utils::SplitAtFirst(
+ result.system_id, ":", ¬_used, &system_id, false)) {
+ result.system_id = prefix + ":" + system_id;
+ }
result.canary_product_id = result.product_id;
- std::string system_version =
- GetStringWithDefault(osrelease, kSystemVersion, "0.0.0");
- std::string product_version =
- GetStringWithDefault(osrelease, kProductVersion, "0");
- result.version = system_version + "." + product_version;
+ result.version = GetStringWithDefault(osrelease, kProductVersion, "0.0.0.0");
+ result.system_version =
+ GetStringWithDefault(osrelease, kSystemVersion, "0.0.0.0");
+ // Can't read it with OsReleaseReader because it has multiple lines.
+ utils::ReadFile(kProductComponentsPath, &result.product_components);
- char prop[PROPERTY_VALUE_MAX];
- property_get(kPropProductName, prop, "brillo");
- result.board = prop;
-
- property_get(kPropBuildFingerprint, prop, "none");
- result.build_fingerprint = prop;
+ result.board = GetProperty(kPropProductName, "brillo");
+ result.build_fingerprint = GetProperty(kPropBuildFingerprint, "none");
+ result.build_type = GetProperty(kPropBuildType, "");
// Brillo images don't have a channel assigned. We stored the name of the
// channel where we got the image from in prefs at the time of the update, so
// we use that as the current channel if available. During provisioning, there
// is no value assigned, so we default to the "stable-channel".
- std::string current_channel_key =
+ string current_channel_key =
kPrefsChannelOnSlotPrefix +
std::to_string(system_state->boot_control()->GetCurrentSlot());
- std::string current_channel;
+ string current_channel;
if (!system_state->prefs()->Exists(current_channel_key) ||
!system_state->prefs()->GetString(current_channel_key, ¤t_channel))
current_channel = "stable-channel";
diff --git a/image_properties_android_unittest.cc b/image_properties_android_unittest.cc
new file mode 100644
index 0000000..7327554
--- /dev/null
+++ b/image_properties_android_unittest.cc
@@ -0,0 +1,90 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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 "update_engine/image_properties.h"
+
+#include <string>
+
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/constants.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/fake_system_state.h"
+
+using chromeos_update_engine::test_utils::WriteFileString;
+using std::string;
+
+namespace chromeos_update_engine {
+
+class ImagePropertiesTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ // Create a uniquely named test directory.
+ ASSERT_TRUE(tempdir_.CreateUniqueTempDir());
+ osrelease_dir_ = tempdir_.GetPath().Append("etc/os-release.d");
+ EXPECT_TRUE(base::CreateDirectory(osrelease_dir_));
+ test::SetImagePropertiesRootPrefix(tempdir_.GetPath().value().c_str());
+ }
+
+ void WriteOsRelease(const string& key, const string& value) {
+ ASSERT_TRUE(WriteFileString(osrelease_dir_.Append(key).value(), value));
+ }
+
+ FakeSystemState fake_system_state_;
+
+ base::ScopedTempDir tempdir_;
+ base::FilePath osrelease_dir_;
+};
+
+TEST_F(ImagePropertiesTest, SimpleTest) {
+ WriteOsRelease("product_id", "abc");
+ WriteOsRelease("system_id", "def");
+ WriteOsRelease("product_version", "1.2.3.4");
+ WriteOsRelease("system_version", "5.6.7.8");
+ ImageProperties props = LoadImageProperties(&fake_system_state_);
+ EXPECT_EQ("abc", props.product_id);
+ EXPECT_EQ("def", props.system_id);
+ EXPECT_EQ("1.2.3.4", props.version);
+ EXPECT_EQ("5.6.7.8", props.system_version);
+ EXPECT_EQ("stable-channel", props.current_channel);
+ EXPECT_EQ(constants::kOmahaDefaultProductionURL, props.omaha_url);
+}
+
+TEST_F(ImagePropertiesTest, IDPrefixTest) {
+ WriteOsRelease("product_id", "abc:def");
+ WriteOsRelease("system_id", "foo:bar");
+ ImageProperties props = LoadImageProperties(&fake_system_state_);
+ EXPECT_EQ("abc:def", props.product_id);
+ EXPECT_EQ("abc:bar", props.system_id);
+}
+
+TEST_F(ImagePropertiesTest, IDInvalidPrefixTest) {
+ WriteOsRelease("product_id", "def");
+ WriteOsRelease("system_id", "foo:bar");
+ ImageProperties props = LoadImageProperties(&fake_system_state_);
+ EXPECT_EQ("def", props.product_id);
+ EXPECT_EQ("foo:bar", props.system_id);
+
+ WriteOsRelease("product_id", "abc:def");
+ WriteOsRelease("system_id", "bar");
+ props = LoadImageProperties(&fake_system_state_);
+ EXPECT_EQ("abc:def", props.product_id);
+ EXPECT_EQ("bar", props.system_id);
+}
+
+} // namespace chromeos_update_engine
diff --git a/image_properties_chromeos_unittest.cc b/image_properties_chromeos_unittest.cc
index 12c2039..d9ed688 100644
--- a/image_properties_chromeos_unittest.cc
+++ b/image_properties_chromeos_unittest.cc
@@ -36,10 +36,10 @@
void SetUp() override {
// Create a uniquely named test directory.
ASSERT_TRUE(tempdir_.CreateUniqueTempDir());
- EXPECT_TRUE(base::CreateDirectory(tempdir_.path().Append("etc")));
- EXPECT_TRUE(base::CreateDirectory(
- base::FilePath(tempdir_.path().value() + kStatefulPartition + "/etc")));
- test::SetImagePropertiesRootPrefix(tempdir_.path().value().c_str());
+ EXPECT_TRUE(base::CreateDirectory(tempdir_.GetPath().Append("etc")));
+ EXPECT_TRUE(base::CreateDirectory(base::FilePath(
+ tempdir_.GetPath().value() + kStatefulPartition + "/etc")));
+ test::SetImagePropertiesRootPrefix(tempdir_.GetPath().value().c_str());
SetLockDown(false);
}
@@ -54,12 +54,13 @@
};
TEST_F(ImagePropertiesTest, SimpleTest) {
- ASSERT_TRUE(WriteFileString(tempdir_.path().Append("etc/lsb-release").value(),
- "CHROMEOS_RELEASE_BOARD=arm-generic\n"
- "CHROMEOS_RELEASE_FOO=bar\n"
- "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
- "CHROMEOS_RELEASE_TRACK=dev-channel\n"
- "CHROMEOS_AUSERVER=http://www.google.com"));
+ ASSERT_TRUE(
+ WriteFileString(tempdir_.GetPath().Append("etc/lsb-release").value(),
+ "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+ "CHROMEOS_RELEASE_FOO=bar\n"
+ "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+ "CHROMEOS_RELEASE_TRACK=dev-channel\n"
+ "CHROMEOS_AUSERVER=http://www.google.com"));
ImageProperties props = LoadImageProperties(&fake_system_state_);
EXPECT_EQ("arm-generic", props.board);
EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", props.product_id);
@@ -70,7 +71,7 @@
TEST_F(ImagePropertiesTest, AppIDTest) {
ASSERT_TRUE(WriteFileString(
- tempdir_.path().Append("etc/lsb-release").value(),
+ tempdir_.GetPath().Append("etc/lsb-release").value(),
"CHROMEOS_RELEASE_APPID={58c35cef-9d30-476e-9098-ce20377d535d}"));
ImageProperties props = LoadImageProperties(&fake_system_state_);
EXPECT_EQ("{58c35cef-9d30-476e-9098-ce20377d535d}", props.product_id);
@@ -78,7 +79,7 @@
TEST_F(ImagePropertiesTest, ConfusingReleaseTest) {
ASSERT_TRUE(
- WriteFileString(tempdir_.path().Append("etc/lsb-release").value(),
+ WriteFileString(tempdir_.GetPath().Append("etc/lsb-release").value(),
"CHROMEOS_RELEASE_FOO=CHROMEOS_RELEASE_VERSION=1.2.3.4\n"
"CHROMEOS_RELEASE_VERSION=0.2.2.3"));
ImageProperties props = LoadImageProperties(&fake_system_state_);
@@ -91,13 +92,14 @@
}
TEST_F(ImagePropertiesTest, OverrideTest) {
- ASSERT_TRUE(WriteFileString(tempdir_.path().Append("etc/lsb-release").value(),
- "CHROMEOS_RELEASE_BOARD=arm-generic\n"
- "CHROMEOS_RELEASE_FOO=bar\n"
- "CHROMEOS_RELEASE_TRACK=dev-channel\n"
- "CHROMEOS_AUSERVER=http://www.google.com"));
+ ASSERT_TRUE(
+ WriteFileString(tempdir_.GetPath().Append("etc/lsb-release").value(),
+ "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+ "CHROMEOS_RELEASE_FOO=bar\n"
+ "CHROMEOS_RELEASE_TRACK=dev-channel\n"
+ "CHROMEOS_AUSERVER=http://www.google.com"));
ASSERT_TRUE(WriteFileString(
- tempdir_.path().value() + kStatefulPartition + "/etc/lsb-release",
+ tempdir_.GetPath().value() + kStatefulPartition + "/etc/lsb-release",
"CHROMEOS_RELEASE_BOARD=x86-generic\n"
"CHROMEOS_RELEASE_TRACK=beta-channel\n"
"CHROMEOS_AUSERVER=https://www.google.com"));
@@ -111,13 +113,14 @@
}
TEST_F(ImagePropertiesTest, OverrideLockDownTest) {
- ASSERT_TRUE(WriteFileString(tempdir_.path().Append("etc/lsb-release").value(),
- "CHROMEOS_RELEASE_BOARD=arm-generic\n"
- "CHROMEOS_RELEASE_FOO=bar\n"
- "CHROMEOS_RELEASE_TRACK=dev-channel\n"
- "CHROMEOS_AUSERVER=https://www.google.com"));
+ ASSERT_TRUE(
+ WriteFileString(tempdir_.GetPath().Append("etc/lsb-release").value(),
+ "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+ "CHROMEOS_RELEASE_FOO=bar\n"
+ "CHROMEOS_RELEASE_TRACK=dev-channel\n"
+ "CHROMEOS_AUSERVER=https://www.google.com"));
ASSERT_TRUE(WriteFileString(
- tempdir_.path().value() + kStatefulPartition + "/etc/lsb-release",
+ tempdir_.GetPath().value() + kStatefulPartition + "/etc/lsb-release",
"CHROMEOS_RELEASE_BOARD=x86-generic\n"
"CHROMEOS_RELEASE_TRACK=stable-channel\n"
"CHROMEOS_AUSERVER=http://www.google.com"));
@@ -132,32 +135,35 @@
}
TEST_F(ImagePropertiesTest, BoardAppIdUsedForNonCanaryChannelTest) {
- ASSERT_TRUE(WriteFileString(tempdir_.path().Append("etc/lsb-release").value(),
- "CHROMEOS_RELEASE_APPID=r\n"
- "CHROMEOS_BOARD_APPID=b\n"
- "CHROMEOS_CANARY_APPID=c\n"
- "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
+ ASSERT_TRUE(
+ WriteFileString(tempdir_.GetPath().Append("etc/lsb-release").value(),
+ "CHROMEOS_RELEASE_APPID=r\n"
+ "CHROMEOS_BOARD_APPID=b\n"
+ "CHROMEOS_CANARY_APPID=c\n"
+ "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
ImageProperties props = LoadImageProperties(&fake_system_state_);
EXPECT_EQ("stable-channel", props.current_channel);
EXPECT_EQ("b", props.product_id);
}
TEST_F(ImagePropertiesTest, CanaryAppIdUsedForCanaryChannelTest) {
- ASSERT_TRUE(WriteFileString(tempdir_.path().Append("etc/lsb-release").value(),
- "CHROMEOS_RELEASE_APPID=r\n"
- "CHROMEOS_BOARD_APPID=b\n"
- "CHROMEOS_CANARY_APPID=c\n"
- "CHROMEOS_RELEASE_TRACK=canary-channel\n"));
+ ASSERT_TRUE(
+ WriteFileString(tempdir_.GetPath().Append("etc/lsb-release").value(),
+ "CHROMEOS_RELEASE_APPID=r\n"
+ "CHROMEOS_BOARD_APPID=b\n"
+ "CHROMEOS_CANARY_APPID=c\n"
+ "CHROMEOS_RELEASE_TRACK=canary-channel\n"));
ImageProperties props = LoadImageProperties(&fake_system_state_);
EXPECT_EQ("canary-channel", props.current_channel);
EXPECT_EQ("c", props.canary_product_id);
}
TEST_F(ImagePropertiesTest, ReleaseAppIdUsedAsDefaultTest) {
- ASSERT_TRUE(WriteFileString(tempdir_.path().Append("etc/lsb-release").value(),
- "CHROMEOS_RELEASE_APPID=r\n"
- "CHROMEOS_CANARY_APPID=c\n"
- "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
+ ASSERT_TRUE(
+ WriteFileString(tempdir_.GetPath().Append("etc/lsb-release").value(),
+ "CHROMEOS_RELEASE_APPID=r\n"
+ "CHROMEOS_CANARY_APPID=c\n"
+ "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
ImageProperties props = LoadImageProperties(&fake_system_state_);
EXPECT_EQ("stable-channel", props.current_channel);
EXPECT_EQ("r", props.product_id);
diff --git a/main.cc b/main.cc
index 4275bc1..0b96307 100644
--- a/main.cc
+++ b/main.cc
@@ -38,6 +38,7 @@
namespace chromeos_update_engine {
namespace {
+#ifndef __ANDROID__
void SetupLogSymlink(const string& symlink_path, const string& log_path) {
// TODO(petkov): To ensure a smooth transition between non-timestamped and
// timestamped logs, move an existing log to start the first timestamped
@@ -75,30 +76,41 @@
SetupLogSymlink(kLogSymlink, kLogPath);
return kLogSymlink;
}
+#endif // __ANDROID__
-void SetupLogging(bool log_to_std_err) {
- string log_file;
+void SetupLogging(bool log_to_system, bool log_to_file) {
logging::LoggingSettings log_settings;
log_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
- log_settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
+ log_settings.logging_dest = static_cast<logging::LoggingDestination>(
+ (log_to_system ? logging::LOG_TO_SYSTEM_DEBUG_LOG : 0) |
+ (log_to_file ? logging::LOG_TO_FILE : 0));
+ log_settings.log_file = nullptr;
- if (log_to_std_err) {
- // Log to stderr initially.
- log_settings.log_file = nullptr;
- log_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
- } else {
+ string log_file;
+ if (log_to_file) {
+#ifdef __ANDROID__
+ log_file = "/data/misc/update_engine_log/update_engine.log";
+ log_settings.delete_old = logging::DELETE_OLD_LOG_FILE;
+#else
log_file = SetupLogFile("/var/log");
+ log_settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
+#endif // __ANDROID__
log_settings.log_file = log_file.c_str();
- log_settings.logging_dest = logging::LOG_TO_FILE;
}
-
logging::InitLogging(log_settings);
+
+#ifdef __ANDROID__
+ // The log file will have AID_LOG as group ID; this GID is inherited from the
+ // parent directory "/data/misc/update_engine_log" which sets the SGID bit.
+ chmod(log_file.c_str(), 0640);
+#endif
}
} // namespace
} // namespace chromeos_update_engine
int main(int argc, char** argv) {
+ DEFINE_bool(logtofile, false, "Write logs to a file in log_dir.");
DEFINE_bool(logtostderr, false,
"Write logs to stderr instead of to a file in log_dir.");
DEFINE_bool(foreground, false,
@@ -106,7 +118,15 @@
chromeos_update_engine::Terminator::Init();
brillo::FlagHelper::Init(argc, argv, "Chromium OS Update Engine");
- chromeos_update_engine::SetupLogging(FLAGS_logtostderr);
+
+ // We have two logging flags "--logtostderr" and "--logtofile"; and the logic
+ // to choose the logging destination is:
+ // 1. --logtostderr --logtofile -> logs to both
+ // 2. --logtostderr -> logs to system debug
+ // 3. --logtofile or no flags -> logs to file
+ bool log_to_system = FLAGS_logtostderr;
+ bool log_to_file = FLAGS_logtofile || !FLAGS_logtostderr;
+ chromeos_update_engine::SetupLogging(log_to_system, log_to_file);
if (!FLAGS_foreground)
PLOG_IF(FATAL, daemon(0, 0) == 1) << "daemon() failed";
diff --git a/metrics.cc b/metrics.cc
deleted file mode 100644
index 742ba7e..0000000
--- a/metrics.cc
+++ /dev/null
@@ -1,526 +0,0 @@
-//
-// Copyright (C) 2014 The Android Open Source Project
-//
-// 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
-//
-// http://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 "update_engine/metrics.h"
-
-#include <string>
-
-#include <base/logging.h>
-#include <metrics/metrics_library.h>
-
-#include "update_engine/common/clock_interface.h"
-#include "update_engine/common/constants.h"
-#include "update_engine/common/prefs_interface.h"
-#include "update_engine/common/utils.h"
-#include "update_engine/metrics_utils.h"
-#include "update_engine/system_state.h"
-
-using std::string;
-
-namespace chromeos_update_engine {
-
-namespace metrics {
-
-// UpdateEngine.Daily.* metrics.
-const char kMetricDailyOSAgeDays[] = "UpdateEngine.Daily.OSAgeDays";
-
-// UpdateEngine.Check.* metrics.
-const char kMetricCheckDownloadErrorCode[] =
- "UpdateEngine.Check.DownloadErrorCode";
-const char kMetricCheckReaction[] = "UpdateEngine.Check.Reaction";
-const char kMetricCheckResult[] = "UpdateEngine.Check.Result";
-const char kMetricCheckTimeSinceLastCheckMinutes[] =
- "UpdateEngine.Check.TimeSinceLastCheckMinutes";
-const char kMetricCheckTimeSinceLastCheckUptimeMinutes[] =
- "UpdateEngine.Check.TimeSinceLastCheckUptimeMinutes";
-
-// UpdateEngine.Attempt.* metrics.
-const char kMetricAttemptNumber[] = "UpdateEngine.Attempt.Number";
-const char kMetricAttemptPayloadType[] =
- "UpdateEngine.Attempt.PayloadType";
-const char kMetricAttemptPayloadSizeMiB[] =
- "UpdateEngine.Attempt.PayloadSizeMiB";
-const char kMetricAttemptConnectionType[] =
- "UpdateEngine.Attempt.ConnectionType";
-const char kMetricAttemptDurationMinutes[] =
- "UpdateEngine.Attempt.DurationMinutes";
-const char kMetricAttemptDurationUptimeMinutes[] =
- "UpdateEngine.Attempt.DurationUptimeMinutes";
-const char kMetricAttemptTimeSinceLastAttemptMinutes[] =
- "UpdateEngine.Attempt.TimeSinceLastAttemptMinutes";
-const char kMetricAttemptTimeSinceLastAttemptUptimeMinutes[] =
- "UpdateEngine.Attempt.TimeSinceLastAttemptUptimeMinutes";
-const char kMetricAttemptPayloadBytesDownloadedMiB[] =
- "UpdateEngine.Attempt.PayloadBytesDownloadedMiB";
-const char kMetricAttemptPayloadDownloadSpeedKBps[] =
- "UpdateEngine.Attempt.PayloadDownloadSpeedKBps";
-const char kMetricAttemptDownloadSource[] =
- "UpdateEngine.Attempt.DownloadSource";
-const char kMetricAttemptResult[] =
- "UpdateEngine.Attempt.Result";
-const char kMetricAttemptInternalErrorCode[] =
- "UpdateEngine.Attempt.InternalErrorCode";
-const char kMetricAttemptDownloadErrorCode[] =
- "UpdateEngine.Attempt.DownloadErrorCode";
-
-// UpdateEngine.SuccessfulUpdate.* metrics.
-const char kMetricSuccessfulUpdateAttemptCount[] =
- "UpdateEngine.SuccessfulUpdate.AttemptCount";
-const char kMetricSuccessfulUpdateBytesDownloadedMiB[] =
- "UpdateEngine.SuccessfulUpdate.BytesDownloadedMiB";
-const char kMetricSuccessfulUpdateDownloadOverheadPercentage[] =
- "UpdateEngine.SuccessfulUpdate.DownloadOverheadPercentage";
-const char kMetricSuccessfulUpdateDownloadSourcesUsed[] =
- "UpdateEngine.SuccessfulUpdate.DownloadSourcesUsed";
-const char kMetricSuccessfulUpdatePayloadType[] =
- "UpdateEngine.SuccessfulUpdate.PayloadType";
-const char kMetricSuccessfulUpdatePayloadSizeMiB[] =
- "UpdateEngine.SuccessfulUpdate.PayloadSizeMiB";
-const char kMetricSuccessfulUpdateRebootCount[] =
- "UpdateEngine.SuccessfulUpdate.RebootCount";
-const char kMetricSuccessfulUpdateTotalDurationMinutes[] =
- "UpdateEngine.SuccessfulUpdate.TotalDurationMinutes";
-const char kMetricSuccessfulUpdateUpdatesAbandonedCount[] =
- "UpdateEngine.SuccessfulUpdate.UpdatesAbandonedCount";
-const char kMetricSuccessfulUpdateUrlSwitchCount[] =
- "UpdateEngine.SuccessfulUpdate.UrlSwitchCount";
-
-// UpdateEngine.Rollback.* metric.
-const char kMetricRollbackResult[] = "UpdateEngine.Rollback.Result";
-
-// UpdateEngine.CertificateCheck.* metrics.
-const char kMetricCertificateCheckUpdateCheck[] =
- "UpdateEngine.CertificateCheck.UpdateCheck";
-const char kMetricCertificateCheckDownload[] =
- "UpdateEngine.CertificateCheck.Download";
-
-// UpdateEngine.* metrics.
-const char kMetricFailedUpdateCount[] = "UpdateEngine.FailedUpdateCount";
-const char kMetricInstallDateProvisioningSource[] =
- "UpdateEngine.InstallDateProvisioningSource";
-const char kMetricTimeToRebootMinutes[] =
- "UpdateEngine.TimeToRebootMinutes";
-
-void ReportDailyMetrics(SystemState *system_state,
- base::TimeDelta os_age) {
- string metric = metrics::kMetricDailyOSAgeDays;
- LOG(INFO) << "Uploading " << utils::FormatTimeDelta(os_age)
- << " for metric " << metric;
- system_state->metrics_lib()->SendToUMA(
- metric,
- static_cast<int>(os_age.InDays()),
- 0, // min: 0 days
- 6*30, // max: 6 months (approx)
- 50); // num_buckets
-}
-
-void ReportUpdateCheckMetrics(SystemState *system_state,
- CheckResult result,
- CheckReaction reaction,
- DownloadErrorCode download_error_code) {
- string metric;
- int value;
- int max_value;
-
- if (result != metrics::CheckResult::kUnset) {
- metric = metrics::kMetricCheckResult;
- value = static_cast<int>(result);
- max_value = static_cast<int>(metrics::CheckResult::kNumConstants) - 1;
- LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)";
- system_state->metrics_lib()->SendEnumToUMA(metric, value, max_value);
- }
- if (reaction != metrics::CheckReaction::kUnset) {
- metric = metrics::kMetricCheckReaction;
- value = static_cast<int>(reaction);
- max_value = static_cast<int>(metrics::CheckReaction::kNumConstants) - 1;
- LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)";
- system_state->metrics_lib()->SendEnumToUMA(metric, value, max_value);
- }
- if (download_error_code != metrics::DownloadErrorCode::kUnset) {
- metric = metrics::kMetricCheckDownloadErrorCode;
- value = static_cast<int>(download_error_code);
- LOG(INFO) << "Sending " << value << " for metric " << metric << " (sparse)";
- system_state->metrics_lib()->SendSparseToUMA(metric, value);
- }
-
- base::TimeDelta time_since_last;
- if (metrics_utils::WallclockDurationHelper(
- system_state,
- kPrefsMetricsCheckLastReportingTime,
- &time_since_last)) {
- metric = kMetricCheckTimeSinceLastCheckMinutes;
- LOG(INFO) << "Sending " << utils::FormatTimeDelta(time_since_last)
- << " for metric " << metric;
- system_state->metrics_lib()->SendToUMA(
- metric,
- time_since_last.InMinutes(),
- 0, // min: 0 min
- 30*24*60, // max: 30 days
- 50); // num_buckets
- }
-
- base::TimeDelta uptime_since_last;
- static int64_t uptime_since_last_storage = 0;
- if (metrics_utils::MonotonicDurationHelper(system_state,
- &uptime_since_last_storage,
- &uptime_since_last)) {
- metric = kMetricCheckTimeSinceLastCheckUptimeMinutes;
- LOG(INFO) << "Sending " << utils::FormatTimeDelta(uptime_since_last)
- << " for metric " << metric;
- system_state->metrics_lib()->SendToUMA(
- metric,
- uptime_since_last.InMinutes(),
- 0, // min: 0 min
- 30*24*60, // max: 30 days
- 50); // num_buckets
- }
-}
-
-void ReportAbnormallyTerminatedUpdateAttemptMetrics(
- SystemState *system_state) {
-
- string metric = metrics::kMetricAttemptResult;
- AttemptResult attempt_result = AttemptResult::kAbnormalTermination;
-
- LOG(INFO) << "Uploading " << static_cast<int>(attempt_result)
- << " for metric " << metric;
- system_state->metrics_lib()->SendEnumToUMA(
- metric,
- static_cast<int>(attempt_result),
- static_cast<int>(AttemptResult::kNumConstants));
-}
-
-void ReportUpdateAttemptMetrics(
- SystemState *system_state,
- int attempt_number,
- PayloadType payload_type,
- base::TimeDelta duration,
- base::TimeDelta duration_uptime,
- int64_t payload_size,
- int64_t payload_bytes_downloaded,
- int64_t payload_download_speed_bps,
- DownloadSource download_source,
- AttemptResult attempt_result,
- ErrorCode internal_error_code,
- DownloadErrorCode payload_download_error_code,
- ConnectionType connection_type) {
- string metric;
-
- metric = metrics::kMetricAttemptNumber;
- LOG(INFO) << "Uploading " << attempt_number << " for metric " << metric;
- system_state->metrics_lib()->SendToUMA(metric,
- attempt_number,
- 0, // min: 0 attempts
- 49, // max: 49 attempts
- 50); // num_buckets
-
- metric = metrics::kMetricAttemptPayloadType;
- LOG(INFO) << "Uploading " << utils::ToString(payload_type)
- << " for metric " << metric;
- system_state->metrics_lib()->SendEnumToUMA(metric,
- payload_type,
- kNumPayloadTypes);
-
- metric = metrics::kMetricAttemptDurationMinutes;
- LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration)
- << " for metric " << metric;
- system_state->metrics_lib()->SendToUMA(metric,
- duration.InMinutes(),
- 0, // min: 0 min
- 10*24*60, // max: 10 days
- 50); // num_buckets
-
- metric = metrics::kMetricAttemptDurationUptimeMinutes;
- LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration_uptime)
- << " for metric " << metric;
- system_state->metrics_lib()->SendToUMA(metric,
- duration_uptime.InMinutes(),
- 0, // min: 0 min
- 10*24*60, // max: 10 days
- 50); // num_buckets
-
- metric = metrics::kMetricAttemptPayloadSizeMiB;
- int64_t payload_size_mib = payload_size / kNumBytesInOneMiB;
- LOG(INFO) << "Uploading " << payload_size_mib << " for metric " << metric;
- system_state->metrics_lib()->SendToUMA(metric,
- payload_size_mib,
- 0, // min: 0 MiB
- 1024, // max: 1024 MiB = 1 GiB
- 50); // num_buckets
-
- metric = metrics::kMetricAttemptPayloadBytesDownloadedMiB;
- int64_t payload_bytes_downloaded_mib =
- payload_bytes_downloaded / kNumBytesInOneMiB;
- LOG(INFO) << "Uploading " << payload_bytes_downloaded_mib
- << " for metric " << metric;
- system_state->metrics_lib()->SendToUMA(metric,
- payload_bytes_downloaded_mib,
- 0, // min: 0 MiB
- 1024, // max: 1024 MiB = 1 GiB
- 50); // num_buckets
-
- metric = metrics::kMetricAttemptPayloadDownloadSpeedKBps;
- int64_t payload_download_speed_kbps = payload_download_speed_bps / 1000;
- LOG(INFO) << "Uploading " << payload_download_speed_kbps
- << " for metric " << metric;
- system_state->metrics_lib()->SendToUMA(metric,
- payload_download_speed_kbps,
- 0, // min: 0 kB/s
- 10*1000, // max: 10000 kB/s = 10 MB/s
- 50); // num_buckets
-
- metric = metrics::kMetricAttemptDownloadSource;
- LOG(INFO) << "Uploading " << download_source
- << " for metric " << metric;
- system_state->metrics_lib()->SendEnumToUMA(metric,
- download_source,
- kNumDownloadSources);
-
- metric = metrics::kMetricAttemptResult;
- LOG(INFO) << "Uploading " << static_cast<int>(attempt_result)
- << " for metric " << metric;
- system_state->metrics_lib()->SendEnumToUMA(
- metric,
- static_cast<int>(attempt_result),
- static_cast<int>(AttemptResult::kNumConstants));
-
- if (internal_error_code != ErrorCode::kSuccess) {
- metric = metrics::kMetricAttemptInternalErrorCode;
- LOG(INFO) << "Uploading " << internal_error_code
- << " for metric " << metric;
- system_state->metrics_lib()->SendEnumToUMA(
- metric,
- static_cast<int>(internal_error_code),
- static_cast<int>(ErrorCode::kUmaReportedMax));
- }
-
- if (payload_download_error_code != DownloadErrorCode::kUnset) {
- metric = metrics::kMetricAttemptDownloadErrorCode;
- LOG(INFO) << "Uploading " << static_cast<int>(payload_download_error_code)
- << " for metric " << metric << " (sparse)";
- system_state->metrics_lib()->SendSparseToUMA(
- metric,
- static_cast<int>(payload_download_error_code));
- }
-
- base::TimeDelta time_since_last;
- if (metrics_utils::WallclockDurationHelper(
- system_state,
- kPrefsMetricsAttemptLastReportingTime,
- &time_since_last)) {
- metric = kMetricAttemptTimeSinceLastAttemptMinutes;
- LOG(INFO) << "Sending " << utils::FormatTimeDelta(time_since_last)
- << " for metric " << metric;
- system_state->metrics_lib()->SendToUMA(
- metric,
- time_since_last.InMinutes(),
- 0, // min: 0 min
- 30*24*60, // max: 30 days
- 50); // num_buckets
- }
-
- static int64_t uptime_since_last_storage = 0;
- base::TimeDelta uptime_since_last;
- if (metrics_utils::MonotonicDurationHelper(system_state,
- &uptime_since_last_storage,
- &uptime_since_last)) {
- metric = kMetricAttemptTimeSinceLastAttemptUptimeMinutes;
- LOG(INFO) << "Sending " << utils::FormatTimeDelta(uptime_since_last)
- << " for metric " << metric;
- system_state->metrics_lib()->SendToUMA(
- metric,
- uptime_since_last.InMinutes(),
- 0, // min: 0 min
- 30*24*60, // max: 30 days
- 50); // num_buckets
- }
-
- metric = metrics::kMetricAttemptConnectionType;
- LOG(INFO) << "Uploading " << static_cast<int>(connection_type)
- << " for metric " << metric;
- system_state->metrics_lib()->SendEnumToUMA(
- metric,
- static_cast<int>(connection_type),
- static_cast<int>(ConnectionType::kNumConstants));
-}
-
-
-void ReportSuccessfulUpdateMetrics(
- SystemState *system_state,
- int attempt_count,
- int updates_abandoned_count,
- PayloadType payload_type,
- int64_t payload_size,
- int64_t num_bytes_downloaded[kNumDownloadSources],
- int download_overhead_percentage,
- base::TimeDelta total_duration,
- int reboot_count,
- int url_switch_count) {
- string metric;
- int64_t mbs;
-
- metric = kMetricSuccessfulUpdatePayloadSizeMiB;
- mbs = payload_size / kNumBytesInOneMiB;
- LOG(INFO) << "Uploading " << mbs << " (MiBs) for metric " << metric;
- system_state->metrics_lib()->SendToUMA(metric,
- mbs,
- 0, // min: 0 MiB
- 1024, // max: 1024 MiB = 1 GiB
- 50); // num_buckets
-
- int64_t total_bytes = 0;
- int download_sources_used = 0;
- for (int i = 0; i < kNumDownloadSources + 1; i++) {
- DownloadSource source = static_cast<DownloadSource>(i);
-
- // Only consider this download source (and send byte counts) as
- // having been used if we downloaded a non-trivial amount of bytes
- // (e.g. at least 1 MiB) that contributed to the
- // update. Otherwise we're going to end up with a lot of zero-byte
- // events in the histogram.
-
- metric = metrics::kMetricSuccessfulUpdateBytesDownloadedMiB;
- if (i < kNumDownloadSources) {
- metric += utils::ToString(source);
- mbs = num_bytes_downloaded[i] / kNumBytesInOneMiB;
- total_bytes += num_bytes_downloaded[i];
- if (mbs > 0)
- download_sources_used |= (1 << i);
- } else {
- mbs = total_bytes / kNumBytesInOneMiB;
- }
-
- if (mbs > 0) {
- LOG(INFO) << "Uploading " << mbs << " (MiBs) for metric " << metric;
- system_state->metrics_lib()->SendToUMA(metric,
- mbs,
- 0, // min: 0 MiB
- 1024, // max: 1024 MiB = 1 GiB
- 50); // num_buckets
- }
- }
-
- metric = metrics::kMetricSuccessfulUpdateDownloadSourcesUsed;
- LOG(INFO) << "Uploading 0x" << std::hex << download_sources_used
- << " (bit flags) for metric " << metric;
- system_state->metrics_lib()->SendToUMA(
- metric,
- download_sources_used,
- 0, // min
- (1 << kNumDownloadSources) - 1, // max
- 1 << kNumDownloadSources); // num_buckets
-
- metric = metrics::kMetricSuccessfulUpdateDownloadOverheadPercentage;
- LOG(INFO) << "Uploading " << download_overhead_percentage
- << "% for metric " << metric;
- system_state->metrics_lib()->SendToUMA(metric,
- download_overhead_percentage,
- 0, // min: 0% overhead
- 1000, // max: 1000% overhead
- 50); // num_buckets
-
- metric = metrics::kMetricSuccessfulUpdateUrlSwitchCount;
- LOG(INFO) << "Uploading " << url_switch_count
- << " (count) for metric " << metric;
- system_state->metrics_lib()->SendToUMA(metric,
- url_switch_count,
- 0, // min: 0 URL switches
- 49, // max: 49 URL switches
- 50); // num_buckets
-
- metric = metrics::kMetricSuccessfulUpdateTotalDurationMinutes;
- LOG(INFO) << "Uploading " << utils::FormatTimeDelta(total_duration)
- << " for metric " << metric;
- system_state->metrics_lib()->SendToUMA(
- metric,
- static_cast<int>(total_duration.InMinutes()),
- 0, // min: 0 min
- 365*24*60, // max: 365 days ~= 1 year
- 50); // num_buckets
-
- metric = metrics::kMetricSuccessfulUpdateRebootCount;
- LOG(INFO) << "Uploading reboot count of " << reboot_count << " for metric "
- << metric;
- system_state->metrics_lib()->SendToUMA(metric,
- reboot_count,
- 0, // min: 0 reboots
- 49, // max: 49 reboots
- 50); // num_buckets
-
- metric = metrics::kMetricSuccessfulUpdatePayloadType;
- system_state->metrics_lib()->SendEnumToUMA(metric,
- payload_type,
- kNumPayloadTypes);
- LOG(INFO) << "Uploading " << utils::ToString(payload_type)
- << " for metric " << metric;
-
- metric = metrics::kMetricSuccessfulUpdateAttemptCount;
- system_state->metrics_lib()->SendToUMA(metric,
- attempt_count,
- 1, // min: 1 attempt
- 50, // max: 50 attempts
- 50); // num_buckets
- LOG(INFO) << "Uploading " << attempt_count
- << " for metric " << metric;
-
- metric = metrics::kMetricSuccessfulUpdateUpdatesAbandonedCount;
- LOG(INFO) << "Uploading " << updates_abandoned_count
- << " (count) for metric " << metric;
- system_state->metrics_lib()->SendToUMA(metric,
- updates_abandoned_count,
- 0, // min: 0 counts
- 49, // max: 49 counts
- 50); // num_buckets
-}
-
-void ReportRollbackMetrics(SystemState *system_state,
- RollbackResult result) {
- string metric;
- int value;
-
- metric = metrics::kMetricRollbackResult;
- value = static_cast<int>(result);
- LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)";
- system_state->metrics_lib()->SendEnumToUMA(
- metric,
- value,
- static_cast<int>(metrics::RollbackResult::kNumConstants));
-}
-
-void ReportCertificateCheckMetrics(SystemState* system_state,
- ServerToCheck server_to_check,
- CertificateCheckResult result) {
- string metric;
- switch (server_to_check) {
- case ServerToCheck::kUpdate:
- metric = kMetricCertificateCheckUpdateCheck;
- break;
- case ServerToCheck::kDownload:
- metric = kMetricCertificateCheckDownload;
- break;
- case ServerToCheck::kNone:
- return;
- }
- LOG(INFO) << "Uploading " << static_cast<int>(result) << " for metric "
- << metric;
- system_state->metrics_lib()->SendEnumToUMA(
- metric, static_cast<int>(result),
- static_cast<int>(CertificateCheckResult::kNumConstants));
-}
-
-} // namespace metrics
-
-} // namespace chromeos_update_engine
diff --git a/metrics.h b/metrics.h
deleted file mode 100644
index 7c369ee..0000000
--- a/metrics.h
+++ /dev/null
@@ -1,321 +0,0 @@
-//
-// Copyright (C) 2014 The Android Open Source Project
-//
-// 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
-//
-// http://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.
-//
-
-#ifndef UPDATE_ENGINE_METRICS_H_
-#define UPDATE_ENGINE_METRICS_H_
-
-#include <base/time/time.h>
-
-#include "update_engine/certificate_checker.h"
-#include "update_engine/common/constants.h"
-#include "update_engine/common/error_code.h"
-
-namespace chromeos_update_engine {
-
-class SystemState;
-
-namespace metrics {
-
-// UpdateEngine.Daily.* metrics.
-extern const char kMetricDailyOSAgeDays[];
-
-// UpdateEngine.Check.* metrics.
-extern const char kMetricCheckDownloadErrorCode[];
-extern const char kMetricCheckReaction[];
-extern const char kMetricCheckResult[];
-extern const char kMetricCheckTimeSinceLastCheckMinutes[];
-extern const char kMetricCheckTimeSinceLastCheckUptimeMinutes[];
-
-// UpdateEngine.Attempt.* metrics.
-extern const char kMetricAttemptNumber[];
-extern const char kMetricAttemptPayloadType[];
-extern const char kMetricAttemptPayloadSizeMiB[];
-extern const char kMetricAttemptConnectionType[];
-extern const char kMetricAttemptDurationMinutes[];
-extern const char kMetricAttemptDurationUptimeMinutes[];
-extern const char kMetricAttemptTimeSinceLastAttemptSeconds[];
-extern const char kMetricAttemptTimeSinceLastAttemptUptimeSeconds[];
-extern const char kMetricAttemptPayloadBytesDownloaded[];
-extern const char kMetricAttemptPayloadDownloadSpeedKBps[];
-extern const char kMetricAttemptDownloadSource[];
-extern const char kMetricAttemptResult[];
-extern const char kMetricAttemptInternalErrorCode[];
-extern const char kMetricAttemptDownloadErrorCode[];
-
-// UpdateEngine.SuccessfulUpdate.* metrics.
-extern const char kMetricSuccessfulUpdateAttemptCount[];
-extern const char kMetricSuccessfulUpdateBytesDownloadedMiB[];
-extern const char kMetricSuccessfulUpdateDownloadOverheadPercentage[];
-extern const char kMetricSuccessfulUpdateDownloadSourcesUsed[];
-extern const char kMetricSuccessfulUpdatePayloadType[];
-extern const char kMetricSuccessfulUpdatePayloadSizeMiB[];
-extern const char kMetricSuccessfulUpdateTotalDurationMinutes[];
-extern const char kMetricSuccessfulUpdateRebootCount[];
-extern const char kMetricSuccessfulUpdateUpdatesAbandonedCount[];
-extern const char kMetricSuccessfulUpdateUrlSwitchCount[];
-
-// UpdateEngine.Rollback.* metric.
-extern const char kMetricRollbackResult[];
-
-// UpdateEngine.* metrics.
-extern const char kMetricFailedUpdateCount[];
-extern const char kMetricInstallDateProvisioningSource[];
-extern const char kMetricTimeToRebootMinutes[];
-
-// The possible outcomes when checking for updates.
-//
-// This is used in the UpdateEngine.Check.Result histogram.
-enum class CheckResult {
- kUpdateAvailable, // Response indicates an update is available.
- kNoUpdateAvailable, // Response indicates no updates are available.
- kDownloadError, // Error downloading response from Omaha.
- kParsingError, // Error parsing response.
- kRebootPending, // No update check was performed a reboot is pending.
-
- kNumConstants,
- kUnset = -1
-};
-
-// Possible ways a device can react to a new update being available.
-//
-// This is used in the UpdateEngine.Check.Reaction histogram.
-enum class CheckReaction {
- kUpdating, // Device proceeds to download and apply update.
- kIgnored , // Device-policy dictates ignoring the update.
- kDeferring, // Device-policy dictates waiting.
- kBackingOff, // Previous errors dictates waiting.
-
- kNumConstants,
- kUnset = -1
-};
-
-// The possible ways that downloading from a HTTP or HTTPS server can fail.
-//
-// This is used in the UpdateEngine.Check.DownloadErrorCode and
-// UpdateEngine.Attempt.DownloadErrorCode histograms.
-enum class DownloadErrorCode {
- // Errors that can happen in the field. See http://crbug.com/355745
- // for how we plan to add more detail in the future.
- kDownloadError = 0, // Error downloading data from server.
-
- // IMPORTANT: When adding a new error code, add at the bottom of the
- // above block and before the kInputMalformed field. This
- // is to ensure that error codes are not reordered.
-
- // This error code is used to convey that malformed input was given
- // to the utils::GetDownloadErrorCode() function. This should never
- // happen but if it does it's because of an internal update_engine
- // error and we're interested in knowing this.
- kInputMalformed = 100,
-
- // Bucket for capturing HTTP status codes not in the 200-599
- // range. This should never happen in practice but if it does we
- // want to know.
- kHttpStatusOther = 101,
-
- // Above 200 and below 600, the value is the HTTP status code.
- kHttpStatus200 = 200,
-
- kNumConstants = 600,
-
- kUnset = -1
-};
-
-// Possible ways an update attempt can end.
-//
-// This is used in the UpdateEngine.Attempt.Result histogram.
-enum class AttemptResult {
- kUpdateSucceeded, // The update succeeded.
- kInternalError, // An internal error occurred.
- kPayloadDownloadError, // Failure while downloading payload.
- kMetadataMalformed, // Metadata was malformed.
- kOperationMalformed, // An operation was malformed.
- kOperationExecutionError, // An operation failed to execute.
- kMetadataVerificationFailed, // Metadata verification failed.
- kPayloadVerificationFailed, // Payload verification failed.
- kVerificationFailed, // Root or Kernel partition verification failed.
- kPostInstallFailed, // The postinstall step failed.
- kAbnormalTermination, // The attempt ended abnormally.
- kUpdateCanceled, // Update canceled by the user.
-
- kNumConstants,
-
- kUnset = -1
-};
-
-// Possible ways the device is connected to the Internet.
-//
-// This is used in the UpdateEngine.Attempt.ConnectionType histogram.
-enum class ConnectionType {
- kUnknown, // Unknown.
- kEthernet, // Ethernet.
- kWifi, // Wireless.
- kWimax, // WiMax.
- kBluetooth, // Bluetooth.
- kCellular, // Cellular.
- kTetheredEthernet, // Tethered (Ethernet).
- kTetheredWifi, // Tethered (Wifi).
-
- kNumConstants,
- kUnset = -1
-};
-
-// Possible ways a rollback can end.
-//
-// This is used in the UpdateEngine.Rollback histogram.
-enum class RollbackResult {
- kFailed,
- kSuccess,
-
- kNumConstants
-};
-
-// Helper function to report metrics related to rollback. The
-// following metrics are reported:
-//
-// |kMetricRollbackResult|
-void ReportRollbackMetrics(SystemState *system_state,
- RollbackResult result);
-
-// Helper function to report metrics reported once a day. The
-// following metrics are reported:
-//
-// |kMetricDailyOSAgeDays|
-void ReportDailyMetrics(SystemState *system_state,
- base::TimeDelta os_age);
-
-// Helper function to report metrics after completing an update check
-// with the ChromeOS update server ("Omaha"). The following metrics
-// are reported:
-//
-// |kMetricCheckResult|
-// |kMetricCheckReaction|
-// |kMetricCheckDownloadErrorCode|
-// |kMetricCheckTimeSinceLastCheckMinutes|
-// |kMetricCheckTimeSinceLastCheckUptimeMinutes|
-//
-// The |kMetricCheckResult| metric will only be reported if |result|
-// is not |kUnset|.
-//
-// The |kMetricCheckReaction| metric will only be reported if
-// |reaction| is not |kUnset|.
-//
-// The |kMetricCheckDownloadErrorCode| will only be reported if
-// |download_error_code| is not |kUnset|.
-//
-// The values for the |kMetricCheckTimeSinceLastCheckMinutes| and
-// |kMetricCheckTimeSinceLastCheckUptimeMinutes| metrics are
-// automatically reported and calculated by maintaining persistent
-// and process-local state variables.
-void ReportUpdateCheckMetrics(SystemState *system_state,
- CheckResult result,
- CheckReaction reaction,
- DownloadErrorCode download_error_code);
-
-
-// Helper function to report metrics after the completion of each
-// update attempt. The following metrics are reported:
-//
-// |kMetricAttemptNumber|
-// |kMetricAttemptPayloadType|
-// |kMetricAttemptPayloadSizeMiB|
-// |kMetricAttemptDurationSeconds|
-// |kMetricAttemptDurationUptimeSeconds|
-// |kMetricAttemptTimeSinceLastAttemptMinutes|
-// |kMetricAttemptTimeSinceLastAttemptUptimeMinutes|
-// |kMetricAttemptPayloadBytesDownloadedMiB|
-// |kMetricAttemptPayloadDownloadSpeedKBps|
-// |kMetricAttemptDownloadSource|
-// |kMetricAttemptResult|
-// |kMetricAttemptInternalErrorCode|
-// |kMetricAttemptDownloadErrorCode|
-//
-// The |kMetricAttemptInternalErrorCode| metric will only be reported
-// if |internal_error_code| is not |kErrorSuccess|.
-//
-// The |kMetricAttemptDownloadErrorCode| metric will only be
-// reported if |payload_download_error_code| is not |kUnset|.
-//
-// The values for the |kMetricAttemptTimeSinceLastAttemptMinutes| and
-// |kMetricAttemptTimeSinceLastAttemptUptimeMinutes| metrics are
-// automatically calculated and reported by maintaining persistent and
-// process-local state variables.
-void ReportUpdateAttemptMetrics(
- SystemState *system_state,
- int attempt_number,
- PayloadType payload_type,
- base::TimeDelta duration,
- base::TimeDelta duration_uptime,
- int64_t payload_size,
- int64_t payload_bytes_downloaded,
- int64_t payload_download_speed_bps,
- DownloadSource download_source,
- AttemptResult attempt_result,
- ErrorCode internal_error_code,
- DownloadErrorCode payload_download_error_code,
- ConnectionType connection_type);
-
-// Reports the |kAbnormalTermination| for the |kMetricAttemptResult|
-// metric. No other metrics in the UpdateEngine.Attempt.* namespace
-// will be reported.
-void ReportAbnormallyTerminatedUpdateAttemptMetrics(SystemState *system_state);
-
-// Helper function to report the after the completion of a successful
-// update attempt. The following metrics are reported:
-//
-// |kMetricSuccessfulUpdateAttemptCount|
-// |kMetricSuccessfulUpdateUpdatesAbandonedCount|
-// |kMetricSuccessfulUpdatePayloadType|
-// |kMetricSuccessfulUpdatePayloadSizeMiB|
-// |kMetricSuccessfulUpdateBytesDownloadedMiBHttpsServer|
-// |kMetricSuccessfulUpdateBytesDownloadedMiBHttpServer|
-// |kMetricSuccessfulUpdateBytesDownloadedMiBHttpPeer|
-// |kMetricSuccessfulUpdateBytesDownloadedMiB|
-// |kMetricSuccessfulUpdateDownloadSourcesUsed|
-// |kMetricSuccessfulUpdateDownloadOverheadPercentage|
-// |kMetricSuccessfulUpdateTotalDurationMinutes|
-// |kMetricSuccessfulUpdateRebootCount|
-// |kMetricSuccessfulUpdateUrlSwitchCount|
-//
-// The values for the |kMetricSuccessfulUpdateDownloadSourcesUsed| are
-// |kMetricSuccessfulUpdateBytesDownloadedMiB| metrics automatically
-// calculated from examining the |num_bytes_downloaded| array.
-void ReportSuccessfulUpdateMetrics(
- SystemState *system_state,
- int attempt_count,
- int updates_abandoned_count,
- PayloadType payload_type,
- int64_t payload_size,
- int64_t num_bytes_downloaded[kNumDownloadSources],
- int download_overhead_percentage,
- base::TimeDelta total_duration,
- int reboot_count,
- int url_switch_count);
-
-// Helper function to report the after the completion of a SSL certificate
-// check. One of the following metrics is reported:
-//
-// |kMetricCertificateCheckUpdateCheck|
-// |kMetricCertificateCheckDownload|
-void ReportCertificateCheckMetrics(SystemState* system_state,
- ServerToCheck server_to_check,
- CertificateCheckResult result);
-
-} // namespace metrics
-
-} // namespace chromeos_update_engine
-
-#endif // UPDATE_ENGINE_METRICS_H_
diff --git a/metrics_constants.h b/metrics_constants.h
new file mode 100644
index 0000000..abec2ad
--- /dev/null
+++ b/metrics_constants.h
@@ -0,0 +1,137 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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.
+//
+
+#ifndef UPDATE_ENGINE_METRICS_CONSTANTS_H_
+#define UPDATE_ENGINE_METRICS_CONSTANTS_H_
+
+namespace chromeos_update_engine {
+
+namespace metrics {
+// The possible outcomes when checking for updates.
+//
+// This is used in the UpdateEngine.Check.Result histogram.
+enum class CheckResult {
+ kUpdateAvailable, // Response indicates an update is available.
+ kNoUpdateAvailable, // Response indicates no updates are available.
+ kDownloadError, // Error downloading response from Omaha.
+ kParsingError, // Error parsing response.
+ kRebootPending, // No update check was performed a reboot is pending.
+
+ kNumConstants,
+ kUnset = -1
+};
+
+// Possible ways a device can react to a new update being available.
+//
+// This is used in the UpdateEngine.Check.Reaction histogram.
+enum class CheckReaction {
+ kUpdating, // Device proceeds to download and apply update.
+ kIgnored, // Device-policy dictates ignoring the update.
+ kDeferring, // Device-policy dictates waiting.
+ kBackingOff, // Previous errors dictates waiting.
+
+ kNumConstants,
+ kUnset = -1
+};
+
+// The possible ways that downloading from a HTTP or HTTPS server can fail.
+//
+// This is used in the UpdateEngine.Check.DownloadErrorCode and
+// UpdateEngine.Attempt.DownloadErrorCode histograms.
+enum class DownloadErrorCode {
+ // Errors that can happen in the field. See http://crbug.com/355745
+ // for how we plan to add more detail in the future.
+ kDownloadError = 0, // Error downloading data from server.
+
+ // IMPORTANT: When adding a new error code, add at the bottom of the
+ // above block and before the kInputMalformed field. This
+ // is to ensure that error codes are not reordered.
+
+ // This error code is used to convey that malformed input was given
+ // to the utils::GetDownloadErrorCode() function. This should never
+ // happen but if it does it's because of an internal update_engine
+ // error and we're interested in knowing this.
+ kInputMalformed = 100,
+
+ // Bucket for capturing HTTP status codes not in the 200-599
+ // range. This should never happen in practice but if it does we
+ // want to know.
+ kHttpStatusOther = 101,
+
+ // Above 200 and below 600, the value is the HTTP status code.
+ kHttpStatus200 = 200,
+
+ kNumConstants = 600,
+
+ kUnset = -1
+};
+
+// Possible ways an update attempt can end.
+//
+// This is used in the UpdateEngine.Attempt.Result histogram.
+enum class AttemptResult {
+ kUpdateSucceeded, // The update succeeded.
+ kInternalError, // An internal error occurred.
+ kPayloadDownloadError, // Failure while downloading payload.
+ kMetadataMalformed, // Metadata was malformed.
+ kOperationMalformed, // An operation was malformed.
+ kOperationExecutionError, // An operation failed to execute.
+ kMetadataVerificationFailed, // Metadata verification failed.
+ kPayloadVerificationFailed, // Payload verification failed.
+ kVerificationFailed, // Root or Kernel partition verification failed.
+ kPostInstallFailed, // The postinstall step failed.
+ kAbnormalTermination, // The attempt ended abnormally.
+ kUpdateCanceled, // Update canceled by the user.
+ kUpdateSucceededNotActive, // Update succeeded but the new slot is not
+ // active.
+
+ kNumConstants,
+
+ kUnset = -1
+};
+
+// Possible ways the device is connected to the Internet.
+//
+// This is used in the UpdateEngine.Attempt.ConnectionType histogram.
+enum class ConnectionType {
+ kUnknown, // Unknown.
+ kEthernet, // Ethernet.
+ kWifi, // Wireless.
+ kWimax, // WiMax.
+ kBluetooth, // Bluetooth.
+ kCellular, // Cellular.
+ kTetheredEthernet, // Tethered (Ethernet).
+ kTetheredWifi, // Tethered (Wifi).
+
+ kNumConstants,
+ kUnset = -1
+};
+
+// Possible ways a rollback can end.
+//
+// This is used in the UpdateEngine.Rollback histogram.
+enum class RollbackResult {
+ kFailed,
+ kSuccess,
+
+ kNumConstants
+};
+
+} // namespace metrics
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_METRICS_CONSTANTS_H_
diff --git a/metrics_reporter_android.cc b/metrics_reporter_android.cc
new file mode 100644
index 0000000..3cb356f
--- /dev/null
+++ b/metrics_reporter_android.cc
@@ -0,0 +1,158 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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 "update_engine/metrics_reporter_android.h"
+
+#include <memory>
+#include <string>
+
+#include <metricslogger/metrics_logger.h>
+
+#include "update_engine/common/constants.h"
+
+namespace {
+void LogHistogram(const std::string& metrics, int value) {
+ android::metricslogger::LogHistogram(metrics, value);
+ LOG(INFO) << "uploading " << value << " to histogram for metric " << metrics;
+}
+} // namespace
+
+namespace chromeos_update_engine {
+
+namespace metrics {
+
+// The histograms are defined in:
+// depot/google3/analysis/uma/configs/clearcut/TRON/histograms.xml
+constexpr char kMetricsUpdateEngineAttemptNumber[] =
+ "ota_update_engine_attempt_number";
+constexpr char kMetricsUpdateEngineAttemptResult[] =
+ "ota_update_engine_attempt_result";
+constexpr char kMetricsUpdateEngineAttemptDurationInMinutes[] =
+ "ota_update_engine_attempt_duration_boottime_in_minutes";
+constexpr char kMetricsUpdateEngineAttemptDurationUptimeInMinutes[] =
+ "ota_update_engine_attempt_duration_monotonic_in_minutes";
+constexpr char kMetricsUpdateEngineAttemptErrorCode[] =
+ "ota_update_engine_attempt_error_code";
+constexpr char kMetricsUpdateEngineAttemptPayloadSizeMiB[] =
+ "ota_update_engine_attempt_payload_size_mib";
+constexpr char kMetricsUpdateEngineAttemptPayloadType[] =
+ "ota_update_engine_attempt_payload_type";
+constexpr char kMetricsUpdateEngineAttemptCurrentBytesDownloadedMiB[] =
+ "ota_update_engine_attempt_current_bytes_downloaded_mib";
+
+constexpr char kMetricsUpdateEngineSuccessfulUpdateAttemptCount[] =
+ "ota_update_engine_successful_update_attempt_count";
+constexpr char kMetricsUpdateEngineSuccessfulUpdateTotalDurationInMinutes[] =
+ "ota_update_engine_successful_update_total_duration_in_minutes";
+constexpr char kMetricsUpdateEngineSuccessfulUpdatePayloadSizeMiB[] =
+ "ota_update_engine_successful_update_payload_size_mib";
+constexpr char kMetricsUpdateEngineSuccessfulUpdatePayloadType[] =
+ "ota_update_engine_successful_update_payload_type";
+constexpr char kMetricsUpdateEngineSuccessfulUpdateRebootCount[] =
+ "ota_update_engine_successful_update_reboot_count";
+constexpr char kMetricsUpdateEngineSuccessfulUpdateTotalBytesDownloadedMiB[] =
+ "ota_update_engine_successful_update_total_bytes_downloaded_mib";
+constexpr char
+ kMetricsUpdateEngineSuccessfulUpdateDownloadOverheadPercentage[] =
+ "ota_update_engine_successful_update_download_overhead_percentage";
+
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter() {
+ return std::make_unique<MetricsReporterAndroid>();
+}
+
+} // namespace metrics
+
+void MetricsReporterAndroid::ReportUpdateAttemptMetrics(
+ SystemState* /* system_state */,
+ int attempt_number,
+ PayloadType payload_type,
+ base::TimeDelta duration,
+ base::TimeDelta duration_uptime,
+ int64_t payload_size,
+ metrics::AttemptResult attempt_result,
+ ErrorCode error_code) {
+ LogHistogram(metrics::kMetricsUpdateEngineAttemptNumber, attempt_number);
+ LogHistogram(metrics::kMetricsUpdateEngineAttemptPayloadType,
+ static_cast<int>(payload_type));
+ LogHistogram(metrics::kMetricsUpdateEngineAttemptDurationInMinutes,
+ duration.InMinutes());
+ LogHistogram(metrics::kMetricsUpdateEngineAttemptDurationUptimeInMinutes,
+ duration_uptime.InMinutes());
+
+ int64_t payload_size_mib = payload_size / kNumBytesInOneMiB;
+ LogHistogram(metrics::kMetricsUpdateEngineAttemptPayloadSizeMiB,
+ payload_size_mib);
+
+ LogHistogram(metrics::kMetricsUpdateEngineAttemptResult,
+ static_cast<int>(attempt_result));
+ LogHistogram(metrics::kMetricsUpdateEngineAttemptErrorCode,
+ static_cast<int>(error_code));
+}
+
+void MetricsReporterAndroid::ReportUpdateAttemptDownloadMetrics(
+ int64_t payload_bytes_downloaded,
+ int64_t /* payload_download_speed_bps */,
+ DownloadSource /* download_source */,
+ metrics::DownloadErrorCode /* payload_download_error_code */,
+ metrics::ConnectionType /* connection_type */) {
+ LogHistogram(metrics::kMetricsUpdateEngineAttemptCurrentBytesDownloadedMiB,
+ payload_bytes_downloaded);
+}
+
+void MetricsReporterAndroid::ReportSuccessfulUpdateMetrics(
+ int attempt_count,
+ int /* updates_abandoned_count */,
+ PayloadType payload_type,
+ int64_t payload_size,
+ int64_t num_bytes_downloaded[kNumDownloadSources],
+ int download_overhead_percentage,
+ base::TimeDelta total_duration,
+ int reboot_count,
+ int /* url_switch_count */) {
+ LogHistogram(metrics::kMetricsUpdateEngineSuccessfulUpdateAttemptCount,
+ attempt_count);
+ LogHistogram(metrics::kMetricsUpdateEngineSuccessfulUpdatePayloadType,
+ static_cast<int>(payload_type));
+
+ int64_t payload_size_mib = payload_size / kNumBytesInOneMiB;
+ LogHistogram(metrics::kMetricsUpdateEngineSuccessfulUpdatePayloadSizeMiB,
+ payload_size_mib);
+
+ int64_t total_bytes_downloaded = 0;
+ for (size_t i = 0; i < kNumDownloadSources; i++) {
+ total_bytes_downloaded += num_bytes_downloaded[i] / kNumBytesInOneMiB;
+ }
+ LogHistogram(
+ metrics::kMetricsUpdateEngineSuccessfulUpdateTotalBytesDownloadedMiB,
+ total_bytes_downloaded);
+ LogHistogram(
+ metrics::kMetricsUpdateEngineSuccessfulUpdateDownloadOverheadPercentage,
+ download_overhead_percentage);
+
+ LogHistogram(
+ metrics::kMetricsUpdateEngineSuccessfulUpdateTotalDurationInMinutes,
+ total_duration.InMinutes());
+ LogHistogram(metrics::kMetricsUpdateEngineSuccessfulUpdateRebootCount,
+ reboot_count);
+}
+
+void MetricsReporterAndroid::ReportAbnormallyTerminatedUpdateAttemptMetrics() {
+ int attempt_result =
+ static_cast<int>(metrics::AttemptResult::kAbnormalTermination);
+ LogHistogram(metrics::kMetricsUpdateEngineAttemptResult, attempt_result);
+}
+
+}; // namespace chromeos_update_engine
diff --git a/metrics_reporter_android.h b/metrics_reporter_android.h
new file mode 100644
index 0000000..ee94e43
--- /dev/null
+++ b/metrics_reporter_android.h
@@ -0,0 +1,88 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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.
+//
+
+#ifndef UPDATE_ENGINE_METRICS_REPORTER_ANDROID_H_
+#define UPDATE_ENGINE_METRICS_REPORTER_ANDROID_H_
+
+#include "update_engine/common/error_code.h"
+#include "update_engine/metrics_constants.h"
+#include "update_engine/metrics_reporter_interface.h"
+
+namespace chromeos_update_engine {
+
+class MetricsReporterAndroid : public MetricsReporterInterface {
+ public:
+ MetricsReporterAndroid() = default;
+
+ ~MetricsReporterAndroid() override = default;
+
+ void Initialize() override {}
+
+ void ReportRollbackMetrics(metrics::RollbackResult result) override {}
+
+ void ReportDailyMetrics(base::TimeDelta os_age) override {}
+
+ void ReportUpdateCheckMetrics(
+ SystemState* system_state,
+ metrics::CheckResult result,
+ metrics::CheckReaction reaction,
+ metrics::DownloadErrorCode download_error_code) override {}
+
+ void ReportUpdateAttemptMetrics(SystemState* system_state,
+ int attempt_number,
+ PayloadType payload_type,
+ base::TimeDelta duration,
+ base::TimeDelta duration_uptime,
+ int64_t payload_size,
+ metrics::AttemptResult attempt_result,
+ ErrorCode internal_error_code) override;
+
+ void ReportUpdateAttemptDownloadMetrics(
+ int64_t payload_bytes_downloaded,
+ int64_t payload_download_speed_bps,
+ DownloadSource download_source,
+ metrics::DownloadErrorCode payload_download_error_code,
+ metrics::ConnectionType connection_type) override;
+
+ void ReportAbnormallyTerminatedUpdateAttemptMetrics() override;
+
+ void ReportSuccessfulUpdateMetrics(
+ int attempt_count,
+ int updates_abandoned_count,
+ PayloadType payload_type,
+ int64_t payload_size,
+ int64_t num_bytes_downloaded[kNumDownloadSources],
+ int download_overhead_percentage,
+ base::TimeDelta total_duration,
+ int reboot_count,
+ int url_switch_count) override;
+
+ void ReportCertificateCheckMetrics(ServerToCheck server_to_check,
+ CertificateCheckResult result) override {}
+
+ void ReportFailedUpdateCount(int target_attempt) override {}
+
+ void ReportTimeToReboot(int time_to_reboot_minutes) override {}
+
+ void ReportInstallDateProvisioningSource(int source, int max) override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MetricsReporterAndroid);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_METRICS_REPORTER_ANDROID_H_
diff --git a/metrics_reporter_interface.h b/metrics_reporter_interface.h
new file mode 100644
index 0000000..2c7ce5b
--- /dev/null
+++ b/metrics_reporter_interface.h
@@ -0,0 +1,200 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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.
+//
+
+#ifndef UPDATE_ENGINE_METRICS_REPORTER_INTERFACE_H_
+#define UPDATE_ENGINE_METRICS_REPORTER_INTERFACE_H_
+
+#include <memory>
+
+#include <base/time/time.h>
+
+#include "update_engine/common/constants.h"
+#include "update_engine/common/error_code.h"
+#include "update_engine/metrics_constants.h"
+#include "update_engine/system_state.h"
+
+namespace chromeos_update_engine {
+
+enum class ServerToCheck;
+enum class CertificateCheckResult;
+
+namespace metrics {
+
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter();
+
+} // namespace metrics
+
+class MetricsReporterInterface {
+ public:
+ virtual ~MetricsReporterInterface() = default;
+
+ virtual void Initialize() = 0;
+
+ // Helper function to report metrics related to rollback. The
+ // following metrics are reported:
+ //
+ // |kMetricRollbackResult|
+ virtual void ReportRollbackMetrics(metrics::RollbackResult result) = 0;
+
+ // Helper function to report metrics reported once a day. The
+ // following metrics are reported:
+ //
+ // |kMetricDailyOSAgeDays|
+ virtual void ReportDailyMetrics(base::TimeDelta os_age) = 0;
+
+ // Helper function to report metrics after completing an update check
+ // with the ChromeOS update server ("Omaha"). The following metrics
+ // are reported:
+ //
+ // |kMetricCheckResult|
+ // |kMetricCheckReaction|
+ // |kMetricCheckDownloadErrorCode|
+ // |kMetricCheckTimeSinceLastCheckMinutes|
+ // |kMetricCheckTimeSinceLastCheckUptimeMinutes|
+ //
+ // The |kMetricCheckResult| metric will only be reported if |result|
+ // is not |kUnset|.
+ //
+ // The |kMetricCheckReaction| metric will only be reported if
+ // |reaction| is not |kUnset|.
+ //
+ // The |kMetricCheckDownloadErrorCode| will only be reported if
+ // |download_error_code| is not |kUnset|.
+ //
+ // The values for the |kMetricCheckTimeSinceLastCheckMinutes| and
+ // |kMetricCheckTimeSinceLastCheckUptimeMinutes| metrics are
+ // automatically reported and calculated by maintaining persistent
+ // and process-local state variables.
+ virtual void ReportUpdateCheckMetrics(
+ SystemState* system_state,
+ metrics::CheckResult result,
+ metrics::CheckReaction reaction,
+ metrics::DownloadErrorCode download_error_code) = 0;
+
+ // Helper function to report metrics after the completion of each
+ // update attempt. The following metrics are reported:
+ //
+ // |kMetricAttemptNumber|
+ // |kMetricAttemptPayloadType|
+ // |kMetricAttemptPayloadSizeMiB|
+ // |kMetricAttemptDurationMinutes|
+ // |kMetricAttemptDurationUptimeMinutes|
+ // |kMetricAttemptTimeSinceLastAttemptMinutes|
+ // |kMetricAttemptTimeSinceLastAttemptUptimeMinutes|
+ // |kMetricAttemptResult|
+ // |kMetricAttemptInternalErrorCode|
+ //
+ // The |kMetricAttemptInternalErrorCode| metric will only be reported
+ // if |internal_error_code| is not |kErrorSuccess|.
+ //
+ // The |kMetricAttemptDownloadErrorCode| metric will only be
+ // reported if |payload_download_error_code| is not |kUnset|.
+ //
+ // The values for the |kMetricAttemptTimeSinceLastAttemptMinutes| and
+ // |kMetricAttemptTimeSinceLastAttemptUptimeMinutes| metrics are
+ // automatically calculated and reported by maintaining persistent and
+ // process-local state variables.
+ virtual void ReportUpdateAttemptMetrics(SystemState* system_state,
+ int attempt_number,
+ PayloadType payload_type,
+ base::TimeDelta duration,
+ base::TimeDelta duration_uptime,
+ int64_t payload_size,
+ metrics::AttemptResult attempt_result,
+ ErrorCode internal_error_code) = 0;
+
+ // Helper function to report download metrics after the completion of each
+ // update attempt. The following metrics are reported:
+ //
+ // |kMetricAttemptPayloadBytesDownloadedMiB|
+ // |kMetricAttemptPayloadDownloadSpeedKBps|
+ // |kMetricAttemptDownloadSource|
+ // |kMetricAttemptDownloadErrorCode|
+ // |kMetricAttemptConnectionType|
+ virtual void ReportUpdateAttemptDownloadMetrics(
+ int64_t payload_bytes_downloaded,
+ int64_t payload_download_speed_bps,
+ DownloadSource download_source,
+ metrics::DownloadErrorCode payload_download_error_code,
+ metrics::ConnectionType connection_type) = 0;
+
+ // Reports the |kAbnormalTermination| for the |kMetricAttemptResult|
+ // metric. No other metrics in the UpdateEngine.Attempt.* namespace
+ // will be reported.
+ virtual void ReportAbnormallyTerminatedUpdateAttemptMetrics() = 0;
+
+ // Helper function to report the after the completion of a successful
+ // update attempt. The following metrics are reported:
+ //
+ // |kMetricSuccessfulUpdateAttemptCount|
+ // |kMetricSuccessfulUpdateUpdatesAbandonedCount|
+ // |kMetricSuccessfulUpdatePayloadType|
+ // |kMetricSuccessfulUpdatePayloadSizeMiB|
+ // |kMetricSuccessfulUpdateBytesDownloadedMiBHttpsServer|
+ // |kMetricSuccessfulUpdateBytesDownloadedMiBHttpServer|
+ // |kMetricSuccessfulUpdateBytesDownloadedMiBHttpPeer|
+ // |kMetricSuccessfulUpdateBytesDownloadedMiB|
+ // |kMetricSuccessfulUpdateDownloadSourcesUsed|
+ // |kMetricSuccessfulUpdateDownloadOverheadPercentage|
+ // |kMetricSuccessfulUpdateTotalDurationMinutes|
+ // |kMetricSuccessfulUpdateRebootCount|
+ // |kMetricSuccessfulUpdateUrlSwitchCount|
+ //
+ // The values for the |kMetricSuccessfulUpdateDownloadSourcesUsed| are
+ // |kMetricSuccessfulUpdateBytesDownloadedMiB| metrics automatically
+ // calculated from examining the |num_bytes_downloaded| array.
+ virtual void ReportSuccessfulUpdateMetrics(
+ int attempt_count,
+ int updates_abandoned_count,
+ PayloadType payload_type,
+ int64_t payload_size,
+ int64_t num_bytes_downloaded[kNumDownloadSources],
+ int download_overhead_percentage,
+ base::TimeDelta total_duration,
+ int reboot_count,
+ int url_switch_count) = 0;
+
+ // Helper function to report the after the completion of a SSL certificate
+ // check. One of the following metrics is reported:
+ //
+ // |kMetricCertificateCheckUpdateCheck|
+ // |kMetricCertificateCheckDownload|
+ virtual void ReportCertificateCheckMetrics(ServerToCheck server_to_check,
+ CertificateCheckResult result) = 0;
+
+ // Helper function to report the number failed update attempts. The following
+ // metrics are reported:
+ //
+ // |kMetricFailedUpdateCount|
+ virtual void ReportFailedUpdateCount(int target_attempt) = 0;
+
+ // Helper function to report the time interval in minutes between a
+ // successful update and the reboot into the updated system. The following
+ // metrics are reported:
+ //
+ // |kMetricTimeToRebootMinutes|
+ virtual void ReportTimeToReboot(int time_to_reboot_minutes) = 0;
+
+ // Helper function to report the source of installation data. The following
+ // metrics are reported:
+ //
+ // |kMetricInstallDateProvisioningSource|
+ virtual void ReportInstallDateProvisioningSource(int source, int max) = 0;
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_METRICS_REPORTER_INTERFACE_H_
diff --git a/metrics_reporter_omaha.cc b/metrics_reporter_omaha.cc
new file mode 100644
index 0000000..0397b83
--- /dev/null
+++ b/metrics_reporter_omaha.cc
@@ -0,0 +1,538 @@
+//
+// Copyright (C) 2014 The Android Open Source Project
+//
+// 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
+//
+// http://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 "update_engine/metrics_reporter_omaha.h"
+
+#include <memory>
+#include <string>
+
+#include <base/logging.h>
+#include <metrics/metrics_library.h>
+
+#include "update_engine/common/clock_interface.h"
+#include "update_engine/common/constants.h"
+#include "update_engine/common/prefs_interface.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/metrics_utils.h"
+#include "update_engine/system_state.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+namespace metrics {
+
+// UpdateEngine.Daily.* metrics.
+const char kMetricDailyOSAgeDays[] = "UpdateEngine.Daily.OSAgeDays";
+
+// UpdateEngine.Check.* metrics.
+const char kMetricCheckDownloadErrorCode[] =
+ "UpdateEngine.Check.DownloadErrorCode";
+const char kMetricCheckReaction[] = "UpdateEngine.Check.Reaction";
+const char kMetricCheckResult[] = "UpdateEngine.Check.Result";
+const char kMetricCheckTimeSinceLastCheckMinutes[] =
+ "UpdateEngine.Check.TimeSinceLastCheckMinutes";
+const char kMetricCheckTimeSinceLastCheckUptimeMinutes[] =
+ "UpdateEngine.Check.TimeSinceLastCheckUptimeMinutes";
+
+// UpdateEngine.Attempt.* metrics.
+const char kMetricAttemptNumber[] = "UpdateEngine.Attempt.Number";
+const char kMetricAttemptPayloadType[] = "UpdateEngine.Attempt.PayloadType";
+const char kMetricAttemptPayloadSizeMiB[] =
+ "UpdateEngine.Attempt.PayloadSizeMiB";
+const char kMetricAttemptConnectionType[] =
+ "UpdateEngine.Attempt.ConnectionType";
+const char kMetricAttemptDurationMinutes[] =
+ "UpdateEngine.Attempt.DurationMinutes";
+const char kMetricAttemptDurationUptimeMinutes[] =
+ "UpdateEngine.Attempt.DurationUptimeMinutes";
+const char kMetricAttemptTimeSinceLastAttemptMinutes[] =
+ "UpdateEngine.Attempt.TimeSinceLastAttemptMinutes";
+const char kMetricAttemptTimeSinceLastAttemptUptimeMinutes[] =
+ "UpdateEngine.Attempt.TimeSinceLastAttemptUptimeMinutes";
+const char kMetricAttemptPayloadBytesDownloadedMiB[] =
+ "UpdateEngine.Attempt.PayloadBytesDownloadedMiB";
+const char kMetricAttemptPayloadDownloadSpeedKBps[] =
+ "UpdateEngine.Attempt.PayloadDownloadSpeedKBps";
+const char kMetricAttemptDownloadSource[] =
+ "UpdateEngine.Attempt.DownloadSource";
+const char kMetricAttemptResult[] = "UpdateEngine.Attempt.Result";
+const char kMetricAttemptInternalErrorCode[] =
+ "UpdateEngine.Attempt.InternalErrorCode";
+const char kMetricAttemptDownloadErrorCode[] =
+ "UpdateEngine.Attempt.DownloadErrorCode";
+
+// UpdateEngine.SuccessfulUpdate.* metrics.
+const char kMetricSuccessfulUpdateAttemptCount[] =
+ "UpdateEngine.SuccessfulUpdate.AttemptCount";
+const char kMetricSuccessfulUpdateBytesDownloadedMiB[] =
+ "UpdateEngine.SuccessfulUpdate.BytesDownloadedMiB";
+const char kMetricSuccessfulUpdateDownloadOverheadPercentage[] =
+ "UpdateEngine.SuccessfulUpdate.DownloadOverheadPercentage";
+const char kMetricSuccessfulUpdateDownloadSourcesUsed[] =
+ "UpdateEngine.SuccessfulUpdate.DownloadSourcesUsed";
+const char kMetricSuccessfulUpdatePayloadType[] =
+ "UpdateEngine.SuccessfulUpdate.PayloadType";
+const char kMetricSuccessfulUpdatePayloadSizeMiB[] =
+ "UpdateEngine.SuccessfulUpdate.PayloadSizeMiB";
+const char kMetricSuccessfulUpdateRebootCount[] =
+ "UpdateEngine.SuccessfulUpdate.RebootCount";
+const char kMetricSuccessfulUpdateTotalDurationMinutes[] =
+ "UpdateEngine.SuccessfulUpdate.TotalDurationMinutes";
+const char kMetricSuccessfulUpdateUpdatesAbandonedCount[] =
+ "UpdateEngine.SuccessfulUpdate.UpdatesAbandonedCount";
+const char kMetricSuccessfulUpdateUrlSwitchCount[] =
+ "UpdateEngine.SuccessfulUpdate.UrlSwitchCount";
+
+// UpdateEngine.Rollback.* metric.
+const char kMetricRollbackResult[] = "UpdateEngine.Rollback.Result";
+
+// UpdateEngine.CertificateCheck.* metrics.
+const char kMetricCertificateCheckUpdateCheck[] =
+ "UpdateEngine.CertificateCheck.UpdateCheck";
+const char kMetricCertificateCheckDownload[] =
+ "UpdateEngine.CertificateCheck.Download";
+
+// UpdateEngine.* metrics.
+const char kMetricFailedUpdateCount[] = "UpdateEngine.FailedUpdateCount";
+const char kMetricInstallDateProvisioningSource[] =
+ "UpdateEngine.InstallDateProvisioningSource";
+const char kMetricTimeToRebootMinutes[] = "UpdateEngine.TimeToRebootMinutes";
+
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter() {
+ return std::make_unique<MetricsReporterOmaha>();
+}
+
+} // namespace metrics
+
+MetricsReporterOmaha::MetricsReporterOmaha()
+ : metrics_lib_(new MetricsLibrary()) {}
+
+void MetricsReporterOmaha::Initialize() {
+ metrics_lib_->Init();
+}
+
+void MetricsReporterOmaha::ReportDailyMetrics(base::TimeDelta os_age) {
+ string metric = metrics::kMetricDailyOSAgeDays;
+ LOG(INFO) << "Uploading " << utils::FormatTimeDelta(os_age) << " for metric "
+ << metric;
+ metrics_lib_->SendToUMA(metric,
+ static_cast<int>(os_age.InDays()),
+ 0, // min: 0 days
+ 6 * 30, // max: 6 months (approx)
+ 50); // num_buckets
+}
+
+void MetricsReporterOmaha::ReportUpdateCheckMetrics(
+ SystemState* system_state,
+ metrics::CheckResult result,
+ metrics::CheckReaction reaction,
+ metrics::DownloadErrorCode download_error_code) {
+ string metric;
+ int value;
+ int max_value;
+
+ if (result != metrics::CheckResult::kUnset) {
+ metric = metrics::kMetricCheckResult;
+ value = static_cast<int>(result);
+ max_value = static_cast<int>(metrics::CheckResult::kNumConstants) - 1;
+ LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)";
+ metrics_lib_->SendEnumToUMA(metric, value, max_value);
+ }
+ if (reaction != metrics::CheckReaction::kUnset) {
+ metric = metrics::kMetricCheckReaction;
+ value = static_cast<int>(reaction);
+ max_value = static_cast<int>(metrics::CheckReaction::kNumConstants) - 1;
+ LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)";
+ metrics_lib_->SendEnumToUMA(metric, value, max_value);
+ }
+ if (download_error_code != metrics::DownloadErrorCode::kUnset) {
+ metric = metrics::kMetricCheckDownloadErrorCode;
+ value = static_cast<int>(download_error_code);
+ LOG(INFO) << "Sending " << value << " for metric " << metric << " (sparse)";
+ metrics_lib_->SendSparseToUMA(metric, value);
+ }
+
+ base::TimeDelta time_since_last;
+ if (metrics_utils::WallclockDurationHelper(
+ system_state,
+ kPrefsMetricsCheckLastReportingTime,
+ &time_since_last)) {
+ metric = metrics::kMetricCheckTimeSinceLastCheckMinutes;
+ LOG(INFO) << "Sending " << utils::FormatTimeDelta(time_since_last)
+ << " for metric " << metric;
+ metrics_lib_->SendToUMA(metric,
+ time_since_last.InMinutes(),
+ 0, // min: 0 min
+ 30 * 24 * 60, // max: 30 days
+ 50); // num_buckets
+ }
+
+ base::TimeDelta uptime_since_last;
+ static int64_t uptime_since_last_storage = 0;
+ if (metrics_utils::MonotonicDurationHelper(
+ system_state, &uptime_since_last_storage, &uptime_since_last)) {
+ metric = metrics::kMetricCheckTimeSinceLastCheckUptimeMinutes;
+ LOG(INFO) << "Sending " << utils::FormatTimeDelta(uptime_since_last)
+ << " for metric " << metric;
+ metrics_lib_->SendToUMA(metric,
+ uptime_since_last.InMinutes(),
+ 0, // min: 0 min
+ 30 * 24 * 60, // max: 30 days
+ 50); // num_buckets
+ }
+}
+
+void MetricsReporterOmaha::ReportAbnormallyTerminatedUpdateAttemptMetrics() {
+ string metric = metrics::kMetricAttemptResult;
+ metrics::AttemptResult attempt_result =
+ metrics::AttemptResult::kAbnormalTermination;
+
+ LOG(INFO) << "Uploading " << static_cast<int>(attempt_result)
+ << " for metric " << metric;
+ metrics_lib_->SendEnumToUMA(
+ metric,
+ static_cast<int>(attempt_result),
+ static_cast<int>(metrics::AttemptResult::kNumConstants));
+}
+
+void MetricsReporterOmaha::ReportUpdateAttemptMetrics(
+ SystemState* system_state,
+ int attempt_number,
+ PayloadType payload_type,
+ base::TimeDelta duration,
+ base::TimeDelta duration_uptime,
+ int64_t payload_size,
+ metrics::AttemptResult attempt_result,
+ ErrorCode internal_error_code) {
+ string metric = metrics::kMetricAttemptNumber;
+ LOG(INFO) << "Uploading " << attempt_number << " for metric " << metric;
+ metrics_lib_->SendToUMA(metric,
+ attempt_number,
+ 0, // min: 0 attempts
+ 49, // max: 49 attempts
+ 50); // num_buckets
+
+ metric = metrics::kMetricAttemptPayloadType;
+ LOG(INFO) << "Uploading " << utils::ToString(payload_type) << " for metric "
+ << metric;
+ metrics_lib_->SendEnumToUMA(metric, payload_type, kNumPayloadTypes);
+
+ metric = metrics::kMetricAttemptDurationMinutes;
+ LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration)
+ << " for metric " << metric;
+ metrics_lib_->SendToUMA(metric,
+ duration.InMinutes(),
+ 0, // min: 0 min
+ 10 * 24 * 60, // max: 10 days
+ 50); // num_buckets
+
+ metric = metrics::kMetricAttemptDurationUptimeMinutes;
+ LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration_uptime)
+ << " for metric " << metric;
+ metrics_lib_->SendToUMA(metric,
+ duration_uptime.InMinutes(),
+ 0, // min: 0 min
+ 10 * 24 * 60, // max: 10 days
+ 50); // num_buckets
+
+ metric = metrics::kMetricAttemptPayloadSizeMiB;
+ int64_t payload_size_mib = payload_size / kNumBytesInOneMiB;
+ LOG(INFO) << "Uploading " << payload_size_mib << " for metric " << metric;
+ metrics_lib_->SendToUMA(metric,
+ payload_size_mib,
+ 0, // min: 0 MiB
+ 1024, // max: 1024 MiB = 1 GiB
+ 50); // num_buckets
+
+
+
+ metric = metrics::kMetricAttemptResult;
+ LOG(INFO) << "Uploading " << static_cast<int>(attempt_result)
+ << " for metric " << metric;
+ metrics_lib_->SendEnumToUMA(
+ metric,
+ static_cast<int>(attempt_result),
+ static_cast<int>(metrics::AttemptResult::kNumConstants));
+
+ if (internal_error_code != ErrorCode::kSuccess) {
+ metric = metrics::kMetricAttemptInternalErrorCode;
+ LOG(INFO) << "Uploading " << internal_error_code << " for metric "
+ << metric;
+ metrics_lib_->SendEnumToUMA(metric,
+ static_cast<int>(internal_error_code),
+ static_cast<int>(ErrorCode::kUmaReportedMax));
+ }
+
+ base::TimeDelta time_since_last;
+ if (metrics_utils::WallclockDurationHelper(
+ system_state,
+ kPrefsMetricsAttemptLastReportingTime,
+ &time_since_last)) {
+ metric = metrics::kMetricAttemptTimeSinceLastAttemptMinutes;
+ LOG(INFO) << "Sending " << utils::FormatTimeDelta(time_since_last)
+ << " for metric " << metric;
+ metrics_lib_->SendToUMA(metric,
+ time_since_last.InMinutes(),
+ 0, // min: 0 min
+ 30 * 24 * 60, // max: 30 days
+ 50); // num_buckets
+ }
+
+ static int64_t uptime_since_last_storage = 0;
+ base::TimeDelta uptime_since_last;
+ if (metrics_utils::MonotonicDurationHelper(
+ system_state, &uptime_since_last_storage, &uptime_since_last)) {
+ metric = metrics::kMetricAttemptTimeSinceLastAttemptUptimeMinutes;
+ LOG(INFO) << "Sending " << utils::FormatTimeDelta(uptime_since_last)
+ << " for metric " << metric;
+ metrics_lib_->SendToUMA(metric,
+ uptime_since_last.InMinutes(),
+ 0, // min: 0 min
+ 30 * 24 * 60, // max: 30 days
+ 50); // num_buckets
+ }
+}
+
+void MetricsReporterOmaha::ReportUpdateAttemptDownloadMetrics(
+ int64_t payload_bytes_downloaded,
+ int64_t payload_download_speed_bps,
+ DownloadSource download_source,
+ metrics::DownloadErrorCode payload_download_error_code,
+ metrics::ConnectionType connection_type) {
+ string metric = metrics::kMetricAttemptPayloadBytesDownloadedMiB;
+ int64_t payload_bytes_downloaded_mib =
+ payload_bytes_downloaded / kNumBytesInOneMiB;
+ LOG(INFO) << "Uploading " << payload_bytes_downloaded_mib << " for metric "
+ << metric;
+ metrics_lib_->SendToUMA(metric,
+ payload_bytes_downloaded_mib,
+ 0, // min: 0 MiB
+ 1024, // max: 1024 MiB = 1 GiB
+ 50); // num_buckets
+
+ metric = metrics::kMetricAttemptPayloadDownloadSpeedKBps;
+ int64_t payload_download_speed_kbps = payload_download_speed_bps / 1000;
+ LOG(INFO) << "Uploading " << payload_download_speed_kbps << " for metric "
+ << metric;
+ metrics_lib_->SendToUMA(metric,
+ payload_download_speed_kbps,
+ 0, // min: 0 kB/s
+ 10 * 1000, // max: 10000 kB/s = 10 MB/s
+ 50); // num_buckets
+
+ metric = metrics::kMetricAttemptDownloadSource;
+ LOG(INFO) << "Uploading " << download_source << " for metric " << metric;
+ metrics_lib_->SendEnumToUMA(metric, download_source, kNumDownloadSources);
+
+ if (payload_download_error_code != metrics::DownloadErrorCode::kUnset) {
+ metric = metrics::kMetricAttemptDownloadErrorCode;
+ LOG(INFO) << "Uploading " << static_cast<int>(payload_download_error_code)
+ << " for metric " << metric << " (sparse)";
+ metrics_lib_->SendSparseToUMA(
+ metric, static_cast<int>(payload_download_error_code));
+ }
+
+ metric = metrics::kMetricAttemptConnectionType;
+ LOG(INFO) << "Uploading " << static_cast<int>(connection_type)
+ << " for metric " << metric;
+ metrics_lib_->SendEnumToUMA(
+ metric,
+ static_cast<int>(connection_type),
+ static_cast<int>(metrics::ConnectionType::kNumConstants));
+}
+
+void MetricsReporterOmaha::ReportSuccessfulUpdateMetrics(
+ int attempt_count,
+ int updates_abandoned_count,
+ PayloadType payload_type,
+ int64_t payload_size,
+ int64_t num_bytes_downloaded[kNumDownloadSources],
+ int download_overhead_percentage,
+ base::TimeDelta total_duration,
+ int reboot_count,
+ int url_switch_count) {
+ string metric = metrics::kMetricSuccessfulUpdatePayloadSizeMiB;
+ int64_t mbs = payload_size / kNumBytesInOneMiB;
+ LOG(INFO) << "Uploading " << mbs << " (MiBs) for metric " << metric;
+ metrics_lib_->SendToUMA(metric,
+ mbs,
+ 0, // min: 0 MiB
+ 1024, // max: 1024 MiB = 1 GiB
+ 50); // num_buckets
+
+ int64_t total_bytes = 0;
+ int download_sources_used = 0;
+ for (int i = 0; i < kNumDownloadSources + 1; i++) {
+ DownloadSource source = static_cast<DownloadSource>(i);
+
+ // Only consider this download source (and send byte counts) as
+ // having been used if we downloaded a non-trivial amount of bytes
+ // (e.g. at least 1 MiB) that contributed to the
+ // update. Otherwise we're going to end up with a lot of zero-byte
+ // events in the histogram.
+
+ metric = metrics::kMetricSuccessfulUpdateBytesDownloadedMiB;
+ if (i < kNumDownloadSources) {
+ metric += utils::ToString(source);
+ mbs = num_bytes_downloaded[i] / kNumBytesInOneMiB;
+ total_bytes += num_bytes_downloaded[i];
+ if (mbs > 0)
+ download_sources_used |= (1 << i);
+ } else {
+ mbs = total_bytes / kNumBytesInOneMiB;
+ }
+
+ if (mbs > 0) {
+ LOG(INFO) << "Uploading " << mbs << " (MiBs) for metric " << metric;
+ metrics_lib_->SendToUMA(metric,
+ mbs,
+ 0, // min: 0 MiB
+ 1024, // max: 1024 MiB = 1 GiB
+ 50); // num_buckets
+ }
+ }
+
+ metric = metrics::kMetricSuccessfulUpdateDownloadSourcesUsed;
+ LOG(INFO) << "Uploading 0x" << std::hex << download_sources_used
+ << " (bit flags) for metric " << metric;
+ metrics_lib_->SendToUMA(metric,
+ download_sources_used,
+ 0, // min
+ (1 << kNumDownloadSources) - 1, // max
+ 1 << kNumDownloadSources); // num_buckets
+
+ metric = metrics::kMetricSuccessfulUpdateDownloadOverheadPercentage;
+ LOG(INFO) << "Uploading " << download_overhead_percentage << "% for metric "
+ << metric;
+ metrics_lib_->SendToUMA(metric,
+ download_overhead_percentage,
+ 0, // min: 0% overhead
+ 1000, // max: 1000% overhead
+ 50); // num_buckets
+
+ metric = metrics::kMetricSuccessfulUpdateUrlSwitchCount;
+ LOG(INFO) << "Uploading " << url_switch_count << " (count) for metric "
+ << metric;
+ metrics_lib_->SendToUMA(metric,
+ url_switch_count,
+ 0, // min: 0 URL switches
+ 49, // max: 49 URL switches
+ 50); // num_buckets
+
+ metric = metrics::kMetricSuccessfulUpdateTotalDurationMinutes;
+ LOG(INFO) << "Uploading " << utils::FormatTimeDelta(total_duration)
+ << " for metric " << metric;
+ metrics_lib_->SendToUMA(metric,
+ static_cast<int>(total_duration.InMinutes()),
+ 0, // min: 0 min
+ 365 * 24 * 60, // max: 365 days ~= 1 year
+ 50); // num_buckets
+
+ metric = metrics::kMetricSuccessfulUpdateRebootCount;
+ LOG(INFO) << "Uploading reboot count of " << reboot_count << " for metric "
+ << metric;
+ metrics_lib_->SendToUMA(metric,
+ reboot_count,
+ 0, // min: 0 reboots
+ 49, // max: 49 reboots
+ 50); // num_buckets
+
+ metric = metrics::kMetricSuccessfulUpdatePayloadType;
+ metrics_lib_->SendEnumToUMA(metric, payload_type, kNumPayloadTypes);
+ LOG(INFO) << "Uploading " << utils::ToString(payload_type) << " for metric "
+ << metric;
+
+ metric = metrics::kMetricSuccessfulUpdateAttemptCount;
+ metrics_lib_->SendToUMA(metric,
+ attempt_count,
+ 1, // min: 1 attempt
+ 50, // max: 50 attempts
+ 50); // num_buckets
+ LOG(INFO) << "Uploading " << attempt_count << " for metric " << metric;
+
+ metric = metrics::kMetricSuccessfulUpdateUpdatesAbandonedCount;
+ LOG(INFO) << "Uploading " << updates_abandoned_count << " (count) for metric "
+ << metric;
+ metrics_lib_->SendToUMA(metric,
+ updates_abandoned_count,
+ 0, // min: 0 counts
+ 49, // max: 49 counts
+ 50); // num_buckets
+}
+
+void MetricsReporterOmaha::ReportRollbackMetrics(
+ metrics::RollbackResult result) {
+ string metric = metrics::kMetricRollbackResult;
+ int value = static_cast<int>(result);
+ LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)";
+ metrics_lib_->SendEnumToUMA(
+ metric, value, static_cast<int>(metrics::RollbackResult::kNumConstants));
+}
+
+void MetricsReporterOmaha::ReportCertificateCheckMetrics(
+ ServerToCheck server_to_check, CertificateCheckResult result) {
+ string metric;
+ switch (server_to_check) {
+ case ServerToCheck::kUpdate:
+ metric = metrics::kMetricCertificateCheckUpdateCheck;
+ break;
+ case ServerToCheck::kDownload:
+ metric = metrics::kMetricCertificateCheckDownload;
+ break;
+ case ServerToCheck::kNone:
+ return;
+ }
+ LOG(INFO) << "Uploading " << static_cast<int>(result) << " for metric "
+ << metric;
+ metrics_lib_->SendEnumToUMA(
+ metric,
+ static_cast<int>(result),
+ static_cast<int>(CertificateCheckResult::kNumConstants));
+}
+
+void MetricsReporterOmaha::ReportFailedUpdateCount(int target_attempt) {
+ string metric = metrics::kMetricFailedUpdateCount;
+ metrics_lib_->SendToUMA(metric,
+ target_attempt,
+ 1, // min value
+ 50, // max value
+ kNumDefaultUmaBuckets);
+
+ LOG(INFO) << "Uploading " << target_attempt << " (count) for metric "
+ << metric;
+}
+
+void MetricsReporterOmaha::ReportTimeToReboot(int time_to_reboot_minutes) {
+ string metric = metrics::kMetricTimeToRebootMinutes;
+ metrics_lib_->SendToUMA(metric,
+ time_to_reboot_minutes,
+ 0, // min: 0 minute
+ 30 * 24 * 60, // max: 1 month (approx)
+ kNumDefaultUmaBuckets);
+
+ LOG(INFO) << "Uploading " << time_to_reboot_minutes << " for metric "
+ << metric;
+}
+
+void MetricsReporterOmaha::ReportInstallDateProvisioningSource(int source,
+ int max) {
+ metrics_lib_->SendEnumToUMA(metrics::kMetricInstallDateProvisioningSource,
+ source, // Sample.
+ max);
+}
+
+} // namespace chromeos_update_engine
diff --git a/metrics_reporter_omaha.h b/metrics_reporter_omaha.h
new file mode 100644
index 0000000..c19fe86
--- /dev/null
+++ b/metrics_reporter_omaha.h
@@ -0,0 +1,156 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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.
+//
+
+#ifndef UPDATE_ENGINE_METRICS_REPORTER_OMAHA_H_
+#define UPDATE_ENGINE_METRICS_REPORTER_OMAHA_H_
+
+#include <memory>
+
+#include <base/time/time.h>
+#include <metrics/metrics_library.h>
+
+#include "update_engine/certificate_checker.h"
+#include "update_engine/common/constants.h"
+#include "update_engine/common/error_code.h"
+#include "update_engine/metrics_constants.h"
+#include "update_engine/metrics_reporter_interface.h"
+#include "update_engine/system_state.h"
+
+namespace chromeos_update_engine {
+
+class SystemState;
+
+namespace metrics {
+
+// UpdateEngine.Daily.* metrics.
+extern const char kMetricDailyOSAgeDays[];
+
+// UpdateEngine.Check.* metrics.
+extern const char kMetricCheckDownloadErrorCode[];
+extern const char kMetricCheckReaction[];
+extern const char kMetricCheckResult[];
+extern const char kMetricCheckTimeSinceLastCheckMinutes[];
+extern const char kMetricCheckTimeSinceLastCheckUptimeMinutes[];
+
+// UpdateEngine.Attempt.* metrics.
+extern const char kMetricAttemptNumber[];
+extern const char kMetricAttemptPayloadType[];
+extern const char kMetricAttemptPayloadSizeMiB[];
+extern const char kMetricAttemptConnectionType[];
+extern const char kMetricAttemptDurationMinutes[];
+extern const char kMetricAttemptDurationUptimeMinutes[];
+extern const char kMetricAttemptTimeSinceLastAttemptMinutes[];
+extern const char kMetricAttemptTimeSinceLastAttemptUptimeMinutes[];
+extern const char kMetricAttemptPayloadBytesDownloadedMiB[];
+extern const char kMetricAttemptPayloadDownloadSpeedKBps[];
+extern const char kMetricAttemptDownloadSource[];
+extern const char kMetricAttemptResult[];
+extern const char kMetricAttemptInternalErrorCode[];
+extern const char kMetricAttemptDownloadErrorCode[];
+
+// UpdateEngine.SuccessfulUpdate.* metrics.
+extern const char kMetricSuccessfulUpdateAttemptCount[];
+extern const char kMetricSuccessfulUpdateBytesDownloadedMiB[];
+extern const char kMetricSuccessfulUpdateDownloadOverheadPercentage[];
+extern const char kMetricSuccessfulUpdateDownloadSourcesUsed[];
+extern const char kMetricSuccessfulUpdatePayloadType[];
+extern const char kMetricSuccessfulUpdatePayloadSizeMiB[];
+extern const char kMetricSuccessfulUpdateRebootCount[];
+extern const char kMetricSuccessfulUpdateTotalDurationMinutes[];
+extern const char kMetricSuccessfulUpdateUpdatesAbandonedCount[];
+extern const char kMetricSuccessfulUpdateUrlSwitchCount[];
+
+// UpdateEngine.Rollback.* metric.
+extern const char kMetricRollbackResult[];
+
+// UpdateEngine.CertificateCheck.* metrics.
+extern const char kMetricCertificateCheckUpdateCheck[];
+extern const char kMetricCertificateCheckDownload[];
+
+// UpdateEngine.* metrics.
+extern const char kMetricFailedUpdateCount[];
+extern const char kMetricInstallDateProvisioningSource[];
+extern const char kMetricTimeToRebootMinutes[];
+
+} // namespace metrics
+
+class MetricsReporterOmaha : public MetricsReporterInterface {
+ public:
+ MetricsReporterOmaha();
+
+ ~MetricsReporterOmaha() override = default;
+
+ void Initialize() override;
+
+ void ReportRollbackMetrics(metrics::RollbackResult result) override;
+
+ void ReportDailyMetrics(base::TimeDelta os_age) override;
+
+ void ReportUpdateCheckMetrics(
+ SystemState* system_state,
+ metrics::CheckResult result,
+ metrics::CheckReaction reaction,
+ metrics::DownloadErrorCode download_error_code) override;
+
+ void ReportUpdateAttemptMetrics(SystemState* system_state,
+ int attempt_number,
+ PayloadType payload_type,
+ base::TimeDelta duration,
+ base::TimeDelta duration_uptime,
+ int64_t payload_size,
+ metrics::AttemptResult attempt_result,
+ ErrorCode internal_error_code) override;
+
+ void ReportUpdateAttemptDownloadMetrics(
+ int64_t payload_bytes_downloaded,
+ int64_t payload_download_speed_bps,
+ DownloadSource download_source,
+ metrics::DownloadErrorCode payload_download_error_code,
+ metrics::ConnectionType connection_type) override;
+
+ void ReportAbnormallyTerminatedUpdateAttemptMetrics() override;
+
+ void ReportSuccessfulUpdateMetrics(
+ int attempt_count,
+ int updates_abandoned_count,
+ PayloadType payload_type,
+ int64_t payload_size,
+ int64_t num_bytes_downloaded[kNumDownloadSources],
+ int download_overhead_percentage,
+ base::TimeDelta total_duration,
+ int reboot_count,
+ int url_switch_count) override;
+
+ void ReportCertificateCheckMetrics(ServerToCheck server_to_check,
+ CertificateCheckResult result) override;
+
+ void ReportFailedUpdateCount(int target_attempt) override;
+
+ void ReportTimeToReboot(int time_to_reboot_minutes) override;
+
+ void ReportInstallDateProvisioningSource(int source, int max) override;
+
+ private:
+ friend class MetricsReporterOmahaTest;
+
+ std::unique_ptr<MetricsLibraryInterface> metrics_lib_;
+
+ DISALLOW_COPY_AND_ASSIGN(MetricsReporterOmaha);
+}; // class metrics
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_METRICS_REPORTER_OMAHA_H_
diff --git a/metrics_reporter_omaha_unittest.cc b/metrics_reporter_omaha_unittest.cc
new file mode 100644
index 0000000..76e33c6
--- /dev/null
+++ b/metrics_reporter_omaha_unittest.cc
@@ -0,0 +1,394 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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 "update_engine/metrics_reporter_omaha.h"
+
+#include <memory>
+#include <string>
+
+#include <base/time/time.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <metrics/metrics_library_mock.h>
+
+#include "update_engine/common/fake_clock.h"
+#include "update_engine/common/fake_prefs.h"
+#include "update_engine/fake_system_state.h"
+
+using base::TimeDelta;
+using testing::AnyNumber;
+using testing::_;
+
+namespace chromeos_update_engine {
+class MetricsReporterOmahaTest : public ::testing::Test {
+ protected:
+ MetricsReporterOmahaTest() = default;
+
+ // Reset the metrics_lib_ to a mock library.
+ void SetUp() override {
+ mock_metrics_lib_ = new testing::NiceMock<MetricsLibraryMock>();
+ reporter_.metrics_lib_.reset(mock_metrics_lib_);
+ }
+
+ testing::NiceMock<MetricsLibraryMock>* mock_metrics_lib_;
+ MetricsReporterOmaha reporter_;
+};
+
+TEST_F(MetricsReporterOmahaTest, ReportDailyMetrics) {
+ TimeDelta age = TimeDelta::FromDays(10);
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendToUMA(metrics::kMetricDailyOSAgeDays, _, _, _, _))
+ .Times(1);
+
+ reporter_.ReportDailyMetrics(age);
+}
+
+TEST_F(MetricsReporterOmahaTest, ReportUpdateCheckMetrics) {
+ FakeSystemState fake_system_state;
+ FakeClock fake_clock;
+ FakePrefs fake_prefs;
+
+ // We need to execute the report twice to test the time since last report.
+ fake_system_state.set_clock(&fake_clock);
+ fake_system_state.set_prefs(&fake_prefs);
+ fake_clock.SetWallclockTime(base::Time::FromInternalValue(1000000));
+ fake_clock.SetMonotonicTime(base::Time::FromInternalValue(1000000));
+
+ metrics::CheckResult result = metrics::CheckResult::kUpdateAvailable;
+ metrics::CheckReaction reaction = metrics::CheckReaction::kIgnored;
+ metrics::DownloadErrorCode error_code =
+ metrics::DownloadErrorCode::kHttpStatus200;
+
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendEnumToUMA(metrics::kMetricCheckResult, static_cast<int>(result), _))
+ .Times(2);
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendEnumToUMA(
+ metrics::kMetricCheckReaction, static_cast<int>(reaction), _))
+ .Times(2);
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendSparseToUMA(metrics::kMetricCheckDownloadErrorCode,
+ static_cast<int>(error_code)))
+ .Times(2);
+
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendToUMA(metrics::kMetricCheckTimeSinceLastCheckMinutes, 1, _, _, _))
+ .Times(1);
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendToUMA(
+ metrics::kMetricCheckTimeSinceLastCheckUptimeMinutes, 1, _, _, _))
+ .Times(1);
+
+ reporter_.ReportUpdateCheckMetrics(
+ &fake_system_state, result, reaction, error_code);
+
+ // Advance the clock by 1 minute and report the same metrics again.
+ fake_clock.SetWallclockTime(base::Time::FromInternalValue(61000000));
+ fake_clock.SetMonotonicTime(base::Time::FromInternalValue(61000000));
+ reporter_.ReportUpdateCheckMetrics(
+ &fake_system_state, result, reaction, error_code);
+}
+
+TEST_F(MetricsReporterOmahaTest,
+ ReportAbnormallyTerminatedUpdateAttemptMetrics) {
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendEnumToUMA(metrics::kMetricAttemptResult,
+ static_cast<int>(
+ metrics::AttemptResult::kAbnormalTermination),
+ _))
+ .Times(1);
+
+ reporter_.ReportAbnormallyTerminatedUpdateAttemptMetrics();
+}
+
+TEST_F(MetricsReporterOmahaTest, ReportUpdateAttemptMetrics) {
+ FakeSystemState fake_system_state;
+ FakeClock fake_clock;
+ FakePrefs fake_prefs;
+
+ fake_system_state.set_clock(&fake_clock);
+ fake_system_state.set_prefs(&fake_prefs);
+ fake_clock.SetWallclockTime(base::Time::FromInternalValue(1000000));
+ fake_clock.SetMonotonicTime(base::Time::FromInternalValue(1000000));
+
+ int attempt_number = 1;
+ PayloadType payload_type = kPayloadTypeFull;
+ TimeDelta duration = TimeDelta::FromMinutes(1000);
+ TimeDelta duration_uptime = TimeDelta::FromMinutes(1000);
+
+ int64_t payload_size = 100 * kNumBytesInOneMiB;
+
+ metrics::AttemptResult attempt_result =
+ metrics::AttemptResult::kInternalError;
+ ErrorCode internal_error_code = ErrorCode::kDownloadInvalidMetadataSignature;
+
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendToUMA(metrics::kMetricAttemptNumber, attempt_number, _, _, _))
+ .Times(2);
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendEnumToUMA(metrics::kMetricAttemptPayloadType,
+ static_cast<int>(payload_type),
+ _))
+ .Times(2);
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendToUMA(metrics::kMetricAttemptDurationMinutes,
+ duration.InMinutes(),
+ _,
+ _,
+ _))
+ .Times(2);
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendToUMA(metrics::kMetricAttemptDurationUptimeMinutes,
+ duration_uptime.InMinutes(),
+ _,
+ _,
+ _))
+ .Times(2);
+
+
+ // Check the report of attempt result.
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendEnumToUMA(
+ metrics::kMetricAttemptResult, static_cast<int>(attempt_result), _))
+ .Times(2);
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendEnumToUMA(metrics::kMetricAttemptInternalErrorCode,
+ static_cast<int>(internal_error_code),
+ _))
+ .Times(2);
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendToUMA(metrics::kMetricAttemptPayloadSizeMiB, 100, _, _, _))
+ .Times(2);
+
+ // Check the duration between two reports.
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendToUMA(metrics::kMetricAttemptTimeSinceLastAttemptMinutes, 1, _, _, _))
+ .Times(1);
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendToUMA(
+ metrics::kMetricAttemptTimeSinceLastAttemptUptimeMinutes, 1, _, _, _))
+ .Times(1);
+
+ reporter_.ReportUpdateAttemptMetrics(&fake_system_state,
+ attempt_number,
+ payload_type,
+ duration,
+ duration_uptime,
+ payload_size,
+ attempt_result,
+ internal_error_code);
+
+ // Advance the clock by 1 minute and report the same metrics again.
+ fake_clock.SetWallclockTime(base::Time::FromInternalValue(61000000));
+ fake_clock.SetMonotonicTime(base::Time::FromInternalValue(61000000));
+ reporter_.ReportUpdateAttemptMetrics(&fake_system_state,
+ attempt_number,
+ payload_type,
+ duration,
+ duration_uptime,
+ payload_size,
+ attempt_result,
+ internal_error_code);
+}
+
+TEST_F(MetricsReporterOmahaTest, ReportUpdateAttemptDownloadMetrics) {
+ int64_t payload_bytes_downloaded = 200 * kNumBytesInOneMiB;
+ int64_t payload_download_speed_bps = 100 * 1000;
+ DownloadSource download_source = kDownloadSourceHttpServer;
+ metrics::DownloadErrorCode payload_download_error_code =
+ metrics::DownloadErrorCode::kDownloadError;
+ metrics::ConnectionType connection_type = metrics::ConnectionType::kCellular;
+
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendToUMA(metrics::kMetricAttemptPayloadBytesDownloadedMiB, 200, _, _, _))
+ .Times(1);
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendToUMA(metrics::kMetricAttemptPayloadDownloadSpeedKBps, 100, _, _, _))
+ .Times(1);
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendEnumToUMA(metrics::kMetricAttemptDownloadSource,
+ static_cast<int>(download_source),
+ _))
+ .Times(1);
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendSparseToUMA(metrics::kMetricAttemptDownloadErrorCode,
+ static_cast<int>(payload_download_error_code)))
+ .Times(1);
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendEnumToUMA(metrics::kMetricAttemptConnectionType,
+ static_cast<int>(connection_type),
+ _))
+ .Times(1);
+
+ reporter_.ReportUpdateAttemptDownloadMetrics(payload_bytes_downloaded,
+ payload_download_speed_bps,
+ download_source,
+ payload_download_error_code,
+ connection_type);
+}
+
+TEST_F(MetricsReporterOmahaTest, ReportSuccessfulUpdateMetrics) {
+ int attempt_count = 3;
+ int updates_abandoned_count = 2;
+ PayloadType payload_type = kPayloadTypeDelta;
+ int64_t payload_size = 200 * kNumBytesInOneMiB;
+ int64_t num_bytes_downloaded[kNumDownloadSources] = {};
+ // 200MiB payload downloaded from HttpsServer.
+ num_bytes_downloaded[0] = 200 * kNumBytesInOneMiB;
+ int download_overhead_percentage = 20;
+ TimeDelta total_duration = TimeDelta::FromMinutes(30);
+ int reboot_count = 2;
+ int url_switch_count = 2;
+
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendToUMA(metrics::kMetricSuccessfulUpdatePayloadSizeMiB, 200, _, _, _))
+ .Times(1);
+
+ // Check the report to both BytesDownloadedMiBHttpsServer and
+ // BytesDownloadedMiB
+ std::string DownloadedMiBMetric =
+ metrics::kMetricSuccessfulUpdateBytesDownloadedMiB;
+ DownloadedMiBMetric += "HttpsServer";
+ EXPECT_CALL(*mock_metrics_lib_, SendToUMA(DownloadedMiBMetric, 200, _, _, _))
+ .Times(1);
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendToUMA(
+ metrics::kMetricSuccessfulUpdateBytesDownloadedMiB, 200, _, _, _))
+ .Times(1);
+
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendToUMA(
+ metrics::kMetricSuccessfulUpdateDownloadSourcesUsed, 1, _, _, _))
+ .Times(1);
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendToUMA(metrics::kMetricSuccessfulUpdateDownloadOverheadPercentage,
+ 20,
+ _,
+ _,
+ _));
+
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendToUMA(metrics::kMetricSuccessfulUpdateUrlSwitchCount,
+ url_switch_count,
+ _,
+ _,
+ _))
+ .Times(1);
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendToUMA(
+ metrics::kMetricSuccessfulUpdateTotalDurationMinutes, 30, _, _, _))
+ .Times(1);
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendToUMA(
+ metrics::kMetricSuccessfulUpdateRebootCount, reboot_count, _, _, _))
+ .Times(1);
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendEnumToUMA(
+ metrics::kMetricSuccessfulUpdatePayloadType, payload_type, _))
+ .Times(1);
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendToUMA(
+ metrics::kMetricSuccessfulUpdateAttemptCount, attempt_count, _, _, _))
+ .Times(1);
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendToUMA(metrics::kMetricSuccessfulUpdateUpdatesAbandonedCount,
+ updates_abandoned_count,
+ _,
+ _,
+ _))
+ .Times(1);
+
+ reporter_.ReportSuccessfulUpdateMetrics(attempt_count,
+ updates_abandoned_count,
+ payload_type,
+ payload_size,
+ num_bytes_downloaded,
+ download_overhead_percentage,
+ total_duration,
+ reboot_count,
+ url_switch_count);
+}
+
+TEST_F(MetricsReporterOmahaTest, ReportRollbackMetrics) {
+ metrics::RollbackResult result = metrics::RollbackResult::kSuccess;
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendEnumToUMA(
+ metrics::kMetricRollbackResult, static_cast<int>(result), _))
+ .Times(1);
+
+ reporter_.ReportRollbackMetrics(result);
+}
+
+TEST_F(MetricsReporterOmahaTest, ReportCertificateCheckMetrics) {
+ ServerToCheck server_to_check = ServerToCheck::kUpdate;
+ CertificateCheckResult result = CertificateCheckResult::kValid;
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendEnumToUMA(metrics::kMetricCertificateCheckUpdateCheck,
+ static_cast<int>(result),
+ _))
+ .Times(1);
+
+ reporter_.ReportCertificateCheckMetrics(server_to_check, result);
+}
+
+TEST_F(MetricsReporterOmahaTest, ReportFailedUpdateCount) {
+ int target_attempt = 3;
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendToUMA(metrics::kMetricFailedUpdateCount, target_attempt, _, _, _))
+ .Times(1);
+
+ reporter_.ReportFailedUpdateCount(target_attempt);
+}
+
+TEST_F(MetricsReporterOmahaTest, ReportTimeToReboot) {
+ int time_to_reboot_minutes = 1000;
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendToUMA(
+ metrics::kMetricTimeToRebootMinutes, time_to_reboot_minutes, _, _, _))
+ .Times(1);
+
+ reporter_.ReportTimeToReboot(time_to_reboot_minutes);
+}
+
+TEST_F(MetricsReporterOmahaTest, ReportInstallDateProvisioningSource) {
+ int source = 2;
+ int max = 5;
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendEnumToUMA(metrics::kMetricInstallDateProvisioningSource, source, max))
+ .Times(1);
+
+ reporter_.ReportInstallDateProvisioningSource(source, max);
+}
+
+} // namespace chromeos_update_engine
diff --git a/metrics_reporter_stub.cc b/metrics_reporter_stub.cc
new file mode 100644
index 0000000..81664a5
--- /dev/null
+++ b/metrics_reporter_stub.cc
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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 "update_engine/metrics_reporter_stub.h"
+
+#include <memory>
+
+namespace chromeos_update_engine {
+
+namespace metrics {
+
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter() {
+ return std::make_unique<MetricsReporterStub>();
+}
+
+} // namespace metrics
+
+} // namespace chromeos_update_engine
diff --git a/metrics_reporter_stub.h b/metrics_reporter_stub.h
new file mode 100644
index 0000000..d0f75ab
--- /dev/null
+++ b/metrics_reporter_stub.h
@@ -0,0 +1,88 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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.
+//
+
+#ifndef UPDATE_ENGINE_METRICS_REPORTER_STUB_H_
+#define UPDATE_ENGINE_METRICS_REPORTER_STUB_H_
+
+#include "update_engine/common/error_code.h"
+#include "update_engine/metrics_constants.h"
+#include "update_engine/metrics_reporter_interface.h"
+
+namespace chromeos_update_engine {
+
+class MetricsReporterStub : public MetricsReporterInterface {
+ public:
+ MetricsReporterStub() = default;
+
+ ~MetricsReporterStub() override = default;
+
+ void Initialize() override {}
+
+ void ReportRollbackMetrics(metrics::RollbackResult result) override {}
+
+ void ReportDailyMetrics(base::TimeDelta os_age) override {}
+
+ void ReportUpdateCheckMetrics(
+ SystemState* system_state,
+ metrics::CheckResult result,
+ metrics::CheckReaction reaction,
+ metrics::DownloadErrorCode download_error_code) override {}
+
+ void ReportUpdateAttemptMetrics(SystemState* system_state,
+ int attempt_number,
+ PayloadType payload_type,
+ base::TimeDelta duration,
+ base::TimeDelta duration_uptime,
+ int64_t payload_size,
+ metrics::AttemptResult attempt_result,
+ ErrorCode internal_error_code) override {}
+
+ void ReportUpdateAttemptDownloadMetrics(
+ int64_t payload_bytes_downloaded,
+ int64_t payload_download_speed_bps,
+ DownloadSource download_source,
+ metrics::DownloadErrorCode payload_download_error_code,
+ metrics::ConnectionType connection_type) override {}
+
+ void ReportAbnormallyTerminatedUpdateAttemptMetrics() override {}
+
+ void ReportSuccessfulUpdateMetrics(
+ int attempt_count,
+ int updates_abandoned_count,
+ PayloadType payload_type,
+ int64_t payload_size,
+ int64_t num_bytes_downloaded[kNumDownloadSources],
+ int download_overhead_percentage,
+ base::TimeDelta total_duration,
+ int reboot_count,
+ int url_switch_count) override {}
+
+ void ReportCertificateCheckMetrics(ServerToCheck server_to_check,
+ CertificateCheckResult result) override {}
+
+ void ReportFailedUpdateCount(int target_attempt) override {}
+
+ void ReportTimeToReboot(int time_to_reboot_minutes) override {}
+
+ void ReportInstallDateProvisioningSource(int source, int max) override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MetricsReporterStub);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_METRICS_REPORTER_STUB_H_
diff --git a/metrics_utils.cc b/metrics_utils.cc
index 15e3a8a..46530f0 100644
--- a/metrics_utils.cc
+++ b/metrics_utils.cc
@@ -21,7 +21,8 @@
#include <base/time/time.h>
#include "update_engine/common/clock_interface.h"
-#include "update_engine/common/prefs_interface.h"
+#include "update_engine/common/constants.h"
+#include "update_engine/common/utils.h"
#include "update_engine/system_state.h"
using base::Time;
@@ -38,6 +39,9 @@
case ErrorCode::kSuccess:
return metrics::AttemptResult::kUpdateSucceeded;
+ case ErrorCode::kUpdatedButNotActive:
+ return metrics::AttemptResult::kUpdateSucceededNotActive;
+
case ErrorCode::kDownloadTransferError:
return metrics::AttemptResult::kPayloadDownloadError;
@@ -74,6 +78,7 @@
case ErrorCode::kDownloadPayloadVerificationError:
case ErrorCode::kSignedDeltaPayloadExpectedError:
case ErrorCode::kDownloadPayloadPubKeyVerificationError:
+ case ErrorCode::kPayloadTimestampError:
return metrics::AttemptResult::kPayloadVerificationFailed;
case ErrorCode::kNewRootfsVerificationError:
@@ -109,7 +114,6 @@
case ErrorCode::kPostinstallPowerwashError:
case ErrorCode::kUpdateCanceledByChannelChange:
case ErrorCode::kOmahaRequestXMLHasEntityDecl:
- case ErrorCode::kOmahaUpdateIgnoredOverCellular:
return metrics::AttemptResult::kInternalError;
// Special flags. These can't happen (we mask them out above) but
@@ -210,7 +214,8 @@
case ErrorCode::kOmahaRequestXMLHasEntityDecl:
case ErrorCode::kFilesystemVerifierError:
case ErrorCode::kUserCanceled:
- case ErrorCode::kOmahaUpdateIgnoredOverCellular:
+ case ErrorCode::kPayloadTimestampError:
+ case ErrorCode::kUpdatedButNotActive:
break;
// Special flags. These can't happen (we mask them out above) but
@@ -307,5 +312,76 @@
return ret;
}
+int64_t GetPersistedValue(const std::string& key, PrefsInterface* prefs) {
+ CHECK(prefs);
+ if (!prefs->Exists(key))
+ return 0;
+
+ int64_t stored_value;
+ if (!prefs->GetInt64(key, &stored_value))
+ return 0;
+
+ if (stored_value < 0) {
+ LOG(ERROR) << key << ": Invalid value (" << stored_value
+ << ") in persisted state. Defaulting to 0";
+ return 0;
+ }
+
+ return stored_value;
+}
+
+void SetNumReboots(int64_t num_reboots, PrefsInterface* prefs) {
+ CHECK(prefs);
+ prefs->SetInt64(kPrefsNumReboots, num_reboots);
+ LOG(INFO) << "Number of Reboots during current update attempt = "
+ << num_reboots;
+}
+
+void SetPayloadAttemptNumber(int64_t payload_attempt_number,
+ PrefsInterface* prefs) {
+ CHECK(prefs);
+ prefs->SetInt64(kPrefsPayloadAttemptNumber, payload_attempt_number);
+ LOG(INFO) << "Payload Attempt Number = " << payload_attempt_number;
+}
+
+void SetSystemUpdatedMarker(ClockInterface* clock, PrefsInterface* prefs) {
+ CHECK(prefs);
+ CHECK(clock);
+ Time update_finish_time = clock->GetMonotonicTime();
+ prefs->SetInt64(kPrefsSystemUpdatedMarker,
+ update_finish_time.ToInternalValue());
+ LOG(INFO) << "Updated Marker = " << utils::ToString(update_finish_time);
+}
+
+void SetUpdateTimestampStart(const Time& update_start_time,
+ PrefsInterface* prefs) {
+ CHECK(prefs);
+ prefs->SetInt64(kPrefsUpdateTimestampStart,
+ update_start_time.ToInternalValue());
+ LOG(INFO) << "Update Timestamp Start = "
+ << utils::ToString(update_start_time);
+}
+
+bool LoadAndReportTimeToReboot(MetricsReporterInterface* metrics_reporter,
+ PrefsInterface* prefs,
+ ClockInterface* clock) {
+ CHECK(prefs);
+ CHECK(clock);
+ int64_t stored_value = GetPersistedValue(kPrefsSystemUpdatedMarker, prefs);
+ if (stored_value == 0)
+ return false;
+
+ Time system_updated_at = Time::FromInternalValue(stored_value);
+ base::TimeDelta time_to_reboot =
+ clock->GetMonotonicTime() - system_updated_at;
+ if (time_to_reboot.ToInternalValue() < 0) {
+ LOG(ERROR) << "time_to_reboot is negative - system_updated_at: "
+ << utils::ToString(system_updated_at);
+ return false;
+ }
+ metrics_reporter->ReportTimeToReboot(time_to_reboot.InMinutes());
+ return true;
+}
+
} // namespace metrics_utils
} // namespace chromeos_update_engine
diff --git a/metrics_utils.h b/metrics_utils.h
index d9826c1..d08cc4a 100644
--- a/metrics_utils.h
+++ b/metrics_utils.h
@@ -17,8 +17,16 @@
#ifndef UPDATE_ENGINE_METRICS_UTILS_H_
#define UPDATE_ENGINE_METRICS_UTILS_H_
+#include <string>
+
+#include <base/time/time.h>
+
+#include "update_engine/common/clock_interface.h"
+#include "update_engine/common/error_code.h"
+#include "update_engine/common/prefs_interface.h"
#include "update_engine/connection_utils.h"
-#include "update_engine/metrics.h"
+#include "update_engine/metrics_constants.h"
+#include "update_engine/metrics_reporter_interface.h"
namespace chromeos_update_engine {
@@ -65,6 +73,33 @@
int64_t* storage,
base::TimeDelta* out_duration);
+// Returns the persisted value from prefs for the given key. It also
+// validates that the value returned is non-negative.
+int64_t GetPersistedValue(const std::string& key, PrefsInterface* prefs);
+
+// Persists the reboot count of the update attempt to |kPrefsNumReboots|.
+void SetNumReboots(int64_t num_reboots, PrefsInterface* prefs);
+
+// Persists the payload attempt number to |kPrefsPayloadAttemptNumber|.
+void SetPayloadAttemptNumber(int64_t payload_attempt_number,
+ PrefsInterface* prefs);
+
+// Persists the finished time of an update to the |kPrefsSystemUpdatedMarker|.
+void SetSystemUpdatedMarker(ClockInterface* clock, PrefsInterface* prefs);
+
+// Persists the start time of an update to |kPrefsUpdateTimestampStart|.
+void SetUpdateTimestampStart(const base::Time& update_start_time,
+ PrefsInterface* prefs);
+
+// Called at program startup if the device booted into a new update.
+// The |time_to_reboot| parameter contains the (monotonic-clock) duration
+// from when the update successfully completed (the value in
+// |kPrefsSystemUpdatedMarker|) until the device was booted into the update
+// (current monotonic-clock time).
+bool LoadAndReportTimeToReboot(MetricsReporterInterface* metrics_reporter,
+ PrefsInterface* prefs,
+ ClockInterface* clock);
+
} // namespace metrics_utils
} // namespace chromeos_update_engine
diff --git a/mock_connection_manager.h b/mock_connection_manager.h
index 2fff68c..e37460b 100644
--- a/mock_connection_manager.h
+++ b/mock_connection_manager.h
@@ -36,7 +36,6 @@
MOCK_CONST_METHOD2(IsUpdateAllowedOver,
bool(ConnectionType type, ConnectionTethering tethering));
- MOCK_CONST_METHOD0(IsAllowedConnectionTypesForUpdateSet, bool());
};
} // namespace chromeos_update_engine
diff --git a/mock_file_writer.h b/mock_file_writer.h
index 72d6a86..26cd45d 100644
--- a/mock_file_writer.h
+++ b/mock_file_writer.h
@@ -24,7 +24,8 @@
class MockFileWriter : public FileWriter {
public:
- MOCK_METHOD2(Write, ssize_t(const void* bytes, size_t count));
+ MOCK_METHOD2(Write, bool(const void* bytes, size_t count));
+ MOCK_METHOD3(Write, bool(const void* bytes, size_t count, ErrorCode* error));
MOCK_METHOD0(Close, int());
};
diff --git a/mock_metrics_reporter.h b/mock_metrics_reporter.h
new file mode 100644
index 0000000..a0f164b
--- /dev/null
+++ b/mock_metrics_reporter.h
@@ -0,0 +1,83 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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.
+//
+
+#ifndef UPDATE_ENGINE_MOCK_METRICS_REPORTER_H
+#define UPDATE_ENGINE_MOCK_METRICS_REPORTER_H
+
+#include <gmock/gmock.h>
+
+#include "update_engine/metrics_reporter_interface.h"
+
+namespace chromeos_update_engine {
+
+class MockMetricsReporter : public MetricsReporterInterface {
+ public:
+ MOCK_METHOD0(Initialize, void());
+
+ MOCK_METHOD1(ReportRollbackMetrics, void(metrics::RollbackResult result));
+
+ MOCK_METHOD1(ReportDailyMetrics, void(base::TimeDelta os_age));
+
+ MOCK_METHOD4(ReportUpdateCheckMetrics,
+ void(SystemState* system_state,
+ metrics::CheckResult result,
+ metrics::CheckReaction reaction,
+ metrics::DownloadErrorCode download_error_code));
+
+ MOCK_METHOD8(ReportUpdateAttemptMetrics,
+ void(SystemState* system_state,
+ int attempt_number,
+ PayloadType payload_type,
+ base::TimeDelta duration,
+ base::TimeDelta duration_uptime,
+ int64_t payload_size,
+ metrics::AttemptResult attempt_result,
+ ErrorCode internal_error_code));
+
+ MOCK_METHOD5(ReportUpdateAttemptDownloadMetrics,
+ void(int64_t payload_bytes_downloaded,
+ int64_t payload_download_speed_bps,
+ DownloadSource download_source,
+ metrics::DownloadErrorCode payload_download_error_code,
+ metrics::ConnectionType connection_type));
+
+ MOCK_METHOD0(ReportAbnormallyTerminatedUpdateAttemptMetrics, void());
+
+ MOCK_METHOD9(ReportSuccessfulUpdateMetrics,
+ void(int attempt_count,
+ int updates_abandoned_count,
+ PayloadType payload_type,
+ int64_t payload_size,
+ int64_t num_bytes_downloaded[kNumDownloadSources],
+ int download_overhead_percentage,
+ base::TimeDelta total_duration,
+ int reboot_count,
+ int url_switch_count));
+
+ MOCK_METHOD2(ReportCertificateCheckMetrics,
+ void(ServerToCheck server_to_check,
+ CertificateCheckResult result));
+
+ MOCK_METHOD1(ReportFailedUpdateCount, void(int target_attempt));
+
+ MOCK_METHOD1(ReportTimeToReboot, void(int time_to_reboot_minutes));
+
+ MOCK_METHOD2(ReportInstallDateProvisioningSource, void(int source, int max));
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_MOCK_METRICS_REPORTER_H
diff --git a/mock_payload_state.h b/mock_payload_state.h
index 2f654c7..6dccc64 100644
--- a/mock_payload_state.h
+++ b/mock_payload_state.h
@@ -52,6 +52,7 @@
MOCK_METHOD1(SetUsingP2PForSharing, void(bool value));
MOCK_METHOD1(SetScatteringWaitPeriod, void(base::TimeDelta));
MOCK_METHOD1(SetP2PUrl, void(const std::string&));
+ MOCK_METHOD0(NextPayload, bool());
// Getters.
MOCK_METHOD0(GetResponseSignature, std::string());
diff --git a/mock_service_observer.h b/mock_service_observer.h
new file mode 100644
index 0000000..e434eab
--- /dev/null
+++ b/mock_service_observer.h
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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.
+//
+
+#ifndef UPDATE_ENGINE_MOCK_SERVICE_OBSERVER_H_
+#define UPDATE_ENGINE_MOCK_SERVICE_OBSERVER_H_
+
+#include <gmock/gmock.h>
+#include "update_engine/service_observer_interface.h"
+
+namespace chromeos_update_engine {
+
+class MockServiceObserver : public ServiceObserverInterface {
+ public:
+ MOCK_METHOD1(
+ SendStatusUpdate,
+ void(const update_engine::UpdateEngineStatus& update_engine_status));
+ MOCK_METHOD1(SendPayloadApplicationComplete, void(ErrorCode error_code));
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_MOCK_SERVICE_OBSERVER_H_
diff --git a/mock_update_attempter.h b/mock_update_attempter.h
index 89f163e..d88b840 100644
--- a/mock_update_attempter.h
+++ b/mock_update_attempter.h
@@ -36,19 +36,18 @@
bool obey_proxies,
bool interactive));
- MOCK_METHOD5(GetStatus, bool(int64_t* last_checked_time,
- double* progress,
- std::string* current_operation,
- std::string* new_version,
- int64_t* new_size));
+ MOCK_METHOD1(GetStatus, bool(update_engine::UpdateEngineStatus* out_status));
MOCK_METHOD1(GetBootTimeAtUpdate, bool(base::Time* out_boot_time));
MOCK_METHOD0(ResetStatus, bool(void));
- MOCK_METHOD3(CheckForUpdate, void(const std::string& app_version,
- const std::string& omaha_url,
- bool is_interactive));
+ MOCK_METHOD0(GetCurrentUpdateAttemptFlags, UpdateAttemptFlags(void));
+
+ MOCK_METHOD3(CheckForUpdate,
+ bool(const std::string& app_version,
+ const std::string& omaha_url,
+ UpdateAttemptFlags flags));
MOCK_METHOD0(RefreshDevicePolicy, void(void));
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index ee9e920..c4db0c7 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -27,9 +27,11 @@
#include <base/logging.h>
#include <base/rand_util.h>
#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <base/time/time.h>
+#include <brillo/key_value_store.h>
#include <expat.h>
#include <metrics/metrics_library.h>
@@ -41,7 +43,7 @@
#include "update_engine/common/prefs_interface.h"
#include "update_engine/common/utils.h"
#include "update_engine/connection_manager_interface.h"
-#include "update_engine/metrics.h"
+#include "update_engine/metrics_reporter_interface.h"
#include "update_engine/metrics_utils.h"
#include "update_engine/omaha_request_params.h"
#include "update_engine/p2p_manager.h"
@@ -70,7 +72,6 @@
static const char* kTagMoreInfo = "MoreInfo";
// Deprecated: "NeedsAdmin"
static const char* kTagPrompt = "Prompt";
-static const char* kTagSha256 = "sha256";
static const char* kTagDisableP2PForDownloading = "DisableP2PForDownloading";
static const char* kTagDisableP2PForSharing = "DisableP2PForSharing";
static const char* kTagPublicKeyRsa = "PublicKeyRsa";
@@ -204,10 +205,25 @@
arg_name.c_str(), escaped_xml_value.c_str());
}
+struct OmahaAppData {
+ string id;
+ string version;
+ string product_components;
+};
+
+bool IsValidComponentID(const string& id) {
+ for (char c : id) {
+ if (!isalnum(c) && c != '-' && c != '_' && c != '.')
+ return false;
+ }
+ return true;
+}
+
// Returns an XML that corresponds to the entire <app> node of the Omaha
// request based on the given parameters.
string GetAppXml(const OmahaEvent* event,
OmahaRequestParams* params,
+ const OmahaAppData& app_data,
bool ping_only,
bool include_ping,
int ping_active_days,
@@ -226,10 +242,10 @@
LOG(INFO) << "Passing OS version as 0.0.0.0 as we are set to powerwash "
<< "on downgrading to the version in the more stable channel";
app_versions = "version=\"0.0.0.0\" from_version=\"" +
- XmlEncodeWithDefault(params->app_version(), "0.0.0.0") + "\" ";
+ XmlEncodeWithDefault(app_data.version, "0.0.0.0") + "\" ";
} else {
app_versions = "version=\"" +
- XmlEncodeWithDefault(params->app_version(), "0.0.0.0") + "\" ";
+ XmlEncodeWithDefault(app_data.version, "0.0.0.0") + "\" ";
}
string download_channel = params->download_channel();
@@ -264,12 +280,47 @@
"fingerprint=\"" + XmlEncodeWithDefault(params->os_build_fingerprint(), "") + "\" ";
}
+ string buildtype_arg;
+ if (!params->os_build_type().empty()) {
+ buildtype_arg = "os_build_type=\"" +
+ XmlEncodeWithDefault(params->os_build_type(), "") + "\" ";
+ }
+
+ string product_components_args;
+ if (!app_data.product_components.empty()) {
+ brillo::KeyValueStore store;
+ if (store.LoadFromString(app_data.product_components)) {
+ for (const string& key : store.GetKeys()) {
+ if (!IsValidComponentID(key)) {
+ LOG(ERROR) << "Invalid component id: " << key;
+ continue;
+ }
+ string version;
+ if (!store.GetString(key, &version)) {
+ LOG(ERROR) << "Failed to get version for " << key
+ << " in product_components.";
+ continue;
+ }
+ product_components_args +=
+ base::StringPrintf("_%s.version=\"%s\" ",
+ key.c_str(),
+ XmlEncodeWithDefault(version, "").c_str());
+ }
+ } else {
+ LOG(ERROR) << "Failed to parse product_components:\n"
+ << app_data.product_components;
+ }
+ }
+
+ // clang-format off
string app_xml = " <app "
- "appid=\"" + XmlEncodeWithDefault(params->GetAppId(), "") + "\" " +
+ "appid=\"" + XmlEncodeWithDefault(app_data.id, "") + "\" " +
app_cohort_args +
app_versions +
app_channels +
+ product_components_args +
fingerprint_arg +
+ buildtype_arg +
"lang=\"" + XmlEncodeWithDefault(params->app_lang(), "en-US") + "\" " +
"board=\"" + XmlEncodeWithDefault(params->os_board(), "") + "\" " +
"hardware_class=\"" + XmlEncodeWithDefault(params->hwid(), "") + "\" " +
@@ -280,7 +331,7 @@
">\n" +
app_body +
" </app>\n";
-
+ // clang-format on
return app_xml;
}
@@ -306,9 +357,32 @@
int install_date_in_days,
SystemState* system_state) {
string os_xml = GetOsXml(params);
- string app_xml = GetAppXml(event, params, ping_only, include_ping,
- ping_active_days, ping_roll_call_days,
- install_date_in_days, system_state);
+ OmahaAppData product_app = {
+ .id = params->GetAppId(),
+ .version = params->app_version(),
+ .product_components = params->product_components()};
+ string app_xml = GetAppXml(event,
+ params,
+ product_app,
+ ping_only,
+ include_ping,
+ ping_active_days,
+ ping_roll_call_days,
+ install_date_in_days,
+ system_state);
+ if (!params->system_app_id().empty()) {
+ OmahaAppData system_app = {.id = params->system_app_id(),
+ .version = params->system_version()};
+ app_xml += GetAppXml(event,
+ params,
+ system_app,
+ ping_only,
+ include_ping,
+ ping_active_days,
+ ping_roll_call_days,
+ install_date_in_days,
+ system_state);
+ }
string install_source = base::StringPrintf("installsource=\"%s\" ",
(params->interactive() ? "ondemandupdate" : "scheduler"));
@@ -346,22 +420,32 @@
string current_path;
// These are the values extracted from the XML.
- string app_cohort;
- string app_cohorthint;
- string app_cohortname;
- bool app_cohort_set = false;
- bool app_cohorthint_set = false;
- bool app_cohortname_set = false;
- string updatecheck_status;
string updatecheck_poll_interval;
map<string, string> updatecheck_attrs;
string daystart_elapsed_days;
string daystart_elapsed_seconds;
- vector<string> url_codebase;
- string package_name;
- string package_size;
- string manifest_version;
- map<string, string> action_postinstall_attrs;
+
+ struct App {
+ string id;
+ vector<string> url_codebase;
+ string manifest_version;
+ map<string, string> action_postinstall_attrs;
+ string updatecheck_status;
+ string cohort;
+ string cohorthint;
+ string cohortname;
+ bool cohort_set = false;
+ bool cohorthint_set = false;
+ bool cohortname_set = false;
+
+ struct Package {
+ string name;
+ string size;
+ string hash;
+ };
+ vector<Package> packages;
+ };
+ vector<App> apps;
};
namespace {
@@ -386,22 +470,28 @@
}
if (data->current_path == "/response/app") {
+ OmahaParserData::App app;
+ if (attrs.find("appid") != attrs.end()) {
+ app.id = attrs["appid"];
+ }
if (attrs.find("cohort") != attrs.end()) {
- data->app_cohort_set = true;
- data->app_cohort = attrs["cohort"];
+ app.cohort_set = true;
+ app.cohort = attrs["cohort"];
}
if (attrs.find("cohorthint") != attrs.end()) {
- data->app_cohorthint_set = true;
- data->app_cohorthint = attrs["cohorthint"];
+ app.cohorthint_set = true;
+ app.cohorthint = attrs["cohorthint"];
}
if (attrs.find("cohortname") != attrs.end()) {
- data->app_cohortname_set = true;
- data->app_cohortname = attrs["cohortname"];
+ app.cohortname_set = true;
+ app.cohortname = attrs["cohortname"];
}
+ data->apps.push_back(std::move(app));
} else if (data->current_path == "/response/app/updatecheck") {
- // There is only supposed to be a single <updatecheck> element.
- data->updatecheck_status = attrs["status"];
- data->updatecheck_poll_interval = attrs["PollInterval"];
+ if (!data->apps.empty())
+ data->apps.back().updatecheck_status = attrs["status"];
+ if (data->updatecheck_poll_interval.empty())
+ data->updatecheck_poll_interval = attrs["PollInterval"];
// Omaha sends arbitrary key-value pairs as extra attributes starting with
// an underscore.
for (const auto& attr : attrs) {
@@ -414,20 +504,24 @@
data->daystart_elapsed_seconds = attrs["elapsed_seconds"];
} else if (data->current_path == "/response/app/updatecheck/urls/url") {
// Look at all <url> elements.
- data->url_codebase.push_back(attrs["codebase"]);
- } else if (data->package_name.empty() && data->current_path ==
+ if (!data->apps.empty())
+ data->apps.back().url_codebase.push_back(attrs["codebase"]);
+ } else if (data->current_path ==
"/response/app/updatecheck/manifest/packages/package") {
- // Only look at the first <package>.
- data->package_name = attrs["name"];
- data->package_size = attrs["size"];
+ // Look at all <package> elements.
+ if (!data->apps.empty())
+ data->apps.back().packages.push_back({.name = attrs["name"],
+ .size = attrs["size"],
+ .hash = attrs["hash_sha256"]});
} else if (data->current_path == "/response/app/updatecheck/manifest") {
// Get the version.
- data->manifest_version = attrs[kTagVersion];
+ if (!data->apps.empty())
+ data->apps.back().manifest_version = attrs[kTagVersion];
} else if (data->current_path ==
"/response/app/updatecheck/manifest/actions/action") {
// We only care about the postinstall action.
- if (attrs["event"] == "postinstall") {
- data->action_postinstall_attrs = attrs;
+ if (attrs["event"] == "postinstall" && !data->apps.empty()) {
+ data->apps.back().action_postinstall_attrs = std::move(attrs);
}
}
}
@@ -731,15 +825,112 @@
prefs->SetInt64(kPrefsLastRollCallPingDay, daystart.ToInternalValue());
return true;
}
+
+// Parses the package node in the given XML document and populates
+// |output_object| if valid. Returns true if we should continue the parsing.
+// False otherwise, in which case it sets any error code using |completer|.
+bool ParsePackage(OmahaParserData::App* app,
+ OmahaResponse* output_object,
+ ScopedActionCompleter* completer) {
+ if (app->updatecheck_status == "noupdate") {
+ if (!app->packages.empty()) {
+ LOG(ERROR) << "No update in this <app> but <package> is not empty.";
+ completer->set_code(ErrorCode::kOmahaResponseInvalid);
+ return false;
+ }
+ return true;
+ }
+ if (app->packages.empty()) {
+ LOG(ERROR) << "Omaha Response has no packages";
+ completer->set_code(ErrorCode::kOmahaResponseInvalid);
+ return false;
+ }
+ if (app->url_codebase.empty()) {
+ LOG(ERROR) << "No Omaha Response URLs";
+ completer->set_code(ErrorCode::kOmahaResponseInvalid);
+ return false;
+ }
+ LOG(INFO) << "Found " << app->url_codebase.size() << " url(s)";
+ vector<string> metadata_sizes =
+ base::SplitString(app->action_postinstall_attrs[kTagMetadataSize],
+ ":",
+ base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_ALL);
+ vector<string> metadata_signatures =
+ base::SplitString(app->action_postinstall_attrs[kTagMetadataSignatureRsa],
+ ":",
+ base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_ALL);
+ vector<string> is_delta_payloads =
+ base::SplitString(app->action_postinstall_attrs[kTagIsDeltaPayload],
+ ":",
+ base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_ALL);
+ for (size_t i = 0; i < app->packages.size(); i++) {
+ const auto& package = app->packages[i];
+ if (package.name.empty()) {
+ LOG(ERROR) << "Omaha Response has empty package name";
+ completer->set_code(ErrorCode::kOmahaResponseInvalid);
+ return false;
+ }
+ LOG(INFO) << "Found package " << package.name;
+
+ OmahaResponse::Package out_package;
+ for (const string& codebase : app->url_codebase) {
+ if (codebase.empty()) {
+ LOG(ERROR) << "Omaha Response URL has empty codebase";
+ completer->set_code(ErrorCode::kOmahaResponseInvalid);
+ return false;
+ }
+ out_package.payload_urls.push_back(codebase + package.name);
+ }
+ // Parse the payload size.
+ base::StringToUint64(package.size, &out_package.size);
+ if (out_package.size <= 0) {
+ LOG(ERROR) << "Omaha Response has invalid payload size: " << package.size;
+ completer->set_code(ErrorCode::kOmahaResponseInvalid);
+ return false;
+ }
+ LOG(INFO) << "Payload size = " << out_package.size << " bytes";
+
+ if (i < metadata_sizes.size())
+ base::StringToUint64(metadata_sizes[i], &out_package.metadata_size);
+ LOG(INFO) << "Payload metadata size = " << out_package.metadata_size
+ << " bytes";
+
+ if (i < metadata_signatures.size())
+ out_package.metadata_signature = metadata_signatures[i];
+ LOG(INFO) << "Payload metadata signature = "
+ << out_package.metadata_signature;
+
+ out_package.hash = package.hash;
+ if (out_package.hash.empty()) {
+ LOG(ERROR) << "Omaha Response has empty hash_sha256 value";
+ completer->set_code(ErrorCode::kOmahaResponseInvalid);
+ return false;
+ }
+ LOG(INFO) << "Payload hash = " << out_package.hash;
+
+ if (i < is_delta_payloads.size())
+ out_package.is_delta = ParseBool(is_delta_payloads[i]);
+ LOG(INFO) << "Payload is delta = " << utils::ToString(out_package.is_delta);
+
+ output_object->packages.push_back(std::move(out_package));
+ }
+
+ return true;
+}
+
} // namespace
bool OmahaRequestAction::ParseResponse(OmahaParserData* parser_data,
OmahaResponse* output_object,
ScopedActionCompleter* completer) {
- if (parser_data->updatecheck_status.empty()) {
+ if (parser_data->apps.empty()) {
completer->set_code(ErrorCode::kOmahaResponseInvalid);
return false;
}
+ LOG(INFO) << "Found " << parser_data->apps.size() << " <app>.";
// chromium-os:37289: The PollInterval is not supported by Omaha server
// currently. But still keeping this existing code in case we ever decide to
@@ -778,12 +969,17 @@
}
// We persist the cohorts sent by omaha even if the status is "noupdate".
- if (parser_data->app_cohort_set)
- PersistCohortData(kPrefsOmahaCohort, parser_data->app_cohort);
- if (parser_data->app_cohorthint_set)
- PersistCohortData(kPrefsOmahaCohortHint, parser_data->app_cohorthint);
- if (parser_data->app_cohortname_set)
- PersistCohortData(kPrefsOmahaCohortName, parser_data->app_cohortname);
+ for (const auto& app : parser_data->apps) {
+ if (app.id == params_->GetAppId()) {
+ if (app.cohort_set)
+ PersistCohortData(kPrefsOmahaCohort, app.cohort);
+ if (app.cohorthint_set)
+ PersistCohortData(kPrefsOmahaCohortHint, app.cohorthint);
+ if (app.cohortname_set)
+ PersistCohortData(kPrefsOmahaCohortName, app.cohortname);
+ break;
+ }
+ }
// Parse the updatecheck attributes.
PersistEolStatus(parser_data->updatecheck_attrs);
@@ -791,97 +987,70 @@
if (!ParseStatus(parser_data, output_object, completer))
return false;
- // Note: ParseUrls MUST be called before ParsePackage as ParsePackage
- // appends the package name to the URLs populated in this method.
- if (!ParseUrls(parser_data, output_object, completer))
- return false;
-
- if (!ParsePackage(parser_data, output_object, completer))
- return false;
-
if (!ParseParams(parser_data, output_object, completer))
return false;
+ // Package has to be parsed after Params now because ParseParams need to make
+ // sure that postinstall action exists.
+ for (auto& app : parser_data->apps)
+ if (!ParsePackage(&app, output_object, completer))
+ return false;
+
return true;
}
bool OmahaRequestAction::ParseStatus(OmahaParserData* parser_data,
OmahaResponse* output_object,
ScopedActionCompleter* completer) {
- const string& status = parser_data->updatecheck_status;
- if (status == "noupdate") {
- LOG(INFO) << "No update.";
- output_object->update_exists = false;
- SetOutputObject(*output_object);
- completer->set_code(ErrorCode::kSuccess);
- return false;
- }
-
- if (status != "ok") {
- LOG(ERROR) << "Unknown Omaha response status: " << status;
- completer->set_code(ErrorCode::kOmahaResponseInvalid);
- return false;
- }
-
- return true;
-}
-
-bool OmahaRequestAction::ParseUrls(OmahaParserData* parser_data,
- OmahaResponse* output_object,
- ScopedActionCompleter* completer) {
- if (parser_data->url_codebase.empty()) {
- LOG(ERROR) << "No Omaha Response URLs";
- completer->set_code(ErrorCode::kOmahaResponseInvalid);
- return false;
- }
-
- LOG(INFO) << "Found " << parser_data->url_codebase.size() << " url(s)";
- output_object->payload_urls.clear();
- for (const auto& codebase : parser_data->url_codebase) {
- if (codebase.empty()) {
- LOG(ERROR) << "Omaha Response URL has empty codebase";
+ output_object->update_exists = false;
+ for (size_t i = 0; i < parser_data->apps.size(); i++) {
+ const string& status = parser_data->apps[i].updatecheck_status;
+ if (status == "noupdate") {
+ // Don't update if any app has status="noupdate".
+ LOG(INFO) << "No update for <app> " << i;
+ output_object->update_exists = false;
+ break;
+ } else if (status == "ok") {
+ if (parser_data->apps[i].action_postinstall_attrs["noupdate"] == "true") {
+ // noupdate="true" in postinstall attributes means it's an update to
+ // self, only update if there's at least one app really have update.
+ LOG(INFO) << "Update to self for <app> " << i;
+ } else {
+ LOG(INFO) << "Update for <app> " << i;
+ output_object->update_exists = true;
+ }
+ } else {
+ LOG(ERROR) << "Unknown Omaha response status: " << status;
completer->set_code(ErrorCode::kOmahaResponseInvalid);
return false;
}
- output_object->payload_urls.push_back(codebase);
+ }
+ if (!output_object->update_exists) {
+ SetOutputObject(*output_object);
+ completer->set_code(ErrorCode::kSuccess);
}
- return true;
-}
-
-bool OmahaRequestAction::ParsePackage(OmahaParserData* parser_data,
- OmahaResponse* output_object,
- ScopedActionCompleter* completer) {
- if (parser_data->package_name.empty()) {
- LOG(ERROR) << "Omaha Response has empty package name";
- completer->set_code(ErrorCode::kOmahaResponseInvalid);
- return false;
- }
-
- // Append the package name to each URL in our list so that we don't
- // propagate the urlBase vs packageName distinctions beyond this point.
- // From now on, we only need to use payload_urls.
- for (auto& payload_url : output_object->payload_urls)
- payload_url += parser_data->package_name;
-
- // Parse the payload size.
- off_t size = ParseInt(parser_data->package_size);
- if (size <= 0) {
- LOG(ERROR) << "Omaha Response has invalid payload size: " << size;
- completer->set_code(ErrorCode::kOmahaResponseInvalid);
- return false;
- }
- output_object->size = size;
-
- LOG(INFO) << "Payload size = " << output_object->size << " bytes";
-
- return true;
+ return output_object->update_exists;
}
bool OmahaRequestAction::ParseParams(OmahaParserData* parser_data,
OmahaResponse* output_object,
ScopedActionCompleter* completer) {
- output_object->version = parser_data->manifest_version;
+ map<string, string> attrs;
+ for (auto& app : parser_data->apps) {
+ if (app.id == params_->GetAppId()) {
+ // this is the app (potentially the only app)
+ output_object->version = app.manifest_version;
+ } else if (!params_->system_app_id().empty() &&
+ app.id == params_->system_app_id()) {
+ // this is the system app (this check is intentionally skipped if there is
+ // no system_app_id set)
+ output_object->system_version = app.manifest_version;
+ }
+ if (!app.action_postinstall_attrs.empty() && attrs.empty()) {
+ attrs = app.action_postinstall_attrs;
+ }
+ }
if (output_object->version.empty()) {
LOG(ERROR) << "Omaha Response does not have version in manifest!";
completer->set_code(ErrorCode::kOmahaResponseInvalid);
@@ -891,24 +1060,14 @@
LOG(INFO) << "Received omaha response to update to version "
<< output_object->version;
- map<string, string> attrs = parser_data->action_postinstall_attrs;
if (attrs.empty()) {
LOG(ERROR) << "Omaha Response has no postinstall event action";
completer->set_code(ErrorCode::kOmahaResponseInvalid);
return false;
}
- output_object->hash = attrs[kTagSha256];
- if (output_object->hash.empty()) {
- LOG(ERROR) << "Omaha Response has empty sha256 value";
- completer->set_code(ErrorCode::kOmahaResponseInvalid);
- return false;
- }
-
// Get the optional properties one by one.
output_object->more_info_url = attrs[kTagMoreInfo];
- output_object->metadata_size = ParseInt(attrs[kTagMetadataSize]);
- output_object->metadata_signature = attrs[kTagMetadataSignatureRsa];
output_object->prompt = ParseBool(attrs[kTagPrompt]);
output_object->deadline = attrs[kTagDeadline];
output_object->max_days_to_scatter = ParseInt(attrs[kTagMaxDaysToScatter]);
@@ -922,8 +1081,6 @@
if (!base::StringToUint(max, &output_object->max_failure_count_per_url))
output_object->max_failure_count_per_url = kDefaultMaxFailureCountPerUrl;
- output_object->is_delta_payload = ParseBool(attrs[kTagIsDeltaPayload]);
-
output_object->disable_payload_backoff =
ParseBool(attrs[kTagDisablePayloadBackoff]);
@@ -1016,11 +1173,9 @@
output_object.update_exists = true;
SetOutputObject(output_object);
- ErrorCode error = ErrorCode::kSuccess;
- if (ShouldIgnoreUpdate(&error, output_object)) {
- // No need to change output_object.update_exists here, since the value
- // has been output to the pipe.
- completer.set_code(error);
+ if (ShouldIgnoreUpdate(output_object)) {
+ output_object.update_exists = false;
+ completer.set_code(ErrorCode::kOmahaUpdateIgnoredPerPolicy);
return;
}
@@ -1150,10 +1305,15 @@
next_data_offset + next_data_length;
}
- string file_id = utils::CalculateP2PFileId(response.hash, response.size);
+ // TODO(senj): Fix P2P for multiple package.
+ brillo::Blob raw_hash;
+ if (!base::HexStringToBytes(response.packages[0].hash, &raw_hash))
+ return;
+ string file_id =
+ utils::CalculateP2PFileId(raw_hash, response.packages[0].size);
if (system_state_->p2p_manager()) {
- LOG(INFO) << "Checking if payload is available via p2p, file_id="
- << file_id << " minimum_size=" << minimum_size;
+ LOG(INFO) << "Checking if payload is available via p2p, file_id=" << file_id
+ << " minimum_size=" << minimum_size;
system_state_->p2p_manager()->LookupUrlForFile(
file_id,
minimum_size,
@@ -1416,12 +1576,9 @@
if (!prefs->SetInt64(kPrefsInstallDateDays, install_date_days))
return false;
- string metric_name = metrics::kMetricInstallDateProvisioningSource;
- system_state->metrics_lib()->SendEnumToUMA(
- metric_name,
+ system_state->metrics_reporter()->ReportInstallDateProvisioningSource(
static_cast<int>(source), // Sample.
kProvisionedMax); // Maximum.
-
return true;
}
@@ -1478,7 +1635,6 @@
break;
case ErrorCode::kOmahaUpdateIgnoredPerPolicy:
- case ErrorCode::kOmahaUpdateIgnoredOverCellular:
result = metrics::CheckResult::kUpdateAvailable;
reaction = metrics::CheckReaction::kIgnored;
break;
@@ -1508,12 +1664,12 @@
break;
}
- metrics::ReportUpdateCheckMetrics(system_state_,
- result, reaction, download_error_code);
+ system_state_->metrics_reporter()->ReportUpdateCheckMetrics(
+ system_state_, result, reaction, download_error_code);
}
bool OmahaRequestAction::ShouldIgnoreUpdate(
- ErrorCode* error, const OmahaResponse& response) const {
+ const OmahaResponse& response) const {
// Note: policy decision to not update to a version we rolled back from.
string rollback_version =
system_state_->payload_state()->GetRollbackVersion();
@@ -1521,12 +1677,11 @@
LOG(INFO) << "Detected previous rollback from version " << rollback_version;
if (rollback_version == response.version) {
LOG(INFO) << "Received version that we rolled back from. Ignoring.";
- *error = ErrorCode::kOmahaUpdateIgnoredPerPolicy;
return true;
}
}
- if (!IsUpdateAllowedOverCurrentConnection(error, response)) {
+ if (!IsUpdateAllowedOverCurrentConnection()) {
LOG(INFO) << "Update is not allowed over current connection.";
return true;
}
@@ -1541,59 +1696,7 @@
return false;
}
-bool OmahaRequestAction::IsUpdateAllowedOverCellularByPrefs(
- const OmahaResponse& response) const {
- PrefsInterface* prefs = system_state_->prefs();
-
- if (!prefs) {
- LOG(INFO) << "Disabling updates over cellular as the preferences are "
- "not available.";
- return false;
- }
-
- bool is_allowed;
-
- if (prefs->Exists(kPrefsUpdateOverCellularPermission) &&
- prefs->GetBoolean(kPrefsUpdateOverCellularPermission, &is_allowed) &&
- is_allowed) {
- LOG(INFO) << "Allowing updates over cellular as permission preference is "
- "set to true.";
- return true;
- }
-
- if (!prefs->Exists(kPrefsUpdateOverCellularTargetVersion) ||
- !prefs->Exists(kPrefsUpdateOverCellularTargetSize)) {
- LOG(INFO) << "Disabling updates over cellular as permission preference is "
- "set to false or does not exist while target does not exist.";
- return false;
- }
-
- std::string target_version;
- int64_t target_size;
-
- if (!prefs->GetString(kPrefsUpdateOverCellularTargetVersion,
- &target_version) ||
- !prefs->GetInt64(kPrefsUpdateOverCellularTargetSize,
- &target_size)) {
- LOG(INFO) << "Disabling updates over cellular as the target version or "
- "size is not accessible.";
- return false;
- }
-
- if (target_version == response.version && target_size == response.size) {
- LOG(INFO) << "Allowing updates over cellular as the target matches the"
- "omaha response.";
- return true;
- } else {
- LOG(INFO) << "Disabling updates over cellular as the target does not"
- "match the omaha response.";
- return false;
- }
-}
-
-bool OmahaRequestAction::IsUpdateAllowedOverCurrentConnection(
- ErrorCode* error,
- const OmahaResponse& response) const {
+bool OmahaRequestAction::IsUpdateAllowedOverCurrentConnection() const {
ConnectionType type;
ConnectionTethering tethering;
ConnectionManagerInterface* connection_manager =
@@ -1603,32 +1706,7 @@
<< "Defaulting to allow updates.";
return true;
}
-
bool is_allowed = connection_manager->IsUpdateAllowedOver(type, tethering);
- bool is_device_policy_set = connection_manager->
- IsAllowedConnectionTypesForUpdateSet();
- // Treats tethered connection as if it is cellular connection.
- bool is_over_cellular = type == ConnectionType::kCellular ||
- tethering == ConnectionTethering::kConfirmed;
-
- if (!is_over_cellular) {
- // There's no need to further check user preferences as we are not over
- // cellular connection.
- if (!is_allowed)
- *error = ErrorCode::kOmahaUpdateIgnoredPerPolicy;
- } else if (is_device_policy_set) {
- // There's no need to further check user preferences as the device policy
- // is set regarding updates over cellular.
- if (!is_allowed)
- *error = ErrorCode::kOmahaUpdateIgnoredPerPolicy;
- } else {
- // Deivce policy is not set, so user preferences overwrite whether to
- // allow updates over cellular.
- is_allowed = IsUpdateAllowedOverCellularByPrefs(response);
- if (!is_allowed)
- *error = ErrorCode::kOmahaUpdateIgnoredOverCellular;
- }
-
LOG(INFO) << "We are connected via "
<< connection_utils::StringForConnectionType(type)
<< ", Updates allowed: " << (is_allowed ? "Yes" : "No");
diff --git a/omaha_request_action.h b/omaha_request_action.h
index 7d35c23..924da40 100644
--- a/omaha_request_action.h
+++ b/omaha_request_action.h
@@ -274,13 +274,6 @@
OmahaResponse* output_object,
ScopedActionCompleter* completer);
- // Parses the package node in the given XML document and populates
- // |output_object| if valid. Returns true if we should continue the parsing.
- // False otherwise, in which case it sets any error code using |completer|.
- bool ParsePackage(OmahaParserData* parser_data,
- OmahaResponse* output_object,
- ScopedActionCompleter* completer);
-
// Parses the other parameters in the given XML document and populates
// |output_object| if valid. Returns true if we should continue the parsing.
// False otherwise, in which case it sets any error code using |completer|.
@@ -299,17 +292,11 @@
void OnLookupPayloadViaP2PCompleted(const std::string& url);
// Returns true if the current update should be ignored.
- bool ShouldIgnoreUpdate(ErrorCode* error,
- const OmahaResponse& response) const;
-
- // Return true if updates are allowed by user preferences.
- bool IsUpdateAllowedOverCellularByPrefs(const OmahaResponse& response) const;
+ bool ShouldIgnoreUpdate(const OmahaResponse& response) const;
// Returns true if updates are allowed over the current type of connection.
// False otherwise.
- bool IsUpdateAllowedOverCurrentConnection(
- ErrorCode* error,
- const OmahaResponse& response) const;
+ bool IsUpdateAllowedOverCurrentConnection() const;
// Global system context.
SystemState* system_state_;
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index c310e72..d57abe5 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -45,7 +45,7 @@
#include "update_engine/common/prefs.h"
#include "update_engine/common/test_utils.h"
#include "update_engine/fake_system_state.h"
-#include "update_engine/metrics.h"
+#include "update_engine/metrics_reporter_interface.h"
#include "update_engine/mock_connection_manager.h"
#include "update_engine/mock_payload_state.h"
#include "update_engine/omaha_request_params.h"
@@ -69,6 +69,7 @@
namespace {
const char kTestAppId[] = "test-app-id";
+const char kTestAppId2[] = "test-app2-id";
// This is a helper struct to allow unit tests build an update response with the
// values they care about.
@@ -77,47 +78,91 @@
string entity_str;
if (include_entity)
entity_str = "<!DOCTYPE response [<!ENTITY CrOS \"ChromeOS\">]>";
- return
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
- entity_str + "<response protocol=\"3.0\">"
- "<daystart elapsed_seconds=\"100\"/>"
- "<app appid=\"" + app_id + "\" " +
- (include_cohorts ? "cohort=\"" + cohort + "\" cohorthint=\"" +
- cohorthint + "\" cohortname=\"" + cohortname + "\" " : "") +
- " status=\"ok\">"
- "<ping status=\"ok\"/>"
- "<updatecheck status=\"noupdate\"/></app></response>";
+ return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + entity_str +
+ "<response protocol=\"3.0\">"
+ "<daystart elapsed_seconds=\"100\"/>"
+ "<app appid=\"" +
+ app_id + "\" " +
+ (include_cohorts
+ ? "cohort=\"" + cohort + "\" cohorthint=\"" + cohorthint +
+ "\" cohortname=\"" + cohortname + "\" "
+ : "") +
+ " status=\"ok\">"
+ "<ping status=\"ok\"/>"
+ "<updatecheck status=\"noupdate\"/></app>" +
+ (multi_app_no_update
+ ? "<app appid=\"" + app_id2 +
+ "\"><updatecheck status=\"noupdate\"/></app>"
+ : "") +
+ "</response>";
}
string GetUpdateResponse() const {
- return
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
- "protocol=\"3.0\">"
- "<daystart elapsed_seconds=\"100\"" +
- (elapsed_days.empty() ? "" : (" elapsed_days=\"" + elapsed_days + "\""))
- + "/>"
- "<app appid=\"" + app_id + "\" " +
- (include_cohorts ? "cohort=\"" + cohort + "\" cohorthint=\"" +
- cohorthint + "\" cohortname=\"" + cohortname + "\" " : "") +
- " status=\"ok\">"
- "<ping status=\"ok\"/><updatecheck status=\"ok\">"
- "<urls><url codebase=\"" + codebase + "\"/></urls>"
- "<manifest version=\"" + version + "\">"
- "<packages><package hash=\"not-used\" name=\"" + filename + "\" "
- "size=\"" + base::Int64ToString(size) + "\"/></packages>"
- "<actions><action event=\"postinstall\" "
- "ChromeOSVersion=\"" + version + "\" "
- "MoreInfo=\"" + more_info_url + "\" Prompt=\"" + prompt + "\" "
- "IsDelta=\"true\" "
- "IsDeltaPayload=\"true\" "
- "MaxDaysToScatter=\"" + max_days_to_scatter + "\" "
- "sha256=\"" + hash + "\" "
- "needsadmin=\"" + needsadmin + "\" " +
- (deadline.empty() ? "" : ("deadline=\"" + deadline + "\" ")) +
- (disable_p2p_for_downloading ?
- "DisableP2PForDownloading=\"true\" " : "") +
- (disable_p2p_for_sharing ? "DisableP2PForSharing=\"true\" " : "") +
- "/></actions></manifest></updatecheck></app></response>";
+ return "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
+ "protocol=\"3.0\">"
+ "<daystart elapsed_seconds=\"100\"" +
+ (elapsed_days.empty() ? ""
+ : (" elapsed_days=\"" + elapsed_days + "\"")) +
+ "/>"
+ "<app appid=\"" +
+ app_id + "\" " +
+ (include_cohorts
+ ? "cohort=\"" + cohort + "\" cohorthint=\"" + cohorthint +
+ "\" cohortname=\"" + cohortname + "\" "
+ : "") +
+ " status=\"ok\">"
+ "<ping status=\"ok\"/><updatecheck status=\"ok\">"
+ "<urls><url codebase=\"" +
+ codebase +
+ "\"/></urls>"
+ "<manifest version=\"" +
+ version +
+ "\">"
+ "<packages><package hash=\"not-used\" name=\"" +
+ filename + "\" size=\"" + base::Int64ToString(size) +
+ "\" hash_sha256=\"" + hash + "\"/>" +
+ (multi_package ? "<package name=\"package2\" size=\"222\" "
+ "hash_sha256=\"hash2\"/>"
+ : "") +
+ "</packages>"
+ "<actions><action event=\"postinstall\" MetadataSize=\"11" +
+ (multi_package ? ":22" : "") + "\" ChromeOSVersion=\"" + version +
+ "\" MoreInfo=\"" + more_info_url + "\" Prompt=\"" + prompt +
+ "\" "
+ "IsDelta=\"true\" "
+ "IsDeltaPayload=\"true" +
+ (multi_package ? ":false" : "") +
+ "\" "
+ "MaxDaysToScatter=\"" +
+ max_days_to_scatter +
+ "\" "
+ "sha256=\"not-used\" "
+ "needsadmin=\"" +
+ needsadmin + "\" " +
+ (deadline.empty() ? "" : ("deadline=\"" + deadline + "\" ")) +
+ (disable_p2p_for_downloading ? "DisableP2PForDownloading=\"true\" "
+ : "") +
+ (disable_p2p_for_sharing ? "DisableP2PForSharing=\"true\" " : "") +
+ "/></actions></manifest></updatecheck></app>" +
+ (multi_app
+ ? "<app appid=\"" + app_id2 + "\"" +
+ (include_cohorts ? " cohort=\"cohort2\"" : "") +
+ "><updatecheck status=\"ok\"><urls><url codebase=\"" +
+ codebase2 + "\"/></urls><manifest version=\"" + version2 +
+ "\"><packages>"
+ "<package name=\"package3\" size=\"333\" "
+ "hash_sha256=\"hash3\"/></packages>"
+ "<actions><action event=\"postinstall\" " +
+ (multi_app_self_update
+ ? "noupdate=\"true\" IsDeltaPayload=\"true\" "
+ : "IsDeltaPayload=\"false\" ") +
+ "MetadataSize=\"33\"/></actions>"
+ "</manifest></updatecheck></app>"
+ : "") +
+ (multi_app_no_update
+ ? "<app><updatecheck status=\"noupdate\"/></app>"
+ : "") +
+ "</response>";
}
// Return the payload URL, which is split in two fields in the XML response.
@@ -126,14 +171,17 @@
}
string app_id = kTestAppId;
+ string app_id2 = kTestAppId2;
string version = "1.2.3.4";
+ string version2 = "2.3.4.5";
string more_info_url = "http://more/info";
string prompt = "true";
string codebase = "http://code/base/";
+ string codebase2 = "http://code/base/2/";
string filename = "file.signed";
- string hash = "HASH1234=";
+ string hash = "4841534831323334";
string needsadmin = "false";
- int64_t size = 123;
+ uint64_t size = 123;
string deadline = "";
string max_days_to_scatter = "7";
string elapsed_days = "42";
@@ -150,6 +198,15 @@
// Whether to include the CrOS <!ENTITY> in the XML response.
bool include_entity = false;
+
+ // Whether to include more than one app.
+ bool multi_app = false;
+ // Whether to include an app with noupdate="true".
+ bool multi_app_self_update = false;
+ // Whether to include an additional app with status="noupdate".
+ bool multi_app_no_update = false;
+ // Whether to include more than one package in an app.
+ bool multi_package = false;
};
} // namespace
@@ -335,23 +392,16 @@
BondActions(&action, &collector_action);
processor.EnqueueAction(&collector_action);
- EXPECT_CALL(*fake_system_state_.mock_metrics_lib(), SendEnumToUMA(_, _, _))
+ EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+ ReportUpdateCheckMetrics(_, _, _, _))
.Times(AnyNumber());
- EXPECT_CALL(*fake_system_state_.mock_metrics_lib(),
- SendEnumToUMA(metrics::kMetricCheckResult,
- static_cast<int>(expected_check_result),
- static_cast<int>(metrics::CheckResult::kNumConstants) - 1))
- .Times(expected_check_result == metrics::CheckResult::kUnset ? 0 : 1);
- EXPECT_CALL(*fake_system_state_.mock_metrics_lib(),
- SendEnumToUMA(metrics::kMetricCheckReaction,
- static_cast<int>(expected_check_reaction),
- static_cast<int>(metrics::CheckReaction::kNumConstants) - 1))
- .Times(expected_check_reaction == metrics::CheckReaction::kUnset ? 0 : 1);
- EXPECT_CALL(*fake_system_state_.mock_metrics_lib(),
- SendSparseToUMA(metrics::kMetricCheckDownloadErrorCode,
- static_cast<int>(expected_download_error_code)))
- .Times(expected_download_error_code == metrics::DownloadErrorCode::kUnset
- ? 0 : 1);
+
+ EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+ ReportUpdateCheckMetrics(_,
+ expected_check_result,
+ expected_check_reaction,
+ expected_download_error_code))
+ .Times(ping_only ? 0 : 1);
loop.PostTask(base::Bind(
[](ActionProcessor* processor) { processor->StartProcessing(); },
@@ -431,6 +481,56 @@
EXPECT_FALSE(response.update_exists);
}
+TEST_F(OmahaRequestActionTest, MultiAppNoUpdateTest) {
+ OmahaResponse response;
+ fake_update_response_.multi_app_no_update = true;
+ ASSERT_TRUE(TestUpdateCheck(nullptr, // request_params
+ fake_update_response_.GetNoUpdateResponse(),
+ -1,
+ false, // ping_only
+ ErrorCode::kSuccess,
+ metrics::CheckResult::kNoUpdateAvailable,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ nullptr));
+ EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, MultiAppNoPartialUpdateTest) {
+ OmahaResponse response;
+ fake_update_response_.multi_app_no_update = true;
+ ASSERT_TRUE(TestUpdateCheck(nullptr, // request_params
+ fake_update_response_.GetUpdateResponse(),
+ -1,
+ false, // ping_only
+ ErrorCode::kSuccess,
+ metrics::CheckResult::kNoUpdateAvailable,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ nullptr));
+ EXPECT_FALSE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, NoSelfUpdateTest) {
+ OmahaResponse response;
+ ASSERT_TRUE(TestUpdateCheck(
+ nullptr, // request_params
+ "<response><app><updatecheck status=\"ok\"><manifest><actions><action "
+ "event=\"postinstall\" noupdate=\"true\"/></actions>"
+ "</manifest></updatecheck></app></response>",
+ -1,
+ false, // ping_only
+ ErrorCode::kSuccess,
+ metrics::CheckResult::kNoUpdateAvailable,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ nullptr));
+ EXPECT_FALSE(response.update_exists);
+}
+
// Test that all the values in the response are parsed in a normal update
// response.
TEST_F(OmahaRequestActionTest, ValidUpdateTest) {
@@ -448,21 +548,184 @@
&response,
nullptr));
EXPECT_TRUE(response.update_exists);
- EXPECT_TRUE(response.update_exists);
EXPECT_EQ(fake_update_response_.version, response.version);
- EXPECT_EQ(fake_update_response_.GetPayloadUrl(), response.payload_urls[0]);
+ EXPECT_EQ("", response.system_version);
+ EXPECT_EQ(fake_update_response_.GetPayloadUrl(),
+ response.packages[0].payload_urls[0]);
EXPECT_EQ(fake_update_response_.more_info_url, response.more_info_url);
- EXPECT_EQ(fake_update_response_.hash, response.hash);
- EXPECT_EQ(fake_update_response_.size, response.size);
+ EXPECT_EQ(fake_update_response_.hash, response.packages[0].hash);
+ EXPECT_EQ(fake_update_response_.size, response.packages[0].size);
+ 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);
- // Omaha cohort attribets are not set in the response, so they should not be
+ // Omaha cohort attributes are not set in the response, so they should not be
// persisted.
EXPECT_FALSE(fake_prefs_.Exists(kPrefsOmahaCohort));
EXPECT_FALSE(fake_prefs_.Exists(kPrefsOmahaCohortHint));
EXPECT_FALSE(fake_prefs_.Exists(kPrefsOmahaCohortName));
}
+TEST_F(OmahaRequestActionTest, MultiPackageUpdateTest) {
+ OmahaResponse response;
+ fake_update_response_.multi_package = true;
+ ASSERT_TRUE(TestUpdateCheck(nullptr, // request_params
+ fake_update_response_.GetUpdateResponse(),
+ -1,
+ false, // ping_only
+ ErrorCode::kSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ nullptr));
+ EXPECT_TRUE(response.update_exists);
+ EXPECT_EQ(fake_update_response_.version, response.version);
+ EXPECT_EQ(fake_update_response_.GetPayloadUrl(),
+ response.packages[0].payload_urls[0]);
+ EXPECT_EQ(fake_update_response_.codebase + "package2",
+ 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(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(22u, response.packages[1].metadata_size);
+ EXPECT_EQ(false, response.packages[1].is_delta);
+}
+
+TEST_F(OmahaRequestActionTest, MultiAppUpdateTest) {
+ OmahaResponse response;
+ fake_update_response_.multi_app = true;
+ ASSERT_TRUE(TestUpdateCheck(nullptr, // request_params
+ fake_update_response_.GetUpdateResponse(),
+ -1,
+ false, // ping_only
+ ErrorCode::kSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ nullptr));
+ EXPECT_TRUE(response.update_exists);
+ EXPECT_EQ(fake_update_response_.version, response.version);
+ EXPECT_EQ(fake_update_response_.GetPayloadUrl(),
+ response.packages[0].payload_urls[0]);
+ EXPECT_EQ(fake_update_response_.codebase2 + "package3",
+ 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(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(33u, response.packages[1].metadata_size);
+ EXPECT_EQ(false, response.packages[1].is_delta);
+}
+
+TEST_F(OmahaRequestActionTest, MultiAppAndSystemUpdateTest) {
+ OmahaResponse response;
+ fake_update_response_.multi_app = true;
+ // trigger the lining up of the app and system versions
+ request_params_.set_system_app_id(fake_update_response_.app_id2);
+
+ ASSERT_TRUE(TestUpdateCheck(nullptr, // request_params
+ fake_update_response_.GetUpdateResponse(),
+ -1,
+ false, // ping_only
+ ErrorCode::kSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ nullptr));
+ EXPECT_TRUE(response.update_exists);
+ EXPECT_EQ(fake_update_response_.version, response.version);
+ EXPECT_EQ(fake_update_response_.version2, response.system_version);
+ EXPECT_EQ(fake_update_response_.GetPayloadUrl(),
+ response.packages[0].payload_urls[0]);
+ EXPECT_EQ(fake_update_response_.codebase2 + "package3",
+ 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(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(33u, response.packages[1].metadata_size);
+ EXPECT_EQ(false, response.packages[1].is_delta);
+}
+
+TEST_F(OmahaRequestActionTest, MultiAppPartialUpdateTest) {
+ OmahaResponse response;
+ fake_update_response_.multi_app = true;
+ fake_update_response_.multi_app_self_update = true;
+ ASSERT_TRUE(TestUpdateCheck(nullptr, // request_params
+ fake_update_response_.GetUpdateResponse(),
+ -1,
+ false, // ping_only
+ ErrorCode::kSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ nullptr));
+ EXPECT_TRUE(response.update_exists);
+ EXPECT_EQ(fake_update_response_.version, response.version);
+ EXPECT_EQ("", response.system_version);
+ EXPECT_EQ(fake_update_response_.GetPayloadUrl(),
+ 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(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(33u, response.packages[1].metadata_size);
+ EXPECT_EQ(true, response.packages[1].is_delta);
+}
+
+TEST_F(OmahaRequestActionTest, MultiAppMultiPackageUpdateTest) {
+ OmahaResponse response;
+ fake_update_response_.multi_app = true;
+ fake_update_response_.multi_package = true;
+ ASSERT_TRUE(TestUpdateCheck(nullptr, // request_params
+ fake_update_response_.GetUpdateResponse(),
+ -1,
+ false, // ping_only
+ ErrorCode::kSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ nullptr));
+ EXPECT_TRUE(response.update_exists);
+ EXPECT_EQ(fake_update_response_.version, response.version);
+ EXPECT_EQ("", response.system_version);
+ EXPECT_EQ(fake_update_response_.GetPayloadUrl(),
+ response.packages[0].payload_urls[0]);
+ EXPECT_EQ(fake_update_response_.codebase + "package2",
+ response.packages[1].payload_urls[0]);
+ EXPECT_EQ(fake_update_response_.codebase2 + "package3",
+ 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(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(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(33u, response.packages[2].metadata_size);
+ EXPECT_EQ(false, response.packages[2].is_delta);
+}
+
TEST_F(OmahaRequestActionTest, ExtraHeadersSentTest) {
const string http_response = "<?xml invalid response";
request_params_.set_interactive(true);
@@ -519,185 +782,6 @@
EXPECT_FALSE(response.update_exists);
}
-TEST_F(OmahaRequestActionTest, ValidUpdateOverCellularAllowedByDevicePolicy) {
- // This test tests that update over cellular is allowed as device policy
- // says yes.
- OmahaResponse response;
- MockConnectionManager mock_cm;
-
- fake_system_state_.set_connection_manager(&mock_cm);
-
- EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
- .WillRepeatedly(
- DoAll(SetArgPointee<0>(ConnectionType::kCellular),
- SetArgPointee<1>(ConnectionTethering::kUnknown),
- Return(true)));
- EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
- .WillRepeatedly(Return(true));
- EXPECT_CALL(mock_cm, IsUpdateAllowedOver(ConnectionType::kCellular, _))
- .WillRepeatedly(Return(true));
-
- ASSERT_TRUE(
- TestUpdateCheck(nullptr, // request_params
- fake_update_response_.GetUpdateResponse(),
- -1,
- false, // ping_only
- ErrorCode::kSuccess,
- metrics::CheckResult::kUpdateAvailable,
- metrics::CheckReaction::kUpdating,
- metrics::DownloadErrorCode::kUnset,
- &response,
- nullptr));
- EXPECT_TRUE(response.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest, ValidUpdateOverCellularBlockedByDevicePolicy) {
- // This test tests that update over cellular is blocked as device policy
- // says no.
- OmahaResponse response;
- MockConnectionManager mock_cm;
-
- fake_system_state_.set_connection_manager(&mock_cm);
-
- EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
- .WillRepeatedly(
- DoAll(SetArgPointee<0>(ConnectionType::kCellular),
- SetArgPointee<1>(ConnectionTethering::kUnknown),
- Return(true)));
- EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
- .WillRepeatedly(Return(true));
- EXPECT_CALL(mock_cm, IsUpdateAllowedOver(ConnectionType::kCellular, _))
- .WillRepeatedly(Return(false));
-
- ASSERT_FALSE(
- TestUpdateCheck(nullptr, // request_params
- fake_update_response_.GetUpdateResponse(),
- -1,
- false, // ping_only
- ErrorCode::kOmahaUpdateIgnoredPerPolicy,
- metrics::CheckResult::kUpdateAvailable,
- metrics::CheckReaction::kIgnored,
- metrics::DownloadErrorCode::kUnset,
- &response,
- nullptr));
- EXPECT_FALSE(response.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest,
- ValidUpdateOverCellularAllowedByUserPermissionTrue) {
- // This test tests that, when device policy is not set, update over cellular
- // is allowed as permission for update over cellular is set to true.
- OmahaResponse response;
- MockConnectionManager mock_cm;
-
- fake_prefs_.SetBoolean(kPrefsUpdateOverCellularPermission, true);
- fake_system_state_.set_connection_manager(&mock_cm);
-
- EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
- .WillRepeatedly(
- DoAll(SetArgPointee<0>(ConnectionType::kCellular),
- SetArgPointee<1>(ConnectionTethering::kUnknown),
- Return(true)));
- EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
- .WillRepeatedly(Return(false));
- EXPECT_CALL(mock_cm, IsUpdateAllowedOver(ConnectionType::kCellular, _))
- .WillRepeatedly(Return(true));
-
- ASSERT_TRUE(
- TestUpdateCheck(nullptr, // request_params
- fake_update_response_.GetUpdateResponse(),
- -1,
- false, // ping_only
- ErrorCode::kSuccess,
- metrics::CheckResult::kUpdateAvailable,
- metrics::CheckReaction::kUpdating,
- metrics::DownloadErrorCode::kUnset,
- &response,
- nullptr));
- EXPECT_TRUE(response.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest,
- ValidUpdateOverCellularBlockedByUpdateTargetNotMatch) {
- // This test tests that, when device policy is not set and permission for
- // update over cellular is set to false or does not exist, update over
- // cellular is blocked as update target does not match the omaha response.
- OmahaResponse response;
- MockConnectionManager mock_cm;
- // A version different from the version in omaha response.
- string diff_version = "99.99.99";
- // A size different from the size in omaha response.
- int64_t diff_size = 999;
-
- fake_prefs_.SetString(kPrefsUpdateOverCellularTargetVersion, diff_version);
- fake_prefs_.SetInt64(kPrefsUpdateOverCellularTargetSize, diff_size);
- // This test tests cellular (3G) being the only connection type being allowed.
- fake_system_state_.set_connection_manager(&mock_cm);
-
- EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
- .WillRepeatedly(
- DoAll(SetArgPointee<0>(ConnectionType::kCellular),
- SetArgPointee<1>(ConnectionTethering::kUnknown),
- Return(true)));
- EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
- .WillRepeatedly(Return(false));
- EXPECT_CALL(mock_cm, IsUpdateAllowedOver(ConnectionType::kCellular, _))
- .WillRepeatedly(Return(true));
-
- ASSERT_FALSE(
- TestUpdateCheck(nullptr, // request_params
- fake_update_response_.GetUpdateResponse(),
- -1,
- false, // ping_only
- ErrorCode::kOmahaUpdateIgnoredOverCellular,
- metrics::CheckResult::kUpdateAvailable,
- metrics::CheckReaction::kIgnored,
- metrics::DownloadErrorCode::kUnset,
- &response,
- nullptr));
- EXPECT_FALSE(response.update_exists);
-}
-
-TEST_F(OmahaRequestActionTest,
- ValidUpdateOverCellularAllowedByUpdateTargetMatch) {
- // This test tests that, when device policy is not set and permission for
- // update over cellular is set to false or does not exist, update over
- // cellular is allowed as update target matches the omaha response.
- OmahaResponse response;
- MockConnectionManager mock_cm;
- // A version same as the version in omaha response.
- string new_version = fake_update_response_.version;
- // A size same as the size in omaha response.
- int64_t new_size = fake_update_response_.size;
-
- fake_prefs_.SetString(kPrefsUpdateOverCellularTargetVersion, new_version);
- fake_prefs_.SetInt64(kPrefsUpdateOverCellularTargetSize, new_size);
- fake_system_state_.set_connection_manager(&mock_cm);
-
- EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
- .WillRepeatedly(
- DoAll(SetArgPointee<0>(ConnectionType::kCellular),
- SetArgPointee<1>(ConnectionTethering::kUnknown),
- Return(true)));
- EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
- .WillRepeatedly(Return(false));
- EXPECT_CALL(mock_cm, IsUpdateAllowedOver(ConnectionType::kCellular, _))
- .WillRepeatedly(Return(true));
-
- ASSERT_TRUE(
- TestUpdateCheck(nullptr, // request_params
- fake_update_response_.GetUpdateResponse(),
- -1,
- false, // ping_only
- ErrorCode::kSuccess,
- metrics::CheckResult::kUpdateAvailable,
- metrics::CheckReaction::kUpdating,
- metrics::DownloadErrorCode::kUnset,
- &response,
- nullptr));
- EXPECT_TRUE(response.update_exists);
-}
-
TEST_F(OmahaRequestActionTest, ValidUpdateBlockedByRollback) {
string rollback_version = "1234.0.0";
OmahaResponse response;
@@ -728,18 +812,19 @@
TEST_F(OmahaRequestActionTest, SkipNonCriticalUpdatesBeforeOOBE) {
OmahaResponse response;
+ // TODO set better default value for metrics::checkresult in
+ // OmahaRequestAction::ActionCompleted.
fake_system_state_.fake_hardware()->UnsetIsOOBEComplete();
- ASSERT_FALSE(
- TestUpdateCheck(nullptr, // request_params
- fake_update_response_.GetUpdateResponse(),
- -1,
- false, // ping_only
- ErrorCode::kNonCriticalUpdateInOOBE,
- metrics::CheckResult::kUnset,
- metrics::CheckReaction::kUnset,
- metrics::DownloadErrorCode::kUnset,
- &response,
- nullptr));
+ ASSERT_FALSE(TestUpdateCheck(nullptr, // request_params
+ fake_update_response_.GetUpdateResponse(),
+ -1,
+ false, // ping_only
+ ErrorCode::kNonCriticalUpdateInOOBE,
+ metrics::CheckResult::kParsingError,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ nullptr));
EXPECT_FALSE(response.update_exists);
// The IsOOBEComplete() value is ignored when the OOBE flow is not enabled.
@@ -1093,6 +1178,37 @@
EXPECT_EQ(fake_update_response_.cohortname, value);
}
+TEST_F(OmahaRequestActionTest, MultiAppCohortTest) {
+ OmahaResponse response;
+ OmahaRequestParams params = request_params_;
+ fake_update_response_.multi_app = true;
+ fake_update_response_.include_cohorts = true;
+ fake_update_response_.cohort = "s/154454/8479665";
+ fake_update_response_.cohorthint = "please-put-me-on-beta";
+ fake_update_response_.cohortname = "stable";
+
+ ASSERT_TRUE(TestUpdateCheck(¶ms,
+ fake_update_response_.GetUpdateResponse(),
+ -1,
+ false, // ping_only
+ ErrorCode::kSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ nullptr));
+
+ string value;
+ EXPECT_TRUE(fake_prefs_.GetString(kPrefsOmahaCohort, &value));
+ EXPECT_EQ(fake_update_response_.cohort, value);
+
+ EXPECT_TRUE(fake_prefs_.GetString(kPrefsOmahaCohortHint, &value));
+ EXPECT_EQ(fake_update_response_.cohorthint, value);
+
+ EXPECT_TRUE(fake_prefs_.GetString(kPrefsOmahaCohortName, &value));
+ EXPECT_EQ(fake_update_response_.cohortname, value);
+}
+
TEST_F(OmahaRequestActionTest, NoOutputPipeTest) {
const string http_response(fake_update_response_.GetNoUpdateResponse());
@@ -1217,18 +1333,21 @@
string input_response =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
"<daystart elapsed_seconds=\"100\"/>"
- "<app appid=\"xyz\" status=\"ok\">"
+ // the appid needs to match that in the request params
+ "<app appid=\"" +
+ fake_update_response_.app_id +
+ "\" status=\"ok\">"
"<updatecheck status=\"ok\">"
"<urls><url codebase=\"http://missing/field/test/\"/></urls>"
"<manifest version=\"10.2.3.4\">"
"<packages><package hash=\"not-used\" name=\"f\" "
- "size=\"587\"/></packages>"
+ "size=\"587\" hash_sha256=\"lkq34j5345\"/></packages>"
"<actions><action event=\"postinstall\" "
"ChromeOSVersion=\"10.2.3.4\" "
"Prompt=\"false\" "
"IsDelta=\"true\" "
"IsDeltaPayload=\"false\" "
- "sha256=\"lkq34j5345\" "
+ "sha256=\"not-used\" "
"needsadmin=\"true\" "
"/></actions></manifest></updatecheck></app></response>";
LOG(INFO) << "Input Response = " << input_response;
@@ -1246,10 +1365,11 @@
nullptr));
EXPECT_TRUE(response.update_exists);
EXPECT_EQ("10.2.3.4", response.version);
- EXPECT_EQ("http://missing/field/test/f", response.payload_urls[0]);
+ EXPECT_EQ("http://missing/field/test/f",
+ response.packages[0].payload_urls[0]);
EXPECT_EQ("", response.more_info_url);
- EXPECT_EQ("lkq34j5345", response.hash);
- EXPECT_EQ(587, response.size);
+ EXPECT_EQ("lkq34j5345", response.packages[0].hash);
+ EXPECT_EQ(587u, response.packages[0].size);
EXPECT_FALSE(response.prompt);
EXPECT_TRUE(response.deadline.empty());
}
@@ -1384,15 +1504,16 @@
&response,
nullptr));
- EXPECT_EQ(response.more_info_url, "testthe<url");
- EXPECT_EQ(response.payload_urls[0], "testthe&codebase/file.signed");
- EXPECT_EQ(response.deadline, "<20110101");
+ EXPECT_EQ("testthe<url", response.more_info_url);
+ EXPECT_EQ("testthe&codebase/file.signed",
+ response.packages[0].payload_urls[0]);
+ EXPECT_EQ("<20110101", response.deadline);
}
TEST_F(OmahaRequestActionTest, ParseIntTest) {
OmahaResponse response;
// overflows int32_t:
- fake_update_response_.size = 123123123123123ll;
+ fake_update_response_.size = 123123123123123ull;
ASSERT_TRUE(
TestUpdateCheck(nullptr, // request_params
fake_update_response_.GetUpdateResponse(),
@@ -1405,7 +1526,7 @@
&response,
nullptr));
- EXPECT_EQ(response.size, 123123123123123ll);
+ EXPECT_EQ(fake_update_response_.size, response.packages[0].size);
}
TEST_F(OmahaRequestActionTest, FormatUpdateCheckOutputTest) {
@@ -1626,17 +1747,16 @@
EXPECT_CALL(prefs, GetInt64(kPrefsLastRollCallPingDay, _))
.WillOnce(DoAll(SetArgPointee<1>(five_days_ago), Return(true)));
brillo::Blob post_data;
- ASSERT_TRUE(
- TestUpdateCheck(nullptr, // request_params
- fake_update_response_.GetNoUpdateResponse(),
- -1,
- ping_only,
- ErrorCode::kSuccess,
- metrics::CheckResult::kUnset,
- metrics::CheckReaction::kUnset,
- metrics::DownloadErrorCode::kUnset,
- nullptr,
- &post_data));
+ ASSERT_TRUE(TestUpdateCheck(nullptr, // request_params
+ fake_update_response_.GetNoUpdateResponse(),
+ -1,
+ ping_only,
+ ErrorCode::kSuccess,
+ metrics::CheckResult::kNoUpdateAvailable,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
+ nullptr,
+ &post_data));
string post_str(post_data.begin(), post_data.end());
EXPECT_NE(post_str.find("<ping active=\"1\" a=\"6\" r=\"5\"></ping>"),
string::npos);
@@ -1989,7 +2109,7 @@
params.set_update_check_count_wait_enabled(false);
Time arbitrary_date;
- Time::FromString("6/4/1989", &arbitrary_date);
+ ASSERT_TRUE(Time::FromString("6/4/1989", &arbitrary_date));
fake_system_state_.fake_clock()->SetWallclockTime(arbitrary_date);
ASSERT_FALSE(TestUpdateCheck(¶ms,
fake_update_response_.GetUpdateResponse(),
@@ -2030,8 +2150,8 @@
params.set_update_check_count_wait_enabled(false);
Time t1, t2;
- Time::FromString("1/1/2012", &t1);
- Time::FromString("1/3/2012", &t2);
+ ASSERT_TRUE(Time::FromString("1/1/2012", &t1));
+ ASSERT_TRUE(Time::FromString("1/3/2012", &t2));
ASSERT_TRUE(
fake_prefs_.SetInt64(kPrefsUpdateFirstSeenAt, t1.ToInternalValue()));
fake_system_state_.fake_clock()->SetWallclockTime(t2);
@@ -2061,7 +2181,7 @@
brillo::Blob post_data;
OmahaRequestParams params(&fake_system_state_);
- params.set_root(tempdir.path().value());
+ params.set_root(tempdir.GetPath().value());
params.set_app_id("{22222222-2222-2222-2222-222222222222}");
params.set_app_version("1.2.3.4");
params.set_current_channel("canary-channel");
@@ -2094,7 +2214,7 @@
brillo::Blob post_data;
OmahaRequestParams params(&fake_system_state_);
- params.set_root(tempdir.path().value());
+ params.set_root(tempdir.GetPath().value());
params.set_app_id("{11111111-1111-1111-1111-111111111111}");
params.set_app_version("5.6.7.8");
params.set_current_channel("stable-channel");
diff --git a/omaha_request_params.cc b/omaha_request_params.cc
index 3402451..3ba7037 100644
--- a/omaha_request_params.cc
+++ b/omaha_request_params.cc
@@ -77,7 +77,10 @@
LOG(INFO) << "Running from channel " << image_props_.current_channel;
os_platform_ = constants::kOmahaPlatformName;
- os_version_ = OmahaRequestParams::kOsVersion;
+ if (!image_props_.system_version.empty())
+ os_version_ = image_props_.system_version;
+ else
+ os_version_ = OmahaRequestParams::kOsVersion;
if (!in_app_version.empty())
image_props_.version = in_app_version;
diff --git a/omaha_request_params.h b/omaha_request_params.h
index 3a28ed1..73edd6f 100644
--- a/omaha_request_params.h
+++ b/omaha_request_params.h
@@ -105,10 +105,15 @@
inline std::string os_build_fingerprint() const {
return image_props_.build_fingerprint;
}
+ inline std::string os_build_type() const { return image_props_.build_type; }
inline std::string board_app_id() const { return image_props_.product_id; }
inline std::string canary_app_id() const {
return image_props_.canary_product_id;
}
+ inline std::string system_app_id() const { return image_props_.system_id; }
+ inline void set_system_app_id(const std::string& system_app_id) {
+ image_props_.system_id = system_app_id;
+ }
inline void set_app_id(const std::string& app_id) {
image_props_.product_id = app_id;
image_props_.canary_product_id = app_id;
@@ -122,6 +127,12 @@
image_props_.version = version;
}
inline std::string app_version() const { return image_props_.version; }
+ inline std::string system_version() const {
+ return image_props_.system_version;
+ }
+ inline std::string product_components() const {
+ return image_props_.product_components;
+ }
inline std::string current_channel() const {
return image_props_.current_channel;
diff --git a/omaha_request_params_unittest.cc b/omaha_request_params_unittest.cc
index 7d4dc2d..57ecf24 100644
--- a/omaha_request_params_unittest.cc
+++ b/omaha_request_params_unittest.cc
@@ -47,7 +47,7 @@
// Create a fresh copy of the params for each test, so there's no
// unintended reuse of state across tests.
params_ = OmahaRequestParams(&fake_system_state_);
- params_.set_root(tempdir_.path().value());
+ params_.set_root(tempdir_.GetPath().value());
SetLockDown(false);
fake_system_state_.set_prefs(&fake_prefs_);
}
@@ -105,7 +105,8 @@
}
TEST_F(OmahaRequestParamsTest, NoDeltasTest) {
- ASSERT_TRUE(WriteFileString(tempdir_.path().Append(".nodelta").value(), ""));
+ ASSERT_TRUE(
+ WriteFileString(tempdir_.GetPath().Append(".nodelta").value(), ""));
EXPECT_TRUE(params_.Init("", "", false));
EXPECT_FALSE(params_.delta_okay());
}
@@ -113,12 +114,12 @@
TEST_F(OmahaRequestParamsTest, SetTargetChannelTest) {
{
OmahaRequestParams params(&fake_system_state_);
- params.set_root(tempdir_.path().value());
+ params.set_root(tempdir_.GetPath().value());
EXPECT_TRUE(params.Init("", "", false));
EXPECT_TRUE(params.SetTargetChannel("canary-channel", false, nullptr));
EXPECT_FALSE(params.is_powerwash_allowed());
}
- params_.set_root(tempdir_.path().value());
+ params_.set_root(tempdir_.GetPath().value());
EXPECT_TRUE(params_.Init("", "", false));
EXPECT_EQ("canary-channel", params_.target_channel());
EXPECT_FALSE(params_.is_powerwash_allowed());
@@ -127,12 +128,12 @@
TEST_F(OmahaRequestParamsTest, SetIsPowerwashAllowedTest) {
{
OmahaRequestParams params(&fake_system_state_);
- params.set_root(tempdir_.path().value());
+ params.set_root(tempdir_.GetPath().value());
EXPECT_TRUE(params.Init("", "", false));
EXPECT_TRUE(params.SetTargetChannel("canary-channel", true, nullptr));
EXPECT_TRUE(params.is_powerwash_allowed());
}
- params_.set_root(tempdir_.path().value());
+ params_.set_root(tempdir_.GetPath().value());
EXPECT_TRUE(params_.Init("", "", false));
EXPECT_EQ("canary-channel", params_.target_channel());
EXPECT_TRUE(params_.is_powerwash_allowed());
@@ -141,7 +142,7 @@
TEST_F(OmahaRequestParamsTest, SetTargetChannelInvalidTest) {
{
OmahaRequestParams params(&fake_system_state_);
- params.set_root(tempdir_.path().value());
+ params.set_root(tempdir_.GetPath().value());
SetLockDown(true);
EXPECT_TRUE(params.Init("", "", false));
string error_message;
@@ -151,7 +152,7 @@
EXPECT_NE(string::npos, error_message.find("stable-channel"));
EXPECT_FALSE(params.is_powerwash_allowed());
}
- params_.set_root(tempdir_.path().value());
+ params_.set_root(tempdir_.GetPath().value());
EXPECT_TRUE(params_.Init("", "", false));
EXPECT_EQ("stable-channel", params_.target_channel());
EXPECT_FALSE(params_.is_powerwash_allowed());
diff --git a/omaha_response.h b/omaha_response.h
index 60ec4ac..b973eb5 100644
--- a/omaha_response.h
+++ b/omaha_response.h
@@ -37,17 +37,24 @@
// These are only valid if update_exists is true:
std::string version;
+ std::string system_version;
- // The ordered list of URLs in the Omaha response. Each item is a complete
- // URL (i.e. in terms of Omaha XML, each value is a urlBase + packageName)
- std::vector<std::string> payload_urls;
+ struct Package {
+ // The ordered list of URLs in the Omaha response. Each item is a complete
+ // URL (i.e. in terms of Omaha XML, each value is a urlBase + packageName)
+ std::vector<std::string> payload_urls;
+ uint64_t size = 0;
+ uint64_t metadata_size = 0;
+ std::string metadata_signature;
+ std::string hash;
+ // True if the payload described in this response is a delta payload.
+ // False if it's a full payload.
+ bool is_delta = false;
+ };
+ std::vector<Package> packages;
std::string more_info_url;
- std::string hash;
- std::string metadata_signature;
std::string deadline;
- off_t size = 0;
- off_t metadata_size = 0;
int max_days_to_scatter = 0;
// The number of URL-related failures to tolerate before moving on to the
// next URL in the current pass. This is a configurable value from the
@@ -55,10 +62,6 @@
uint32_t max_failure_count_per_url = 0;
bool prompt = false;
- // True if the payload described in this response is a delta payload.
- // False if it's a full payload.
- bool is_delta_payload = false;
-
// True if the Omaha rule instructs us to disable the back-off logic
// on the client altogether. False otherwise.
bool disable_payload_backoff = false;
diff --git a/omaha_response_handler_action.cc b/omaha_response_handler_action.cc
index 33380d7..9c5fb4a 100644
--- a/omaha_response_handler_action.cc
+++ b/omaha_response_handler_action.cc
@@ -19,6 +19,7 @@
#include <string>
#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <policy/device_policy.h>
@@ -30,7 +31,11 @@
#include "update_engine/omaha_request_params.h"
#include "update_engine/payload_consumer/delta_performer.h"
#include "update_engine/payload_state_interface.h"
+#include "update_engine/update_manager/policy.h"
+#include "update_engine/update_manager/update_manager.h"
+using chromeos_update_manager::Policy;
+using chromeos_update_manager::UpdateManager;
using std::string;
namespace chromeos_update_engine {
@@ -68,8 +73,10 @@
return;
}
+ // This is the url to the first package, not all packages.
install_plan_.download_url = current_url;
install_plan_.version = response.version;
+ install_plan_.system_version = response.system_version;
OmahaRequestParams* const params = system_state_->request_params();
PayloadStateInterface* const payload_state = system_state_->payload_state();
@@ -85,28 +92,40 @@
}
// Fill up the other properties based on the response.
- install_plan_.payload_size = response.size;
- install_plan_.payload_hash = response.hash;
- install_plan_.metadata_size = response.metadata_size;
- install_plan_.metadata_signature = response.metadata_signature;
+ string update_check_response_hash;
+ for (const auto& package : response.packages) {
+ brillo::Blob raw_hash;
+ if (!base::HexStringToBytes(package.hash, &raw_hash)) {
+ LOG(ERROR) << "Failed to convert payload hash from hex string to bytes: "
+ << package.hash;
+ completer.set_code(ErrorCode::kOmahaResponseInvalid);
+ return;
+ }
+ install_plan_.payloads.push_back(
+ {.size = package.size,
+ .metadata_size = package.metadata_size,
+ .metadata_signature = package.metadata_signature,
+ .hash = raw_hash,
+ .type = package.is_delta ? InstallPayloadType::kDelta
+ : InstallPayloadType::kFull});
+ update_check_response_hash += package.hash + ":";
+ }
install_plan_.public_key_rsa = response.public_key_rsa;
install_plan_.hash_checks_mandatory = AreHashChecksMandatory(response);
- install_plan_.is_resume =
- DeltaPerformer::CanResumeUpdate(system_state_->prefs(), response.hash);
+ install_plan_.is_resume = DeltaPerformer::CanResumeUpdate(
+ system_state_->prefs(), update_check_response_hash);
if (install_plan_.is_resume) {
payload_state->UpdateResumed();
} else {
payload_state->UpdateRestarted();
- LOG_IF(WARNING, !DeltaPerformer::ResetUpdateProgress(
- system_state_->prefs(), false))
+ LOG_IF(WARNING,
+ !DeltaPerformer::ResetUpdateProgress(system_state_->prefs(), false))
<< "Unable to reset the update progress.";
- LOG_IF(WARNING, !system_state_->prefs()->SetString(
- kPrefsUpdateCheckResponseHash, response.hash))
+ LOG_IF(WARNING,
+ !system_state_->prefs()->SetString(kPrefsUpdateCheckResponseHash,
+ update_check_response_hash))
<< "Unable to save the update check response hash.";
}
- install_plan_.payload_type = response.is_delta_payload
- ? InstallPayloadType::kDelta
- : InstallPayloadType::kFull;
install_plan_.source_slot = system_state_->boot_control()->GetCurrentSlot();
install_plan_.target_slot = install_plan_.source_slot == 0 ? 1 : 0;
@@ -143,7 +162,14 @@
chmod(deadline_file_.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
}
- completer.set_code(ErrorCode::kSuccess);
+ // Check the generated install-plan with the Policy to confirm that
+ // it can be applied at this time (or at all).
+ UpdateManager* const update_manager = system_state_->update_manager();
+ CHECK(update_manager);
+ auto ec = ErrorCode::kSuccess;
+ update_manager->PolicyRequest(
+ &Policy::UpdateCanBeApplied, &ec, &install_plan_);
+ completer.set_code(ec);
}
bool OmahaResponseHandlerAction::AreHashChecksMandatory(
@@ -193,12 +219,14 @@
// mandatory because we could be downloading the payload from any URL later
// on. It's really hard to do book-keeping based on each byte being
// downloaded to see whether we only used HTTPS throughout.
- for (size_t i = 0; i < response.payload_urls.size(); i++) {
- if (!base::StartsWith(response.payload_urls[i], "https://",
- base::CompareCase::INSENSITIVE_ASCII)) {
- LOG(INFO) << "Mandating payload hash checks since Omaha response "
- << "contains non-HTTPS URL(s)";
- return true;
+ for (const auto& package : response.packages) {
+ for (const string& payload_url : package.payload_urls) {
+ if (!base::StartsWith(
+ payload_url, "https://", base::CompareCase::INSENSITIVE_ASCII)) {
+ LOG(INFO) << "Mandating payload hash checks since Omaha response "
+ << "contains non-HTTPS URL(s)";
+ return true;
+ }
}
}
diff --git a/omaha_response_handler_action.h b/omaha_response_handler_action.h
index 51dfa7a..2974841 100644
--- a/omaha_response_handler_action.h
+++ b/omaha_response_handler_action.h
@@ -89,6 +89,7 @@
friend class OmahaResponseHandlerActionTest;
FRIEND_TEST(UpdateAttempterTest, CreatePendingErrorEventResumedTest);
+ FRIEND_TEST(UpdateAttempterTest, UpdateDeferredByPolicyTest);
DISALLOW_COPY_AND_ASSIGN(OmahaResponseHandlerAction);
};
diff --git a/omaha_response_handler_action_unittest.cc b/omaha_response_handler_action_unittest.cc
index 60b139b..568d11d 100644
--- a/omaha_response_handler_action_unittest.cc
+++ b/omaha_response_handler_action_unittest.cc
@@ -16,10 +16,12 @@
#include "update_engine/omaha_response_handler_action.h"
+#include <memory>
#include <string>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
+#include <brillo/message_loops/fake_message_loop.h>
#include <gtest/gtest.h>
#include "update_engine/common/constants.h"
@@ -29,12 +31,18 @@
#include "update_engine/fake_system_state.h"
#include "update_engine/mock_payload_state.h"
#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/update_manager/mock_policy.h"
using chromeos_update_engine::test_utils::System;
using chromeos_update_engine::test_utils::WriteFileString;
+using chromeos_update_manager::EvalStatus;
+using chromeos_update_manager::FakeUpdateManager;
+using chromeos_update_manager::MockPolicy;
using std::string;
-using testing::Return;
using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArgPointee;
namespace chromeos_update_engine {
@@ -58,15 +66,23 @@
const string& deadline_file,
InstallPlan* out);
+ // Pointer to the Action, valid after |DoTest|, released when the test is
+ // finished.
+ std::unique_ptr<OmahaResponseHandlerAction> action_;
+ // Captures the action's result code, for tests that need to directly verify
+ // it in non-success cases.
+ ErrorCode action_result_code_;
+
FakeSystemState fake_system_state_;
+ // "Hash+"
+ const brillo::Blob expected_hash_ = {0x48, 0x61, 0x73, 0x68, 0x2b};
};
class OmahaResponseHandlerActionProcessorDelegate
: public ActionProcessorDelegate {
public:
OmahaResponseHandlerActionProcessorDelegate()
- : code_(ErrorCode::kError),
- code_set_(false) {}
+ : code_(ErrorCode::kError), code_set_(false) {}
void ActionCompleted(ActionProcessor* processor,
AbstractAction* action,
ErrorCode code) {
@@ -90,12 +106,14 @@
"very_long_name_and_no_slashes-very_long_name_and_no_slashes"
"-the_update_a.b.c.d_DELTA_.tgz";
const char* const kBadVersion = "don't update me";
+const char* const kPayloadHashHex = "486173682b";
} // namespace
-bool OmahaResponseHandlerActionTest::DoTest(
- const OmahaResponse& in,
- const string& test_deadline_file,
- InstallPlan* out) {
+bool OmahaResponseHandlerActionTest::DoTest(const OmahaResponse& in,
+ const string& test_deadline_file,
+ InstallPlan* out) {
+ brillo::FakeMessageLoop loop(nullptr);
+ loop.SetAsCurrent();
ActionProcessor processor;
OmahaResponseHandlerActionProcessorDelegate delegate;
processor.set_delegate(&delegate);
@@ -103,8 +121,11 @@
ObjectFeederAction<OmahaResponse> feeder_action;
feeder_action.set_obj(in);
if (in.update_exists && in.version != kBadVersion) {
+ string expected_hash;
+ for (const auto& package : in.packages)
+ expected_hash += package.hash + ":";
EXPECT_CALL(*(fake_system_state_.mock_prefs()),
- SetString(kPrefsUpdateCheckResponseHash, in.hash))
+ SetString(kPrefsUpdateCheckResponseHash, expected_hash))
.WillOnce(Return(true));
int slot = 1 - fake_system_state_.fake_boot_control()->GetCurrentSlot();
@@ -113,19 +134,19 @@
.WillOnce(Return(true));
}
- string current_url = in.payload_urls.size() ? in.payload_urls[0] : "";
+ string current_url = in.packages.size() ? in.packages[0].payload_urls[0] : "";
EXPECT_CALL(*(fake_system_state_.mock_payload_state()), GetCurrentUrl())
.WillRepeatedly(Return(current_url));
- OmahaResponseHandlerAction response_handler_action(
+ action_.reset(new OmahaResponseHandlerAction(
&fake_system_state_,
- (test_deadline_file.empty() ?
- constants::kOmahaResponseDeadlineFile : test_deadline_file));
- BondActions(&feeder_action, &response_handler_action);
+ (test_deadline_file.empty() ? constants::kOmahaResponseDeadlineFile
+ : test_deadline_file)));
+ BondActions(&feeder_action, action_.get());
ObjectCollectorAction<InstallPlan> collector_action;
- BondActions(&response_handler_action, &collector_action);
+ BondActions(action_.get(), &collector_action);
processor.EnqueueAction(&feeder_action);
- processor.EnqueueAction(&response_handler_action);
+ processor.EnqueueAction(action_.get());
processor.EnqueueAction(&collector_action);
processor.StartProcessing();
EXPECT_TRUE(!processor.IsRunning())
@@ -133,29 +154,31 @@
if (out)
*out = collector_action.object();
EXPECT_TRUE(delegate.code_set_);
+ action_result_code_ = delegate.code_;
return delegate.code_ == ErrorCode::kSuccess;
}
TEST_F(OmahaResponseHandlerActionTest, SimpleTest) {
string test_deadline_file;
- CHECK(utils::MakeTempFile(
- "omaha_response_handler_action_unittest-XXXXXX",
- &test_deadline_file, nullptr));
+ CHECK(utils::MakeTempFile("omaha_response_handler_action_unittest-XXXXXX",
+ &test_deadline_file,
+ nullptr));
ScopedPathUnlinker deadline_unlinker(test_deadline_file);
{
OmahaResponse in;
in.update_exists = true;
in.version = "a.b.c.d";
- in.payload_urls.push_back("http://foo/the_update_a.b.c.d.tgz");
+ in.packages.push_back(
+ {.payload_urls = {"http://foo/the_update_a.b.c.d.tgz"},
+ .size = 12,
+ .hash = kPayloadHashHex});
in.more_info_url = "http://more/info";
- in.hash = "HASH+";
- in.size = 12;
in.prompt = false;
in.deadline = "20101020";
InstallPlan install_plan;
EXPECT_TRUE(DoTest(in, test_deadline_file, &install_plan));
- EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
- EXPECT_EQ(in.hash, install_plan.payload_hash);
+ EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+ EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
EXPECT_EQ(1U, install_plan.target_slot);
string deadline;
EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline));
@@ -171,17 +194,18 @@
OmahaResponse in;
in.update_exists = true;
in.version = "a.b.c.d";
- in.payload_urls.push_back("http://foo/the_update_a.b.c.d.tgz");
+ in.packages.push_back(
+ {.payload_urls = {"http://foo/the_update_a.b.c.d.tgz"},
+ .size = 12,
+ .hash = kPayloadHashHex});
in.more_info_url = "http://more/info";
- in.hash = "HASHj+";
- in.size = 12;
in.prompt = true;
InstallPlan install_plan;
// Set the other slot as current.
fake_system_state_.fake_boot_control()->SetCurrentSlot(1);
EXPECT_TRUE(DoTest(in, test_deadline_file, &install_plan));
- EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
- EXPECT_EQ(in.hash, install_plan.payload_hash);
+ EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+ EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
EXPECT_EQ(0U, install_plan.target_slot);
string deadline;
EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline) &&
@@ -192,17 +216,16 @@
OmahaResponse in;
in.update_exists = true;
in.version = "a.b.c.d";
- in.payload_urls.push_back(kLongName);
+ in.packages.push_back(
+ {.payload_urls = {kLongName}, .size = 12, .hash = kPayloadHashHex});
in.more_info_url = "http://more/info";
- in.hash = "HASHj+";
- in.size = 12;
in.prompt = true;
in.deadline = "some-deadline";
InstallPlan install_plan;
fake_system_state_.fake_boot_control()->SetCurrentSlot(0);
EXPECT_TRUE(DoTest(in, test_deadline_file, &install_plan));
- EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
- EXPECT_EQ(in.hash, install_plan.payload_hash);
+ EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+ EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
EXPECT_EQ(1U, install_plan.target_slot);
string deadline;
EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline));
@@ -219,22 +242,45 @@
EXPECT_TRUE(install_plan.partitions.empty());
}
+TEST_F(OmahaResponseHandlerActionTest, MultiPackageTest) {
+ OmahaResponse in;
+ in.update_exists = true;
+ in.version = "a.b.c.d";
+ in.packages.push_back({.payload_urls = {"http://package/1"},
+ .size = 1,
+ .hash = kPayloadHashHex});
+ in.packages.push_back({.payload_urls = {"http://package/2"},
+ .size = 2,
+ .hash = kPayloadHashHex});
+ in.more_info_url = "http://more/info";
+ InstallPlan install_plan;
+ EXPECT_TRUE(DoTest(in, "", &install_plan));
+ EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+ EXPECT_EQ(2u, install_plan.payloads.size());
+ EXPECT_EQ(in.packages[0].size, install_plan.payloads[0].size);
+ 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.version, install_plan.version);
+}
+
TEST_F(OmahaResponseHandlerActionTest, HashChecksForHttpTest) {
OmahaResponse in;
in.update_exists = true;
in.version = "a.b.c.d";
- in.payload_urls.push_back("http://test.should/need/hash.checks.signed");
+ in.packages.push_back(
+ {.payload_urls = {"http://test.should/need/hash.checks.signed"},
+ .size = 12,
+ .hash = kPayloadHashHex});
in.more_info_url = "http://more/info";
- in.hash = "HASHj+";
- in.size = 12;
// Hash checks are always skipped for non-official update URLs.
EXPECT_CALL(*(fake_system_state_.mock_request_params()),
IsUpdateUrlOfficial())
.WillRepeatedly(Return(true));
InstallPlan install_plan;
EXPECT_TRUE(DoTest(in, "", &install_plan));
- EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
- EXPECT_EQ(in.hash, install_plan.payload_hash);
+ EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+ EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
EXPECT_TRUE(install_plan.hash_checks_mandatory);
EXPECT_EQ(in.version, install_plan.version);
}
@@ -243,17 +289,18 @@
OmahaResponse in;
in.update_exists = true;
in.version = "a.b.c.d";
- in.payload_urls.push_back("http://url.normally/needs/hash.checks.signed");
+ in.packages.push_back(
+ {.payload_urls = {"http://url.normally/needs/hash.checks.signed"},
+ .size = 12,
+ .hash = kPayloadHashHex});
in.more_info_url = "http://more/info";
- in.hash = "HASHj+";
- in.size = 12;
EXPECT_CALL(*(fake_system_state_.mock_request_params()),
IsUpdateUrlOfficial())
.WillRepeatedly(Return(false));
InstallPlan install_plan;
EXPECT_TRUE(DoTest(in, "", &install_plan));
- EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
- EXPECT_EQ(in.hash, install_plan.payload_hash);
+ EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+ EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
EXPECT_FALSE(install_plan.hash_checks_mandatory);
EXPECT_EQ(in.version, install_plan.version);
}
@@ -264,18 +311,19 @@
OmahaResponse in;
in.update_exists = true;
in.version = "a.b.c.d";
- in.payload_urls.push_back("http://url.normally/needs/hash.checks.signed");
+ in.packages.push_back(
+ {.payload_urls = {"http://url.normally/needs/hash.checks.signed"},
+ .size = 12,
+ .hash = kPayloadHashHex});
in.more_info_url = "http://more/info";
- in.hash = "HASHj+";
- in.size = 12;
EXPECT_CALL(*(fake_system_state_.mock_request_params()),
IsUpdateUrlOfficial())
.WillRepeatedly(Return(true));
fake_system_state_.fake_hardware()->SetIsOfficialBuild(false);
InstallPlan install_plan;
EXPECT_TRUE(DoTest(in, "", &install_plan));
- EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
- EXPECT_EQ(in.hash, install_plan.payload_hash);
+ EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+ EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
EXPECT_FALSE(install_plan.hash_checks_mandatory);
EXPECT_EQ(in.version, install_plan.version);
}
@@ -284,17 +332,18 @@
OmahaResponse in;
in.update_exists = true;
in.version = "a.b.c.d";
- in.payload_urls.push_back("https://test.should.not/need/hash.checks.signed");
+ in.packages.push_back(
+ {.payload_urls = {"https://test.should/need/hash.checks.signed"},
+ .size = 12,
+ .hash = kPayloadHashHex});
in.more_info_url = "http://more/info";
- in.hash = "HASHj+";
- in.size = 12;
EXPECT_CALL(*(fake_system_state_.mock_request_params()),
IsUpdateUrlOfficial())
.WillRepeatedly(Return(true));
InstallPlan install_plan;
EXPECT_TRUE(DoTest(in, "", &install_plan));
- EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
- EXPECT_EQ(in.hash, install_plan.payload_hash);
+ EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+ EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
EXPECT_FALSE(install_plan.hash_checks_mandatory);
EXPECT_EQ(in.version, install_plan.version);
}
@@ -303,18 +352,19 @@
OmahaResponse in;
in.update_exists = true;
in.version = "a.b.c.d";
- in.payload_urls.push_back("http://test.should.still/need/hash.checks");
- in.payload_urls.push_back("https://test.should.still/need/hash.checks");
+ in.packages.push_back(
+ {.payload_urls = {"http://test.should.still/need/hash.checks",
+ "https://test.should.still/need/hash.checks"},
+ .size = 12,
+ .hash = kPayloadHashHex});
in.more_info_url = "http://more/info";
- in.hash = "HASHj+";
- in.size = 12;
EXPECT_CALL(*(fake_system_state_.mock_request_params()),
IsUpdateUrlOfficial())
.WillRepeatedly(Return(true));
InstallPlan install_plan;
EXPECT_TRUE(DoTest(in, "", &install_plan));
- EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
- EXPECT_EQ(in.hash, install_plan.payload_hash);
+ EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+ EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
EXPECT_TRUE(install_plan.hash_checks_mandatory);
EXPECT_EQ(in.version, install_plan.version);
}
@@ -323,10 +373,10 @@
OmahaResponse in;
in.update_exists = true;
in.version = "a.b.c.d";
- in.payload_urls.push_back("https://MoreStableChannelTest");
+ in.packages.push_back({.payload_urls = {"https://MoreStableChannelTest"},
+ .size = 1,
+ .hash = kPayloadHashHex});
in.more_info_url = "http://more/info";
- in.hash = "HASHjk";
- in.size = 15;
// Create a uniquely named test directory.
base::ScopedTempDir tempdir;
@@ -334,7 +384,7 @@
OmahaRequestParams params(&fake_system_state_);
fake_system_state_.fake_hardware()->SetIsOfficialBuild(false);
- params.set_root(tempdir.path().value());
+ params.set_root(tempdir.GetPath().value());
params.set_current_channel("canary-channel");
// The ImageProperties in Android uses prefs to store MutableImageProperties.
#ifdef __ANDROID__
@@ -358,10 +408,10 @@
OmahaResponse in;
in.update_exists = true;
in.version = "a.b.c.d";
- in.payload_urls.push_back("https://LessStableChannelTest");
+ in.packages.push_back({.payload_urls = {"https://LessStableChannelTest"},
+ .size = 15,
+ .hash = kPayloadHashHex});
in.more_info_url = "http://more/info";
- in.hash = "HASHjk";
- in.size = 15;
// Create a uniquely named test directory.
base::ScopedTempDir tempdir;
@@ -369,7 +419,7 @@
OmahaRequestParams params(&fake_system_state_);
fake_system_state_.fake_hardware()->SetIsOfficialBuild(false);
- params.set_root(tempdir.path().value());
+ params.set_root(tempdir.GetPath().value());
params.set_current_channel("stable-channel");
// The ImageProperties in Android uses prefs to store MutableImageProperties.
#ifdef __ANDROID__
@@ -393,10 +443,11 @@
OmahaResponse in;
in.update_exists = true;
in.version = "a.b.c.d";
- in.payload_urls.push_back("https://would.not/cause/hash/checks");
+ in.packages.push_back(
+ {.payload_urls = {"https://would.not/cause/hash/checks"},
+ .size = 12,
+ .hash = kPayloadHashHex});
in.more_info_url = "http://more/info";
- in.hash = "HASHj+";
- in.size = 12;
OmahaRequestParams params(&fake_system_state_);
// We're using a real OmahaRequestParams object here so we can't mock
@@ -412,13 +463,70 @@
EXPECT_CALL(*fake_system_state_.mock_payload_state(), GetP2PUrl())
.WillRepeatedly(Return(p2p_url));
EXPECT_CALL(*fake_system_state_.mock_payload_state(),
- GetUsingP2PForDownloading()).WillRepeatedly(Return(true));
+ GetUsingP2PForDownloading())
+ .WillRepeatedly(Return(true));
InstallPlan install_plan;
EXPECT_TRUE(DoTest(in, "", &install_plan));
- EXPECT_EQ(in.hash, install_plan.payload_hash);
- EXPECT_EQ(install_plan.download_url, p2p_url);
+ EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+ EXPECT_EQ(p2p_url, install_plan.download_url);
EXPECT_TRUE(install_plan.hash_checks_mandatory);
}
+TEST_F(OmahaResponseHandlerActionTest, SystemVersionTest) {
+ OmahaResponse in;
+ in.update_exists = true;
+ in.version = "a.b.c.d";
+ in.system_version = "b.c.d.e";
+ in.packages.push_back({.payload_urls = {"http://package/1"},
+ .size = 1,
+ .hash = kPayloadHashHex});
+ in.packages.push_back({.payload_urls = {"http://package/2"},
+ .size = 2,
+ .hash = kPayloadHashHex});
+ in.more_info_url = "http://more/info";
+ InstallPlan install_plan;
+ EXPECT_TRUE(DoTest(in, "", &install_plan));
+ EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+ EXPECT_EQ(2u, install_plan.payloads.size());
+ EXPECT_EQ(in.packages[0].size, install_plan.payloads[0].size);
+ 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.version, install_plan.version);
+ EXPECT_EQ(in.system_version, install_plan.system_version);
+}
+
+TEST_F(OmahaResponseHandlerActionTest, TestDeferredByPolicy) {
+ OmahaResponse in;
+ in.update_exists = true;
+ 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});
+ // Setup the UpdateManager to disallow the update.
+ FakeClock fake_clock;
+ MockPolicy* mock_policy = new MockPolicy(&fake_clock);
+ FakeUpdateManager* fake_update_manager =
+ fake_system_state_.fake_update_manager();
+ fake_update_manager->set_policy(mock_policy);
+ EXPECT_CALL(*mock_policy, UpdateCanBeApplied(_, _, _, _, _))
+ .WillOnce(
+ DoAll(SetArgPointee<3>(ErrorCode::kOmahaUpdateDeferredPerPolicy),
+ Return(EvalStatus::kSucceeded)));
+ // Perform the Action. It should "fail" with kOmahaUpdateDeferredPerPolicy.
+ InstallPlan install_plan;
+ EXPECT_FALSE(DoTest(in, "", &install_plan));
+ EXPECT_EQ(ErrorCode::kOmahaUpdateDeferredPerPolicy, action_result_code_);
+ // Verify that DoTest() didn't set the output install plan.
+ EXPECT_EQ("", install_plan.version);
+ // Copy the underlying InstallPlan from the Action (like a real Delegate).
+ install_plan = action_->install_plan();
+ // Now verify the InstallPlan that was generated.
+ EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
+ EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+ EXPECT_EQ(1U, install_plan.target_slot);
+ EXPECT_EQ(in.version, install_plan.version);
+}
+
} // namespace chromeos_update_engine
diff --git a/parcelable_update_engine_status.cc b/parcelable_update_engine_status.cc
index d8eb6db..8a2dbeb 100644
--- a/parcelable_update_engine_status.cc
+++ b/parcelable_update_engine_status.cc
@@ -15,12 +15,27 @@
//
#include "update_engine/parcelable_update_engine_status.h"
+#include "update_engine/update_status_utils.h"
#include <binder/Parcel.h>
+using update_engine::UpdateEngineStatus;
+
namespace android {
namespace brillo {
+ParcelableUpdateEngineStatus::ParcelableUpdateEngineStatus(
+ const UpdateEngineStatus& status)
+ : last_checked_time_(status.last_checked_time),
+ current_operation_(
+ chromeos_update_engine::UpdateStatusToString(status.status)),
+ progress_(status.progress),
+ current_version_(String16{status.current_version.c_str()}),
+ current_system_version_(String16{status.current_system_version.c_str()}),
+ new_size_(status.new_size_bytes),
+ new_version_(String16{status.new_version.c_str()}),
+ new_system_version_(String16{status.new_system_version.c_str()}) {}
+
status_t ParcelableUpdateEngineStatus::writeToParcel(Parcel* parcel) const {
status_t status;
@@ -29,12 +44,27 @@
return status;
}
+ status = parcel->writeString16(current_operation_);
+ if (status != OK) {
+ return status;
+ }
+
status = parcel->writeDouble(progress_);
if (status != OK) {
return status;
}
- status = parcel->writeString16(current_operation_);
+ status = parcel->writeString16(current_version_);
+ if (status != OK) {
+ return status;
+ }
+
+ status = parcel->writeString16(current_system_version_);
+ if (status != OK) {
+ return status;
+ }
+
+ status = parcel->writeInt64(new_size_);
if (status != OK) {
return status;
}
@@ -44,7 +74,7 @@
return status;
}
- return parcel->writeInt64(new_size_);
+ return parcel->writeString16(new_system_version_);
}
status_t ParcelableUpdateEngineStatus::readFromParcel(const Parcel* parcel) {
@@ -55,12 +85,27 @@
return status;
}
+ status = parcel->readString16(¤t_operation_);
+ if (status != OK) {
+ return status;
+ }
+
status = parcel->readDouble(&progress_);
if (status != OK) {
return status;
}
- status = parcel->readString16(¤t_operation_);
+ status = parcel->readString16(¤t_version_);
+ if (status != OK) {
+ return status;
+ }
+
+ status = parcel->readString16(¤t_system_version_);
+ if (status != OK) {
+ return status;
+ }
+
+ status = parcel->readInt64(&new_size_);
if (status != OK) {
return status;
}
@@ -70,7 +115,7 @@
return status;
}
- return parcel->readInt64(&new_size_);
+ return parcel->readString16(&new_system_version_);
}
} // namespace brillo
diff --git a/parcelable_update_engine_status.h b/parcelable_update_engine_status.h
index 2cfedd9..82006e4 100644
--- a/parcelable_update_engine_status.h
+++ b/parcelable_update_engine_status.h
@@ -20,6 +20,8 @@
#include <binder/Parcelable.h>
#include <utils/String16.h>
+#include "update_engine/client_library/include/update_engine/update_status.h"
+
namespace android {
namespace brillo {
@@ -28,16 +30,31 @@
class ParcelableUpdateEngineStatus : public Parcelable {
public:
ParcelableUpdateEngineStatus() = default;
+ explicit ParcelableUpdateEngineStatus(
+ const update_engine::UpdateEngineStatus& status);
virtual ~ParcelableUpdateEngineStatus() = default;
status_t writeToParcel(Parcel* parcel) const override;
status_t readFromParcel(const Parcel* parcel) override;
+ // This list is kept in the Parcelable serialization order.
+
+ // When the update_engine last checked for updates (seconds since unix Epoch)
int64_t last_checked_time_;
- double progress_;
+ // The current status/operation of the update_engine.
android::String16 current_operation_;
- android::String16 new_version_;
+ // The current progress (0.0f-1.0f).
+ double progress_;
+ // The current product version.
+ android::String16 current_version_;
+ // The current system version.
+ android::String16 current_system_version_;
+ // The size of the update (bytes). This is int64_t for java compatibility.
int64_t new_size_;
+ // The new product version.
+ android::String16 new_version_;
+ // The new system version, if there is one (empty, otherwise).
+ android::String16 new_system_version_;
};
} // namespace brillo
diff --git a/parcelable_update_engine_status_unittest.cc b/parcelable_update_engine_status_unittest.cc
new file mode 100644
index 0000000..f4bd518
--- /dev/null
+++ b/parcelable_update_engine_status_unittest.cc
@@ -0,0 +1,92 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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 "update_engine/parcelable_update_engine_status.h"
+#include "update_engine/update_status_utils.h"
+
+#include <binder/Parcel.h>
+#include <gtest/gtest.h>
+
+using android::Parcel;
+using android::String16;
+using android::brillo::ParcelableUpdateEngineStatus;
+using android::status_t;
+using update_engine::UpdateEngineStatus;
+using update_engine::UpdateStatus;
+
+TEST(ParcelableUpdateEngineStatusTest, TestCreationFromUpdateEngineStatus) {
+ // This test creates an object and verifies that all the UpdateEngineStatus
+ // values are properly reflected in the Parcelable version of the class.
+
+ UpdateEngineStatus ue_status = {123456789,
+ UpdateStatus::DOWNLOADING,
+ "0.1.2.3",
+ "1.2.3.4",
+ 0.5f,
+ 34567,
+ "2.3.4.5",
+ "3.4.5.6"};
+ ParcelableUpdateEngineStatus parcelable_status(ue_status);
+ EXPECT_EQ(ue_status.last_checked_time, parcelable_status.last_checked_time_);
+ EXPECT_EQ(
+ String16{chromeos_update_engine::UpdateStatusToString(ue_status.status)},
+ parcelable_status.current_operation_);
+ EXPECT_EQ(String16{ue_status.current_version.c_str()},
+ parcelable_status.current_version_);
+ EXPECT_EQ(String16{ue_status.current_system_version.c_str()},
+ parcelable_status.current_system_version_);
+ EXPECT_EQ(ue_status.progress, parcelable_status.progress_);
+ EXPECT_EQ(static_cast<int64_t>(ue_status.new_size_bytes),
+ parcelable_status.new_size_);
+ EXPECT_EQ(String16{ue_status.new_version.c_str()},
+ parcelable_status.new_version_);
+ EXPECT_EQ(String16{ue_status.new_system_version.c_str()},
+ parcelable_status.new_system_version_);
+}
+
+TEST(ParcelableUpdateEngineStatusTest, TestParceling) {
+ // This tests the writeToParcel and readFromParcel methods for being correctly
+ // matched.
+ UpdateEngineStatus ue_status = {123456789,
+ UpdateStatus::DOWNLOADING,
+ "0.1.2.3",
+ "1.2.3.4",
+ 0.5f,
+ 34567,
+ "2.3.4.5",
+ "3.4.5.6"};
+ ParcelableUpdateEngineStatus source_status(ue_status);
+ Parcel parcel_source, parcel_target;
+ status_t status = source_status.writeToParcel(&parcel_source);
+ EXPECT_EQ(::android::OK, status);
+ size_t parcel_len = parcel_source.dataSize();
+ status = parcel_target.setData(parcel_source.data(), parcel_len);
+ EXPECT_EQ(::android::OK, status);
+ ParcelableUpdateEngineStatus target_status;
+ status = target_status.readFromParcel(&parcel_target);
+ EXPECT_EQ(::android::OK, status);
+
+ EXPECT_EQ(source_status.last_checked_time_, target_status.last_checked_time_);
+ EXPECT_EQ(source_status.current_operation_, target_status.current_operation_);
+ EXPECT_EQ(source_status.current_version_, target_status.current_version_);
+ EXPECT_EQ(source_status.current_system_version_,
+ target_status.current_system_version_);
+ EXPECT_EQ(source_status.progress_, target_status.progress_);
+ EXPECT_EQ(source_status.new_size_, target_status.new_size_);
+ EXPECT_EQ(source_status.new_version_, target_status.new_version_);
+ EXPECT_EQ(source_status.new_system_version_,
+ target_status.new_system_version_);
+}
diff --git a/payload_consumer/cached_file_descriptor_unittest.cc b/payload_consumer/cached_file_descriptor_unittest.cc
index 3c13486..6a6302a 100644
--- a/payload_consumer/cached_file_descriptor_unittest.cc
+++ b/payload_consumer/cached_file_descriptor_unittest.cc
@@ -117,7 +117,7 @@
size_t start = rand_r(&rand_seed) % blob_in.size();
size_t size = rand_r(&rand_seed) % (blob_in.size() - start);
std::fill_n(&blob_in[start], size, idx % 256);
- EXPECT_EQ(cfd_->Seek(start, SEEK_SET), start);
+ EXPECT_EQ(cfd_->Seek(start, SEEK_SET), static_cast<off64_t>(start));
Write(&blob_in[start], size);
}
EXPECT_TRUE(cfd_->Flush());
@@ -130,16 +130,19 @@
TEST_F(CachedFileDescriptorTest, SeekTest) {
EXPECT_EQ(cfd_->Seek(0, SEEK_SET), 0);
EXPECT_EQ(cfd_->Seek(1, SEEK_SET), 1);
- EXPECT_EQ(cfd_->Seek(kFileSize - 1, SEEK_SET), kFileSize - 1);
- EXPECT_EQ(cfd_->Seek(kFileSize, SEEK_SET), kFileSize);
- EXPECT_EQ(cfd_->Seek(kFileSize + 1, SEEK_SET), kFileSize + 1);
+ EXPECT_EQ(cfd_->Seek(kFileSize - 1, SEEK_SET),
+ static_cast<off64_t>(kFileSize - 1));
+ EXPECT_EQ(cfd_->Seek(kFileSize, SEEK_SET), static_cast<off64_t>(kFileSize));
+ EXPECT_EQ(cfd_->Seek(kFileSize + 1, SEEK_SET),
+ static_cast<off64_t>(kFileSize + 1));
EXPECT_EQ(cfd_->Seek(0, SEEK_SET), 0);
EXPECT_EQ(cfd_->Seek(1, SEEK_CUR), 1);
EXPECT_EQ(cfd_->Seek(1, SEEK_CUR), 2);
- EXPECT_EQ(cfd_->Seek(kFileSize - 1, SEEK_SET), kFileSize - 1);
- EXPECT_EQ(cfd_->Seek(1, SEEK_CUR), kFileSize);
- EXPECT_EQ(cfd_->Seek(1, SEEK_CUR), kFileSize + 1);
+ EXPECT_EQ(cfd_->Seek(kFileSize - 1, SEEK_SET),
+ static_cast<off64_t>(kFileSize - 1));
+ EXPECT_EQ(cfd_->Seek(1, SEEK_CUR), static_cast<off64_t>(kFileSize));
+ EXPECT_EQ(cfd_->Seek(1, SEEK_CUR), static_cast<off64_t>(kFileSize + 1));
}
TEST_F(CachedFileDescriptorTest, NoFlushTest) {
@@ -153,7 +156,7 @@
}
TEST_F(CachedFileDescriptorTest, CacheSizeWriteTest) {
- size_t seek = 10;
+ off64_t seek = 10;
brillo::Blob blob_in(kFileSize, 0);
std::fill_n(&blob_in[seek], kCacheSize, value_);
// We are writing exactly one cache size; Then it should be commited.
@@ -166,7 +169,7 @@
}
TEST_F(CachedFileDescriptorTest, UnderCacheSizeWriteTest) {
- size_t seek = 100;
+ off64_t seek = 100;
size_t less_than_cache_size = kCacheSize - 1;
EXPECT_EQ(cfd_->Seek(seek, SEEK_SET), seek);
brillo::Blob blob_in(kFileSize, 0);
@@ -182,7 +185,7 @@
}
TEST_F(CachedFileDescriptorTest, SeekAfterWriteTest) {
- size_t seek = 100;
+ off64_t seek = 100;
size_t less_than_cache_size = kCacheSize - 3;
EXPECT_EQ(cfd_->Seek(seek, SEEK_SET), seek);
brillo::Blob blob_in(kFileSize, 0);
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 5ad4e16..001c84a 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -203,7 +203,7 @@
}
// Format download total count and percentage.
- size_t payload_size = install_plan_->payload_size;
+ size_t payload_size = payload_->size;
string payload_size_str("?");
string downloaded_percentage_str("");
if (payload_size) {
@@ -238,7 +238,7 @@
// eliminated once we ensure that the payload_size in the install plan is
// always given and is non-zero. This currently isn't the case during unit
// tests (see chromium-os:37969).
- size_t payload_size = install_plan_->payload_size;
+ size_t payload_size = payload_->size;
unsigned actual_operations_weight = kProgressOperationsWeight;
if (payload_size)
new_overall_progress += min(
@@ -287,6 +287,7 @@
size_t read_len = min(count, max - buffer_.size());
const char* bytes_start = *bytes_p;
const char* bytes_end = bytes_start + read_len;
+ buffer_.reserve(max);
buffer_.insert(buffer_.end(), bytes_start, bytes_end);
*bytes_p = bytes_end;
*count_p = count - read_len;
@@ -351,10 +352,14 @@
return false;
const PartitionUpdate& partition = partitions_[current_partition_];
+ size_t num_previous_partitions =
+ install_plan_->partitions.size() - partitions_.size();
+ const InstallPlan::Partition& install_part =
+ install_plan_->partitions[num_previous_partitions + current_partition_];
// Open source fds if we have a delta payload with minor version >= 2.
- if (install_plan_->payload_type == InstallPayloadType::kDelta &&
+ if (payload_->type == InstallPayloadType::kDelta &&
GetMinorVersion() != kInPlaceMinorPayloadVersion) {
- source_path_ = install_plan_->partitions[current_partition_].source_path;
+ source_path_ = install_part.source_path;
int err;
source_fd_ = OpenFile(source_path_.c_str(), O_RDONLY, false, &err);
if (!source_fd_) {
@@ -366,7 +371,7 @@
}
}
- target_path_ = install_plan_->partitions[current_partition_].target_path;
+ target_path_ = install_part.target_path;
int err;
int flags = O_RDWR;
@@ -390,8 +395,7 @@
<< "\"";
// Discard the end of the partition, but ignore failures.
- DiscardPartitionTail(
- target_fd_, install_plan_->partitions[current_partition_].target_size);
+ DiscardPartitionTail(target_fd_, install_part.target_size);
return true;
}
@@ -452,7 +456,7 @@
if (manifest_.has_minor_version()) {
return manifest_.minor_version();
} else {
- return install_plan_->payload_type == InstallPayloadType::kDelta
+ return payload_->type == InstallPayloadType::kDelta
? kSupportedMinorPayloadVersion
: kFullPayloadMinorVersion;
}
@@ -542,9 +546,9 @@
// beyond the expected metadata size.
metadata_size_ = manifest_offset + manifest_size_;
if (install_plan_->hash_checks_mandatory) {
- if (install_plan_->metadata_size != metadata_size_) {
+ if (payload_->metadata_size != metadata_size_) {
LOG(ERROR) << "Mandatory metadata size in Omaha response ("
- << install_plan_->metadata_size
+ << payload_->metadata_size
<< ") is missing/incorrect, actual = " << metadata_size_;
*error = ErrorCode::kDownloadInvalidMetadataSize;
return kMetadataParseError;
@@ -561,13 +565,13 @@
// here. This is logged here (after we received the full metadata data) so
// that we just log once (instead of logging n times) if it takes n
// DeltaPerformer::Write calls to download the full manifest.
- if (install_plan_->metadata_size == metadata_size_) {
+ if (payload_->metadata_size == metadata_size_) {
LOG(INFO) << "Manifest size in payload matches expected value from Omaha";
} else {
// For mandatory-cases, we'd have already returned a kMetadataParseError
// above. We'll be here only for non-mandatory cases. Just send a UMA stat.
LOG(WARNING) << "Ignoring missing/incorrect metadata size ("
- << install_plan_->metadata_size
+ << payload_->metadata_size
<< ") in Omaha response as validation is not mandatory. "
<< "Trusting metadata size in payload = " << metadata_size_;
}
@@ -655,6 +659,12 @@
if (!ParseManifestPartitions(error))
return false;
+ // |install_plan.partitions| was filled in, nothing need to be done here if
+ // the payload was already applied, returns false to terminate http fetcher,
+ // but keep |error| as ErrorCode::kSuccess.
+ if (payload_->already_applied)
+ return false;
+
num_total_operations_ = 0;
for (const auto& partition : partitions_) {
num_total_operations_ += partition.operations_size();
@@ -719,7 +729,7 @@
// NOTE: If hash checks are mandatory and if metadata_signature is empty,
// we would have already failed in ParsePayloadMetadata method and thus not
// even be here. So no need to handle that case again here.
- if (!install_plan_->metadata_signature.empty()) {
+ if (!payload_->metadata_signature.empty()) {
// Note: Validate must be called only if CanPerformInstallOperation is
// called. Otherwise, we might be failing operations before even if there
// isn't sufficient data to compute the proper hash.
@@ -876,7 +886,6 @@
// Fill in the InstallPlan::partitions based on the partitions from the
// payload.
- install_plan_->partitions.clear();
for (const auto& partition : partitions_) {
InstallPlan::Partition install_part;
install_part.name = partition.partition_name();
@@ -1491,18 +1500,18 @@
return ErrorCode::kDownloadMetadataSignatureError;
brillo::Blob metadata_signature_blob, metadata_signature_protobuf_blob;
- if (!install_plan_->metadata_signature.empty()) {
+ if (!payload_->metadata_signature.empty()) {
// Convert base64-encoded signature to raw bytes.
- if (!brillo::data_encoding::Base64Decode(
- install_plan_->metadata_signature, &metadata_signature_blob)) {
+ if (!brillo::data_encoding::Base64Decode(payload_->metadata_signature,
+ &metadata_signature_blob)) {
LOG(ERROR) << "Unable to decode base64 metadata signature: "
- << install_plan_->metadata_signature;
+ << payload_->metadata_signature;
return ErrorCode::kDownloadMetadataSignatureError;
}
} else if (major_payload_version_ == kBrilloMajorPayloadVersion) {
- metadata_signature_protobuf_blob.assign(payload.begin() + metadata_size_,
- payload.begin() + metadata_size_ +
- metadata_signature_size_);
+ metadata_signature_protobuf_blob.assign(
+ payload.begin() + metadata_size_,
+ payload.begin() + metadata_size_ + metadata_signature_size_);
}
if (metadata_signature_blob.empty() &&
@@ -1529,14 +1538,13 @@
LOG(INFO) << "Verifying metadata hash signature using public key: "
<< path_to_public_key.value();
- HashCalculator metadata_hasher;
- metadata_hasher.Update(payload.data(), metadata_size_);
- if (!metadata_hasher.Finalize()) {
+ brillo::Blob calculated_metadata_hash;
+ if (!HashCalculator::RawHashOfBytes(
+ payload.data(), metadata_size_, &calculated_metadata_hash)) {
LOG(ERROR) << "Unable to compute actual hash of manifest";
return ErrorCode::kDownloadMetadataSignatureVerificationError;
}
- brillo::Blob calculated_metadata_hash = metadata_hasher.raw_hash();
PayloadVerifier::PadRSA2048SHA256Hash(&calculated_metadata_hash);
if (calculated_metadata_hash.empty()) {
LOG(ERROR) << "Computed actual hash of metadata is empty.";
@@ -1588,14 +1596,14 @@
InstallPayloadType actual_payload_type =
has_old_fields ? InstallPayloadType::kDelta : InstallPayloadType::kFull;
- if (install_plan_->payload_type == InstallPayloadType::kUnknown) {
+ if (payload_->type == InstallPayloadType::kUnknown) {
LOG(INFO) << "Detected a '"
<< InstallPayloadTypeToString(actual_payload_type)
<< "' payload.";
- install_plan_->payload_type = actual_payload_type;
- } else if (install_plan_->payload_type != actual_payload_type) {
+ payload_->type = actual_payload_type;
+ } else if (payload_->type != actual_payload_type) {
LOG(ERROR) << "InstallPlan expected a '"
- << InstallPayloadTypeToString(install_plan_->payload_type)
+ << InstallPayloadTypeToString(payload_->type)
<< "' payload but the downloaded manifest contains a '"
<< InstallPayloadTypeToString(actual_payload_type)
<< "' payload.";
@@ -1635,6 +1643,14 @@
}
}
+ if (manifest_.max_timestamp() < hardware_->GetBuildTimestamp()) {
+ LOG(ERROR) << "The current OS build timestamp ("
+ << hardware_->GetBuildTimestamp()
+ << ") is newer than the maximum timestamp in the manifest ("
+ << manifest_.max_timestamp() << ")";
+ return ErrorCode::kPayloadTimestampError;
+ }
+
// TODO(garnold) we should be adding more and more manifest checks, such as
// partition boundaries etc (see chromium-os:37661).
@@ -1683,15 +1699,14 @@
(operation.data_sha256_hash().data() +
operation.data_sha256_hash().size()));
- HashCalculator operation_hasher;
- operation_hasher.Update(buffer_.data(), operation.data_length());
- if (!operation_hasher.Finalize()) {
+ brillo::Blob calculated_op_hash;
+ if (!HashCalculator::RawHashOfBytes(
+ buffer_.data(), operation.data_length(), &calculated_op_hash)) {
LOG(ERROR) << "Unable to compute actual hash of operation "
<< next_operation_num_;
return ErrorCode::kDownloadOperationHashVerificationError;
}
- brillo::Blob calculated_op_hash = operation_hasher.raw_hash();
if (calculated_op_hash != expected_op_hash) {
LOG(ERROR) << "Hash verification failed for operation "
<< next_operation_num_ << ". Expected hash = ";
@@ -1714,7 +1729,7 @@
} while (0);
ErrorCode DeltaPerformer::VerifyPayload(
- const string& update_check_response_hash,
+ const brillo::Blob& update_check_response_hash,
const uint64_t update_check_response_size) {
// See if we should use the public RSA key in the Omaha response.
@@ -1736,11 +1751,11 @@
buffer_offset_);
// Verifies the payload hash.
- const string& payload_hash_data = payload_hash_calculator_.hash();
TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadVerificationError,
- !payload_hash_data.empty());
- TEST_AND_RETURN_VAL(ErrorCode::kPayloadHashMismatchError,
- payload_hash_data == update_check_response_hash);
+ !payload_hash_calculator_.raw_hash().empty());
+ TEST_AND_RETURN_VAL(
+ ErrorCode::kPayloadHashMismatchError,
+ payload_hash_calculator_.raw_hash() == update_check_response_hash);
// Verifies the signed payload hash.
if (!utils::FileExists(path_to_public_key.value().c_str())) {
@@ -1841,7 +1856,6 @@
TEST_AND_RETURN_FALSE(prefs->SetInt64(kPrefsUpdateStateNextOperation,
kUpdateStateOperationInvalid));
if (!quick) {
- prefs->SetString(kPrefsUpdateCheckResponseHash, "");
prefs->SetInt64(kPrefsUpdateStateNextDataOffset, -1);
prefs->SetInt64(kPrefsUpdateStateNextDataLength, 0);
prefs->SetString(kPrefsUpdateStateSHA256Context, "");
@@ -1850,6 +1864,7 @@
prefs->SetInt64(kPrefsManifestMetadataSize, -1);
prefs->SetInt64(kPrefsManifestSignatureSize, -1);
prefs->SetInt64(kPrefsResumedUpdateFailures, 0);
+ prefs->Delete(kPrefsPostInstallSucceeded);
}
return true;
}
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index 8aa036e..731e7f1 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -80,12 +80,14 @@
HardwareInterface* hardware,
DownloadActionDelegate* download_delegate,
InstallPlan* install_plan,
+ InstallPlan::Payload* payload,
bool is_interactive)
: prefs_(prefs),
boot_control_(boot_control),
hardware_(hardware),
download_delegate_(download_delegate),
install_plan_(install_plan),
+ payload_(payload),
is_interactive_(is_interactive) {}
// FileWriter's Write implementation where caller doesn't care about
@@ -116,13 +118,13 @@
bool IsManifestValid();
// Verifies the downloaded payload against the signed hash included in the
- // payload, against the update check hash (which is in base64 format) and
- // size using the public key and returns ErrorCode::kSuccess on success, an
- // error code on failure. This method should be called after closing the
- // stream. Note this method skips the signed hash check if the public key is
- // unavailable; it returns ErrorCode::kSignedDeltaPayloadExpectedError if the
- // public key is available but the delta payload doesn't include a signature.
- ErrorCode VerifyPayload(const std::string& update_check_response_hash,
+ // payload, against the update check hash and size using the public key and
+ // returns ErrorCode::kSuccess on success, an error code on failure.
+ // This method should be called after closing the stream. Note this method
+ // skips the signed hash check if the public key is unavailable; it returns
+ // ErrorCode::kSignedDeltaPayloadExpectedError if the public key is available
+ // but the delta payload doesn't include a signature.
+ ErrorCode VerifyPayload(const brillo::Blob& update_check_response_hash,
const uint64_t update_check_response_size);
// Converts an ordered collection of Extent objects which contain data of
@@ -312,6 +314,9 @@
// Install Plan based on Omaha Response.
InstallPlan* install_plan_;
+ // Pointer to the current payload in install_plan_.payloads.
+ InstallPlan::Payload* payload_{nullptr};
+
// File descriptor of the source partition. Only set while updating a
// partition when using a delta payload.
FileDescriptorPtr source_fd_{nullptr};
diff --git a/payload_consumer/delta_performer_integration_test.cc b/payload_consumer/delta_performer_integration_test.cc
index 6fdaba5..3572a6d 100644
--- a/payload_consumer/delta_performer_integration_test.cc
+++ b/payload_consumer/delta_performer_integration_test.cc
@@ -720,10 +720,10 @@
// Update the A image in place.
InstallPlan* install_plan = &state->install_plan;
install_plan->hash_checks_mandatory = hash_checks_mandatory;
- install_plan->metadata_size = state->metadata_size;
- install_plan->payload_type = (full_kernel && full_rootfs)
- ? InstallPayloadType::kFull
- : InstallPayloadType::kDelta;
+ install_plan->payloads = {{.metadata_size = state->metadata_size,
+ .type = (full_kernel && full_rootfs)
+ ? InstallPayloadType::kFull
+ : InstallPayloadType::kDelta}};
install_plan->source_slot = 0;
install_plan->target_slot = 1;
@@ -739,14 +739,15 @@
state->delta.data(),
state->metadata_size,
GetBuildArtifactsPath(kUnittestPrivateKeyPath),
- &install_plan->metadata_signature));
- EXPECT_FALSE(install_plan->metadata_signature.empty());
+ &install_plan->payloads[0].metadata_signature));
+ EXPECT_FALSE(install_plan->payloads[0].metadata_signature.empty());
*performer = new DeltaPerformer(&prefs,
&state->fake_boot_control_,
&state->fake_hardware_,
&state->mock_delegate_,
install_plan,
+ &install_plan->payloads[0],
false /* is_interactive */);
string public_key_path = GetBuildArtifactsPath(kUnittestPublicKeyPath);
EXPECT_TRUE(utils::FileExists(public_key_path.c_str()));
@@ -762,9 +763,8 @@
state->old_kernel_data,
&kernel_part.source_hash));
- // This partitions are normally filed by the FilesystemVerifierAction with
- // the source hashes used for deltas.
- install_plan->partitions = {root_part, kernel_part};
+ // The partitions should be empty before DeltaPerformer.
+ install_plan->partitions.clear();
// With minor version 2, we want the target to be the new image, result_img,
// but with version 1, we want to update A in place.
@@ -855,11 +855,11 @@
int expected_times = (expected_result == ErrorCode::kSuccess) ? 1 : 0;
EXPECT_CALL(state->mock_delegate_, DownloadComplete()).Times(expected_times);
- LOG(INFO) << "Verifying payload for expected result "
- << expected_result;
- EXPECT_EQ(expected_result, performer->VerifyPayload(
- HashCalculator::HashOfData(state->delta),
- state->delta.size()));
+ LOG(INFO) << "Verifying payload for expected result " << expected_result;
+ brillo::Blob expected_hash;
+ HashCalculator::RawHashOfData(state->delta, &expected_hash);
+ EXPECT_EQ(expected_result,
+ performer->VerifyPayload(expected_hash, state->delta.size()));
LOG(INFO) << "Verified payload.";
if (expected_result != ErrorCode::kSuccess) {
diff --git a/payload_consumer/delta_performer_unittest.cc b/payload_consumer/delta_performer_unittest.cc
index ea35c47..420efd2 100644
--- a/payload_consumer/delta_performer_unittest.cc
+++ b/payload_consumer/delta_performer_unittest.cc
@@ -158,7 +158,7 @@
uint64_t major_version,
InstallPayloadType payload_type,
ErrorCode expected) {
- install_plan_.payload_type = payload_type;
+ payload_.type = payload_type;
// The Manifest we are validating.
performer_.manifest_.CopyFrom(manifest);
@@ -219,7 +219,7 @@
string private_key =
sign_payload ? GetBuildArtifactsPath(kUnittestPrivateKeyPath) : "";
EXPECT_TRUE(payload.WritePayload(
- payload_path, blob_path, private_key, &install_plan_.metadata_size));
+ payload_path, blob_path, private_key, &payload_.metadata_size));
brillo::Blob payload_data;
EXPECT_TRUE(utils::ReadFile(payload_path, &payload_data));
@@ -286,7 +286,7 @@
uint64_t version = htobe64(kChromeOSMajorPayloadVersion);
EXPECT_TRUE(performer_.Write(&version, 8));
- install_plan_.metadata_size = expected_metadata_size;
+ payload_.metadata_size = expected_metadata_size;
ErrorCode error_code;
// When filling in size in manifest, exclude the size of the 20-byte header.
uint64_t size_in_manifest = htobe64(actual_metadata_size - 20);
@@ -323,13 +323,13 @@
// Fill up the metadata signature in install plan according to the test.
switch (metadata_signature_test) {
case kEmptyMetadataSignature:
- install_plan_.metadata_signature.clear();
+ payload_.metadata_signature.clear();
expected_result = DeltaPerformer::kMetadataParseError;
expected_error = ErrorCode::kDownloadMetadataSignatureMissingError;
break;
case kInvalidMetadataSignature:
- install_plan_.metadata_signature = kBogusMetadataSignature1;
+ payload_.metadata_signature = kBogusMetadataSignature1;
expected_result = DeltaPerformer::kMetadataParseError;
expected_error = ErrorCode::kDownloadMetadataSignatureMismatch;
break;
@@ -341,10 +341,10 @@
// then we can get to manifest signature checks.
ASSERT_TRUE(PayloadSigner::GetMetadataSignature(
payload.data(),
- install_plan_.metadata_size,
+ payload_.metadata_size,
GetBuildArtifactsPath(kUnittestPrivateKeyPath),
- &install_plan_.metadata_signature));
- EXPECT_FALSE(install_plan_.metadata_signature.empty());
+ &payload_.metadata_signature));
+ EXPECT_FALSE(payload_.metadata_signature.empty());
expected_result = DeltaPerformer::kMetadataParseSuccess;
expected_error = ErrorCode::kSuccess;
break;
@@ -372,7 +372,7 @@
// Check that the parsed metadata size is what's expected. This test
// implicitly confirms that the metadata signature is valid, if required.
- EXPECT_EQ(install_plan_.metadata_size, performer_.GetMetadataSize());
+ EXPECT_EQ(payload_.metadata_size, performer_.GetMetadataSize());
}
void SetSupportedMajorVersion(uint64_t major_version) {
@@ -380,6 +380,7 @@
}
FakePrefs prefs_;
InstallPlan install_plan_;
+ InstallPlan::Payload payload_;
FakeBootControl fake_boot_control_;
FakeHardware fake_hardware_;
MockDownloadActionDelegate mock_delegate_;
@@ -388,11 +389,12 @@
&fake_hardware_,
&mock_delegate_,
&install_plan_,
+ &payload_,
false /* is_interactive*/};
};
TEST_F(DeltaPerformerTest, FullPayloadWriteTest) {
- install_plan_.payload_type = InstallPayloadType::kFull;
+ payload_.type = InstallPayloadType::kFull;
brillo::Blob expected_data = brillo::Blob(std::begin(kRandomString),
std::end(kRandomString));
expected_data.resize(4096); // block size
@@ -411,7 +413,7 @@
}
TEST_F(DeltaPerformerTest, ShouldCancelTest) {
- install_plan_.payload_type = InstallPayloadType::kFull;
+ payload_.type = InstallPayloadType::kFull;
brillo::Blob expected_data = brillo::Blob(std::begin(kRandomString),
std::end(kRandomString));
expected_data.resize(4096); // block size
@@ -719,6 +721,20 @@
ErrorCode::kUnsupportedMinorPayloadVersion);
}
+TEST_F(DeltaPerformerTest, ValidateManifestDowngrade) {
+ // The Manifest we are validating.
+ DeltaArchiveManifest manifest;
+
+ manifest.set_minor_version(kFullPayloadMinorVersion);
+ manifest.set_max_timestamp(1);
+ fake_hardware_.SetBuildTimestamp(2);
+
+ RunManifestValidation(manifest,
+ DeltaPerformer::kSupportedMajorPayloadVersion,
+ InstallPayloadType::kFull,
+ ErrorCode::kPayloadTimestampError);
+}
+
TEST_F(DeltaPerformerTest, BrilloMetadataSignatureSizeTest) {
EXPECT_TRUE(performer_.Write(kDeltaMagic, sizeof(kDeltaMagic)));
@@ -751,7 +767,7 @@
install_plan_.hash_checks_mandatory = true;
// Just set these value so that we can use ValidateMetadataSignature directly.
performer_.major_payload_version_ = kBrilloMajorPayloadVersion;
- performer_.metadata_size_ = install_plan_.metadata_size;
+ performer_.metadata_size_ = payload_.metadata_size;
uint64_t signature_length;
EXPECT_TRUE(PayloadSigner::SignatureBlobLength(
{GetBuildArtifactsPath(kUnittestPrivateKeyPath)}, &signature_length));
@@ -832,8 +848,8 @@
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- string non_existing_file = temp_dir.path().Append("non-existing").value();
- string existing_file = temp_dir.path().Append("existing").value();
+ string non_existing_file = temp_dir.GetPath().Append("non-existing").value();
+ string existing_file = temp_dir.GetPath().Append("existing").value();
EXPECT_EQ(0, System(base::StringPrintf("touch %s", existing_file.c_str())));
// Non-official build, non-existing public-key, key in response -> true
diff --git a/payload_consumer/download_action.cc b/payload_consumer/download_action.cc
index cf550f5..f1b6e33 100644
--- a/payload_consumer/download_action.cc
+++ b/payload_consumer/download_action.cc
@@ -28,6 +28,7 @@
#include "update_engine/common/action_pipe.h"
#include "update_engine/common/boot_control_interface.h"
#include "update_engine/common/error_code_utils.h"
+#include "update_engine/common/multi_range_http_fetcher.h"
#include "update_engine/common/utils.h"
#include "update_engine/omaha_request_params.h"
#include "update_engine/p2p_manager.h"
@@ -48,12 +49,11 @@
boot_control_(boot_control),
hardware_(hardware),
system_state_(system_state),
- http_fetcher_(http_fetcher),
+ http_fetcher_(new MultiRangeHttpFetcher(http_fetcher)),
is_interactive_(is_interactive),
writer_(nullptr),
code_(ErrorCode::kSuccess),
delegate_(nullptr),
- bytes_received_(0),
p2p_sharing_fd_(-1),
p2p_visible_(true) {
base::StatisticsRecorder::Initialize();
@@ -86,7 +86,7 @@
bool DownloadAction::SetupP2PSharingFd() {
P2PManager *p2p_manager = system_state_->p2p_manager();
- if (!p2p_manager->FileShare(p2p_file_id_, install_plan_.payload_size)) {
+ if (!p2p_manager->FileShare(p2p_file_id_, payload_->size)) {
LOG(ERROR) << "Unable to share file via p2p";
CloseP2PSharingFd(true); // delete p2p file
return false;
@@ -174,10 +174,29 @@
// Get the InstallPlan and read it
CHECK(HasInputObject());
install_plan_ = GetInputObject();
- bytes_received_ = 0;
-
install_plan_.Dump();
+ bytes_received_ = 0;
+ bytes_received_previous_payloads_ = 0;
+ bytes_total_ = 0;
+ for (const auto& payload : install_plan_.payloads)
+ bytes_total_ += payload.size;
+
+ if (install_plan_.is_resume) {
+ int64_t payload_index = 0;
+ if (prefs_->GetInt64(kPrefsUpdateStatePayloadIndex, &payload_index) &&
+ static_cast<size_t>(payload_index) < install_plan_.payloads.size()) {
+ // Save the index for the resume payload before downloading any previous
+ // payload, otherwise it will be overwritten.
+ resume_payload_index_ = payload_index;
+ for (int i = 0; i < payload_index; i++)
+ install_plan_.payloads[i].already_applied = true;
+ }
+ }
+ // TODO(senj): check that install plan has at least one payload.
+ if (!payload_)
+ payload_ = &install_plan_.payloads[0];
+
LOG(INFO) << "Marking new slot as unbootable";
if (!boot_control_->MarkSlotUnbootable(install_plan_.target_slot)) {
LOG(WARNING) << "Unable to mark new slot "
@@ -185,7 +204,45 @@
<< ". Proceeding with the update anyway.";
}
- if (writer_) {
+ StartDownloading();
+}
+
+void DownloadAction::StartDownloading() {
+ download_active_ = true;
+ http_fetcher_->ClearRanges();
+ if (install_plan_.is_resume &&
+ payload_ == &install_plan_.payloads[resume_payload_index_]) {
+ // Resuming an update so fetch the update manifest metadata first.
+ int64_t manifest_metadata_size = 0;
+ int64_t manifest_signature_size = 0;
+ prefs_->GetInt64(kPrefsManifestMetadataSize, &manifest_metadata_size);
+ prefs_->GetInt64(kPrefsManifestSignatureSize, &manifest_signature_size);
+ http_fetcher_->AddRange(base_offset_,
+ manifest_metadata_size + manifest_signature_size);
+ // If there're remaining unprocessed data blobs, fetch them. Be careful not
+ // to request data beyond the end of the payload to avoid 416 HTTP response
+ // error codes.
+ int64_t next_data_offset = 0;
+ prefs_->GetInt64(kPrefsUpdateStateNextDataOffset, &next_data_offset);
+ uint64_t resume_offset =
+ manifest_metadata_size + manifest_signature_size + next_data_offset;
+ if (!payload_->size) {
+ http_fetcher_->AddRange(base_offset_ + resume_offset);
+ } else if (resume_offset < payload_->size) {
+ http_fetcher_->AddRange(base_offset_ + resume_offset,
+ payload_->size - resume_offset);
+ }
+ } else {
+ if (payload_->size) {
+ http_fetcher_->AddRange(base_offset_, payload_->size);
+ } else {
+ // If no payload size is passed we assume we read until the end of the
+ // stream.
+ http_fetcher_->AddRange(base_offset_);
+ }
+ }
+
+ if (writer_ && writer_ != delta_performer_.get()) {
LOG(INFO) << "Using writer for test.";
} else {
delta_performer_.reset(new DeltaPerformer(prefs_,
@@ -193,15 +250,13 @@
hardware_,
delegate_,
&install_plan_,
+ payload_,
is_interactive_));
writer_ = delta_performer_.get();
}
- download_active_ = true;
-
if (system_state_ != nullptr) {
const PayloadStateInterface* payload_state = system_state_->payload_state();
- string file_id = utils::CalculateP2PFileId(install_plan_.payload_hash,
- install_plan_.payload_size);
+ string file_id = utils::CalculateP2PFileId(payload_->hash, payload_->size);
if (payload_state->GetUsingP2PForSharing()) {
// If we're sharing the update, store the file_id to convey
// that we should write to the file.
@@ -272,14 +327,17 @@
}
bytes_received_ += length;
+ uint64_t bytes_downloaded_total =
+ bytes_received_previous_payloads_ + bytes_received_;
if (delegate_ && download_active_) {
- delegate_->BytesReceived(
- length, bytes_received_, install_plan_.payload_size);
+ delegate_->BytesReceived(length, bytes_downloaded_total, bytes_total_);
}
if (writer_ && !writer_->Write(bytes, length, &code_)) {
- LOG(ERROR) << "Error " << utils::ErrorCodeToString(code_) << " (" << code_
- << ") in DeltaPerformer's Write method when "
- << "processing the received payload -- Terminating processing";
+ if (code_ != ErrorCode::kSuccess) {
+ LOG(ERROR) << "Error " << utils::ErrorCodeToString(code_) << " (" << code_
+ << ") in DeltaPerformer's Write method when "
+ << "processing the received payload -- Terminating processing";
+ }
// Delete p2p file, if applicable.
if (!p2p_file_id_.empty())
CloseP2PSharingFd(true);
@@ -303,15 +361,32 @@
void DownloadAction::TransferComplete(HttpFetcher* fetcher, bool successful) {
if (writer_) {
LOG_IF(WARNING, writer_->Close() != 0) << "Error closing the writer.";
- writer_ = nullptr;
+ if (delta_performer_.get() == writer_) {
+ // no delta_performer_ in tests, so leave the test writer in place
+ writer_ = nullptr;
+ }
}
download_active_ = false;
ErrorCode code =
successful ? ErrorCode::kSuccess : ErrorCode::kDownloadTransferError;
- if (code == ErrorCode::kSuccess && delta_performer_.get()) {
- code = delta_performer_->VerifyPayload(install_plan_.payload_hash,
- install_plan_.payload_size);
+ if (code == ErrorCode::kSuccess) {
+ if (delta_performer_ && !payload_->already_applied)
+ code = delta_performer_->VerifyPayload(payload_->hash, payload_->size);
if (code == ErrorCode::kSuccess) {
+ if (payload_ < &install_plan_.payloads.back() &&
+ system_state_->payload_state()->NextPayload()) {
+ LOG(INFO) << "Incrementing to next payload";
+ // No need to reset if this payload was already applied.
+ if (delta_performer_ && !payload_->already_applied)
+ DeltaPerformer::ResetUpdateProgress(prefs_, false);
+ // Start downloading next payload.
+ bytes_received_previous_payloads_ += payload_->size;
+ payload_++;
+ install_plan_.download_url =
+ system_state_->payload_state()->GetCurrentUrl();
+ StartDownloading();
+ return;
+ }
// Log UpdateEngine.DownloadAction.* histograms to help diagnose
// long-blocking oeprations.
std::string histogram_output;
@@ -333,9 +408,13 @@
processor_->ActionComplete(this, code);
}
-void DownloadAction::TransferTerminated(HttpFetcher *fetcher) {
+void DownloadAction::TransferTerminated(HttpFetcher* fetcher) {
if (code_ != ErrorCode::kSuccess) {
processor_->ActionComplete(this, code_);
+ } else if (payload_->already_applied) {
+ LOG(INFO) << "TransferTerminated with ErrorCode::kSuccess when the current "
+ "payload has already applied, treating as TransferComplete.";
+ TransferComplete(fetcher, true);
}
}
diff --git a/payload_consumer/download_action.h b/payload_consumer/download_action.h
index 618a5b9..81d7333 100644
--- a/payload_consumer/download_action.h
+++ b/payload_consumer/download_action.h
@@ -27,6 +27,7 @@
#include "update_engine/common/action.h"
#include "update_engine/common/boot_control_interface.h"
#include "update_engine/common/http_fetcher.h"
+#include "update_engine/common/multi_range_http_fetcher.h"
#include "update_engine/payload_consumer/delta_performer.h"
#include "update_engine/payload_consumer/install_plan.h"
#include "update_engine/system_state.h"
@@ -107,6 +108,8 @@
delegate_ = delegate;
}
+ void set_base_offset(int64_t base_offset) { base_offset_ = base_offset; }
+
HttpFetcher* http_fetcher() { return http_fetcher_.get(); }
// Returns the p2p file id for the file being written or the empty
@@ -132,9 +135,15 @@
// called or if CloseP2PSharingFd() has been called.
void WriteToP2PFile(const void* data, size_t length, off_t file_offset);
+ // Start downloading the current payload using delta_performer.
+ void StartDownloading();
+
// The InstallPlan passed in
InstallPlan install_plan_;
+ // Pointer to the current payload in install_plan_.payloads.
+ InstallPlan::Payload* payload_{nullptr};
+
// SystemState required pointers.
PrefsInterface* prefs_;
BootControlInterface* boot_control_;
@@ -143,8 +152,8 @@
// Global context for the system.
SystemState* system_state_;
- // Pointer to the HttpFetcher that does the http work.
- std::unique_ptr<HttpFetcher> http_fetcher_;
+ // Pointer to the MultiRangeHttpFetcher that does the http work.
+ std::unique_ptr<MultiRangeHttpFetcher> http_fetcher_;
// If |true|, the update is user initiated (vs. periodic update checks). Hence
// the |delta_performer_| can decide not to use O_DSYNC flag for faster
@@ -163,7 +172,9 @@
// For reporting status to outsiders
DownloadActionDelegate* delegate_;
- uint64_t bytes_received_;
+ uint64_t bytes_received_{0}; // per file/range
+ uint64_t bytes_received_previous_payloads_{0};
+ uint64_t bytes_total_{0};
bool download_active_{false};
// The file-id for the file we're sharing or the empty string
@@ -177,6 +188,12 @@
// Set to |false| if p2p file is not visible.
bool p2p_visible_;
+ // Loaded from prefs before downloading any payload.
+ size_t resume_payload_index_{0};
+
+ // Offset of the payload in the download URL, used by UpdateAttempterAndroid.
+ int64_t base_offset_{0};
+
DISALLOW_COPY_AND_ASSIGN(DownloadAction);
};
diff --git a/payload_consumer/download_action_unittest.cc b/payload_consumer/download_action_unittest.cc
index 9d74116..21ce461 100644
--- a/payload_consumer/download_action_unittest.cc
+++ b/payload_consumer/download_action_unittest.cc
@@ -28,7 +28,6 @@
#include <base/files/file_util.h>
#include <base/location.h>
#include <base/strings/stringprintf.h>
-#include <brillo/bind_lambda.h>
#include <brillo/message_loops/fake_message_loop.h>
#include <brillo/message_loops/message_loop.h>
@@ -40,6 +39,7 @@
#include "update_engine/common/utils.h"
#include "update_engine/fake_p2p_manager_configuration.h"
#include "update_engine/fake_system_state.h"
+#include "update_engine/mock_file_writer.h"
#include "update_engine/payload_consumer/mock_download_action.h"
#include "update_engine/update_manager/fake_update_manager.h"
@@ -54,6 +54,7 @@
using testing::AtLeast;
using testing::InSequence;
using testing::Return;
+using testing::SetArgPointee;
using testing::_;
class DownloadActionTest : public ::testing::Test { };
@@ -137,13 +138,13 @@
0, writer.Open(output_temp_file.path().c_str(), O_WRONLY | O_CREAT, 0));
writer.set_fail_write(fail_write);
- // We pull off the first byte from data and seek past it.
- string hash = HashCalculator::HashOfBytes(&data[1], data.size() - 1);
- uint64_t size = data.size();
+ uint64_t size = data.size() - 1;
InstallPlan install_plan;
- install_plan.payload_type = InstallPayloadType::kDelta;
- install_plan.payload_size = size;
- install_plan.payload_hash = hash;
+ install_plan.payloads.push_back(
+ {.size = size, .type = InstallPayloadType::kDelta});
+ // We pull off the first byte from data and seek past it.
+ EXPECT_TRUE(HashCalculator::RawHashOfBytes(
+ &data[1], data.size() - 1, &install_plan.payloads[0].hash));
install_plan.source_slot = 0;
install_plan.target_slot = 1;
// We mark both slots as bootable. Only the target slot should be unbootable
@@ -173,7 +174,7 @@
download_action.set_delegate(&download_delegate);
if (data.size() > kMockHttpFetcherChunkSize)
EXPECT_CALL(download_delegate,
- BytesReceived(_, 1 + kMockHttpFetcherChunkSize, _));
+ BytesReceived(_, kMockHttpFetcherChunkSize, _));
EXPECT_CALL(download_delegate, BytesReceived(_, _, _)).Times(AtLeast(1));
}
ErrorCode expected_code = ErrorCode::kSuccess;
@@ -241,6 +242,93 @@
false); // use_download_delegate
}
+TEST(DownloadActionTest, MultiPayloadProgressTest) {
+ std::vector<brillo::Blob> payload_datas;
+ // the first payload must be the largest, as it's the actual payload used by
+ // the MockHttpFetcher for all downloaded data.
+ payload_datas.emplace_back(4 * kMockHttpFetcherChunkSize + 256);
+ payload_datas.emplace_back(2 * kMockHttpFetcherChunkSize);
+ brillo::FakeMessageLoop loop(nullptr);
+ loop.SetAsCurrent();
+ FakeSystemState fake_system_state;
+ EXPECT_CALL(*fake_system_state.mock_payload_state(), NextPayload())
+ .WillOnce(Return(true));
+
+ MockFileWriter mock_file_writer;
+ EXPECT_CALL(mock_file_writer, Close()).WillRepeatedly(Return(0));
+ EXPECT_CALL(mock_file_writer, Write(_, _, _))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<2>(ErrorCode::kSuccess), Return(true)));
+
+ InstallPlan install_plan;
+ uint64_t total_expected_download_size{0};
+ for (const auto& data : payload_datas) {
+ uint64_t size = data.size();
+ install_plan.payloads.push_back(
+ {.size = size, .type = InstallPayloadType::kFull});
+ total_expected_download_size += size;
+ }
+ ObjectFeederAction<InstallPlan> feeder_action;
+ feeder_action.set_obj(install_plan);
+ MockPrefs prefs;
+ MockHttpFetcher* http_fetcher = new MockHttpFetcher(
+ payload_datas[0].data(), payload_datas[0].size(), nullptr);
+ // takes ownership of passed in HttpFetcher
+ DownloadAction download_action(&prefs,
+ fake_system_state.boot_control(),
+ fake_system_state.hardware(),
+ &fake_system_state,
+ http_fetcher,
+ false /* is_interactive */);
+ download_action.SetTestFileWriter(&mock_file_writer);
+ BondActions(&feeder_action, &download_action);
+ MockDownloadActionDelegate download_delegate;
+ {
+ InSequence s;
+ download_action.set_delegate(&download_delegate);
+ // these are hand-computed based on the payloads specified above
+ EXPECT_CALL(download_delegate,
+ BytesReceived(kMockHttpFetcherChunkSize,
+ kMockHttpFetcherChunkSize,
+ total_expected_download_size));
+ EXPECT_CALL(download_delegate,
+ BytesReceived(kMockHttpFetcherChunkSize,
+ kMockHttpFetcherChunkSize * 2,
+ total_expected_download_size));
+ EXPECT_CALL(download_delegate,
+ BytesReceived(kMockHttpFetcherChunkSize,
+ kMockHttpFetcherChunkSize * 3,
+ total_expected_download_size));
+ EXPECT_CALL(download_delegate,
+ BytesReceived(kMockHttpFetcherChunkSize,
+ kMockHttpFetcherChunkSize * 4,
+ total_expected_download_size));
+ EXPECT_CALL(download_delegate,
+ BytesReceived(256,
+ kMockHttpFetcherChunkSize * 4 + 256,
+ total_expected_download_size));
+ EXPECT_CALL(download_delegate,
+ BytesReceived(kMockHttpFetcherChunkSize,
+ kMockHttpFetcherChunkSize * 5 + 256,
+ total_expected_download_size));
+ EXPECT_CALL(download_delegate,
+ BytesReceived(kMockHttpFetcherChunkSize,
+ total_expected_download_size,
+ total_expected_download_size));
+ }
+ ActionProcessor processor;
+ processor.EnqueueAction(&feeder_action);
+ processor.EnqueueAction(&download_action);
+
+ loop.PostTask(
+ FROM_HERE,
+ base::Bind(
+ [](ActionProcessor* processor) { processor->StartProcessing(); },
+ base::Unretained(&processor)));
+ loop.Run();
+ EXPECT_FALSE(loop.PendingTasks());
+}
+
namespace {
class TerminateEarlyTestProcessorDelegate : public ActionProcessorDelegate {
public:
@@ -271,6 +359,7 @@
// takes ownership of passed in HttpFetcher
ObjectFeederAction<InstallPlan> feeder_action;
InstallPlan install_plan;
+ install_plan.payloads.resize(1);
feeder_action.set_obj(install_plan);
FakeSystemState fake_system_state_;
MockPrefs prefs;
@@ -370,8 +459,9 @@
// takes ownership of passed in HttpFetcher
InstallPlan install_plan;
- install_plan.payload_size = 1;
- install_plan.payload_hash = HashCalculator::HashOfString("x");
+ install_plan.payloads.push_back({.size = 1});
+ EXPECT_TRUE(
+ HashCalculator::RawHashOfData({'x'}, &install_plan.payloads[0].hash));
ObjectFeederAction<InstallPlan> feeder_action;
feeder_action.set_obj(install_plan);
MockPrefs prefs;
@@ -456,8 +546,9 @@
EXPECT_EQ(
0, writer.Open(output_temp_file.path().c_str(), O_WRONLY | O_CREAT, 0));
InstallPlan install_plan;
- install_plan.payload_size = data_.length();
- install_plan.payload_hash = "1234hash";
+ install_plan.payloads.push_back(
+ {.size = data_.length(),
+ .hash = {'1', '2', '3', '4', 'h', 'a', 's', 'h'}});
ObjectFeederAction<InstallPlan> feeder_action;
feeder_action.set_obj(install_plan);
MockPrefs prefs;
@@ -571,7 +662,8 @@
// Prepare the file with existing data before starting to write to
// it via DownloadAction.
- string file_id = utils::CalculateP2PFileId("1234hash", data_.length());
+ string file_id = utils::CalculateP2PFileId(
+ {'1', '2', '3', '4', 'h', 'a', 's', 'h'}, data_.length());
ASSERT_TRUE(p2p_manager_->FileShare(file_id, data_.length()));
string existing_data;
for (unsigned int i = 0; i < 1000; i++)
@@ -608,7 +700,8 @@
// Prepare the file with all existing data before starting to write
// to it via DownloadAction.
- string file_id = utils::CalculateP2PFileId("1234hash", data_.length());
+ string file_id = utils::CalculateP2PFileId(
+ {'1', '2', '3', '4', 'h', 'a', 's', 'h'}, data_.length());
ASSERT_TRUE(p2p_manager_->FileShare(file_id, data_.length()));
string existing_data;
for (unsigned int i = 0; i < 1000; i++)
diff --git a/payload_consumer/extent_reader.cc b/payload_consumer/extent_reader.cc
index 428ecfe..96ea918 100644
--- a/payload_consumer/extent_reader.cc
+++ b/payload_consumer/extent_reader.cc
@@ -68,9 +68,10 @@
if (cur_extent_ == extents_.end()) {
TEST_AND_RETURN_FALSE(bytes_read == count);
}
- uint64_t bytes_to_read = std::min(
- count - bytes_read,
- cur_extent_->num_blocks() * block_size_ - cur_extent_bytes_read_);
+ uint64_t cur_extent_bytes_left =
+ cur_extent_->num_blocks() * block_size_ - cur_extent_bytes_read_;
+ uint64_t bytes_to_read =
+ std::min(count - bytes_read, cur_extent_bytes_left);
ssize_t out_bytes_read;
TEST_AND_RETURN_FALSE(utils::PReadAll(
diff --git a/payload_consumer/filesystem_verifier_action.cc b/payload_consumer/filesystem_verifier_action.cc
index 5156f96..5edde9e 100644
--- a/payload_consumer/filesystem_verifier_action.cc
+++ b/payload_consumer/filesystem_verifier_action.cc
@@ -34,16 +34,13 @@
#include "update_engine/payload_consumer/delta_performer.h"
#include "update_engine/payload_consumer/payload_constants.h"
+using brillo::data_encoding::Base64Encode;
using std::string;
namespace chromeos_update_engine {
namespace {
const off_t kReadFileBufferSize = 128 * 1024;
-
-string StringForHashBytes(const brillo::Blob& hash) {
- return brillo::data_encoding::Base64Encode(hash.data(), hash.size());
-}
} // namespace
void FilesystemVerifierAction::PerformAction() {
@@ -199,15 +196,18 @@
}
InstallPlan::Partition& partition =
install_plan_.partitions[partition_index_];
- LOG(INFO) << "Hash of " << partition.name << ": " << hasher_->hash();
+ LOG(INFO) << "Hash of " << partition.name << ": "
+ << Base64Encode(hasher_->raw_hash());
switch (verifier_step_) {
case VerifierStep::kVerifyTargetHash:
if (partition.target_hash != hasher_->raw_hash()) {
LOG(ERROR) << "New '" << partition.name
<< "' partition verification failed.";
- if (install_plan_.payload_type == InstallPayloadType::kFull)
+ if (partition.source_hash.empty()) {
+ // No need to verify source if it is a full payload.
return Cleanup(ErrorCode::kNewRootfsVerificationError);
+ }
// If we have not verified source partition yet, now that the target
// partition does not match, and it's not a full payload, we need to
// switch to kVerifySourceHash step to check if it's because the source
@@ -231,9 +231,9 @@
" means that the delta I've been given doesn't match my"
" existing system. The "
<< partition.name << " partition I have has hash: "
- << StringForHashBytes(hasher_->raw_hash())
+ << Base64Encode(hasher_->raw_hash())
<< " but the update expected me to have "
- << StringForHashBytes(partition.source_hash) << " .";
+ << Base64Encode(partition.source_hash) << " .";
LOG(INFO) << "To get the checksum of the " << partition.name
<< " partition run this command: dd if="
<< partition.source_path
diff --git a/payload_consumer/filesystem_verifier_action_unittest.cc b/payload_consumer/filesystem_verifier_action_unittest.cc
index 2e1d95d..b4f7f7f 100644
--- a/payload_consumer/filesystem_verifier_action_unittest.cc
+++ b/payload_consumer/filesystem_verifier_action_unittest.cc
@@ -26,7 +26,6 @@
#include <base/posix/eintr_wrapper.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <brillo/bind_lambda.h>
#include <brillo/message_loops/fake_message_loop.h>
#include <brillo/message_loops/message_loop_utils.h>
#include <gmock/gmock.h>
diff --git a/payload_consumer/install_plan.cc b/payload_consumer/install_plan.cc
index b04da74..45112d6 100644
--- a/payload_consumer/install_plan.cc
+++ b/payload_consumer/install_plan.cc
@@ -18,6 +18,7 @@
#include <base/format_macros.h>
#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
#include <base/strings/stringprintf.h>
#include "update_engine/common/utils.h"
@@ -41,15 +42,9 @@
bool InstallPlan::operator==(const InstallPlan& that) const {
return ((is_resume == that.is_resume) &&
- (payload_type == that.payload_type) &&
- (download_url == that.download_url) &&
- (payload_size == that.payload_size) &&
- (payload_hash == that.payload_hash) &&
- (metadata_size == that.metadata_size) &&
- (metadata_signature == that.metadata_signature) &&
+ (download_url == that.download_url) && (payloads == that.payloads) &&
(source_slot == that.source_slot) &&
- (target_slot == that.target_slot) &&
- (partitions == that.partitions));
+ (target_slot == that.target_slot) && (partitions == that.partitions));
}
bool InstallPlan::operator!=(const InstallPlan& that) const {
@@ -67,21 +62,35 @@
partition.target_size,
utils::ToString(partition.run_postinstall).c_str());
}
+ string payloads_str;
+ for (const auto& payload : payloads) {
+ payloads_str += base::StringPrintf(
+ ", payload: (size: %" PRIu64 ", metadata_size: %" PRIu64
+ ", metadata signature: %s, hash: %s, payload type: %s)",
+ payload.size,
+ payload.metadata_size,
+ payload.metadata_signature.c_str(),
+ base::HexEncode(payload.hash.data(), payload.hash.size()).c_str(),
+ InstallPayloadTypeToString(payload.type).c_str());
+ }
- LOG(INFO) << "InstallPlan: "
- << (is_resume ? "resume" : "new_update")
- << ", payload type: " << InstallPayloadTypeToString(payload_type)
+ string version_str = base::StringPrintf(", version: %s", version.c_str());
+ if (!system_version.empty()) {
+ version_str +=
+ base::StringPrintf(", system_version: %s", system_version.c_str());
+ }
+
+ LOG(INFO) << "InstallPlan: " << (is_resume ? "resume" : "new_update")
+ << version_str
<< ", source_slot: " << BootControlInterface::SlotName(source_slot)
<< ", target_slot: " << BootControlInterface::SlotName(target_slot)
- << ", url: " << download_url
- << ", payload size: " << payload_size
- << ", payload hash: " << payload_hash
- << ", metadata size: " << metadata_size
- << ", metadata signature: " << metadata_signature
- << partitions_str
- << ", hash_checks_mandatory: " << utils::ToString(
- hash_checks_mandatory)
- << ", powerwash_required: " << utils::ToString(powerwash_required);
+ << ", url: " << download_url << payloads_str << partitions_str
+ << ", hash_checks_mandatory: "
+ << utils::ToString(hash_checks_mandatory)
+ << ", powerwash_required: " << utils::ToString(powerwash_required)
+ << ", switch_slot_on_reboot: "
+ << utils::ToString(switch_slot_on_reboot)
+ << ", run_post_install: " << utils::ToString(run_post_install);
}
bool InstallPlan::LoadPartitionsFromSlots(BootControlInterface* boot_control) {
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index 3f0005c..5cdfbc1 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -52,14 +52,30 @@
bool LoadPartitionsFromSlots(BootControlInterface* boot_control);
bool is_resume{false};
- InstallPayloadType payload_type{InstallPayloadType::kUnknown};
std::string download_url; // url to download from
std::string version; // version we are installing.
+ // system version, if present and separate from version
+ std::string system_version;
- uint64_t payload_size{0}; // size of the payload
- std::string payload_hash; // SHA256 hash of the payload
- uint64_t metadata_size{0}; // size of the metadata
- std::string metadata_signature; // signature of the metadata
+ struct Payload {
+ uint64_t size = 0; // size of the payload
+ uint64_t metadata_size = 0; // size of the metadata
+ std::string metadata_signature; // signature of the metadata in base64
+ brillo::Blob hash; // SHA256 hash of the payload
+ InstallPayloadType type{InstallPayloadType::kUnknown};
+ // Only download manifest and fill in partitions in install plan without
+ // apply the payload if true. Will be set by DownloadAction when resuming
+ // multi-payload.
+ bool already_applied = false;
+
+ bool operator==(const Payload& that) const {
+ return size == that.size && metadata_size == that.metadata_size &&
+ metadata_signature == that.metadata_signature &&
+ hash == that.hash && type == that.type &&
+ already_applied == that.already_applied;
+ }
+ };
+ std::vector<Payload> payloads;
// The partition slots used for the update.
BootControlInterface::Slot source_slot{BootControlInterface::kInvalidSlot};
@@ -103,6 +119,14 @@
// False otherwise.
bool powerwash_required{false};
+ // True if the updated slot should be marked active on success.
+ // False otherwise.
+ bool switch_slot_on_reboot{true};
+
+ // True if the update should run its post-install step.
+ // False otherwise.
+ bool run_post_install{true};
+
// If not blank, a base-64 encoded representation of the PEM-encoded
// public key in the response.
std::string public_key_rsa;
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index 27a9ed6..cedecda 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -82,6 +82,11 @@
}
void PostinstallRunnerAction::PerformPartitionPostinstall() {
+ if (!install_plan_.run_post_install) {
+ LOG(INFO) << "Skipping post-install according to install plan.";
+ return CompletePostinstall(ErrorCode::kSuccess);
+ }
+
if (install_plan_.download_url.empty()) {
LOG(INFO) << "Skipping post-install during rollback";
return CompletePostinstall(ErrorCode::kSuccess);
@@ -331,15 +336,21 @@
void PostinstallRunnerAction::CompletePostinstall(ErrorCode error_code) {
// We only attempt to mark the new slot as active if all the postinstall
// steps succeeded.
- if (error_code == ErrorCode::kSuccess &&
- !boot_control_->SetActiveBootSlot(install_plan_.target_slot)) {
- error_code = ErrorCode::kPostinstallRunnerError;
+ if (error_code == ErrorCode::kSuccess) {
+ if (install_plan_.switch_slot_on_reboot) {
+ if (!boot_control_->SetActiveBootSlot(install_plan_.target_slot)) {
+ error_code = ErrorCode::kPostinstallRunnerError;
+ }
+ } else {
+ error_code = ErrorCode::kUpdatedButNotActive;
+ }
}
ScopedActionCompleter completer(processor_, this);
completer.set_code(error_code);
- if (error_code != ErrorCode::kSuccess) {
+ if (error_code != ErrorCode::kSuccess &&
+ error_code != ErrorCode::kUpdatedButNotActive) {
LOG(ERROR) << "Postinstall action failed.";
// Undo any changes done to trigger Powerwash.
diff --git a/payload_consumer/postinstall_runner_action_unittest.cc b/payload_consumer/postinstall_runner_action_unittest.cc
index a319299..f15171b 100644
--- a/payload_consumer/postinstall_runner_action_unittest.cc
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -28,7 +28,6 @@
#include <base/message_loop/message_loop.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <brillo/bind_lambda.h>
#include <brillo/message_loops/base_message_loop.h>
#include <brillo/message_loops/message_loop_utils.h>
#include <gmock/gmock.h>
diff --git a/payload_generator/delta_diff_utils.cc b/payload_generator/delta_diff_utils.cc
index fbb6066..bcbc3a5 100644
--- a/payload_generator/delta_diff_utils.cc
+++ b/payload_generator/delta_diff_utils.cc
@@ -38,6 +38,7 @@
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <base/threading/simple_thread.h>
+#include <brillo/data_encoding.h>
#include <bsdiff/bsdiff.h>
#include "update_engine/common/hash_calculator.h"
@@ -210,8 +211,8 @@
void MergeOperation(vector<AnnotatedOperation>* aops);
private:
- const string old_part_;
- const string new_part_;
+ const string& old_part_;
+ const string& new_part_;
const PayloadVersion& version_;
// The block ranges of the old/new file within the src/tgt image
@@ -883,7 +884,8 @@
TEST_AND_RETURN_FALSE(hasher.Finalize());
const brillo::Blob& hash = hasher.raw_hash();
info->set_hash(hash.data(), hash.size());
- LOG(INFO) << part.path << ": size=" << part.size << " hash=" << hasher.hash();
+ LOG(INFO) << part.path << ": size=" << part.size
+ << " hash=" << brillo::data_encoding::Base64Encode(hash);
return true;
}
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index 96915c1..2729bc4 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -167,15 +167,19 @@
}
}
-void VerifySignedPayload(const string& in_file,
- const string& public_key) {
+int VerifySignedPayload(const string& in_file, const string& public_key) {
LOG(INFO) << "Verifying signed payload.";
LOG_IF(FATAL, in_file.empty())
<< "Must pass --in_file to verify signed payload.";
LOG_IF(FATAL, public_key.empty())
<< "Must pass --public_key to verify signed payload.";
- CHECK(PayloadSigner::VerifySignedPayload(in_file, public_key));
+ if (!PayloadSigner::VerifySignedPayload(in_file, public_key)) {
+ LOG(INFO) << "VerifySignedPayload failed";
+ return 1;
+ }
+
LOG(INFO) << "Done verifying signed payload.";
+ return 0;
}
// TODO(deymo): This function is likely broken for deltas minor version 2 or
@@ -190,10 +194,11 @@
FakeHardware fake_hardware;
MemoryPrefs prefs;
InstallPlan install_plan;
+ InstallPlan::Payload payload;
install_plan.source_slot =
config.is_delta ? 0 : BootControlInterface::kInvalidSlot;
install_plan.target_slot = 1;
- install_plan.payload_type =
+ payload.type =
config.is_delta ? InstallPayloadType::kDelta : InstallPayloadType::kFull;
for (size_t i = 0; i < config.target.partitions.size(); i++) {
@@ -220,6 +225,7 @@
&fake_hardware,
nullptr,
&install_plan,
+ &payload,
true); // is_interactive
brillo::Blob buf(1024 * 1024);
@@ -329,6 +335,10 @@
DEFINE_string(properties_file, "",
"If passed, dumps the payload properties of the payload passed "
"in --in_file and exits.");
+ DEFINE_int64(max_timestamp,
+ 0,
+ "The maximum timestamp of the OS allowed to apply this "
+ "payload.");
DEFINE_string(old_channel, "",
"The channel for the old image. 'dev-channel', 'npo-channel', "
@@ -404,8 +414,7 @@
if (!FLAGS_public_key.empty()) {
LOG_IF(WARNING, FLAGS_public_key_version != -1)
<< "--public_key_version is deprecated and ignored.";
- VerifySignedPayload(FLAGS_in_file, FLAGS_public_key);
- return 0;
+ return VerifySignedPayload(FLAGS_in_file, FLAGS_public_key);
}
if (!FLAGS_properties_file.empty()) {
return ExtractProperties(FLAGS_in_file, FLAGS_properties_file) ? 0 : 1;
@@ -571,6 +580,8 @@
LOG(INFO) << "Using provided minor_version=" << FLAGS_minor_version;
}
+ payload_config.max_timestamp = FLAGS_max_timestamp;
+
LOG(INFO) << "Generating " << (payload_config.is_delta ? "delta" : "full")
<< " update";
diff --git a/payload_generator/payload_file.cc b/payload_generator/payload_file.cc
index 38aa0da..f48d2a2 100644
--- a/payload_generator/payload_file.cc
+++ b/payload_generator/payload_file.cc
@@ -73,6 +73,7 @@
*(manifest_.mutable_new_image_info()) = config.target.image_info;
manifest_.set_block_size(config.block_size);
+ manifest_.set_max_timestamp(config.max_timestamp);
return true;
}
@@ -315,10 +316,8 @@
bool PayloadFile::AddOperationHash(InstallOperation* op,
const brillo::Blob& buf) {
- HashCalculator hasher;
- TEST_AND_RETURN_FALSE(hasher.Update(buf.data(), buf.size()));
- TEST_AND_RETURN_FALSE(hasher.Finalize());
- const brillo::Blob& hash = hasher.raw_hash();
+ brillo::Blob hash;
+ TEST_AND_RETURN_FALSE(HashCalculator::RawHashOfData(buf, &hash));
op->set_data_sha256_hash(hash.data(), hash.size());
return true;
}
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
index ca6fb04..c553d29 100644
--- a/payload_generator/payload_generation_config.h
+++ b/payload_generator/payload_generation_config.h
@@ -186,6 +186,9 @@
// The block size used for all the operations in the manifest.
size_t block_size = 4096;
+
+ // The maximum timestamp of the OS allowed to apply this payload.
+ int64_t max_timestamp = 0;
};
} // namespace chromeos_update_engine
diff --git a/payload_generator/squashfs_filesystem_unittest.cc b/payload_generator/squashfs_filesystem_unittest.cc
index 13b4dd3..29fcf1c 100644
--- a/payload_generator/squashfs_filesystem_unittest.cc
+++ b/payload_generator/squashfs_filesystem_unittest.cc
@@ -108,6 +108,8 @@
}
};
+// CreateFromFile() depends on unsquashfs -m, which only exists in Chrome OS.
+#ifdef __CHROMEOS__
TEST_F(SquashfsFilesystemTest, EmptyFilesystemTest) {
unique_ptr<SquashfsFilesystem> fs = SquashfsFilesystem::CreateFromFile(
GetBuildArtifactsPath("gen/disk_sqfs_empty.img"), true);
@@ -146,6 +148,7 @@
EXPECT_EQ(files[0].name, file.name);
EXPECT_EQ(files[0].extents, file.extents);
}
+#endif // __CHROMEOS__
TEST_F(SquashfsFilesystemTest, SimpleFileMapTest) {
string filemap = R"(dir1/file1 96 4000
diff --git a/payload_state.cc b/payload_state.cc
index 846f901..4992606 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -32,6 +32,7 @@
#include "update_engine/common/prefs.h"
#include "update_engine/common/utils.h"
#include "update_engine/connection_manager_interface.h"
+#include "update_engine/metrics_reporter_interface.h"
#include "update_engine/metrics_utils.h"
#include "update_engine/omaha_request_params.h"
#include "update_engine/payload_consumer/install_plan.h"
@@ -44,6 +45,8 @@
namespace chromeos_update_engine {
+using metrics_utils::GetPersistedValue;
+
const TimeDelta PayloadState::kDurationSlack = TimeDelta::FromSeconds(600);
// We want to upperbound backoffs to 16 days
@@ -122,11 +125,16 @@
return;
}
+ // Always start from payload index 0, even for resume, to download partition
+ // info from previous payloads.
+ payload_index_ = 0;
+
// This is the earliest point at which we can validate whether the URL index
// we loaded from the persisted state is a valid value. If the response
// hasn't changed but the URL index is invalid, it's indicative of some
// tampering of the persisted state.
- if (static_cast<uint32_t>(url_index_) >= candidate_urls_.size()) {
+ if (payload_index_ >= candidate_urls_.size() ||
+ url_index_ >= candidate_urls_[payload_index_].size()) {
LOG(INFO) << "Resetting all payload state as the url index seems to have "
"been tampered with";
ResetPersistedState();
@@ -230,8 +238,8 @@
break;
case AttemptType::kRollback:
- metrics::ReportRollbackMetrics(system_state_,
- metrics::RollbackResult::kSuccess);
+ system_state_->metrics_reporter()->ReportRollbackMetrics(
+ metrics::RollbackResult::kSuccess);
break;
}
attempt_error_code_ = ErrorCode::kSuccess;
@@ -239,8 +247,9 @@
// Reset the number of responses seen since it counts from the last
// successful update, e.g. now.
SetNumResponsesSeen(0);
+ SetPayloadIndex(0);
- CreateSystemUpdatedMarkerFile();
+ metrics_utils::SetSystemUpdatedMarker(system_state_->clock(), prefs_);
}
void PayloadState::UpdateFailed(ErrorCode error) {
@@ -264,8 +273,8 @@
break;
case AttemptType::kRollback:
- metrics::ReportRollbackMetrics(system_state_,
- metrics::RollbackResult::kFailed);
+ system_state_->metrics_reporter()->ReportRollbackMetrics(
+ metrics::RollbackResult::kFailed);
break;
}
@@ -296,6 +305,7 @@
case ErrorCode::kPayloadMismatchedType:
case ErrorCode::kUnsupportedMajorPayloadVersion:
case ErrorCode::kUnsupportedMinorPayloadVersion:
+ case ErrorCode::kPayloadTimestampError:
IncrementUrlIndex();
break;
@@ -348,7 +358,7 @@
case ErrorCode::kOmahaRequestXMLHasEntityDecl:
case ErrorCode::kFilesystemVerifierError:
case ErrorCode::kUserCanceled:
- case ErrorCode::kOmahaUpdateIgnoredOverCellular:
+ case ErrorCode::kUpdatedButNotActive:
LOG(INFO) << "Not incrementing URL index or failure count for this error";
break;
@@ -386,14 +396,16 @@
LOG(INFO) << "Payload backoff disabled for interactive update checks.";
return false;
}
- if (response_.is_delta_payload) {
- // If delta payloads fail, we want to fallback quickly to full payloads as
- // they are more likely to succeed. Exponential backoffs would greatly
- // slow down the fallback to full payloads. So we don't backoff for delta
- // payloads.
- LOG(INFO) << "No backoffs for delta payloads. "
- << "Can proceed with the download";
- return false;
+ for (const auto& package : response_.packages) {
+ if (package.is_delta) {
+ // If delta payloads fail, we want to fallback quickly to full payloads as
+ // they are more likely to succeed. Exponential backoffs would greatly
+ // slow down the fallback to full payloads. So we don't backoff for delta
+ // payloads.
+ LOG(INFO) << "No backoffs for delta payloads. "
+ << "Can proceed with the download";
+ return false;
+ }
}
if (!system_state_->hardware()->IsOfficialBuild()) {
@@ -434,7 +446,7 @@
void PayloadState::IncrementFullPayloadAttemptNumber() {
// Update the payload attempt number for full payloads and the backoff time.
- if (response_.is_delta_payload) {
+ if (response_.packages[payload_index_].is_delta) {
LOG(INFO) << "Not incrementing payload attempt number for delta payloads";
return;
}
@@ -445,21 +457,23 @@
}
void PayloadState::IncrementUrlIndex() {
- uint32_t next_url_index = GetUrlIndex() + 1;
- if (next_url_index < candidate_urls_.size()) {
+ size_t next_url_index = url_index_ + 1;
+ size_t max_url_size = 0;
+ for (const auto& urls : candidate_urls_)
+ max_url_size = std::max(max_url_size, urls.size());
+ if (next_url_index < max_url_size) {
LOG(INFO) << "Incrementing the URL index for next attempt";
SetUrlIndex(next_url_index);
} else {
- LOG(INFO) << "Resetting the current URL index (" << GetUrlIndex() << ") to "
- << "0 as we only have " << candidate_urls_.size()
- << " candidate URL(s)";
+ LOG(INFO) << "Resetting the current URL index (" << url_index_ << ") to "
+ << "0 as we only have " << max_url_size << " candidate URL(s)";
SetUrlIndex(0);
IncrementPayloadAttemptNumber();
IncrementFullPayloadAttemptNumber();
}
// If we have multiple URLs, record that we just switched to another one
- if (candidate_urls_.size() > 1)
+ if (max_url_size > 1)
SetUrlSwitchCount(url_switch_count_ + 1);
// Whenever we update the URL index, we should also clear the URL failure
@@ -520,12 +534,14 @@
if (using_p2p_for_downloading_) {
current_download_source_ = kDownloadSourceHttpPeer;
- } else if (GetUrlIndex() < candidate_urls_.size()) {
- string current_url = candidate_urls_[GetUrlIndex()];
- if (base::StartsWith(current_url, "https://",
- base::CompareCase::INSENSITIVE_ASCII)) {
+ } else if (payload_index_ < candidate_urls_.size() &&
+ candidate_urls_[payload_index_].size() != 0) {
+ const string& current_url = candidate_urls_[payload_index_][GetUrlIndex()];
+ if (base::StartsWith(
+ current_url, "https://", base::CompareCase::INSENSITIVE_ASCII)) {
current_download_source_ = kDownloadSourceHttpsServer;
- } else if (base::StartsWith(current_url, "http://",
+ } else if (base::StartsWith(current_url,
+ "http://",
base::CompareCase::INSENSITIVE_ASCII)) {
current_download_source_ = kDownloadSourceHttpServer;
}
@@ -549,16 +565,17 @@
}
PayloadType PayloadState::CalculatePayloadType() {
- PayloadType payload_type;
- OmahaRequestParams* params = system_state_->request_params();
- if (response_.is_delta_payload) {
- payload_type = kPayloadTypeDelta;
- } else if (params->delta_okay()) {
- payload_type = kPayloadTypeFull;
- } else { // Full payload, delta was not allowed by request.
- payload_type = kPayloadTypeForcedFull;
+ for (const auto& package : response_.packages) {
+ if (package.is_delta) {
+ return kPayloadTypeDelta;
+ }
}
- return payload_type;
+ OmahaRequestParams* params = system_state_->request_params();
+ if (params->delta_okay()) {
+ return kPayloadTypeFull;
+ }
+ // Full payload, delta was not allowed by request.
+ return kPayloadTypeForcedFull;
}
// TODO(zeuthen): Currently we don't report the UpdateEngine.Attempt.*
@@ -570,7 +587,7 @@
PayloadType payload_type = CalculatePayloadType();
- int64_t payload_size = response_.size;
+ int64_t payload_size = GetPayloadSize();
int64_t payload_bytes_downloaded = attempt_num_bytes_downloaded_;
@@ -618,24 +635,28 @@
case metrics::AttemptResult::kPostInstallFailed:
case metrics::AttemptResult::kAbnormalTermination:
case metrics::AttemptResult::kUpdateCanceled:
+ case metrics::AttemptResult::kUpdateSucceededNotActive:
case metrics::AttemptResult::kNumConstants:
case metrics::AttemptResult::kUnset:
break;
}
- metrics::ReportUpdateAttemptMetrics(system_state_,
- attempt_number,
- payload_type,
- duration,
- duration_uptime,
- payload_size,
- payload_bytes_downloaded,
- payload_download_speed_bps,
- download_source,
- attempt_result,
- internal_error_code,
- payload_download_error_code,
- attempt_connection_type_);
+ system_state_->metrics_reporter()->ReportUpdateAttemptMetrics(
+ system_state_,
+ attempt_number,
+ payload_type,
+ duration,
+ duration_uptime,
+ payload_size,
+ attempt_result,
+ internal_error_code);
+
+ system_state_->metrics_reporter()->ReportUpdateAttemptDownloadMetrics(
+ payload_bytes_downloaded,
+ payload_download_speed_bps,
+ download_source,
+ payload_download_error_code,
+ attempt_connection_type_);
}
void PayloadState::PersistAttemptMetrics() {
@@ -660,7 +681,8 @@
if (!attempt_in_progress)
return;
- metrics::ReportAbnormallyTerminatedUpdateAttemptMetrics(system_state_);
+ system_state_->metrics_reporter()
+ ->ReportAbnormallyTerminatedUpdateAttemptMetrics();
ClearPersistedAttemptMetrics();
}
@@ -716,22 +738,22 @@
PayloadType payload_type = CalculatePayloadType();
- int64_t payload_size = response_.size;
+ int64_t payload_size = GetPayloadSize();
int attempt_count = GetPayloadAttemptNumber();
int updates_abandoned_count = num_responses_seen_ - 1;
- metrics::ReportSuccessfulUpdateMetrics(system_state_,
- attempt_count,
- updates_abandoned_count,
- payload_type,
- payload_size,
- total_bytes_by_source,
- download_overhead_percentage,
- duration,
- reboot_count,
- url_switch_count);
+ system_state_->metrics_reporter()->ReportSuccessfulUpdateMetrics(
+ attempt_count,
+ updates_abandoned_count,
+ payload_type,
+ payload_size,
+ total_bytes_by_source,
+ download_overhead_percentage,
+ duration,
+ reboot_count,
+ url_switch_count);
}
void PayloadState::UpdateNumReboots() {
@@ -745,16 +767,14 @@
}
void PayloadState::SetNumReboots(uint32_t num_reboots) {
- CHECK(prefs_);
num_reboots_ = num_reboots;
- prefs_->SetInt64(kPrefsNumReboots, num_reboots);
- LOG(INFO) << "Number of Reboots during current update attempt = "
- << num_reboots_;
+ metrics_utils::SetNumReboots(num_reboots, prefs_);
}
void PayloadState::ResetPersistedState() {
SetPayloadAttemptNumber(0);
SetFullPayloadAttemptNumber(0);
+ SetPayloadIndex(0);
SetUrlIndex(0);
SetUrlFailureCount(0);
SetUrlSwitchCount(0);
@@ -785,46 +805,34 @@
}
}
-int64_t PayloadState::GetPersistedValue(const string& key) {
- CHECK(prefs_);
- if (!prefs_->Exists(key))
- return 0;
+string PayloadState::CalculateResponseSignature() {
+ string response_sign;
+ for (size_t i = 0; i < response_.packages.size(); i++) {
+ const auto& package = response_.packages[i];
+ response_sign += base::StringPrintf(
+ "Payload %zu:\n"
+ " Size = %ju\n"
+ " Sha256 Hash = %s\n"
+ " Metadata Size = %ju\n"
+ " Metadata Signature = %s\n"
+ " Is Delta = %d\n"
+ " NumURLs = %zu\n",
+ i,
+ static_cast<uintmax_t>(package.size),
+ package.hash.c_str(),
+ static_cast<uintmax_t>(package.metadata_size),
+ package.metadata_signature.c_str(),
+ package.is_delta,
+ candidate_urls_[i].size());
- int64_t stored_value;
- if (!prefs_->GetInt64(key, &stored_value))
- return 0;
-
- if (stored_value < 0) {
- LOG(ERROR) << key << ": Invalid value (" << stored_value
- << ") in persisted state. Defaulting to 0";
- return 0;
+ for (size_t j = 0; j < candidate_urls_[i].size(); j++)
+ response_sign += base::StringPrintf(
+ " Candidate Url%zu = %s\n", j, candidate_urls_[i][j].c_str());
}
- return stored_value;
-}
-
-string PayloadState::CalculateResponseSignature() {
- string response_sign = base::StringPrintf(
- "NumURLs = %d\n", static_cast<int>(candidate_urls_.size()));
-
- for (size_t i = 0; i < candidate_urls_.size(); i++)
- response_sign += base::StringPrintf("Candidate Url%d = %s\n",
- static_cast<int>(i),
- candidate_urls_[i].c_str());
-
response_sign += base::StringPrintf(
- "Payload Size = %ju\n"
- "Payload Sha256 Hash = %s\n"
- "Metadata Size = %ju\n"
- "Metadata Signature = %s\n"
- "Is Delta Payload = %d\n"
"Max Failure Count Per Url = %d\n"
"Disable Payload Backoff = %d\n",
- static_cast<uintmax_t>(response_.size),
- response_.hash.c_str(),
- static_cast<uintmax_t>(response_.metadata_size),
- response_.metadata_signature.c_str(),
- response_.is_delta_payload,
response_.max_failure_count_per_url,
response_.disable_payload_backoff);
return response_sign;
@@ -847,19 +855,18 @@
}
void PayloadState::LoadPayloadAttemptNumber() {
- SetPayloadAttemptNumber(GetPersistedValue(kPrefsPayloadAttemptNumber));
+ SetPayloadAttemptNumber(
+ GetPersistedValue(kPrefsPayloadAttemptNumber, prefs_));
}
void PayloadState::LoadFullPayloadAttemptNumber() {
- SetFullPayloadAttemptNumber(GetPersistedValue(
- kPrefsFullPayloadAttemptNumber));
+ SetFullPayloadAttemptNumber(
+ GetPersistedValue(kPrefsFullPayloadAttemptNumber, prefs_));
}
void PayloadState::SetPayloadAttemptNumber(int payload_attempt_number) {
- CHECK(prefs_);
payload_attempt_number_ = payload_attempt_number;
- LOG(INFO) << "Payload Attempt Number = " << payload_attempt_number_;
- prefs_->SetInt64(kPrefsPayloadAttemptNumber, payload_attempt_number_);
+ metrics_utils::SetPayloadAttemptNumber(payload_attempt_number, prefs_);
}
void PayloadState::SetFullPayloadAttemptNumber(
@@ -871,8 +878,22 @@
full_payload_attempt_number_);
}
+void PayloadState::SetPayloadIndex(size_t payload_index) {
+ CHECK(prefs_);
+ payload_index_ = payload_index;
+ LOG(INFO) << "Payload Index = " << payload_index_;
+ prefs_->SetInt64(kPrefsUpdateStatePayloadIndex, payload_index_);
+}
+
+bool PayloadState::NextPayload() {
+ if (payload_index_ + 1 >= candidate_urls_.size())
+ return false;
+ SetPayloadIndex(payload_index_ + 1);
+ return true;
+}
+
void PayloadState::LoadUrlIndex() {
- SetUrlIndex(GetPersistedValue(kPrefsCurrentUrlIndex));
+ SetUrlIndex(GetPersistedValue(kPrefsCurrentUrlIndex, prefs_));
}
void PayloadState::SetUrlIndex(uint32_t url_index) {
@@ -887,8 +908,8 @@
}
void PayloadState::LoadScatteringWaitPeriod() {
- SetScatteringWaitPeriod(
- TimeDelta::FromSeconds(GetPersistedValue(kPrefsWallClockWaitPeriod)));
+ SetScatteringWaitPeriod(TimeDelta::FromSeconds(
+ GetPersistedValue(kPrefsWallClockWaitPeriod, prefs_)));
}
void PayloadState::SetScatteringWaitPeriod(TimeDelta wait_period) {
@@ -905,7 +926,7 @@
}
void PayloadState::LoadUrlSwitchCount() {
- SetUrlSwitchCount(GetPersistedValue(kPrefsUrlSwitchCount));
+ SetUrlSwitchCount(GetPersistedValue(kPrefsUrlSwitchCount, prefs_));
}
void PayloadState::SetUrlSwitchCount(uint32_t url_switch_count) {
@@ -916,7 +937,7 @@
}
void PayloadState::LoadUrlFailureCount() {
- SetUrlFailureCount(GetPersistedValue(kPrefsCurrentUrlFailureCount));
+ SetUrlFailureCount(GetPersistedValue(kPrefsCurrentUrlFailureCount, prefs_));
}
void PayloadState::SetUrlFailureCount(uint32_t url_failure_count) {
@@ -999,12 +1020,8 @@
}
void PayloadState::SetUpdateTimestampStart(const Time& value) {
- CHECK(prefs_);
update_timestamp_start_ = value;
- prefs_->SetInt64(kPrefsUpdateTimestampStart,
- update_timestamp_start_.ToInternalValue());
- LOG(INFO) << "Update Timestamp Start = "
- << utils::ToString(update_timestamp_start_);
+ metrics_utils::SetUpdateTimestampStart(value, prefs_);
}
void PayloadState::SetUpdateTimestampEnd(const Time& value) {
@@ -1050,7 +1067,7 @@
}
void PayloadState::LoadNumReboots() {
- SetNumReboots(GetPersistedValue(kPrefsNumReboots));
+ SetNumReboots(GetPersistedValue(kPrefsNumReboots, prefs_));
}
void PayloadState::LoadRollbackVersion() {
@@ -1102,7 +1119,7 @@
void PayloadState::LoadCurrentBytesDownloaded(DownloadSource source) {
string key = GetPrefsKey(kPrefsCurrentBytesDownloaded, source);
- SetCurrentBytesDownloaded(source, GetPersistedValue(key), true);
+ SetCurrentBytesDownloaded(source, GetPersistedValue(key, prefs_), true);
}
void PayloadState::SetCurrentBytesDownloaded(
@@ -1126,7 +1143,7 @@
void PayloadState::LoadTotalBytesDownloaded(DownloadSource source) {
string key = GetPrefsKey(kPrefsTotalBytesDownloaded, source);
- SetTotalBytesDownloaded(source, GetPersistedValue(key), true);
+ SetTotalBytesDownloaded(source, GetPersistedValue(key, prefs_), true);
}
void PayloadState::SetTotalBytesDownloaded(
@@ -1150,7 +1167,7 @@
}
void PayloadState::LoadNumResponsesSeen() {
- SetNumResponsesSeen(GetPersistedValue(kPrefsNumResponsesSeen));
+ SetNumResponsesSeen(GetPersistedValue(kPrefsNumResponsesSeen, prefs_));
}
void PayloadState::SetNumResponsesSeen(int num_responses_seen) {
@@ -1173,38 +1190,22 @@
}
candidate_urls_.clear();
- for (size_t i = 0; i < response_.payload_urls.size(); i++) {
- string candidate_url = response_.payload_urls[i];
- if (base::StartsWith(candidate_url, "http://",
- base::CompareCase::INSENSITIVE_ASCII) &&
- !http_url_ok) {
- continue;
+ for (const auto& package : response_.packages) {
+ candidate_urls_.emplace_back();
+ for (const string& candidate_url : package.payload_urls) {
+ if (base::StartsWith(
+ candidate_url, "http://", base::CompareCase::INSENSITIVE_ASCII) &&
+ !http_url_ok) {
+ continue;
+ }
+ candidate_urls_.back().push_back(candidate_url);
+ LOG(INFO) << "Candidate Url" << (candidate_urls_.back().size() - 1)
+ << ": " << candidate_url;
}
- candidate_urls_.push_back(candidate_url);
- LOG(INFO) << "Candidate Url" << (candidate_urls_.size() - 1)
- << ": " << candidate_url;
+ LOG(INFO) << "Found " << candidate_urls_.back().size() << " candidate URLs "
+ << "out of " << package.payload_urls.size()
+ << " URLs supplied in package " << candidate_urls_.size() - 1;
}
-
- LOG(INFO) << "Found " << candidate_urls_.size() << " candidate URLs "
- << "out of " << response_.payload_urls.size() << " URLs supplied";
-}
-
-void PayloadState::CreateSystemUpdatedMarkerFile() {
- CHECK(prefs_);
- int64_t value = system_state_->clock()->GetWallclockTime().ToInternalValue();
- prefs_->SetInt64(kPrefsSystemUpdatedMarker, value);
-}
-
-void PayloadState::BootedIntoUpdate(TimeDelta time_to_reboot) {
- // Send |time_to_reboot| as a UMA stat.
- string metric = metrics::kMetricTimeToRebootMinutes;
- system_state_->metrics_lib()->SendToUMA(metric,
- time_to_reboot.InMinutes(),
- 0, // min: 0 minute
- 30*24*60, // max: 1 month (approx)
- kNumDefaultUmaBuckets);
- LOG(INFO) << "Uploading " << utils::FormatTimeDelta(time_to_reboot)
- << " for metric " << metric;
}
void PayloadState::UpdateEngineStarted() {
@@ -1216,24 +1217,11 @@
if (!system_state_->system_rebooted())
return;
- // Figure out if we just booted into a new update
- if (prefs_->Exists(kPrefsSystemUpdatedMarker)) {
- int64_t stored_value;
- if (prefs_->GetInt64(kPrefsSystemUpdatedMarker, &stored_value)) {
- Time system_updated_at = Time::FromInternalValue(stored_value);
- if (!system_updated_at.is_null()) {
- TimeDelta time_to_reboot =
- system_state_->clock()->GetWallclockTime() - system_updated_at;
- if (time_to_reboot.ToInternalValue() < 0) {
- LOG(ERROR) << "time_to_reboot is negative - system_updated_at: "
- << utils::ToString(system_updated_at);
- } else {
- BootedIntoUpdate(time_to_reboot);
- }
- }
- }
- prefs_->Delete(kPrefsSystemUpdatedMarker);
- }
+ // Report time_to_reboot if we booted into a new update.
+ metrics_utils::LoadAndReportTimeToReboot(
+ system_state_->metrics_reporter(), prefs_, system_state_->clock());
+ prefs_->Delete(kPrefsSystemUpdatedMarker);
+
// Check if it is needed to send metrics about a failed reboot into a new
// version.
ReportFailedBootIfNeeded();
@@ -1267,15 +1255,8 @@
}
// Report the UMA metric of the current boot failure.
- string metric = metrics::kMetricFailedUpdateCount;
- LOG(INFO) << "Uploading " << target_attempt
- << " (count) for metric " << metric;
- system_state_->metrics_lib()->SendToUMA(
- metric,
- target_attempt,
- 1, // min value
- 50, // max value
- kNumDefaultUmaBuckets);
+ system_state_->metrics_reporter()->ReportFailedUpdateCount(
+ target_attempt);
} else {
prefs_->Delete(kPrefsTargetVersionAttempt);
prefs_->Delete(kPrefsTargetVersionUniqueId);
@@ -1332,7 +1313,7 @@
}
void PayloadState::LoadP2PNumAttempts() {
- SetP2PNumAttempts(GetPersistedValue(kPrefsP2PNumAttempts));
+ SetP2PNumAttempts(GetPersistedValue(kPrefsP2PNumAttempts, prefs_));
}
Time PayloadState::GetP2PFirstAttemptTimestamp() {
@@ -1349,7 +1330,8 @@
}
void PayloadState::LoadP2PFirstAttemptTimestamp() {
- int64_t stored_value = GetPersistedValue(kPrefsP2PFirstAttemptTimestamp);
+ int64_t stored_value =
+ GetPersistedValue(kPrefsP2PFirstAttemptTimestamp, prefs_);
Time stored_time = Time::FromInternalValue(stored_value);
SetP2PFirstAttemptTimestamp(stored_time);
}
@@ -1395,4 +1377,11 @@
return true;
}
+int64_t PayloadState::GetPayloadSize() {
+ int64_t payload_size = 0;
+ for (const auto& package : response_.packages)
+ payload_size += package.size;
+ return payload_size;
+}
+
} // namespace chromeos_update_engine
diff --git a/payload_state.h b/payload_state.h
index 46711b6..24e9900 100644
--- a/payload_state.h
+++ b/payload_state.h
@@ -17,6 +17,7 @@
#ifndef UPDATE_ENGINE_PAYLOAD_STATE_H_
#define UPDATE_ENGINE_PAYLOAD_STATE_H_
+#include <algorithm>
#include <string>
#include <vector>
@@ -24,7 +25,7 @@
#include <gtest/gtest_prod.h> // for FRIEND_TEST
#include "update_engine/common/prefs_interface.h"
-#include "update_engine/metrics.h"
+#include "update_engine/metrics_constants.h"
#include "update_engine/payload_state_interface.h"
namespace chromeos_update_engine {
@@ -79,7 +80,9 @@
}
inline std::string GetCurrentUrl() override {
- return candidate_urls_.size() ? candidate_urls_[url_index_] : "";
+ return candidate_urls_.size() && candidate_urls_[payload_index_].size()
+ ? candidate_urls_[payload_index_][url_index_]
+ : "";
}
inline uint32_t GetUrlFailureCount() override {
@@ -151,6 +154,8 @@
return attempt_error_code_;
}
+ bool NextPayload() override;
+
private:
enum class AttemptType {
kUpdate,
@@ -236,10 +241,6 @@
// reset on a new update.
void ResetDownloadSourcesOnNewUpdate();
- // Returns the persisted value from prefs_ for the given key. It also
- // validates that the value returned is non-negative.
- int64_t GetPersistedValue(const std::string& key);
-
// Calculates the response "signature", which is basically a string composed
// of the subset of the fields in the current response that affect the
// behavior of the PayloadState.
@@ -270,6 +271,11 @@
// of a process restart.
void SetFullPayloadAttemptNumber(int payload_attempt_number);
+ // Sets the current payload index to the given value. Also persists the value
+ // being set so that we resume from the same value in case of a process
+ // restart.
+ void SetPayloadIndex(size_t payload_index);
+
// Initializes the current URL index from the persisted state.
void LoadUrlIndex();
@@ -368,7 +374,9 @@
void ResetRollbackVersion();
inline uint32_t GetUrlIndex() {
- return url_index_;
+ return url_index_ ? std::min(candidate_urls_[payload_index_].size() - 1,
+ url_index_)
+ : 0;
}
// Computes the list of candidate URLs from the total list of payload URLs in
@@ -393,16 +401,7 @@
// increments num_reboots.
void UpdateNumReboots();
- // Writes the current wall-clock time to the kPrefsSystemUpdatedMarker
- // state variable.
- void CreateSystemUpdatedMarkerFile();
- // Called at program startup if the device booted into a new update.
- // The |time_to_reboot| parameter contains the (wall-clock) duration
- // from when the update successfully completed (the value written
- // into the kPrefsSystemUpdatedMarker state variable) until the device
- // was booted into the update (current wall-clock time).
- void BootedIntoUpdate(base::TimeDelta time_to_reboot);
// Loads the |kPrefsP2PFirstAttemptTimestamp| state variable from disk
// into |p2p_first_attempt_timestamp_|.
@@ -420,6 +419,9 @@
// Loads the persisted scattering wallclock-based wait period.
void LoadScatteringWaitPeriod();
+ // Get the total size of all payloads.
+ int64_t GetPayloadSize();
+
// The global state of the system.
SystemState* system_state_;
@@ -468,12 +470,15 @@
// we resume from the same value in case of a process restart.
int full_payload_attempt_number_;
+ // The index of the current payload.
+ size_t payload_index_ = 0;
+
// The index of the current URL. This type is different from the one in the
// accessor methods because PrefsInterface supports only int64_t but we want
// to provide a stronger abstraction of uint32_t. Each update to this value
// is persisted so we resume from the same value in case of a process
// restart.
- int64_t url_index_;
+ size_t url_index_;
// The count of failures encountered in the current attempt to download using
// the current URL (specified by url_index_). Each update to this value is
@@ -543,7 +548,7 @@
// The ordered list of the subset of payload URL candidates which are
// allowed as per device policy.
- std::vector<std::string> candidate_urls_;
+ std::vector<std::vector<std::string>> candidate_urls_;
// This stores a blacklisted version set as part of rollback. When we rollback
// we store the version of the os from which we are rolling back from in order
diff --git a/payload_state_interface.h b/payload_state_interface.h
index 68798ee..4aa25e3 100644
--- a/payload_state_interface.h
+++ b/payload_state_interface.h
@@ -193,6 +193,9 @@
virtual void SetP2PUrl(const std::string& url) = 0;
virtual std::string GetP2PUrl() const = 0;
virtual ErrorCode GetAttemptErrorCode() const = 0;
+
+ // Switch to next payload.
+ virtual bool NextPayload() = 0;
};
} // namespace chromeos_update_engine
diff --git a/payload_state_unittest.cc b/payload_state_unittest.cc
index 1fa45c2..f1c3835 100644
--- a/payload_state_unittest.cc
+++ b/payload_state_unittest.cc
@@ -31,6 +31,7 @@
#include "update_engine/common/test_utils.h"
#include "update_engine/common/utils.h"
#include "update_engine/fake_system_state.h"
+#include "update_engine/metrics_reporter_interface.h"
#include "update_engine/omaha_request_action.h"
using base::Time;
@@ -61,41 +62,44 @@
static void SetupPayloadStateWith2Urls(string hash,
bool http_enabled,
+ bool is_delta_payload,
PayloadState* payload_state,
OmahaResponse* response) {
- response->payload_urls.clear();
- response->payload_urls.push_back("http://test");
- response->payload_urls.push_back("https://test");
- response->size = 523456789;
- response->hash = hash;
- response->metadata_size = 558123;
- response->metadata_signature = "metasign";
+ response->packages.clear();
+ response->packages.push_back({.payload_urls = {"http://test", "https://test"},
+ .size = 523456789,
+ .metadata_size = 558123,
+ .metadata_signature = "metasign",
+ .hash = hash,
+ .is_delta = is_delta_payload});
response->max_failure_count_per_url = 3;
payload_state->SetResponse(*response);
string stored_response_sign = payload_state->GetResponseSignature();
string expected_url_https_only =
- "NumURLs = 1\n"
- "Candidate Url0 = https://test\n";
+ " NumURLs = 1\n"
+ " Candidate Url0 = https://test\n";
string expected_urls_both =
- "NumURLs = 2\n"
- "Candidate Url0 = http://test\n"
- "Candidate Url1 = https://test\n";
+ " NumURLs = 2\n"
+ " Candidate Url0 = http://test\n"
+ " Candidate Url1 = https://test\n";
- string expected_response_sign =
- (http_enabled ? expected_urls_both : expected_url_https_only) +
- base::StringPrintf("Payload Size = 523456789\n"
- "Payload Sha256 Hash = %s\n"
- "Metadata Size = 558123\n"
- "Metadata Signature = metasign\n"
- "Is Delta Payload = %d\n"
- "Max Failure Count Per Url = %d\n"
- "Disable Payload Backoff = %d\n",
- hash.c_str(),
- response->is_delta_payload,
- response->max_failure_count_per_url,
- response->disable_payload_backoff);
+ string expected_response_sign = base::StringPrintf(
+ "Payload 0:\n"
+ " Size = 523456789\n"
+ " Sha256 Hash = %s\n"
+ " Metadata Size = 558123\n"
+ " Metadata Signature = metasign\n"
+ " Is Delta = %d\n"
+ "%s"
+ "Max Failure Count Per Url = %d\n"
+ "Disable Payload Backoff = %d\n",
+ hash.c_str(),
+ response->packages[0].is_delta,
+ (http_enabled ? expected_urls_both : expected_url_https_only).c_str(),
+ response->max_failure_count_per_url,
+ response->disable_payload_backoff);
EXPECT_EQ(expected_response_sign, stored_response_sign);
}
@@ -129,14 +133,9 @@
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
payload_state.SetResponse(response);
string stored_response_sign = payload_state.GetResponseSignature();
- string expected_response_sign = "NumURLs = 0\n"
- "Payload Size = 0\n"
- "Payload Sha256 Hash = \n"
- "Metadata Size = 0\n"
- "Metadata Signature = \n"
- "Is Delta Payload = 0\n"
- "Max Failure Count Per Url = 0\n"
- "Disable Payload Backoff = 0\n";
+ string expected_response_sign =
+ "Max Failure Count Per Url = 0\n"
+ "Disable Payload Backoff = 0\n";
EXPECT_EQ(expected_response_sign, stored_response_sign);
EXPECT_EQ("", payload_state.GetCurrentUrl());
EXPECT_EQ(0U, payload_state.GetUrlFailureCount());
@@ -146,11 +145,11 @@
TEST(PayloadStateTest, SetResponseWorksWithSingleUrl) {
OmahaResponse response;
- response.payload_urls.push_back("https://single.url.test");
- response.size = 123456789;
- response.hash = "hash";
- response.metadata_size = 58123;
- response.metadata_signature = "msign";
+ response.packages.push_back({.payload_urls = {"https://single.url.test"},
+ .size = 123456789,
+ .metadata_size = 58123,
+ .metadata_signature = "msign",
+ .hash = "hash"});
FakeSystemState fake_system_state;
NiceMock<MockPrefs>* prefs = fake_system_state.mock_prefs();
EXPECT_CALL(*prefs, SetInt64(_, _)).Times(AnyNumber());
@@ -180,15 +179,17 @@
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
payload_state.SetResponse(response);
string stored_response_sign = payload_state.GetResponseSignature();
- string expected_response_sign = "NumURLs = 1\n"
- "Candidate Url0 = https://single.url.test\n"
- "Payload Size = 123456789\n"
- "Payload Sha256 Hash = hash\n"
- "Metadata Size = 58123\n"
- "Metadata Signature = msign\n"
- "Is Delta Payload = 0\n"
- "Max Failure Count Per Url = 0\n"
- "Disable Payload Backoff = 0\n";
+ string expected_response_sign =
+ "Payload 0:\n"
+ " Size = 123456789\n"
+ " Sha256 Hash = hash\n"
+ " Metadata Size = 58123\n"
+ " Metadata Signature = msign\n"
+ " Is Delta = 0\n"
+ " NumURLs = 1\n"
+ " Candidate Url0 = https://single.url.test\n"
+ "Max Failure Count Per Url = 0\n"
+ "Disable Payload Backoff = 0\n";
EXPECT_EQ(expected_response_sign, stored_response_sign);
EXPECT_EQ("https://single.url.test", payload_state.GetCurrentUrl());
EXPECT_EQ(0U, payload_state.GetUrlFailureCount());
@@ -198,12 +199,12 @@
TEST(PayloadStateTest, SetResponseWorksWithMultipleUrls) {
OmahaResponse response;
- response.payload_urls.push_back("http://multiple.url.test");
- response.payload_urls.push_back("https://multiple.url.test");
- response.size = 523456789;
- response.hash = "rhash";
- response.metadata_size = 558123;
- response.metadata_signature = "metasign";
+ response.packages.push_back({.payload_urls = {"http://multiple.url.test",
+ "https://multiple.url.test"},
+ .size = 523456789,
+ .metadata_size = 558123,
+ .metadata_signature = "metasign",
+ .hash = "rhash"});
FakeSystemState fake_system_state;
NiceMock<MockPrefs>* prefs = fake_system_state.mock_prefs();
EXPECT_CALL(*prefs, SetInt64(_, _)).Times(AnyNumber());
@@ -230,16 +231,18 @@
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
payload_state.SetResponse(response);
string stored_response_sign = payload_state.GetResponseSignature();
- string expected_response_sign = "NumURLs = 2\n"
- "Candidate Url0 = http://multiple.url.test\n"
- "Candidate Url1 = https://multiple.url.test\n"
- "Payload Size = 523456789\n"
- "Payload Sha256 Hash = rhash\n"
- "Metadata Size = 558123\n"
- "Metadata Signature = metasign\n"
- "Is Delta Payload = 0\n"
- "Max Failure Count Per Url = 0\n"
- "Disable Payload Backoff = 0\n";
+ string expected_response_sign =
+ "Payload 0:\n"
+ " Size = 523456789\n"
+ " Sha256 Hash = rhash\n"
+ " Metadata Size = 558123\n"
+ " Metadata Signature = metasign\n"
+ " Is Delta = 0\n"
+ " NumURLs = 2\n"
+ " Candidate Url0 = http://multiple.url.test\n"
+ " Candidate Url1 = https://multiple.url.test\n"
+ "Max Failure Count Per Url = 0\n"
+ "Disable Payload Backoff = 0\n";
EXPECT_EQ(expected_response_sign, stored_response_sign);
EXPECT_EQ("http://multiple.url.test", payload_state.GetCurrentUrl());
EXPECT_EQ(0U, payload_state.GetUrlFailureCount());
@@ -281,7 +284,8 @@
// This does a SetResponse which causes all the states to be set to 0 for
// the first time.
- SetupPayloadStateWith2Urls("Hash1235", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash1235", true, false, &payload_state, &response);
EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
// Verify that on the first error, the URL index advances to 1.
@@ -308,13 +312,9 @@
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(_, _, _, _, _))
- .Times(AnyNumber());
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
- .Times(AnyNumber());
-
// Set the first response.
- SetupPayloadStateWith2Urls("Hash5823", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash5823", true, false, &payload_state, &response);
EXPECT_EQ(1, payload_state.GetNumResponsesSeen());
// Advance the URL index to 1 by faking an error.
@@ -324,7 +324,8 @@
EXPECT_EQ(1U, payload_state.GetUrlSwitchCount());
// Now, slightly change the response and set it again.
- SetupPayloadStateWith2Urls("Hash8225", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash8225", true, false, &payload_state, &response);
EXPECT_EQ(2, payload_state.GetNumResponsesSeen());
// Fake an error again.
@@ -333,7 +334,8 @@
EXPECT_EQ(1U, payload_state.GetUrlSwitchCount());
// Return a third different response.
- SetupPayloadStateWith2Urls("Hash9999", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash9999", true, false, &payload_state, &response);
EXPECT_EQ(3, payload_state.GetNumResponsesSeen());
// Make sure the url index was reset to 0 because of the new response.
@@ -404,7 +406,8 @@
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash5873", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash5873", true, false, &payload_state, &response);
EXPECT_EQ(1, payload_state.GetNumResponsesSeen());
// This should advance the URL index.
@@ -483,7 +486,8 @@
EXPECT_TRUE(payload_state.ShouldBackoffDownload());
// Now, slightly change the response and set it again.
- SetupPayloadStateWith2Urls("Hash8532", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash8532", true, false, &payload_state, &response);
EXPECT_EQ(2, payload_state.GetNumResponsesSeen());
// Make sure the url index was reset to 0 because of the new response.
@@ -497,7 +501,6 @@
TEST(PayloadStateTest, PayloadAttemptNumberIncreasesOnSuccessfulFullDownload) {
OmahaResponse response;
- response.is_delta_payload = false;
PayloadState payload_state;
FakeSystemState fake_system_state;
NiceMock<MockPrefs>* prefs = fake_system_state.mock_prefs();
@@ -523,7 +526,8 @@
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash8593", true, false, &payload_state, &response);
// This should just advance the payload attempt number;
EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
@@ -538,7 +542,6 @@
TEST(PayloadStateTest, PayloadAttemptNumberIncreasesOnSuccessfulDeltaDownload) {
OmahaResponse response;
- response.is_delta_payload = true;
PayloadState payload_state;
FakeSystemState fake_system_state;
NiceMock<MockPrefs>* prefs = fake_system_state.mock_prefs();
@@ -563,7 +566,7 @@
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls("Hash8593", true, true, &payload_state, &response);
// This should just advance the payload attempt number;
EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
@@ -582,7 +585,8 @@
FakeSystemState fake_system_state;
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash4427", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash4427", true, false, &payload_state, &response);
// Generate enough events to advance URL index, failure count and
// payload attempt number all to 1.
@@ -618,7 +622,8 @@
// response was different. We want to specifically test that even if the
// response is same, we should reset the state if we find it corrupted.
EXPECT_TRUE(payload_state.Initialize(&fake_system_state2));
- SetupPayloadStateWith2Urls("Hash4427", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash4427", true, false, &payload_state, &response);
// Make sure all counters get reset to 0 because of the corrupted URL index
// we supplied above.
@@ -631,7 +636,6 @@
TEST(PayloadStateTest, NoBackoffInteractiveChecks) {
OmahaResponse response;
- response.is_delta_payload = false;
PayloadState payload_state;
FakeSystemState fake_system_state;
OmahaRequestParams params(&fake_system_state);
@@ -639,7 +643,8 @@
fake_system_state.set_request_params(¶ms);
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash6437", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash6437", true, false, &payload_state, &response);
// Simulate two failures (enough to cause payload backoff) and check
// again that we're ready to re-download without any backoff as this is
@@ -654,7 +659,6 @@
TEST(PayloadStateTest, NoBackoffForP2PUpdates) {
OmahaResponse response;
- response.is_delta_payload = false;
PayloadState payload_state;
FakeSystemState fake_system_state;
OmahaRequestParams params(&fake_system_state);
@@ -662,7 +666,8 @@
fake_system_state.set_request_params(¶ms);
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash6437", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash6437", true, false, &payload_state, &response);
// Simulate two failures (enough to cause payload backoff) and check
// again that we're ready to re-download without any backoff as this is
@@ -685,12 +690,11 @@
TEST(PayloadStateTest, NoBackoffForDeltaPayloads) {
OmahaResponse response;
- response.is_delta_payload = true;
PayloadState payload_state;
FakeSystemState fake_system_state;
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash6437", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls("Hash6437", true, true, &payload_state, &response);
// Simulate a successful download and see that we're ready to download
// again without any backoff as this is a delta payload.
@@ -730,12 +734,12 @@
TEST(PayloadStateTest, BackoffPeriodsAreInCorrectRange) {
OmahaResponse response;
- response.is_delta_payload = false;
PayloadState payload_state;
FakeSystemState fake_system_state;
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash8939", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash8939", true, false, &payload_state, &response);
CheckPayloadBackoffState(&payload_state, 1, TimeDelta::FromDays(1));
CheckPayloadBackoffState(&payload_state, 2, TimeDelta::FromDays(2));
@@ -756,7 +760,8 @@
FakeSystemState fake_system_state;
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash8939", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash8939", true, false, &payload_state, &response);
// Simulate a successful download and see that we are ready to download
// again without any backoff.
@@ -784,7 +789,8 @@
uint64_t http_total = 0;
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash3286", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash3286", true, false, &payload_state, &response);
EXPECT_EQ(1, payload_state.GetNumResponsesSeen());
// Simulate a previous attempt with in order to set an initial non-zero value
@@ -801,7 +807,8 @@
// Change the response hash so as to simulate a new response which will
// reset the current bytes downloaded, but not the total bytes downloaded.
- SetupPayloadStateWith2Urls("Hash9904", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash9904", true, false, &payload_state, &response);
EXPECT_EQ(2, payload_state.GetNumResponsesSeen());
// First, simulate successful download of a few bytes over HTTP.
@@ -862,26 +869,9 @@
EXPECT_EQ(p2p_total,
payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpPeer));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(_, _, _, _, _))
- .Times(AnyNumber());
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
- .Times(AnyNumber());
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- metrics::kMetricSuccessfulUpdateUrlSwitchCount,
- 3, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- metrics::kMetricSuccessfulUpdateTotalDurationMinutes,
- _, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- metrics::kMetricSuccessfulUpdateDownloadOverheadPercentage,
- 314, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
- metrics::kMetricAttemptPayloadType, kPayloadTypeFull, kNumPayloadTypes));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
- metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeFull,
- kNumPayloadTypes));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- metrics::kMetricSuccessfulUpdateAttemptCount, 1, _, _, _));
+ EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
+ ReportSuccessfulUpdateMetrics(
+ 1, _, kPayloadTypeFull, _, _, 314, _, _, 3));
payload_state.UpdateSucceeded();
@@ -903,7 +893,8 @@
FakeSystemState fake_system_state;
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash3286", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash3286", true, false, &payload_state, &response);
// Simulate progress in order to mark HTTP as one of the sources used.
uint64_t num_bytes = 42 * 1000 * 1000;
@@ -916,12 +907,21 @@
payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpServer));
// Check that only HTTP is reported as a download source.
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(_, _, _, _, _))
- .Times(AnyNumber());
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- metrics::kMetricSuccessfulUpdateDownloadSourcesUsed,
- (1 << kDownloadSourceHttpServer),
- _, _, _));
+ int64_t total_bytes[kNumDownloadSources] = {};
+ total_bytes[kDownloadSourceHttpServer] = num_bytes;
+
+ EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
+ ReportSuccessfulUpdateMetrics(
+ _,
+ _,
+ _,
+ _,
+ test_utils::DownloadSourceMatcher(total_bytes),
+ _,
+ _,
+ _,
+ _))
+ .Times(1);
payload_state.UpdateSucceeded();
}
@@ -934,7 +934,8 @@
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
// Set the first response.
- SetupPayloadStateWith2Urls("Hash5823", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash5823", true, false, &payload_state, &response);
uint64_t num_bytes = 10000;
payload_state.DownloadProgress(num_bytes);
@@ -1019,20 +1020,16 @@
// Check that we report only UpdateEngine.Rollback.* metrics in
// UpdateSucceeded().
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(_, _, _, _, _))
- .Times(0);
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
- .Times(0);
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(),
- SendEnumToUMA(
- metrics::kMetricRollbackResult,
- static_cast<int>(metrics::RollbackResult::kSuccess),
- static_cast<int>(metrics::RollbackResult::kNumConstants)));
+ EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
+ ReportRollbackMetrics(metrics::RollbackResult::kSuccess))
+ .Times(1);
+
payload_state.UpdateSucceeded();
}
TEST(PayloadStateTest, DurationsAreCorrect) {
OmahaResponse response;
+ response.packages.resize(1);
PayloadState payload_state;
FakeSystemState fake_system_state;
FakeClock fake_clock;
@@ -1050,7 +1047,8 @@
// Check that durations are correct for a successful update where
// time has advanced 7 seconds on the wall clock and 4 seconds on
// the monotonic clock.
- SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash8593", true, false, &payload_state, &response);
fake_clock.SetWallclockTime(Time::FromInternalValue(8000000));
fake_clock.SetMonotonicTime(Time::FromInternalValue(6000000));
payload_state.UpdateSucceeded();
@@ -1058,7 +1056,8 @@
EXPECT_EQ(payload_state.GetUpdateDurationUptime().InMicroseconds(), 4000000);
// Check that durations are reset when a new response comes in.
- SetupPayloadStateWith2Urls("Hash8594", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash8594", true, false, &payload_state, &response);
EXPECT_EQ(payload_state.GetUpdateDuration().InMicroseconds(), 0);
EXPECT_EQ(payload_state.GetUpdateDurationUptime().InMicroseconds(), 0);
@@ -1076,6 +1075,7 @@
fake_clock.SetMonotonicTime(Time::FromInternalValue(5000));
PayloadState payload_state2;
EXPECT_TRUE(payload_state2.Initialize(&fake_system_state));
+ payload_state2.SetResponse(response);
EXPECT_EQ(payload_state2.GetUpdateDuration().InMicroseconds(), 10000000);
EXPECT_EQ(payload_state2.GetUpdateDurationUptime().InMicroseconds(),
10000000);
@@ -1098,15 +1098,16 @@
FakePrefs fake_prefs;
// Set the clock to a well-known time (t = 30 seconds).
- fake_clock.SetWallclockTime(Time::FromInternalValue(
- 30 * Time::kMicrosecondsPerSecond));
+ fake_clock.SetMonotonicTime(
+ Time::FromInternalValue(30 * Time::kMicrosecondsPerSecond));
fake_system_state.set_clock(&fake_clock);
fake_system_state.set_prefs(&fake_prefs);
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
// Make the update succeed.
- SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash8593", true, false, &payload_state, &response);
payload_state.UpdateSucceeded();
// Check that the marker was written.
@@ -1116,15 +1117,14 @@
// (t = 500 seconds). We do this by using a new PayloadState object
// and checking that it emits the right UMA metric with the right
// value.
- fake_clock.SetWallclockTime(Time::FromInternalValue(
- 500 * Time::kMicrosecondsPerSecond));
+ fake_clock.SetMonotonicTime(
+ Time::FromInternalValue(500 * Time::kMicrosecondsPerSecond));
PayloadState payload_state2;
EXPECT_TRUE(payload_state2.Initialize(&fake_system_state));
// Expect 500 - 30 seconds = 470 seconds ~= 7 min 50 sec
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- metrics::kMetricTimeToRebootMinutes,
- 7, _, _, _));
+ EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
+ ReportTimeToReboot(7));
fake_system_state.set_system_rebooted(true);
payload_state2.UpdateEngineStarted();
@@ -1136,6 +1136,8 @@
TEST(PayloadStateTest, RestartAfterCrash) {
PayloadState payload_state;
FakeSystemState fake_system_state;
+ testing::StrictMock<MockMetricsReporter> mock_metrics_reporter;
+ fake_system_state.set_metrics_reporter(&mock_metrics_reporter);
NiceMock<MockPrefs>* prefs = fake_system_state.mock_prefs();
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
@@ -1150,10 +1152,6 @@
EXPECT_CALL(*prefs, GetBoolean(_, _)).Times(0);
EXPECT_CALL(*prefs, GetBoolean(kPrefsAttemptInProgress, _));
- // No metrics are reported after a crash.
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(),
- SendToUMA(_, _, _, _, _)).Times(0);
-
// Simulate an update_engine restart without a reboot.
fake_system_state.set_system_rebooted(false);
@@ -1166,11 +1164,9 @@
// If there's no marker at startup, ensure we don't report a metric.
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(),
- SendEnumToUMA(
- metrics::kMetricAttemptResult,
- static_cast<int>(metrics::AttemptResult::kAbnormalTermination),
- _)).Times(0);
+ EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
+ ReportAbnormallyTerminatedUpdateAttemptMetrics())
+ .Times(0);
payload_state.UpdateEngineStarted();
}
@@ -1186,11 +1182,9 @@
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(),
- SendEnumToUMA(
- metrics::kMetricAttemptResult,
- static_cast<int>(metrics::AttemptResult::kAbnormalTermination),
- _)).Times(1);
+ EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
+ ReportAbnormallyTerminatedUpdateAttemptMetrics())
+ .Times(1);
payload_state.UpdateEngineStarted();
EXPECT_FALSE(fake_prefs.Exists(kPrefsAttemptInProgress));
@@ -1206,16 +1200,13 @@
// abnormally).
fake_system_state.set_prefs(&fake_prefs);
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
+ OmahaResponse response;
+ response.packages.resize(1);
+ payload_state.SetResponse(response);
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(_, _, _, _, _))
- .Times(AnyNumber());
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
- .Times(AnyNumber());
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(),
- SendEnumToUMA(
- metrics::kMetricAttemptResult,
- static_cast<int>(metrics::AttemptResult::kAbnormalTermination),
- _)).Times(0);
+ EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
+ ReportAbnormallyTerminatedUpdateAttemptMetrics())
+ .Times(0);
// Attempt not in progress, should be clear.
EXPECT_FALSE(fake_prefs.Exists(kPrefsAttemptInProgress));
@@ -1245,7 +1236,8 @@
.WillRepeatedly(Return(false));
// Set the first response.
- SetupPayloadStateWith2Urls("Hash8433", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash8433", true, false, &payload_state, &response);
// Check that we use the HTTP URL since there is no value set for allowing
// http.
@@ -1256,7 +1248,8 @@
.WillRepeatedly(DoAll(SetArgPointee<0>(false), Return(true)));
// Reset state and set again.
- SetupPayloadStateWith2Urls("Hash8433", false, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash8433", false, false, &payload_state, &response);
// Check that we skip the HTTP URL and use only the HTTPS url.
EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
@@ -1270,7 +1263,8 @@
EXPECT_EQ(0U, payload_state.GetUrlSwitchCount());
// Now, slightly change the response and set it again.
- SetupPayloadStateWith2Urls("Hash2399", false, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash2399", false, false, &payload_state, &response);
// Check that we still skip the HTTP URL and use only the HTTPS url.
EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
@@ -1286,7 +1280,8 @@
// so that we can test that the state is reset not because of the
// hash but because of the policy change which results in candidate url
// list change.
- SetupPayloadStateWith2Urls("Hash2399", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash2399", true, false, &payload_state, &response);
// Check that we use the HTTP URL now and the failure count is reset.
EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
@@ -1302,23 +1297,17 @@
TEST(PayloadStateTest, PayloadTypeMetricWhenTypeIsDelta) {
OmahaResponse response;
- response.is_delta_payload = true;
PayloadState payload_state;
FakeSystemState fake_system_state;
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash6437", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls("Hash6437", true, true, &payload_state, &response);
// Simulate a successful download and update.
payload_state.DownloadComplete();
-
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
- .Times(AnyNumber());
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
- metrics::kMetricAttemptPayloadType, kPayloadTypeDelta, kNumPayloadTypes));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
- metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeDelta,
- kNumPayloadTypes));
+ EXPECT_CALL(
+ *fake_system_state.mock_metrics_reporter(),
+ ReportSuccessfulUpdateMetrics(_, _, kPayloadTypeDelta, _, _, _, _, _, _));
payload_state.UpdateSucceeded();
// Mock the request to a request where the delta was disabled but Omaha sends
@@ -1328,27 +1317,24 @@
fake_system_state.set_request_params(¶ms);
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash6437", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls("Hash6437", true, true, &payload_state, &response);
payload_state.DownloadComplete();
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
- metrics::kMetricAttemptPayloadType, kPayloadTypeDelta,
- kNumPayloadTypes));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
- metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeDelta,
- kNumPayloadTypes));
+ EXPECT_CALL(
+ *fake_system_state.mock_metrics_reporter(),
+ ReportSuccessfulUpdateMetrics(_, _, kPayloadTypeDelta, _, _, _, _, _, _));
payload_state.UpdateSucceeded();
}
TEST(PayloadStateTest, PayloadTypeMetricWhenTypeIsForcedFull) {
OmahaResponse response;
- response.is_delta_payload = false;
PayloadState payload_state;
FakeSystemState fake_system_state;
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash6437", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash6437", true, false, &payload_state, &response);
// Mock the request to a request where the delta was disabled.
OmahaRequestParams params(&fake_system_state);
@@ -1358,25 +1344,20 @@
// Simulate a successful download and update.
payload_state.DownloadComplete();
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
- .Times(AnyNumber());
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
- metrics::kMetricAttemptPayloadType, kPayloadTypeForcedFull,
- kNumPayloadTypes));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
- metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeForcedFull,
- kNumPayloadTypes));
+ EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
+ ReportSuccessfulUpdateMetrics(
+ _, _, kPayloadTypeForcedFull, _, _, _, _, _, _));
payload_state.UpdateSucceeded();
}
TEST(PayloadStateTest, PayloadTypeMetricWhenTypeIsFull) {
OmahaResponse response;
- response.is_delta_payload = false;
PayloadState payload_state;
FakeSystemState fake_system_state;
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash6437", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash6437", true, false, &payload_state, &response);
// Mock the request to a request where the delta is enabled, although the
// result is full.
@@ -1387,14 +1368,9 @@
// Simulate a successful download and update.
payload_state.DownloadComplete();
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
- .Times(AnyNumber());
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
- metrics::kMetricAttemptPayloadType, kPayloadTypeFull,
- kNumPayloadTypes));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
- metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeFull,
- kNumPayloadTypes));
+ EXPECT_CALL(
+ *fake_system_state.mock_metrics_reporter(),
+ ReportSuccessfulUpdateMetrics(_, _, kPayloadTypeFull, _, _, _, _, _, _));
payload_state.UpdateSucceeded();
}
@@ -1406,7 +1382,8 @@
fake_system_state.set_prefs(&fake_prefs);
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash3141", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash3141", true, false, &payload_state, &response);
// Simulate a successful download and update.
payload_state.DownloadComplete();
@@ -1414,27 +1391,27 @@
payload_state.ExpectRebootInNewVersion("Version:12345678");
// Reboot into the same environment to get an UMA metric with a value of 1.
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- metrics::kMetricFailedUpdateCount, 1, _, _, _));
+ EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
+ ReportFailedUpdateCount(1));
payload_state.ReportFailedBootIfNeeded();
- Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_lib());
+ Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_reporter());
// Simulate a second update and reboot into the same environment, this should
// send a value of 2.
payload_state.ExpectRebootInNewVersion("Version:12345678");
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- metrics::kMetricFailedUpdateCount, 2, _, _, _));
+ EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
+ ReportFailedUpdateCount(2));
payload_state.ReportFailedBootIfNeeded();
- Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_lib());
+ Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_reporter());
// Simulate a third failed reboot to new version, but this time for a
// different payload. This should send a value of 1 this time.
payload_state.ExpectRebootInNewVersion("Version:3141592");
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- metrics::kMetricFailedUpdateCount, 1, _, _, _));
+ EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
+ ReportFailedUpdateCount(1));
payload_state.ReportFailedBootIfNeeded();
- Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_lib());
+ Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_reporter());
}
TEST(PayloadStateTest, RebootAfterUpdateSucceed) {
@@ -1448,7 +1425,8 @@
fake_boot_control->SetCurrentSlot(0);
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash3141", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash3141", true, false, &payload_state, &response);
// Simulate a successful download and update.
payload_state.DownloadComplete();
@@ -1458,8 +1436,8 @@
// Change the BootDevice to a different one, no metric should be sent.
fake_boot_control->SetCurrentSlot(1);
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- metrics::kMetricFailedUpdateCount, _, _, _, _))
+ EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
+ ReportFailedUpdateCount(_))
.Times(0);
payload_state.ReportFailedBootIfNeeded();
@@ -1477,15 +1455,16 @@
fake_system_state.set_prefs(&fake_prefs);
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash3141", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash3141", true, false, &payload_state, &response);
// Simulate a successful download and update.
payload_state.DownloadComplete();
payload_state.UpdateSucceeded();
payload_state.ExpectRebootInNewVersion("Version:12345678");
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- metrics::kMetricFailedUpdateCount, _, _, _, _))
+ EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
+ ReportFailedUpdateCount(_))
.Times(0);
// Cancel the applied update.
@@ -1503,8 +1482,8 @@
fake_system_state.set_prefs(&fake_prefs);
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- metrics::kMetricFailedUpdateCount, _, _, _, _))
+ EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
+ ReportFailedUpdateCount(_))
.Times(0);
// Simulate a reboot in this environment.
@@ -1519,7 +1498,8 @@
fake_system_state.set_prefs(&fake_prefs);
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash8593", true, false, &payload_state, &response);
// Should allow exactly kMaxP2PAttempts...
for (int n = 0; n < kMaxP2PAttempts; n++) {
@@ -1541,7 +1521,8 @@
fake_system_state.set_clock(&fake_clock);
fake_system_state.set_prefs(&fake_prefs);
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash8593", true, false, &payload_state, &response);
// Set the clock to 1 second.
Time epoch = Time::FromInternalValue(1000000);
@@ -1584,7 +1565,8 @@
fake_system_state.set_prefs(&fake_prefs);
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash8593", true, false, &payload_state, &response);
Time null_time = Time();
EXPECT_EQ(null_time, payload_state.GetP2PFirstAttemptTimestamp());
@@ -1600,7 +1582,8 @@
fake_system_state.set_clock(&fake_clock);
fake_system_state.set_prefs(&fake_prefs);
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash8593", true, false, &payload_state, &response);
// Set the clock to something known.
Time time = Time::FromInternalValue(12345);
@@ -1629,7 +1612,8 @@
fake_system_state.set_prefs(&fake_prefs);
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
- SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash8593", true, false, &payload_state, &response);
// Set the clock to something known.
Time time = Time::FromInternalValue(12345);
@@ -1641,7 +1625,8 @@
EXPECT_EQ(time, payload_state.GetP2PFirstAttemptTimestamp());
// Set a new response...
- SetupPayloadStateWith2Urls("Hash9904", true, &payload_state, &response);
+ SetupPayloadStateWith2Urls(
+ "Hash9904", true, false, &payload_state, &response);
// ... and check that it clears the P2P state vars.
Time null_time = Time();
diff --git a/proxy_resolver_unittest.cc b/proxy_resolver_unittest.cc
index 070b361..484aae1 100644
--- a/proxy_resolver_unittest.cc
+++ b/proxy_resolver_unittest.cc
@@ -22,7 +22,6 @@
#include <gtest/gtest.h>
#include <base/bind.h>
-#include <brillo/bind_lambda.h>
#include <brillo/message_loops/fake_message_loop.h>
using std::deque;
diff --git a/real_system_state.cc b/real_system_state.cc
index 2a208ae..8e7ad51 100644
--- a/real_system_state.cc
+++ b/real_system_state.cc
@@ -33,6 +33,7 @@
#include "update_engine/common/constants.h"
#include "update_engine/common/hardware.h"
#include "update_engine/common/utils.h"
+#include "update_engine/metrics_reporter_omaha.h"
#if USE_DBUS
#include "update_engine/dbus_connection.h"
#endif // USE_DBUS
@@ -50,7 +51,7 @@
}
bool RealSystemState::Initialize() {
- metrics_lib_.Init();
+ metrics_reporter_.Initialize();
boot_control_ = boot_control::CreateBootControl();
if (!boot_control_) {
diff --git a/real_system_state.h b/real_system_state.h
index e25b34e..49f7c31 100644
--- a/real_system_state.h
+++ b/real_system_state.h
@@ -22,7 +22,6 @@
#include <memory>
#include <set>
-#include <metrics/metrics_library.h>
#include <policy/device_policy.h>
#if USE_CHROME_KIOSK_APP
@@ -36,6 +35,8 @@
#include "update_engine/common/prefs.h"
#include "update_engine/connection_manager_interface.h"
#include "update_engine/daemon_state_interface.h"
+#include "update_engine/metrics_reporter_interface.h"
+#include "update_engine/metrics_reporter_omaha.h"
#include "update_engine/p2p_manager.h"
#include "update_engine/payload_state.h"
#include "update_engine/power_manager_interface.h"
@@ -91,8 +92,8 @@
inline HardwareInterface* hardware() override { return hardware_.get(); }
- inline MetricsLibraryInterface* metrics_lib() override {
- return &metrics_lib_;
+ inline MetricsReporterInterface* metrics_reporter() override {
+ return &metrics_reporter_;
}
inline PrefsInterface* prefs() override { return prefs_.get(); }
@@ -150,8 +151,8 @@
// Interface for the hardware functions.
std::unique_ptr<HardwareInterface> hardware_;
- // The Metrics Library interface for reporting UMA stats.
- MetricsLibrary metrics_lib_;
+ // The Metrics reporter for reporting UMA stats.
+ MetricsReporterOmaha metrics_reporter_;
// Interface for persisted store.
std::unique_ptr<PrefsInterface> prefs_;
diff --git a/scripts/brillo_update_payload b/scripts/brillo_update_payload
index 868e723..65c63f5 100755
--- a/scripts/brillo_update_payload
+++ b/scripts/brillo_update_payload
@@ -167,6 +167,10 @@
"Optional: Path to a source image. If specified, this makes a delta update."
DEFINE_string metadata_size_file "" \
"Optional: Path to output metadata size."
+ DEFINE_string max_timestamp "" \
+ "Optional: The maximum unix timestamp of the OS allowed to apply this \
+payload, should be set to a number higher than the build timestamp of the \
+system running on the device, 0 if not specified."
fi
if [[ "${COMMAND}" == "hash" || "${COMMAND}" == "sign" ]]; then
DEFINE_string unsigned_payload "" "Path to the input unsigned payload."
@@ -397,7 +401,8 @@
if grep -v -E '^[a-zA-Z0-9_-]*$' "${ab_partitions_list}" >&2; then
die "Invalid partition names found in the partition list."
fi
- partitions=($(cat "${ab_partitions_list}"))
+ # Get partition list without duplicates.
+ partitions=($(awk '!seen[$0]++' "${ab_partitions_list}"))
if [[ ${#partitions[@]} -eq 0 ]]; then
die "The list of partitions is empty. Can't generate a payload."
fi
@@ -566,6 +571,10 @@
GENERATOR_ARGS+=( --out_metadata_size_file="${FLAGS_metadata_size_file}" )
fi
+ if [[ -n "${FLAGS_max_timestamp}" ]]; then
+ GENERATOR_ARGS+=( --max_timestamp="${FLAGS_max_timestamp}" )
+ fi
+
if [[ -n "${POSTINSTALL_CONFIG_FILE}" ]]; then
GENERATOR_ARGS+=(
--new_postinstall_config_file="${POSTINSTALL_CONFIG_FILE}"
diff --git a/scripts/update_device.py b/scripts/update_device.py
index 63d0fc9..64cfbe3 100755
--- a/scripts/update_device.py
+++ b/scripts/update_device.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python2
#
# Copyright (C) 2017 The Android Open Source Project
#
@@ -19,18 +19,27 @@
import argparse
import BaseHTTPServer
+import hashlib
import logging
import os
import socket
import subprocess
import sys
import threading
+import xml.etree.ElementTree
import zipfile
+import update_payload.payload
+
# The path used to store the OTA package when applying the package from a file.
OTA_PACKAGE_PATH = '/data/ota_package'
+# The path to the payload public key on the device.
+PAYLOAD_KEY_PATH = '/etc/update_engine/update-payload-key.pub.pem'
+
+# The port on the device that update_engine should connect to.
+DEVICE_PORT = 1234
def CopyFileObjLength(fsrc, fdst, buffer_size=128 * 1024, copy_length=None):
"""Copy from a file object to another.
@@ -90,6 +99,7 @@
Attributes:
serving_payload: path to the only payload file we are serving.
+ serving_range: the start offset and size tuple of the payload.
"""
@staticmethod
@@ -140,12 +150,12 @@
else:
self.send_response(200)
- stat = os.fstat(f.fileno())
+ serving_start, serving_size = self.serving_range
start_range, end_range = self._parse_range(self.headers.get('range'),
- stat.st_size)
+ serving_size)
logging.info('Serving request for %s from %s [%d, %d) length: %d',
- self.path, self.serving_payload, start_range, end_range,
- end_range - start_range)
+ self.path, self.serving_payload, serving_start + start_range,
+ serving_start + end_range, end_range - start_range)
self.send_header('Accept-Ranges', 'bytes')
self.send_header('Content-Range',
@@ -153,22 +163,99 @@
'/' + str(end_range - start_range))
self.send_header('Content-Length', end_range - start_range)
+ stat = os.fstat(f.fileno())
self.send_header('Last-Modified', self.date_time_string(stat.st_mtime))
self.send_header('Content-type', 'application/octet-stream')
self.end_headers()
- f.seek(start_range)
+ f.seek(serving_start + start_range)
CopyFileObjLength(f, self.wfile, copy_length=end_range - start_range)
+ def do_POST(self): # pylint: disable=invalid-name
+ """Reply with the omaha response xml."""
+ if self.path != '/update':
+ self.send_error(404, 'Unknown request')
+ return
+
+ if not self.serving_payload:
+ self.send_error(500, 'No serving payload set')
+ return
+
+ try:
+ f = open(self.serving_payload, 'rb')
+ except IOError:
+ self.send_error(404, 'File not found')
+ return
+
+ content_length = int(self.headers.getheader('Content-Length'))
+ request_xml = self.rfile.read(content_length)
+ xml_root = xml.etree.ElementTree.fromstring(request_xml)
+ appid = None
+ for app in xml_root.iter('app'):
+ if 'appid' in app.attrib:
+ appid = app.attrib['appid']
+ break
+ if not appid:
+ self.send_error(400, 'No appid in Omaha request')
+ return
+
+ self.send_response(200)
+ self.send_header("Content-type", "text/xml")
+ self.end_headers()
+
+ serving_start, serving_size = self.serving_range
+ sha256 = hashlib.sha256()
+ f.seek(serving_start)
+ bytes_to_hash = serving_size
+ while bytes_to_hash:
+ buf = f.read(min(bytes_to_hash, 1024 * 1024))
+ if not buf:
+ self.send_error(500, 'Payload too small')
+ return
+ sha256.update(buf)
+ bytes_to_hash -= len(buf)
+
+ payload = update_payload.Payload(f, payload_file_offset=serving_start)
+ payload.Init()
+
+ response_xml = '''
+ <?xml version="1.0" encoding="UTF-8"?>
+ <response protocol="3.0">
+ <app appid="{appid}">
+ <updatecheck status="ok">
+ <urls>
+ <url codebase="http://127.0.0.1:{port}/"/>
+ </urls>
+ <manifest version="0.0.0.1">
+ <actions>
+ <action event="install" run="payload"/>
+ <action event="postinstall" MetadataSize="{metadata_size}"/>
+ </actions>
+ <packages>
+ <package hash_sha256="{payload_hash}" name="payload" size="{payload_size}"/>
+ </packages>
+ </manifest>
+ </updatecheck>
+ </app>
+ </response>
+ '''.format(appid=appid, port=DEVICE_PORT,
+ metadata_size=payload.metadata_size,
+ payload_hash=sha256.hexdigest(),
+ payload_size=serving_size)
+ self.wfile.write(response_xml.strip())
+ return
+
+
class ServerThread(threading.Thread):
"""A thread for serving HTTP requests."""
- def __init__(self, ota_filename):
+ def __init__(self, ota_filename, serving_range):
threading.Thread.__init__(self)
- # serving_payload is a class attribute and the UpdateHandler class is
- # instantiated with every request.
+ # serving_payload and serving_range are class attributes and the
+ # UpdateHandler class is instantiated with every request.
UpdateHandler.serving_payload = ota_filename
+ UpdateHandler.serving_range = serving_range
self._httpd = BaseHTTPServer.HTTPServer(('127.0.0.1', 0), UpdateHandler)
self.port = self._httpd.server_port
@@ -183,26 +270,31 @@
self._httpd.socket.close()
-def StartServer(ota_filename):
- t = ServerThread(ota_filename)
+def StartServer(ota_filename, serving_range):
+ t = ServerThread(ota_filename, serving_range)
t.start()
return t
-def AndroidUpdateCommand(ota_filename, payload_url):
+def AndroidUpdateCommand(ota_filename, payload_url, extra_headers):
"""Return the command to run to start the update in the Android device."""
ota = AndroidOTAPackage(ota_filename)
headers = ota.properties
headers += 'USER_AGENT=Dalvik (something, something)\n'
-
- # headers += 'POWERWASH=1\n'
headers += 'NETWORK_ID=0\n'
+ headers += extra_headers
return ['update_engine_client', '--update', '--follow',
'--payload=%s' % payload_url, '--offset=%d' % ota.offset,
'--size=%d' % ota.size, '--headers="%s"' % headers]
+def OmahaUpdateCommand(omaha_url):
+ """Return the command to run to start the update in a device using Omaha."""
+ return ['update_engine_client', '--update', '--follow',
+ '--omaha_url=%s' % omaha_url]
+
+
class AdbHost(object):
"""Represents a device connected via ADB."""
@@ -235,11 +327,28 @@
p.wait()
return p.returncode
+ def adb_output(self, command):
+ """Run an ADB command like "adb push" and return the output.
+
+ Args:
+ command: list of strings containing command and arguments to run
+
+ Returns:
+ the program's output as a string.
+
+ Raises:
+ subprocess.CalledProcessError on command exit != 0.
+ """
+ command = self._command_prefix + command
+ logging.info('Running: %s', ' '.join(str(x) for x in command))
+ return subprocess.check_output(command, universal_newlines=True)
+
def main():
parser = argparse.ArgumentParser(description='Android A/B OTA helper.')
- parser.add_argument('otafile', metavar='ZIP', type=str,
- help='the OTA package file (a .zip file).')
+ parser.add_argument('otafile', metavar='PAYLOAD', type=str,
+ help='the OTA package file (a .zip file) or raw payload \
+ if device uses Omaha.')
parser.add_argument('--file', action='store_true',
help='Push the file to the device before updating.')
parser.add_argument('--no-push', action='store_true',
@@ -248,6 +357,10 @@
help='The specific device to use.')
parser.add_argument('--no-verbose', action='store_true',
help='Less verbose output')
+ parser.add_argument('--public-key', type=str, default='',
+ help='Override the public key used to verify payload.')
+ parser.add_argument('--extra-headers', type=str, default='',
+ help='Extra headers to pass to the device.')
args = parser.parse_args()
logging.basicConfig(
level=logging.WARNING if args.no_verbose else logging.INFO)
@@ -262,27 +375,57 @@
# List of commands to perform the update.
cmds = []
+ help_cmd = ['shell', 'su', '0', 'update_engine_client', '--help']
+ use_omaha = 'omaha' in dut.adb_output(help_cmd)
+
if args.file:
# Update via pushing a file to /data.
device_ota_file = os.path.join(OTA_PACKAGE_PATH, 'debug.zip')
payload_url = 'file://' + device_ota_file
if not args.no_push:
- cmds.append(['push', args.otafile, device_ota_file])
+ data_local_tmp_file = '/data/local/tmp/debug.zip'
+ cmds.append(['push', args.otafile, data_local_tmp_file])
+ cmds.append(['shell', 'su', '0', 'mv', data_local_tmp_file,
+ device_ota_file])
+ cmds.append(['shell', 'su', '0', 'chcon',
+ 'u:object_r:ota_package_file:s0', device_ota_file])
cmds.append(['shell', 'su', '0', 'chown', 'system:cache', device_ota_file])
cmds.append(['shell', 'su', '0', 'chmod', '0660', device_ota_file])
else:
# Update via sending the payload over the network with an "adb reverse"
# command.
- device_port = 1234
- payload_url = 'http://127.0.0.1:%d/payload' % device_port
- server_thread = StartServer(args.otafile)
+ payload_url = 'http://127.0.0.1:%d/payload' % DEVICE_PORT
+ if use_omaha and zipfile.is_zipfile(args.otafile):
+ ota = AndroidOTAPackage(args.otafile)
+ serving_range = (ota.offset, ota.size)
+ else:
+ serving_range = (0, os.stat(args.otafile).st_size)
+ server_thread = StartServer(args.otafile, serving_range)
cmds.append(
- ['reverse', 'tcp:%d' % device_port, 'tcp:%d' % server_thread.port])
- finalize_cmds.append(['reverse', '--remove', 'tcp:%d' % device_port])
+ ['reverse', 'tcp:%d' % DEVICE_PORT, 'tcp:%d' % server_thread.port])
+ finalize_cmds.append(['reverse', '--remove', 'tcp:%d' % DEVICE_PORT])
+
+ if args.public_key:
+ payload_key_dir = os.path.dirname(PAYLOAD_KEY_PATH)
+ cmds.append(
+ ['shell', 'su', '0', 'mount', '-t', 'tmpfs', 'tmpfs', payload_key_dir])
+ # Allow adb push to payload_key_dir
+ cmds.append(['shell', 'su', '0', 'chcon', 'u:object_r:shell_data_file:s0',
+ payload_key_dir])
+ cmds.append(['push', args.public_key, PAYLOAD_KEY_PATH])
+ # Allow update_engine to read it.
+ cmds.append(['shell', 'su', '0', 'chcon', '-R', 'u:object_r:system_file:s0',
+ payload_key_dir])
+ finalize_cmds.append(['shell', 'su', '0', 'umount', payload_key_dir])
try:
# The main update command using the configured payload_url.
- update_cmd = AndroidUpdateCommand(args.otafile, payload_url)
+ if use_omaha:
+ update_cmd = \
+ OmahaUpdateCommand('http://127.0.0.1:%d/update' % DEVICE_PORT)
+ else:
+ update_cmd = \
+ AndroidUpdateCommand(args.otafile, payload_url, args.extra_headers)
cmds.append(['shell', 'su', '0'] + update_cmd)
for cmd in cmds:
diff --git a/scripts/update_payload/payload.py b/scripts/update_payload/payload.py
index 8e50c1f..8d9a20e 100644
--- a/scripts/update_payload/payload.py
+++ b/scripts/update_payload/payload.py
@@ -101,13 +101,15 @@
hasher=hasher)
- def __init__(self, payload_file):
+ def __init__(self, payload_file, payload_file_offset=0):
"""Initialize the payload object.
Args:
payload_file: update payload file object open for reading
+ payload_file_offset: the offset of the actual payload
"""
self.payload_file = payload_file
+ self.payload_file_offset = payload_file_offset
self.manifest_hasher = None
self.is_init = False
self.header = None
@@ -159,7 +161,8 @@
return common.Read(
self.payload_file, self.header.metadata_signature_len,
- offset=self.header.size + self.header.manifest_len)
+ offset=self.payload_file_offset + self.header.size +
+ self.header.manifest_len)
def ReadDataBlob(self, offset, length):
"""Reads and returns a single data blob from the update payload.
@@ -175,7 +178,8 @@
PayloadError if a read error occurred.
"""
return common.Read(self.payload_file, length,
- offset=self.data_offset + offset)
+ offset=self.payload_file_offset + self.data_offset +
+ offset)
def Init(self):
"""Initializes the payload object.
@@ -192,6 +196,7 @@
self.manifest_hasher = hashlib.sha256()
# Read the file header.
+ self.payload_file.seek(self.payload_file_offset)
self.header = self._ReadHeader()
# Read the manifest.
@@ -242,7 +247,7 @@
def ResetFile(self):
"""Resets the offset of the payload file to right past the manifest."""
- self.payload_file.seek(self.data_offset)
+ self.payload_file.seek(self.payload_file_offset + self.data_offset)
def IsDelta(self):
"""Returns True iff the payload appears to be a delta."""
diff --git a/service_observer_interface.h b/service_observer_interface.h
index 893df04..4edb0ac 100644
--- a/service_observer_interface.h
+++ b/service_observer_interface.h
@@ -31,11 +31,8 @@
// Called whenever the value of these parameters changes. For |progress|
// value changes, this method will be called only if it changes significantly.
- virtual void SendStatusUpdate(int64_t last_checked_time,
- double progress,
- update_engine::UpdateStatus status,
- const std::string& new_version,
- int64_t new_size) = 0;
+ virtual void SendStatusUpdate(
+ const update_engine::UpdateEngineStatus& update_engine_status) = 0;
// Called whenever an update attempt is completed.
virtual void SendPayloadApplicationComplete(ErrorCode error_code) = 0;
diff --git a/sideload_main.cc b/sideload_main.cc
index 614d553..ddb312e 100644
--- a/sideload_main.cc
+++ b/sideload_main.cc
@@ -41,6 +41,7 @@
using std::string;
using std::vector;
using update_engine::UpdateStatus;
+using update_engine::UpdateEngineStatus;
namespace {
// The root directory used for temporary files in update_engine_sideload.
@@ -80,11 +81,10 @@
}
// ServiceObserverInterface overrides.
- void SendStatusUpdate(int64_t last_checked_time,
- double progress,
- UpdateStatus status,
- const string& new_version,
- int64_t new_size) override {
+ void SendStatusUpdate(
+ const UpdateEngineStatus& update_engine_status) override {
+ UpdateStatus status = update_engine_status.status;
+ double progress = update_engine_status.progress;
if (status_ != status && (status == UpdateStatus::DOWNLOADING ||
status == UpdateStatus::FINALIZING)) {
// Split the progress bar in two parts for the two stages DOWNLOADING and
diff --git a/system_state.h b/system_state.h
index d538427..1b0ad08 100644
--- a/system_state.h
+++ b/system_state.h
@@ -17,8 +17,6 @@
#ifndef UPDATE_ENGINE_SYSTEM_STATE_H_
#define UPDATE_ENGINE_SYSTEM_STATE_H_
-class MetricsLibraryInterface;
-
namespace chromeos_update_manager {
class UpdateManager;
@@ -40,6 +38,7 @@
class ClockInterface;
class ConnectionManagerInterface;
class HardwareInterface;
+class MetricsReporterInterface;
class OmahaRequestParams;
class P2PManager;
class PayloadStateInterface;
@@ -76,7 +75,7 @@
virtual HardwareInterface* hardware() = 0;
// Gets the Metrics Library interface for reporting UMA stats.
- virtual MetricsLibraryInterface* metrics_lib() = 0;
+ virtual MetricsReporterInterface* metrics_reporter() = 0;
// Gets the interface object for persisted store.
virtual PrefsInterface* prefs() = 0;
diff --git a/test_http_server.cc b/test_http_server.cc
index 2955e79..93aa11c 100644
--- a/test_http_server.cc
+++ b/test_http_server.cc
@@ -98,9 +98,11 @@
request->raw_headers = headers;
// Break header into lines.
- vector<string> lines;
- base::SplitStringUsingSubstr(
- headers.substr(0, headers.length() - strlen(EOL EOL)), EOL, &lines);
+ vector<string> lines = base::SplitStringUsingSubstr(
+ headers.substr(0, headers.length() - strlen(EOL EOL)),
+ EOL,
+ base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_ALL);
// Decode URL line.
vector<string> terms = base::SplitString(lines[0], base::kWhitespaceASCII,
diff --git a/update_attempter.cc b/update_attempter.cc
index 4fef9ce..9cef154 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -31,7 +31,7 @@
#include <base/rand_util.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <brillo/bind_lambda.h>
+#include <brillo/data_encoding.h>
#include <brillo/errors/error_codes.h>
#include <brillo/message_loops/message_loop.h>
#include <policy/device_policy.h>
@@ -43,14 +43,12 @@
#include "update_engine/common/clock_interface.h"
#include "update_engine/common/constants.h"
#include "update_engine/common/hardware_interface.h"
-#include "update_engine/common/multi_range_http_fetcher.h"
#include "update_engine/common/platform_constants.h"
#include "update_engine/common/prefs_interface.h"
#include "update_engine/common/subprocess.h"
#include "update_engine/common/utils.h"
-#include "update_engine/connection_manager_interface.h"
#include "update_engine/libcurl_http_fetcher.h"
-#include "update_engine/metrics.h"
+#include "update_engine/metrics_reporter_interface.h"
#include "update_engine/omaha_request_action.h"
#include "update_engine/omaha_request_params.h"
#include "update_engine/omaha_response_handler_action.h"
@@ -78,6 +76,8 @@
using std::shared_ptr;
using std::string;
using std::vector;
+using update_engine::UpdateAttemptFlags;
+using update_engine::UpdateEngineStatus;
namespace chromeos_update_engine {
@@ -119,13 +119,11 @@
return code;
}
-UpdateAttempter::UpdateAttempter(
- SystemState* system_state,
- CertificateChecker* cert_checker)
+UpdateAttempter::UpdateAttempter(SystemState* system_state,
+ CertificateChecker* cert_checker)
: processor_(new ActionProcessor()),
system_state_(system_state),
- cert_checker_(cert_checker) {
-}
+ cert_checker_(cert_checker) {}
UpdateAttempter::~UpdateAttempter() {
// CertificateChecker might not be initialized in unittests.
@@ -171,9 +169,8 @@
void UpdateAttempter::CertificateChecked(ServerToCheck server_to_check,
CertificateCheckResult result) {
- metrics::ReportCertificateCheckMetrics(system_state_,
- server_to_check,
- result);
+ system_state_->metrics_reporter()->ReportCertificateCheckMetrics(
+ server_to_check, result);
}
bool UpdateAttempter::CheckAndReportDailyMetrics() {
@@ -234,7 +231,7 @@
return;
}
- metrics::ReportDailyMetrics(system_state_, age);
+ system_state_->metrics_reporter()->ReportDailyMetrics(age);
}
void UpdateAttempter::Update(const string& app_version,
@@ -262,10 +259,11 @@
// not performing an update check because of this.
LOG(INFO) << "Not updating b/c we already updated and we're waiting for "
<< "reboot, we'll ping Omaha instead";
- metrics::ReportUpdateCheckMetrics(system_state_,
- metrics::CheckResult::kRebootPending,
- metrics::CheckReaction::kUnset,
- metrics::DownloadErrorCode::kUnset);
+ system_state_->metrics_reporter()->ReportUpdateCheckMetrics(
+ system_state_,
+ metrics::CheckResult::kRebootPending,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset);
PingOmaha();
return;
}
@@ -598,24 +596,24 @@
shared_ptr<OmahaResponseHandlerAction> response_handler_action(
new OmahaResponseHandlerAction(system_state_));
- shared_ptr<OmahaRequestAction> download_started_action(
- new OmahaRequestAction(
- system_state_,
- new OmahaEvent(OmahaEvent::kTypeUpdateDownloadStarted),
- std::make_unique<LibcurlHttpFetcher>(GetProxyResolver(),
- system_state_->hardware()),
- false));
+ shared_ptr<OmahaRequestAction> download_started_action(new OmahaRequestAction(
+ system_state_,
+ new OmahaEvent(OmahaEvent::kTypeUpdateDownloadStarted),
+ std::make_unique<LibcurlHttpFetcher>(GetProxyResolver(),
+ system_state_->hardware()),
+ false));
LibcurlHttpFetcher* download_fetcher =
new LibcurlHttpFetcher(GetProxyResolver(), system_state_->hardware());
download_fetcher->set_server_to_check(ServerToCheck::kDownload);
+ if (interactive)
+ download_fetcher->set_max_retry_count(kDownloadMaxRetryCountInteractive);
shared_ptr<DownloadAction> download_action(
new DownloadAction(prefs_,
system_state_->boot_control(),
system_state_->hardware(),
system_state_,
- // passes ownership
- new MultiRangeHttpFetcher(download_fetcher),
+ download_fetcher, // passes ownership
interactive));
shared_ptr<OmahaRequestAction> download_finished_action(
new OmahaRequestAction(
@@ -627,12 +625,11 @@
shared_ptr<FilesystemVerifierAction> filesystem_verifier_action(
new FilesystemVerifierAction());
shared_ptr<OmahaRequestAction> update_complete_action(
- new OmahaRequestAction(
- system_state_,
- new OmahaEvent(OmahaEvent::kTypeUpdateComplete),
- std::make_unique<LibcurlHttpFetcher>(GetProxyResolver(),
- system_state_->hardware()),
- false));
+ new OmahaRequestAction(system_state_,
+ new OmahaEvent(OmahaEvent::kTypeUpdateComplete),
+ std::make_unique<LibcurlHttpFetcher>(
+ GetProxyResolver(), system_state_->hardware()),
+ false));
download_action->set_delegate(this);
response_handler_action_ = response_handler_action;
@@ -762,9 +759,20 @@
return BootControlInterface::kInvalidSlot;
}
-void UpdateAttempter::CheckForUpdate(const string& app_version,
+bool UpdateAttempter::CheckForUpdate(const string& app_version,
const string& omaha_url,
- bool interactive) {
+ UpdateAttemptFlags flags) {
+ bool interactive = !(flags & UpdateAttemptFlags::kFlagNonInteractive);
+
+ if (interactive && status_ != UpdateStatus::IDLE) {
+ // An update check is either in-progress, or an update has completed and the
+ // system is in UPDATED_NEED_REBOOT. Either way, don't do an interactive
+ // update at this time
+ LOG(INFO) << "Refusing to do an interactive update with an update already "
+ "in progress";
+ return false;
+ }
+
LOG(INFO) << "Forced update check requested.";
forced_app_version_.clear();
forced_omaha_url_.clear();
@@ -786,15 +794,31 @@
forced_omaha_url_ = constants::kOmahaDefaultAUTestURL;
}
+ if (interactive) {
+ // Use the passed-in update attempt flags for this update attempt instead
+ // of the previously set ones.
+ current_update_attempt_flags_ = flags;
+ // Note: The caching for non-interactive update checks happens in
+ // OnUpdateScheduled().
+ }
+
if (forced_update_pending_callback_.get()) {
// Make sure that a scheduling request is made prior to calling the forced
// update pending callback.
ScheduleUpdates();
forced_update_pending_callback_->Run(true, interactive);
}
+
+ return true;
}
bool UpdateAttempter::RebootIfNeeded() {
+ if (status_ != UpdateStatus::UPDATED_NEED_REBOOT) {
+ LOG(INFO) << "Reboot requested, but status is "
+ << UpdateStatusToString(status_) << ", so not rebooting.";
+ return false;
+ }
+
if (system_state_->power_manager()->RequestReboot())
return true;
@@ -843,6 +867,15 @@
<< (params.is_interactive ? "interactive" : "periodic")
<< " update.";
+ if (!params.is_interactive) {
+ // Cache the update attempt flags that will be used by this update attempt
+ // so that they can't be changed mid-way through.
+ current_update_attempt_flags_ = update_attempt_flags_;
+ }
+
+ LOG(INFO) << "Update attempt flags in use = 0x" << std::hex
+ << current_update_attempt_flags_;
+
Update(forced_app_version_, forced_omaha_url_, params.target_channel,
params.target_version_prefix, false, params.is_interactive);
// Always clear the forced app_version and omaha_url after an update attempt
@@ -876,6 +909,9 @@
// Reset cpu shares back to normal.
cpu_limiter_.StopLimiter();
+ // reset the state that's only valid for a single update pass
+ current_update_attempt_flags_ = UpdateAttemptFlags::kNone;
+
if (status_ == UpdateStatus::REPORTING_ERROR_EVENT) {
LOG(INFO) << "Error event sent.";
@@ -923,8 +959,12 @@
response_handler_action_->install_plan();
// Generate an unique payload identifier.
- const string target_version_uid =
- install_plan.payload_hash + ":" + install_plan.metadata_signature;
+ string target_version_uid;
+ for (const auto& payload : install_plan.payloads) {
+ target_version_uid +=
+ brillo::data_encoding::Base64Encode(payload.hash) + ":" +
+ payload.metadata_signature + ":";
+ }
// Expect to reboot into the new version to send the proper metric during
// next boot.
@@ -988,22 +1028,32 @@
consecutive_failed_update_checks_ = 0;
}
- const OmahaResponse& omaha_response =
- omaha_request_action->GetOutputObject();
// Store the server-dictated poll interval, if any.
server_dictated_poll_interval_ =
- std::max(0, omaha_response.poll_interval);
-
- // This update is ignored by omaha request action because update over
- // cellular connection is not allowed. Needs to ask for user's permissions
- // to update.
- if (code == ErrorCode::kOmahaUpdateIgnoredOverCellular) {
- new_version_ = omaha_response.version;
- new_payload_size_ = omaha_response.size;
- SetStatusAndNotify(UpdateStatus::NEED_PERMISSION_TO_UPDATE);
- }
+ std::max(0, omaha_request_action->GetOutputObject().poll_interval);
+ }
+ } else if (type == OmahaResponseHandlerAction::StaticType()) {
+ // Depending on the returned error code, note that an update is available.
+ if (code == ErrorCode::kOmahaUpdateDeferredPerPolicy ||
+ code == ErrorCode::kSuccess) {
+ // Note that the status will be updated to DOWNLOADING when some bytes
+ // get actually downloaded from the server and the BytesReceived
+ // callback is invoked. This avoids notifying the user that a download
+ // has started in cases when the server and the client are unable to
+ // initiate the download.
+ CHECK(action == response_handler_action_.get());
+ auto plan = response_handler_action_->install_plan();
+ UpdateLastCheckedTime();
+ new_version_ = plan.version;
+ new_system_version_ = plan.system_version;
+ new_payload_size_ = 0;
+ for (const auto& payload : plan.payloads)
+ new_payload_size_ += payload.size;
+ cpu_limiter_.StartLimiter();
+ SetStatusAndNotify(UpdateStatus::UPDATE_AVAILABLE);
}
}
+ // General failure cases.
if (code != ErrorCode::kSuccess) {
// If the current state is at or past the download phase, count the failure
// in case a switch to full update becomes necessary. Ignore network
@@ -1016,21 +1066,8 @@
CreatePendingErrorEvent(action, code);
return;
}
- // Find out which action completed.
- if (type == OmahaResponseHandlerAction::StaticType()) {
- // Note that the status will be updated to DOWNLOADING when some bytes get
- // actually downloaded from the server and the BytesReceived callback is
- // invoked. This avoids notifying the user that a download has started in
- // cases when the server and the client are unable to initiate the download.
- CHECK(action == response_handler_action_.get());
- const InstallPlan& plan = response_handler_action_->install_plan();
- UpdateLastCheckedTime();
- new_version_ = plan.version;
- new_payload_size_ = plan.payload_size;
- SetupDownload();
- cpu_limiter_.StartLimiter();
- SetStatusAndNotify(UpdateStatus::UPDATE_AVAILABLE);
- } else if (type == DownloadAction::StaticType()) {
+ // Find out which action completed (successfully).
+ if (type == DownloadAction::StaticType()) {
SetStatusAndNotify(UpdateStatus::FINALIZING);
} else if (type == FilesystemVerifierAction::StaticType()) {
// Log the system properties before the postinst and after the file system
@@ -1127,16 +1164,15 @@
}
}
-bool UpdateAttempter::GetStatus(int64_t* last_checked_time,
- double* progress,
- string* current_operation,
- string* new_version,
- int64_t* new_payload_size) {
- *last_checked_time = last_checked_time_;
- *progress = download_progress_;
- *current_operation = UpdateStatusToString(status_);
- *new_version = new_version_;
- *new_payload_size = new_payload_size_;
+bool UpdateAttempter::GetStatus(UpdateEngineStatus* out_status) {
+ out_status->last_checked_time = last_checked_time_;
+ out_status->status = status_;
+ out_status->current_version = omaha_request_params_->app_version();
+ out_status->current_system_version = omaha_request_params_->system_version();
+ out_status->progress = download_progress_;
+ out_status->new_size_bytes = new_payload_size_;
+ out_status->new_version = new_version_;
+ out_status->new_system_version = new_system_version_;
return true;
}
@@ -1173,12 +1209,12 @@
}
void UpdateAttempter::BroadcastStatus() {
+ UpdateEngineStatus broadcast_status;
+ // Use common method for generating the current status.
+ GetStatus(&broadcast_status);
+
for (const auto& observer : service_observers_) {
- observer->SendStatusUpdate(last_checked_time_,
- download_progress_,
- status_,
- new_version_,
- new_payload_size_);
+ observer->SendStatusUpdate(broadcast_status);
}
last_notify_time_ = TimeTicks::Now();
}
@@ -1281,12 +1317,11 @@
// Send it to Omaha.
LOG(INFO) << "Reporting the error event";
shared_ptr<OmahaRequestAction> error_event_action(
- new OmahaRequestAction(
- system_state_,
- error_event_.release(), // Pass ownership.
- std::make_unique<LibcurlHttpFetcher>(GetProxyResolver(),
- system_state_->hardware()),
- false));
+ new OmahaRequestAction(system_state_,
+ error_event_.release(), // Pass ownership.
+ std::make_unique<LibcurlHttpFetcher>(
+ GetProxyResolver(), system_state_->hardware()),
+ false));
actions_.push_back(shared_ptr<AbstractAction>(error_event_action));
processor_->EnqueueAction(error_event_action.get());
SetStatusAndNotify(UpdateStatus::REPORTING_ERROR_EVENT);
@@ -1324,32 +1359,6 @@
prefs_->SetInt64(kPrefsDeltaUpdateFailures, ++delta_failures);
}
-void UpdateAttempter::SetupDownload() {
- MultiRangeHttpFetcher* fetcher =
- static_cast<MultiRangeHttpFetcher*>(download_action_->http_fetcher());
- fetcher->ClearRanges();
- if (response_handler_action_->install_plan().is_resume) {
- // Resuming an update so fetch the update manifest metadata first.
- int64_t manifest_metadata_size = 0;
- int64_t manifest_signature_size = 0;
- prefs_->GetInt64(kPrefsManifestMetadataSize, &manifest_metadata_size);
- prefs_->GetInt64(kPrefsManifestSignatureSize, &manifest_signature_size);
- fetcher->AddRange(0, manifest_metadata_size + manifest_signature_size);
- // If there're remaining unprocessed data blobs, fetch them. Be careful not
- // to request data beyond the end of the payload to avoid 416 HTTP response
- // error codes.
- int64_t next_data_offset = 0;
- prefs_->GetInt64(kPrefsUpdateStateNextDataOffset, &next_data_offset);
- uint64_t resume_offset =
- manifest_metadata_size + manifest_signature_size + next_data_offset;
- if (resume_offset < response_handler_action_->install_plan().payload_size) {
- fetcher->AddRange(resume_offset);
- }
- } else {
- fetcher->AddRange(0);
- }
-}
-
void UpdateAttempter::PingOmaha() {
if (!processor_->IsRunning()) {
shared_ptr<OmahaRequestAction> ping_action(new OmahaRequestAction(
diff --git a/update_attempter.h b/update_attempter.h
index f0747e2..76e93a2 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -46,8 +46,6 @@
#include "update_engine/update_manager/policy.h"
#include "update_engine/update_manager/update_manager.h"
-class MetricsLibraryInterface;
-
namespace policy {
class PolicyProvider;
}
@@ -62,6 +60,7 @@
public PostinstallRunnerAction::DelegateInterface {
public:
using UpdateStatus = update_engine::UpdateStatus;
+ using UpdateAttemptFlags = update_engine::UpdateAttemptFlags;
static const int kMaxDeltaUpdateFailures;
UpdateAttempter(SystemState* system_state, CertificateChecker* cert_checker);
@@ -105,12 +104,8 @@
// for testing purposes.
virtual bool ResetStatus();
- // Returns the current status in the out params. Returns true on success.
- virtual bool GetStatus(int64_t* last_checked_time,
- double* progress,
- std::string* current_operation,
- std::string* new_version,
- int64_t* new_size);
+ // Returns the current status in the out param. Returns true on success.
+ virtual bool GetStatus(update_engine::UpdateEngineStatus* out_status);
// Runs chromeos-setgoodkernel, whose responsibility it is to mark the
// currently booted partition has high priority/permanent/etc. The execution
@@ -127,12 +122,28 @@
int http_response_code() const { return http_response_code_; }
void set_http_response_code(int code) { http_response_code_ = code; }
+ // Set flags that influence how updates and checks are performed. These
+ // influence all future checks and updates until changed or the device
+ // reboots.
+ void SetUpdateAttemptFlags(UpdateAttemptFlags flags) {
+ update_attempt_flags_ = flags;
+ }
+
+ // Returns the update attempt flags that are in place for the current update
+ // attempt. These are cached at the start of an update attempt so that they
+ // remain constant throughout the process.
+ virtual UpdateAttemptFlags GetCurrentUpdateAttemptFlags() {
+ return current_update_attempt_flags_;
+ }
+
// This is the internal entry point for going through an
// update. If the current status is idle invokes Update.
// This is called by the DBus implementation.
- virtual void CheckForUpdate(const std::string& app_version,
+ // This returns true if an update check was started, false if a check or an
+ // update was already in progress.
+ virtual bool CheckForUpdate(const std::string& app_version,
const std::string& omaha_url,
- bool is_interactive);
+ UpdateAttemptFlags flags);
// This is the internal entry point for going through a rollback. This will
// attempt to run the postinstall on the non-active partition and set it as
@@ -243,17 +254,23 @@
FRIEND_TEST(UpdateAttempterTest, ActionCompletedDownloadTest);
FRIEND_TEST(UpdateAttempterTest, ActionCompletedErrorTest);
FRIEND_TEST(UpdateAttempterTest, ActionCompletedOmahaRequestTest);
+ FRIEND_TEST(UpdateAttempterTest, BootTimeInUpdateMarkerFile);
+ FRIEND_TEST(UpdateAttempterTest, BroadcastCompleteDownloadTest);
+ FRIEND_TEST(UpdateAttempterTest, ChangeToDownloadingOnReceivedBytesTest);
FRIEND_TEST(UpdateAttempterTest, CreatePendingErrorEventTest);
FRIEND_TEST(UpdateAttempterTest, CreatePendingErrorEventResumedTest);
FRIEND_TEST(UpdateAttempterTest, DisableDeltaUpdateIfNeededTest);
+ FRIEND_TEST(UpdateAttempterTest, DownloadProgressAccumulationTest);
FRIEND_TEST(UpdateAttempterTest, MarkDeltaUpdateFailureTest);
FRIEND_TEST(UpdateAttempterTest, PingOmahaTest);
+ FRIEND_TEST(UpdateAttempterTest, ReportDailyMetrics);
FRIEND_TEST(UpdateAttempterTest, ScheduleErrorEventActionNoEventTest);
FRIEND_TEST(UpdateAttempterTest, ScheduleErrorEventActionTest);
- FRIEND_TEST(UpdateAttempterTest, UpdateTest);
- FRIEND_TEST(UpdateAttempterTest, ReportDailyMetrics);
- FRIEND_TEST(UpdateAttempterTest, BootTimeInUpdateMarkerFile);
FRIEND_TEST(UpdateAttempterTest, TargetVersionPrefixSetAndReset);
+ FRIEND_TEST(UpdateAttempterTest, UpdateAttemptFlagsCachedAtUpdateStart);
+ FRIEND_TEST(UpdateAttempterTest, UpdateDeferredByPolicyTest);
+ FRIEND_TEST(UpdateAttempterTest, UpdateIsNotRunningWhenUpdateAvailable);
+ FRIEND_TEST(UpdateAttempterTest, UpdateTest);
// CertificateChecker::Observer method.
// Report metrics about the certificate being checked.
@@ -272,9 +289,6 @@
// Sets the status to the given status and notifies a status update over dbus.
void SetStatusAndNotify(UpdateStatus status);
- // Sets up the download parameters after receiving the update check response.
- void SetupDownload();
-
// Creates an error event object in |error_event_| to be included in an
// OmahaRequestAction once the current action processor is done.
void CreatePendingErrorEvent(AbstractAction* action, ErrorCode code);
@@ -432,7 +446,13 @@
int64_t last_checked_time_ = 0;
std::string prev_version_;
std::string new_version_ = "0.0.0.0";
- int64_t new_payload_size_ = 0;
+ std::string new_system_version_;
+ uint64_t new_payload_size_ = 0;
+ // Flags influencing all periodic update checks
+ UpdateAttemptFlags update_attempt_flags_ = UpdateAttemptFlags::kNone;
+ // Flags influencing the currently in-progress check (cached at the start of
+ // the update check).
+ UpdateAttemptFlags current_update_attempt_flags_ = UpdateAttemptFlags::kNone;
// Common parameters for all Omaha requests.
OmahaRequestParams* omaha_request_params_ = nullptr;
diff --git a/update_attempter_android.cc b/update_attempter_android.cc
index 9a765b9..aacb06b 100644
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -18,20 +18,25 @@
#include <algorithm>
#include <map>
+#include <memory>
#include <utility>
+#include <android-base/properties.h>
#include <base/bind.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <brillo/bind_lambda.h>
+#include <brillo/data_encoding.h>
#include <brillo/message_loops/message_loop.h>
#include <brillo/strings/string_utils.h>
+#include <log/log_safetynet.h>
#include "update_engine/common/constants.h"
#include "update_engine/common/file_fetcher.h"
-#include "update_engine/common/multi_range_http_fetcher.h"
#include "update_engine/common/utils.h"
#include "update_engine/daemon_state_interface.h"
+#include "update_engine/metrics_reporter_interface.h"
+#include "update_engine/metrics_utils.h"
#include "update_engine/network_selector.h"
#include "update_engine/payload_consumer/download_action.h"
#include "update_engine/payload_consumer/filesystem_verifier_action.h"
@@ -45,11 +50,13 @@
#endif
using base::Bind;
+using base::Time;
using base::TimeDelta;
using base::TimeTicks;
using std::shared_ptr;
using std::string;
using std::vector;
+using update_engine::UpdateEngineStatus;
namespace chromeos_update_engine {
@@ -74,6 +81,13 @@
return false;
}
+bool GetHeaderAsBool(const string& header, bool default_value) {
+ int value = 0;
+ if (base::StringToInt(header, &value) && (value == 0 || value == 1))
+ return value == 1;
+ return default_value;
+}
+
} // namespace
UpdateAttempterAndroid::UpdateAttempterAndroid(
@@ -85,7 +99,9 @@
prefs_(prefs),
boot_control_(boot_control),
hardware_(hardware),
- processor_(new ActionProcessor()) {
+ processor_(new ActionProcessor()),
+ clock_(new Clock()) {
+ metrics_reporter_ = metrics::CreateMetricsReporter();
network_selector_ = network::CreateNetworkSelector();
}
@@ -98,10 +114,12 @@
void UpdateAttempterAndroid::Init() {
// In case of update_engine restart without a reboot we need to restore the
// reboot needed state.
- if (UpdateCompletedOnThisBoot())
+ if (UpdateCompletedOnThisBoot()) {
SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
- else
+ } else {
SetStatusAndNotify(UpdateStatus::IDLE);
+ UpdatePrefsAndReportUpdateMetricsOnReboot();
+ }
}
bool UpdateAttempterAndroid::ApplyPayload(
@@ -144,19 +162,27 @@
install_plan_.download_url = payload_url;
install_plan_.version = "";
base_offset_ = payload_offset;
- install_plan_.payload_size = payload_size;
- if (!install_plan_.payload_size) {
+ InstallPlan::Payload payload;
+ payload.size = payload_size;
+ if (!payload.size) {
if (!base::StringToUint64(headers[kPayloadPropertyFileSize],
- &install_plan_.payload_size)) {
- install_plan_.payload_size = 0;
+ &payload.size)) {
+ payload.size = 0;
}
}
- install_plan_.payload_hash = headers[kPayloadPropertyFileHash];
- if (!base::StringToUint64(headers[kPayloadPropertyMetadataSize],
- &install_plan_.metadata_size)) {
- install_plan_.metadata_size = 0;
+ if (!brillo::data_encoding::Base64Decode(headers[kPayloadPropertyFileHash],
+ &payload.hash)) {
+ LOG(WARNING) << "Unable to decode base64 file hash: "
+ << headers[kPayloadPropertyFileHash];
}
- install_plan_.metadata_signature = "";
+ if (!base::StringToUint64(headers[kPayloadPropertyMetadataSize],
+ &payload.metadata_size)) {
+ payload.metadata_size = 0;
+ }
+ // The |payload.type| is not used anymore since minor_version 3.
+ payload.type = InstallPayloadType::kUnknown;
+ install_plan_.payloads.push_back(payload);
+
// The |public_key_rsa| key would override the public key stored on disk.
install_plan_.public_key_rsa = "";
@@ -171,16 +197,28 @@
LOG(WARNING) << "Unable to save the update check response hash.";
}
}
- // The |payload_type| is not used anymore since minor_version 3.
- install_plan_.payload_type = InstallPayloadType::kUnknown;
-
install_plan_.source_slot = boot_control_->GetCurrentSlot();
install_plan_.target_slot = install_plan_.source_slot == 0 ? 1 : 0;
- int data_wipe = 0;
install_plan_.powerwash_required =
- base::StringToInt(headers[kPayloadPropertyPowerwash], &data_wipe) &&
- data_wipe != 0;
+ GetHeaderAsBool(headers[kPayloadPropertyPowerwash], false);
+
+ install_plan_.switch_slot_on_reboot =
+ GetHeaderAsBool(headers[kPayloadPropertySwitchSlotOnReboot], true);
+
+ install_plan_.run_post_install = true;
+ // Optionally skip post install if and only if:
+ // a) we're resuming
+ // b) post install has already succeeded before
+ // c) RUN_POST_INSTALL is set to 0.
+ if (install_plan_.is_resume && prefs_->Exists(kPrefsPostInstallSucceeded)) {
+ bool post_install_succeeded = false;
+ prefs_->GetBoolean(kPrefsPostInstallSucceeded, &post_install_succeeded);
+ if (post_install_succeeded) {
+ install_plan_.run_post_install =
+ GetHeaderAsBool(headers[kPayloadPropertyRunPostInstall], true);
+ }
+ }
NetworkId network_id = kDefaultNetworkId;
if (!headers[kPayloadPropertyNetworkId].empty()) {
@@ -192,7 +230,10 @@
"Invalid network_id: " + headers[kPayloadPropertyNetworkId]);
}
if (!network_selector_->SetProcessNetwork(network_id)) {
- LOG(WARNING) << "Unable to set network_id, continuing with the update.";
+ return LogAndSetError(
+ error,
+ FROM_HERE,
+ "Unable to set network_id: " + headers[kPayloadPropertyNetworkId]);
}
}
@@ -200,7 +241,6 @@
install_plan_.Dump();
BuildUpdateActions(payload_url);
- SetupDownload();
// Setup extra headers.
HttpFetcher* fetcher = download_action_->http_fetcher();
if (!headers[kPayloadPropertyAuthorization].empty())
@@ -214,6 +254,10 @@
// Just in case we didn't update boot flags yet, make sure they're updated
// before any update processing starts. This will start the update process.
UpdateBootFlags();
+
+ UpdatePrefsOnUpdateStart(install_plan_.is_resume);
+ // TODO(xunchang) report the metrics for unresumable updates
+
return true;
}
@@ -251,6 +295,7 @@
// after resetting to idle state, it doesn't go back to
// UpdateStatus::UPDATED_NEED_REBOOT state.
bool ret_value = prefs_->Delete(kPrefsUpdateCompletedOnBootId);
+ ClearMetricsPrefs();
// Update the boot flags so the current slot has higher priority.
if (!boot_control_->SetActiveBootSlot(boot_control_->GetCurrentSlot()))
@@ -291,7 +336,6 @@
// Update succeeded.
WriteUpdateCompletedMarker();
prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0);
- DeltaPerformer::ResetUpdateProgress(prefs_, false);
LOG(INFO) << "Update successfully applied, waiting to reboot.";
break;
@@ -307,6 +351,11 @@
LOG(INFO) << "Resetting update progress.";
break;
+ case ErrorCode::kPayloadTimestampError:
+ // SafetyNet logging, b/36232423
+ android_errorWriteLog(0x534e4554, "36232423");
+ break;
+
default:
// Ignore all other error codes.
break;
@@ -329,6 +378,11 @@
if (type == DownloadAction::StaticType()) {
download_progress_ = 0;
}
+ if (type == PostinstallRunnerAction::StaticType()) {
+ bool succeeded =
+ code == ErrorCode::kSuccess || code == ErrorCode::kUpdatedButNotActive;
+ prefs_->SetBoolean(kPrefsPostInstallSucceeded, succeeded);
+ }
if (code != ErrorCode::kSuccess) {
// If an action failed, the ActionProcessor will cancel the whole thing.
return;
@@ -350,6 +404,16 @@
} else {
ProgressUpdate(progress);
}
+
+ // Update the bytes downloaded in prefs.
+ int64_t current_bytes_downloaded =
+ metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, prefs_);
+ int64_t total_bytes_downloaded =
+ metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, prefs_);
+ prefs_->SetInt64(kPrefsCurrentBytesDownloaded,
+ current_bytes_downloaded + bytes_progressed);
+ prefs_->SetInt64(kPrefsTotalBytesDownloaded,
+ total_bytes_downloaded + bytes_progressed);
}
bool UpdateAttempterAndroid::ShouldCancel(ErrorCode* cancel_reason) {
@@ -417,15 +481,34 @@
SetStatusAndNotify(new_status);
ongoing_update_ = false;
+ // The network id is only applicable to one download attempt and once it's
+ // done the network id should not be re-used anymore.
+ if (!network_selector_->SetProcessNetwork(kDefaultNetworkId)) {
+ LOG(WARNING) << "Unable to unbind network.";
+ }
+
for (auto observer : daemon_state_->service_observers())
observer->SendPayloadApplicationComplete(error_code);
+
+ CollectAndReportUpdateMetricsOnUpdateFinished(error_code);
+ ClearMetricsPrefs();
+ if (error_code == ErrorCode::kSuccess) {
+ metrics_utils::SetSystemUpdatedMarker(clock_.get(), prefs_);
+ // Clear the total bytes downloaded if and only if the update succeeds.
+ prefs_->SetInt64(kPrefsTotalBytesDownloaded, 0);
+ }
}
void UpdateAttempterAndroid::SetStatusAndNotify(UpdateStatus status) {
status_ = status;
+ size_t payload_size =
+ install_plan_.payloads.empty() ? 0 : install_plan_.payloads[0].size;
+ UpdateEngineStatus status_to_send = {.status = status_,
+ .progress = download_progress_,
+ .new_size_bytes = payload_size};
+
for (auto observer : daemon_state_->service_observers()) {
- observer->SendStatusUpdate(
- 0, download_progress_, status_, "", install_plan_.payload_size);
+ observer->SendStatusUpdate(status_to_send);
}
last_notify_time_ = TimeTicks::Now();
}
@@ -456,9 +539,8 @@
new DownloadAction(prefs_,
boot_control_,
hardware_,
- nullptr, // system_state, not used.
- // passes ownership
- new MultiRangeHttpFetcher(download_fetcher),
+ nullptr, // system_state, not used.
+ download_fetcher, // passes ownership
true /* is_interactive */));
shared_ptr<FilesystemVerifierAction> filesystem_verifier_action(
new FilesystemVerifierAction());
@@ -467,6 +549,7 @@
new PostinstallRunnerAction(boot_control_, hardware_));
download_action->set_delegate(this);
+ download_action->set_base_offset(base_offset_);
download_action_ = download_action;
postinstall_runner_action->set_delegate(this);
@@ -487,42 +570,6 @@
processor_->EnqueueAction(action.get());
}
-void UpdateAttempterAndroid::SetupDownload() {
- MultiRangeHttpFetcher* fetcher =
- static_cast<MultiRangeHttpFetcher*>(download_action_->http_fetcher());
- fetcher->ClearRanges();
- if (install_plan_.is_resume) {
- // Resuming an update so fetch the update manifest metadata first.
- int64_t manifest_metadata_size = 0;
- int64_t manifest_signature_size = 0;
- prefs_->GetInt64(kPrefsManifestMetadataSize, &manifest_metadata_size);
- prefs_->GetInt64(kPrefsManifestSignatureSize, &manifest_signature_size);
- fetcher->AddRange(base_offset_,
- manifest_metadata_size + manifest_signature_size);
- // If there're remaining unprocessed data blobs, fetch them. Be careful not
- // to request data beyond the end of the payload to avoid 416 HTTP response
- // error codes.
- int64_t next_data_offset = 0;
- prefs_->GetInt64(kPrefsUpdateStateNextDataOffset, &next_data_offset);
- uint64_t resume_offset =
- manifest_metadata_size + manifest_signature_size + next_data_offset;
- if (!install_plan_.payload_size) {
- fetcher->AddRange(base_offset_ + resume_offset);
- } else if (resume_offset < install_plan_.payload_size) {
- fetcher->AddRange(base_offset_ + resume_offset,
- install_plan_.payload_size - resume_offset);
- }
- } else {
- if (install_plan_.payload_size) {
- fetcher->AddRange(base_offset_, install_plan_.payload_size);
- } else {
- // If no payload size is passed we assume we read until the end of the
- // stream.
- fetcher->AddRange(base_offset_);
- }
- }
-}
-
bool UpdateAttempterAndroid::WriteUpdateCompletedMarker() {
string boot_id;
TEST_AND_RETURN_FALSE(utils::GetBootId(&boot_id));
@@ -544,4 +591,153 @@
update_completed_on_boot_id == boot_id);
}
+// Collect and report the android metrics when we terminate the update.
+void UpdateAttempterAndroid::CollectAndReportUpdateMetricsOnUpdateFinished(
+ ErrorCode error_code) {
+ int64_t attempt_number =
+ metrics_utils::GetPersistedValue(kPrefsPayloadAttemptNumber, prefs_);
+ PayloadType payload_type = kPayloadTypeFull;
+ int64_t payload_size = 0;
+ for (const auto& p : install_plan_.payloads) {
+ if (p.type == InstallPayloadType::kDelta)
+ payload_type = kPayloadTypeDelta;
+ payload_size += p.size;
+ }
+
+ metrics::AttemptResult attempt_result =
+ metrics_utils::GetAttemptResult(error_code);
+ Time attempt_start_time = Time::FromInternalValue(
+ metrics_utils::GetPersistedValue(kPrefsUpdateTimestampStart, prefs_));
+ TimeDelta duration = clock_->GetBootTime() - attempt_start_time;
+ TimeDelta duration_uptime = clock_->GetMonotonicTime() - attempt_start_time;
+
+ metrics_reporter_->ReportUpdateAttemptMetrics(
+ nullptr, // system_state
+ static_cast<int>(attempt_number),
+ payload_type,
+ duration,
+ duration_uptime,
+ payload_size,
+ attempt_result,
+ error_code);
+
+ int64_t current_bytes_downloaded =
+ metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, prefs_);
+ metrics_reporter_->ReportUpdateAttemptDownloadMetrics(
+ current_bytes_downloaded,
+ 0,
+ DownloadSource::kNumDownloadSources,
+ metrics::DownloadErrorCode::kUnset,
+ metrics::ConnectionType::kUnset);
+
+ if (error_code == ErrorCode::kSuccess) {
+ int64_t reboot_count =
+ metrics_utils::GetPersistedValue(kPrefsNumReboots, prefs_);
+ string build_version;
+ prefs_->GetString(kPrefsPreviousVersion, &build_version);
+
+ // For android metrics, we only care about the total bytes downloaded
+ // for all sources; for now we assume the only download source is
+ // HttpsServer.
+ int64_t total_bytes_downloaded =
+ metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, prefs_);
+ int64_t num_bytes_downloaded[kNumDownloadSources] = {};
+ num_bytes_downloaded[DownloadSource::kDownloadSourceHttpsServer] =
+ total_bytes_downloaded;
+
+ int download_overhead_percentage = 0;
+ if (current_bytes_downloaded > 0) {
+ download_overhead_percentage =
+ (total_bytes_downloaded - current_bytes_downloaded) * 100ull /
+ current_bytes_downloaded;
+ }
+ metrics_reporter_->ReportSuccessfulUpdateMetrics(
+ static_cast<int>(attempt_number),
+ 0, // update abandoned count
+ payload_type,
+ payload_size,
+ num_bytes_downloaded,
+ download_overhead_percentage,
+ duration,
+ static_cast<int>(reboot_count),
+ 0); // url_switch_count
+ }
+}
+
+void UpdateAttempterAndroid::UpdatePrefsAndReportUpdateMetricsOnReboot() {
+ string current_boot_id;
+ TEST_AND_RETURN(utils::GetBootId(¤t_boot_id));
+ // Example: [ro.build.version.incremental]: [4292972]
+ string current_version =
+ android::base::GetProperty("ro.build.version.incremental", "");
+ TEST_AND_RETURN(!current_version.empty());
+
+ // If there's no record of previous version (e.g. due to a data wipe), we
+ // save the info of current boot and skip the metrics report.
+ if (!prefs_->Exists(kPrefsPreviousVersion)) {
+ prefs_->SetString(kPrefsBootId, current_boot_id);
+ prefs_->SetString(kPrefsPreviousVersion, current_version);
+ ClearMetricsPrefs();
+ return;
+ }
+ string previous_version;
+ // update_engine restarted under the same build.
+ // TODO(xunchang) identify and report rollback by checking UpdateMarker.
+ if (prefs_->GetString(kPrefsPreviousVersion, &previous_version) &&
+ previous_version == current_version) {
+ string last_boot_id;
+ bool is_reboot = prefs_->Exists(kPrefsBootId) &&
+ (prefs_->GetString(kPrefsBootId, &last_boot_id) &&
+ last_boot_id != current_boot_id);
+ // Increment the reboot number if |kPrefsNumReboots| exists. That pref is
+ // set when we start a new update.
+ if (is_reboot && prefs_->Exists(kPrefsNumReboots)) {
+ prefs_->SetString(kPrefsBootId, current_boot_id);
+ int64_t reboot_count =
+ metrics_utils::GetPersistedValue(kPrefsNumReboots, prefs_);
+ metrics_utils::SetNumReboots(reboot_count + 1, prefs_);
+ }
+ return;
+ }
+
+ // Now that the build version changes, report the update metrics.
+ // TODO(xunchang) check the build version is larger than the previous one.
+ prefs_->SetString(kPrefsBootId, current_boot_id);
+ prefs_->SetString(kPrefsPreviousVersion, current_version);
+
+ bool previous_attempt_exists = prefs_->Exists(kPrefsPayloadAttemptNumber);
+ // |kPrefsPayloadAttemptNumber| should be cleared upon successful update.
+ if (previous_attempt_exists) {
+ metrics_reporter_->ReportAbnormallyTerminatedUpdateAttemptMetrics();
+ }
+
+ metrics_utils::LoadAndReportTimeToReboot(
+ metrics_reporter_.get(), prefs_, clock_.get());
+ ClearMetricsPrefs();
+}
+
+// Save the update start time. Reset the reboot count and attempt number if the
+// update isn't a resume; otherwise increment the attempt number.
+void UpdateAttempterAndroid::UpdatePrefsOnUpdateStart(bool is_resume) {
+ if (!is_resume) {
+ metrics_utils::SetNumReboots(0, prefs_);
+ metrics_utils::SetPayloadAttemptNumber(1, prefs_);
+ } else {
+ int64_t attempt_number =
+ metrics_utils::GetPersistedValue(kPrefsPayloadAttemptNumber, prefs_);
+ metrics_utils::SetPayloadAttemptNumber(attempt_number + 1, prefs_);
+ }
+ Time update_start_time = clock_->GetMonotonicTime();
+ metrics_utils::SetUpdateTimestampStart(update_start_time, prefs_);
+}
+
+void UpdateAttempterAndroid::ClearMetricsPrefs() {
+ CHECK(prefs_);
+ prefs_->Delete(kPrefsCurrentBytesDownloaded);
+ prefs_->Delete(kPrefsNumReboots);
+ prefs_->Delete(kPrefsPayloadAttemptNumber);
+ prefs_->Delete(kPrefsSystemUpdatedMarker);
+ prefs_->Delete(kPrefsUpdateTimestampStart);
+}
+
} // namespace chromeos_update_engine
diff --git a/update_attempter_android.h b/update_attempter_android.h
index 6a5c227..28bf90a 100644
--- a/update_attempter_android.h
+++ b/update_attempter_android.h
@@ -28,9 +28,12 @@
#include "update_engine/client_library/include/update_engine/update_status.h"
#include "update_engine/common/action_processor.h"
#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/clock.h"
#include "update_engine/common/hardware_interface.h"
#include "update_engine/common/prefs_interface.h"
#include "update_engine/daemon_state_interface.h"
+#include "update_engine/metrics_reporter_interface.h"
+#include "update_engine/metrics_utils.h"
#include "update_engine/network_selector_interface.h"
#include "update_engine/payload_consumer/download_action.h"
#include "update_engine/payload_consumer/postinstall_runner_action.h"
@@ -86,6 +89,8 @@
void ProgressUpdate(double progress) override;
private:
+ friend class UpdateAttempterAndroidTest;
+
// Asynchronously marks the current slot as successful if needed. If already
// marked as good, CompleteUpdateBootFlags() is called starting the action
// processor.
@@ -110,10 +115,6 @@
// applying an update from the given |url|.
void BuildUpdateActions(const std::string& url);
- // Sets up the download parameters based on the update requested on the
- // |install_plan_|.
- void SetupDownload();
-
// Writes to the processing completed marker. Does nothing if
// |update_completed_marker_| is empty.
bool WriteUpdateCompletedMarker();
@@ -121,6 +122,41 @@
// Returns whether an update was completed in the current boot.
bool UpdateCompletedOnThisBoot();
+ // Prefs to use for metrics report
+ // |kPrefsPayloadAttemptNumber|: number of update attempts for the current
+ // payload_id.
+ // |KprefsNumReboots|: number of reboots when applying the current update.
+ // |kPrefsSystemUpdatedMarker|: end timestamp of the last successful update.
+ // |kPrefsUpdateTimestampStart|: start timestamp of the current update.
+ // |kPrefsCurrentBytesDownloaded|: number of bytes downloaded for the current
+ // payload_id.
+ // |kPrefsTotalBytesDownloaded|: number of bytes downloaded in total since
+ // the last successful update.
+
+ // Metrics report function to call:
+ // |ReportUpdateAttemptMetrics|
+ // |ReportSuccessfulUpdateMetrics|
+ // Prefs to update:
+ // |kPrefsSystemUpdatedMarker|
+ void CollectAndReportUpdateMetricsOnUpdateFinished(ErrorCode error_code);
+
+ // Metrics report function to call:
+ // |ReportAbnormallyTerminatedUpdateAttemptMetrics|
+ // |ReportTimeToRebootMetrics|
+ // Prefs to update:
+ // |kPrefsBootId|, |kPrefsPreviousVersion|
+ void UpdatePrefsAndReportUpdateMetricsOnReboot();
+
+ // Prefs to update:
+ // |kPrefsPayloadAttemptNumber|, |kPrefsUpdateTimestampStart|
+ void UpdatePrefsOnUpdateStart(bool is_resume);
+
+ // Prefs to delete:
+ // |kPrefsNumReboots|, |kPrefsPayloadAttemptNumber|,
+ // |kPrefsSystemUpdatedMarker|, |kPrefsUpdateTimestampStart|,
+ // |kPrefsCurrentBytesDownloaded|
+ void ClearMetricsPrefs();
+
DaemonStateInterface* daemon_state_;
// DaemonStateAndroid pointers.
@@ -166,6 +202,10 @@
// before applying an update to the other slot.
bool updated_boot_flags_ = false;
+ std::unique_ptr<ClockInterface> clock_;
+
+ std::unique_ptr<MetricsReporterInterface> metrics_reporter_;
+
DISALLOW_COPY_AND_ASSIGN(UpdateAttempterAndroid);
};
diff --git a/update_attempter_android_unittest.cc b/update_attempter_android_unittest.cc
new file mode 100644
index 0000000..94452df
--- /dev/null
+++ b/update_attempter_android_unittest.cc
@@ -0,0 +1,209 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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 "update_engine/update_attempter_android.h"
+
+#include <memory>
+#include <string>
+
+#include <android-base/properties.h>
+#include <base/time/time.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/fake_boot_control.h"
+#include "update_engine/common/fake_clock.h"
+#include "update_engine/common/fake_hardware.h"
+#include "update_engine/common/fake_prefs.h"
+#include "update_engine/common/mock_action_processor.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/daemon_state_android.h"
+#include "update_engine/mock_metrics_reporter.h"
+
+using base::Time;
+using base::TimeDelta;
+using testing::_;
+using update_engine::UpdateStatus;
+
+namespace chromeos_update_engine {
+
+class UpdateAttempterAndroidTest : public ::testing::Test {
+ protected:
+ UpdateAttempterAndroidTest() = default;
+
+ void SetUp() override {
+ clock_ = new FakeClock();
+ metrics_reporter_ = new testing::NiceMock<MockMetricsReporter>();
+ update_attempter_android_.metrics_reporter_.reset(metrics_reporter_);
+ update_attempter_android_.clock_.reset(clock_);
+ update_attempter_android_.processor_.reset(
+ new testing::NiceMock<MockActionProcessor>());
+ }
+
+ void SetUpdateStatus(update_engine::UpdateStatus status) {
+ update_attempter_android_.status_ = status;
+ }
+
+ UpdateAttempterAndroid update_attempter_android_{
+ &daemon_state_, &prefs_, &boot_control_, &hardware_};
+
+ DaemonStateAndroid daemon_state_;
+ FakePrefs prefs_;
+ FakeBootControl boot_control_;
+ FakeHardware hardware_;
+
+ FakeClock* clock_;
+ testing::NiceMock<MockMetricsReporter>* metrics_reporter_;
+};
+
+TEST_F(UpdateAttempterAndroidTest, UpdatePrefsSameBuildVersionOnInit) {
+ std::string build_version =
+ android::base::GetProperty("ro.build.version.incremental", "");
+ prefs_.SetString(kPrefsPreviousVersion, build_version);
+ prefs_.SetString(kPrefsBootId, "oldboot");
+ prefs_.SetInt64(kPrefsNumReboots, 1);
+
+ EXPECT_CALL(*metrics_reporter_, ReportTimeToReboot(_)).Times(0);
+ update_attempter_android_.Init();
+
+ // Check that the boot_id and reboot_count are updated.
+ std::string boot_id;
+ utils::GetBootId(&boot_id);
+ EXPECT_TRUE(prefs_.Exists(kPrefsBootId));
+ std::string prefs_boot_id;
+ EXPECT_TRUE(prefs_.GetString(kPrefsBootId, &prefs_boot_id));
+ EXPECT_EQ(boot_id, prefs_boot_id);
+
+ EXPECT_TRUE(prefs_.Exists(kPrefsNumReboots));
+ int64_t reboot_count;
+ EXPECT_TRUE(prefs_.GetInt64(kPrefsNumReboots, &reboot_count));
+ EXPECT_EQ(2, reboot_count);
+}
+
+TEST_F(UpdateAttempterAndroidTest, UpdatePrefsBuildVersionChangeOnInit) {
+ prefs_.SetString(kPrefsPreviousVersion, "00001"); // Set the fake version
+ prefs_.SetInt64(kPrefsPayloadAttemptNumber, 1);
+ prefs_.SetInt64(kPrefsSystemUpdatedMarker, 23456);
+
+ EXPECT_CALL(*metrics_reporter_,
+ ReportAbnormallyTerminatedUpdateAttemptMetrics())
+ .Times(1);
+
+ Time now = Time::FromInternalValue(34456);
+ clock_->SetMonotonicTime(now);
+ TimeDelta duration = now - Time::FromInternalValue(23456);
+ EXPECT_CALL(*metrics_reporter_, ReportTimeToReboot(duration.InMinutes()))
+ .Times(1);
+
+ update_attempter_android_.Init();
+ // Check that we reset the metric prefs.
+ EXPECT_FALSE(prefs_.Exists(kPrefsNumReboots));
+ EXPECT_FALSE(prefs_.Exists(kPrefsPayloadAttemptNumber));
+ EXPECT_FALSE(prefs_.Exists(kPrefsUpdateTimestampStart));
+ EXPECT_FALSE(prefs_.Exists(kPrefsSystemUpdatedMarker));
+}
+
+TEST_F(UpdateAttempterAndroidTest, ReportMetricsOnUpdateTerminated) {
+ prefs_.SetInt64(kPrefsNumReboots, 3);
+ prefs_.SetInt64(kPrefsPayloadAttemptNumber, 2);
+ prefs_.SetString(kPrefsPreviousVersion, "56789");
+ prefs_.SetInt64(kPrefsUpdateTimestampStart, 12345);
+
+ Time boot_time = Time::FromInternalValue(22345);
+ Time up_time = Time::FromInternalValue(21345);
+ clock_->SetBootTime(boot_time);
+ clock_->SetMonotonicTime(up_time);
+ TimeDelta duration = boot_time - Time::FromInternalValue(12345);
+ TimeDelta duration_uptime = up_time - Time::FromInternalValue(12345);
+ EXPECT_CALL(
+ *metrics_reporter_,
+ ReportUpdateAttemptMetrics(_,
+ 2,
+ _,
+ duration,
+ duration_uptime,
+ _,
+ metrics::AttemptResult::kUpdateSucceeded,
+ ErrorCode::kSuccess))
+ .Times(1);
+ EXPECT_CALL(*metrics_reporter_,
+ ReportSuccessfulUpdateMetrics(2, 0, _, _, _, _, duration, 3, _))
+ .Times(1);
+
+ SetUpdateStatus(UpdateStatus::UPDATE_AVAILABLE);
+ update_attempter_android_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+
+ EXPECT_FALSE(prefs_.Exists(kPrefsNumReboots));
+ EXPECT_FALSE(prefs_.Exists(kPrefsPayloadAttemptNumber));
+ EXPECT_FALSE(prefs_.Exists(kPrefsUpdateTimestampStart));
+ EXPECT_TRUE(prefs_.Exists(kPrefsSystemUpdatedMarker));
+}
+
+TEST_F(UpdateAttempterAndroidTest, ReportMetricsForBytesDownloaded) {
+ // Check both prefs are updated correctly.
+ update_attempter_android_.BytesReceived(20, 50, 200);
+ EXPECT_EQ(
+ 20,
+ metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, &prefs_));
+ EXPECT_EQ(
+ 20,
+ metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, &prefs_));
+
+ EXPECT_CALL(*metrics_reporter_,
+ ReportUpdateAttemptDownloadMetrics(50, _, _, _, _))
+ .Times(1);
+ EXPECT_CALL(*metrics_reporter_,
+ ReportUpdateAttemptDownloadMetrics(40, _, _, _, _))
+ .Times(1);
+
+ int64_t total_bytes[kNumDownloadSources] = {};
+ total_bytes[kDownloadSourceHttpsServer] = 90;
+ EXPECT_CALL(*metrics_reporter_,
+ ReportSuccessfulUpdateMetrics(
+ _,
+ _,
+ _,
+ _,
+ test_utils::DownloadSourceMatcher(total_bytes),
+ 125,
+ _,
+ _,
+ _))
+ .Times(1);
+
+ // The first update fails after receving 50 bytes in total.
+ update_attempter_android_.BytesReceived(30, 50, 200);
+ update_attempter_android_.ProcessingDone(nullptr, ErrorCode::kError);
+ EXPECT_EQ(
+ 0,
+ metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, &prefs_));
+ EXPECT_EQ(
+ 50,
+ metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, &prefs_));
+
+ // The second update succeeds after receiving 40 bytes, which leads to a
+ // overhead of 50 / 40 = 125%.
+ update_attempter_android_.BytesReceived(40, 40, 50);
+ update_attempter_android_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+ // Both prefs should be cleared.
+ EXPECT_EQ(
+ 0,
+ metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, &prefs_));
+ EXPECT_EQ(
+ 0, metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, &prefs_));
+}
+
+} // namespace chromeos_update_engine
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 06c82c9..240e4ec 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -22,7 +22,6 @@
#include <base/files/file_util.h>
#include <base/message_loop/message_loop.h>
-#include <brillo/bind_lambda.h>
#include <brillo/message_loops/base_message_loop.h>
#include <brillo/message_loops/message_loop.h>
#include <brillo/message_loops/message_loop_utils.h>
@@ -43,6 +42,7 @@
#include "update_engine/fake_system_state.h"
#include "update_engine/mock_p2p_manager.h"
#include "update_engine/mock_payload_state.h"
+#include "update_engine/mock_service_observer.h"
#include "update_engine/payload_consumer/filesystem_verifier_action.h"
#include "update_engine/payload_consumer/install_plan.h"
#include "update_engine/payload_consumer/payload_constants.h"
@@ -50,9 +50,13 @@
using base::Time;
using base::TimeDelta;
+using chromeos_update_manager::EvalStatus;
+using chromeos_update_manager::UpdateCheckParams;
using std::string;
using std::unique_ptr;
+using testing::_;
using testing::DoAll;
+using testing::Field;
using testing::InSequence;
using testing::Ne;
using testing::NiceMock;
@@ -61,7 +65,8 @@
using testing::ReturnPointee;
using testing::SaveArg;
using testing::SetArgPointee;
-using testing::_;
+using update_engine::UpdateAttemptFlags;
+using update_engine::UpdateEngineStatus;
using update_engine::UpdateStatus;
namespace chromeos_update_engine {
@@ -121,7 +126,7 @@
EXPECT_EQ(0.0, attempter_.download_progress_);
EXPECT_EQ(0, attempter_.last_checked_time_);
EXPECT_EQ("0.0.0.0", attempter_.new_version_);
- EXPECT_EQ(0, attempter_.new_payload_size_);
+ EXPECT_EQ(0ULL, attempter_.new_payload_size_);
processor_ = new NiceMock<MockActionProcessor>();
attempter_.processor_.reset(processor_); // Transfers ownership.
prefs_ = fake_system_state_.mock_prefs();
@@ -202,8 +207,8 @@
false /* is_interactive */);
EXPECT_CALL(*prefs_, GetInt64(kPrefsDeltaUpdateFailures, _)).Times(0);
attempter_.ActionCompleted(nullptr, &action, ErrorCode::kSuccess);
- EXPECT_EQ(503, attempter_.http_response_code());
EXPECT_EQ(UpdateStatus::FINALIZING, attempter_.status());
+ EXPECT_EQ(0.0, attempter_.download_progress_);
ASSERT_EQ(nullptr, attempter_.error_event_.get());
}
@@ -217,6 +222,86 @@
ASSERT_NE(nullptr, attempter_.error_event_.get());
}
+TEST_F(UpdateAttempterTest, DownloadProgressAccumulationTest) {
+ // Simple test case, where all the values match (nothing was skipped)
+ uint64_t bytes_progressed_1 = 1024 * 1024; // 1MB
+ uint64_t bytes_progressed_2 = 1024 * 1024; // 1MB
+ uint64_t bytes_received_1 = bytes_progressed_1;
+ uint64_t bytes_received_2 = bytes_received_1 + bytes_progressed_2;
+ uint64_t bytes_total = 20 * 1024 * 1024; // 20MB
+
+ double progress_1 =
+ static_cast<double>(bytes_received_1) / static_cast<double>(bytes_total);
+ double progress_2 =
+ static_cast<double>(bytes_received_2) / static_cast<double>(bytes_total);
+
+ EXPECT_EQ(0.0, attempter_.download_progress_);
+ // This is set via inspecting the InstallPlan payloads when the
+ // OmahaResponseAction is completed
+ attempter_.new_payload_size_ = bytes_total;
+ NiceMock<MockServiceObserver> observer;
+ EXPECT_CALL(observer,
+ SendStatusUpdate(AllOf(
+ Field(&UpdateEngineStatus::progress, progress_1),
+ Field(&UpdateEngineStatus::status, UpdateStatus::DOWNLOADING),
+ Field(&UpdateEngineStatus::new_size_bytes, bytes_total))));
+ EXPECT_CALL(observer,
+ SendStatusUpdate(AllOf(
+ Field(&UpdateEngineStatus::progress, progress_2),
+ Field(&UpdateEngineStatus::status, UpdateStatus::DOWNLOADING),
+ Field(&UpdateEngineStatus::new_size_bytes, bytes_total))));
+ attempter_.AddObserver(&observer);
+ attempter_.BytesReceived(bytes_progressed_1, bytes_received_1, bytes_total);
+ EXPECT_EQ(progress_1, attempter_.download_progress_);
+ // This iteration validates that a later set of updates to the variables are
+ // properly handled (so that |getStatus()| will return the same progress info
+ // as the callback is receiving.
+ attempter_.BytesReceived(bytes_progressed_2, bytes_received_2, bytes_total);
+ EXPECT_EQ(progress_2, attempter_.download_progress_);
+}
+
+TEST_F(UpdateAttempterTest, ChangeToDownloadingOnReceivedBytesTest) {
+ // The transition into UpdateStatus::DOWNLOADING happens when the
+ // first bytes are received.
+ uint64_t bytes_progressed = 1024 * 1024; // 1MB
+ uint64_t bytes_received = 2 * 1024 * 1024; // 2MB
+ uint64_t bytes_total = 20 * 1024 * 1024; // 300MB
+ attempter_.status_ = UpdateStatus::CHECKING_FOR_UPDATE;
+ // This is set via inspecting the InstallPlan payloads when the
+ // OmahaResponseAction is completed
+ attempter_.new_payload_size_ = bytes_total;
+ EXPECT_EQ(0.0, attempter_.download_progress_);
+ NiceMock<MockServiceObserver> observer;
+ EXPECT_CALL(observer,
+ SendStatusUpdate(AllOf(
+ Field(&UpdateEngineStatus::status, UpdateStatus::DOWNLOADING),
+ Field(&UpdateEngineStatus::new_size_bytes, bytes_total))));
+ attempter_.AddObserver(&observer);
+ attempter_.BytesReceived(bytes_progressed, bytes_received, bytes_total);
+ EXPECT_EQ(UpdateStatus::DOWNLOADING, attempter_.status_);
+}
+
+TEST_F(UpdateAttempterTest, BroadcastCompleteDownloadTest) {
+ // There is a special case to ensure that at 100% downloaded,
+ // download_progress_ is updated and that value broadcast. This test confirms
+ // that.
+ uint64_t bytes_progressed = 0; // ignored
+ uint64_t bytes_received = 5 * 1024 * 1024; // ignored
+ uint64_t bytes_total = 5 * 1024 * 1024; // 300MB
+ attempter_.status_ = UpdateStatus::DOWNLOADING;
+ attempter_.new_payload_size_ = bytes_total;
+ EXPECT_EQ(0.0, attempter_.download_progress_);
+ NiceMock<MockServiceObserver> observer;
+ EXPECT_CALL(observer,
+ SendStatusUpdate(AllOf(
+ Field(&UpdateEngineStatus::progress, 1.0),
+ Field(&UpdateEngineStatus::status, UpdateStatus::DOWNLOADING),
+ Field(&UpdateEngineStatus::new_size_bytes, bytes_total))));
+ attempter_.AddObserver(&observer);
+ attempter_.BytesReceived(bytes_progressed, bytes_received, bytes_total);
+ EXPECT_EQ(1.0, attempter_.download_progress_);
+}
+
TEST_F(UpdateAttempterTest, ActionCompletedOmahaRequestTest) {
unique_ptr<MockHttpFetcher> fetcher(new MockHttpFetcher("", 0, nullptr));
fetcher->FailTransfer(500); // Sets the HTTP response code.
@@ -323,8 +408,7 @@
.Times(0);
OmahaResponse response;
string url1 = "http://url1";
- response.payload_urls.push_back(url1);
- response.payload_urls.push_back("https://url");
+ response.packages.push_back({.payload_urls = {url1, "https://url"}});
EXPECT_CALL(*(fake_system_state_.mock_payload_state()), GetCurrentUrl())
.WillRepeatedly(Return(url1));
fake_system_state_.mock_payload_state()->SetResponse(response);
@@ -948,14 +1032,14 @@
TEST_F(UpdateAttempterTest, CheckForUpdateAUTest) {
fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
fake_system_state_.fake_hardware()->SetAreDevFeaturesEnabled(false);
- attempter_.CheckForUpdate("", "autest", true);
+ attempter_.CheckForUpdate("", "autest", UpdateAttemptFlags::kNone);
EXPECT_EQ(constants::kOmahaDefaultAUTestURL, attempter_.forced_omaha_url());
}
TEST_F(UpdateAttempterTest, CheckForUpdateScheduledAUTest) {
fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
fake_system_state_.fake_hardware()->SetAreDevFeaturesEnabled(false);
- attempter_.CheckForUpdate("", "autest-scheduled", true);
+ attempter_.CheckForUpdate("", "autest-scheduled", UpdateAttemptFlags::kNone);
EXPECT_EQ(constants::kOmahaDefaultAUTestURL, attempter_.forced_omaha_url());
}
@@ -969,4 +1053,90 @@
fake_system_state_.request_params()->target_version_prefix().empty());
}
+TEST_F(UpdateAttempterTest, UpdateDeferredByPolicyTest) {
+ // Construct an OmahaResponseHandlerAction that has processed an InstallPlan,
+ // but the update is being deferred by the Policy.
+ OmahaResponseHandlerAction* response_action =
+ new OmahaResponseHandlerAction(&fake_system_state_);
+ response_action->install_plan_.version = "a.b.c.d";
+ response_action->install_plan_.system_version = "b.c.d.e";
+ response_action->install_plan_.payloads.push_back(
+ {.size = 1234ULL, .type = InstallPayloadType::kFull});
+ attempter_.response_handler_action_.reset(response_action);
+ // Inform the UpdateAttempter that the OmahaResponseHandlerAction has
+ // completed, with the deferred-update error code.
+ attempter_.ActionCompleted(
+ nullptr, response_action, ErrorCode::kOmahaUpdateDeferredPerPolicy);
+ {
+ UpdateEngineStatus status;
+ attempter_.GetStatus(&status);
+ EXPECT_EQ(UpdateStatus::UPDATE_AVAILABLE, status.status);
+ EXPECT_EQ(response_action->install_plan_.version, status.new_version);
+ EXPECT_EQ(response_action->install_plan_.system_version,
+ status.new_system_version);
+ EXPECT_EQ(response_action->install_plan_.payloads[0].size,
+ status.new_size_bytes);
+ }
+ // An "error" event should have been created to tell Omaha that the update is
+ // being deferred.
+ EXPECT_TRUE(nullptr != attempter_.error_event_);
+ EXPECT_EQ(OmahaEvent::kTypeUpdateComplete, attempter_.error_event_->type);
+ EXPECT_EQ(OmahaEvent::kResultUpdateDeferred, attempter_.error_event_->result);
+ ErrorCode expected_code = static_cast<ErrorCode>(
+ static_cast<int>(ErrorCode::kOmahaUpdateDeferredPerPolicy) |
+ static_cast<int>(ErrorCode::kTestOmahaUrlFlag));
+ EXPECT_EQ(expected_code, attempter_.error_event_->error_code);
+ // End the processing
+ attempter_.ProcessingDone(nullptr, ErrorCode::kOmahaUpdateDeferredPerPolicy);
+ // Validate the state of the attempter.
+ {
+ UpdateEngineStatus status;
+ attempter_.GetStatus(&status);
+ EXPECT_EQ(UpdateStatus::REPORTING_ERROR_EVENT, status.status);
+ EXPECT_EQ(response_action->install_plan_.version, status.new_version);
+ EXPECT_EQ(response_action->install_plan_.system_version,
+ status.new_system_version);
+ EXPECT_EQ(response_action->install_plan_.payloads[0].size,
+ status.new_size_bytes);
+ }
+}
+
+TEST_F(UpdateAttempterTest, UpdateIsNotRunningWhenUpdateAvailable) {
+ EXPECT_FALSE(attempter_.IsUpdateRunningOrScheduled());
+ // Verify in-progress update with UPDATE_AVAILABLE is running
+ attempter_.status_ = UpdateStatus::UPDATE_AVAILABLE;
+ EXPECT_TRUE(attempter_.IsUpdateRunningOrScheduled());
+}
+
+TEST_F(UpdateAttempterTest, UpdateAttemptFlagsCachedAtUpdateStart) {
+ attempter_.SetUpdateAttemptFlags(UpdateAttemptFlags::kFlagRestrictDownload);
+
+ UpdateCheckParams params = {.updates_enabled = true};
+ attempter_.OnUpdateScheduled(EvalStatus::kSucceeded, params);
+
+ EXPECT_EQ(UpdateAttemptFlags::kFlagRestrictDownload,
+ attempter_.GetCurrentUpdateAttemptFlags());
+}
+
+TEST_F(UpdateAttempterTest, InteractiveUpdateUsesPassedRestrictions) {
+ attempter_.SetUpdateAttemptFlags(UpdateAttemptFlags::kFlagRestrictDownload);
+
+ attempter_.CheckForUpdate("", "", UpdateAttemptFlags::kNone);
+ EXPECT_EQ(UpdateAttemptFlags::kNone,
+ attempter_.GetCurrentUpdateAttemptFlags());
+}
+
+TEST_F(UpdateAttempterTest, NonInteractiveUpdateUsesSetRestrictions) {
+ attempter_.SetUpdateAttemptFlags(UpdateAttemptFlags::kNone);
+
+ // This tests that when CheckForUpdate() is called with the non-interactive
+ // flag set, that it doesn't change the current UpdateAttemptFlags.
+ attempter_.CheckForUpdate("",
+ "",
+ UpdateAttemptFlags::kFlagNonInteractive |
+ UpdateAttemptFlags::kFlagRestrictDownload);
+ EXPECT_EQ(UpdateAttemptFlags::kNone,
+ attempter_.GetCurrentUpdateAttemptFlags());
+}
+
} // namespace chromeos_update_engine
diff --git a/update_engine.gyp b/update_engine.gyp
index 4da04d4..f312a1d 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -257,7 +257,7 @@
'hardware_chromeos.cc',
'image_properties_chromeos.cc',
'libcurl_http_fetcher.cc',
- 'metrics.cc',
+ 'metrics_reporter_omaha.cc',
'metrics_utils.cc',
'omaha_request_action.cc',
'omaha_request_params.cc',
@@ -510,6 +510,7 @@
'fake_system_state.cc',
'hardware_chromeos_unittest.cc',
'image_properties_chromeos_unittest.cc',
+ 'metrics_reporter_omaha_unittest.cc',
'metrics_utils_unittest.cc',
'omaha_request_action_unittest.cc',
'omaha_request_params_unittest.cc',
diff --git a/update_engine.rc b/update_engine.rc
index b6a706a..a7d6235 100644
--- a/update_engine.rc
+++ b/update_engine.rc
@@ -1,5 +1,9 @@
-service update_engine /system/bin/update_engine --logtostderr --foreground
+service update_engine /system/bin/update_engine --logtostderr --logtofile --foreground
class late_start
user root
group root system wakelock inet cache
writepid /dev/cpuset/system-background/tasks
+ disabled
+
+on property:ro.boot.slot_suffix=*
+ enable update_engine
diff --git a/update_engine_client.cc b/update_engine_client.cc
index ad6ded0..bb19632 100644
--- a/update_engine_client.cc
+++ b/update_engine_client.cc
@@ -79,7 +79,7 @@
// We can't call QuitWithExitCode from OnInit(), so we delay the execution
// of the ProcessFlags method after the Daemon initialization is done.
- base::MessageLoop::current()->PostTask(
+ base::MessageLoop::current()->task_runner()->PostTask(
FROM_HERE,
base::Bind(&UpdateEngineClient::ProcessFlagsAndExit,
base::Unretained(this)));
diff --git a/update_engine_client_android.cc b/update_engine_client_android.cc
index 989a97e..267f6e9 100644
--- a/update_engine_client_android.cc
+++ b/update_engine_client_android.cc
@@ -97,7 +97,10 @@
ErrorCode code = static_cast<ErrorCode>(error_code);
LOG(INFO) << "onPayloadApplicationComplete(" << utils::ErrorCodeToString(code)
<< " (" << error_code << "))";
- client_->ExitWhenIdle(code == ErrorCode::kSuccess ? EX_OK : 1);
+ client_->ExitWhenIdle(
+ (code == ErrorCode::kSuccess || code == ErrorCode::kUpdatedButNotActive)
+ ? EX_OK
+ : 1);
return Status::ok();
}
diff --git a/update_manager/android_things_policy.cc b/update_manager/android_things_policy.cc
new file mode 100644
index 0000000..5fbda46
--- /dev/null
+++ b/update_manager/android_things_policy.cc
@@ -0,0 +1,180 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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 "update_engine/update_manager/android_things_policy.h"
+
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/time/time.h>
+
+#include "update_engine/update_manager/api_restricted_downloads_policy_impl.h"
+#include "update_engine/update_manager/enough_slots_ab_updates_policy_impl.h"
+#include "update_engine/update_manager/interactive_update_policy_impl.h"
+#include "update_engine/update_manager/official_build_check_policy_impl.h"
+
+using base::Time;
+using chromeos_update_engine::ErrorCode;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_manager {
+
+const NextUpdateCheckPolicyConstants
+ AndroidThingsPolicy::kNextUpdateCheckPolicyConstants = {
+ .timeout_initial_interval = 7 * 60,
+ .timeout_periodic_interval = 5 * 60 * 60,
+ .timeout_max_backoff_interval = 26 * 60 * 60,
+ .timeout_regular_fuzz = 10 * 60,
+ .attempt_backoff_max_interval_in_days = 16,
+ .attempt_backoff_fuzz_in_hours = 12,
+};
+
+EvalStatus AndroidThingsPolicy::UpdateCheckAllowed(
+ EvaluationContext* ec,
+ State* state,
+ string* error,
+ UpdateCheckParams* result) const {
+ // Set the default return values.
+ result->updates_enabled = true;
+ result->target_channel.clear();
+ result->target_version_prefix.clear();
+ result->is_interactive = false;
+
+ // Build a list of policies to consult. Note that each policy may modify the
+ // result structure, even if it signals kContinue.
+ EnoughSlotsAbUpdatesPolicyImpl enough_slots_ab_updates_policy;
+ OnlyUpdateOfficialBuildsPolicyImpl only_update_official_builds_policy;
+ InteractiveUpdatePolicyImpl interactive_update_policy;
+ NextUpdateCheckTimePolicyImpl next_update_check_time_policy(
+ kNextUpdateCheckPolicyConstants);
+
+ vector<Policy const*> policies_to_consult = {
+ // Do not perform any updates if there are not enough slots to do
+ // A/B updates
+ &enough_slots_ab_updates_policy,
+
+ // Unofficial builds should not perform periodic update checks.
+ &only_update_official_builds_policy,
+
+ // Check to see if an interactive update was requested.
+ &interactive_update_policy,
+
+ // Ensure that periodic update checks are timed properly.
+ &next_update_check_time_policy,
+ };
+
+ // Now that the list of policy implementations, and the order to consult them,
+ // as been setup, do that. If none of the policies make a definitive
+ // decisions about whether or not to check for updates, then allow the update
+ // check to happen.
+ EvalStatus status = ConsultPolicies(policies_to_consult,
+ &Policy::UpdateCheckAllowed,
+ ec,
+ state,
+ error,
+ result);
+ if (status != EvalStatus::kContinue) {
+ return status;
+ } else {
+ // It is time to check for an update.
+ LOG(INFO) << "Allowing update check.";
+ return EvalStatus::kSucceeded;
+ }
+}
+
+// Uses the |UpdateRestrictions| to determine if the download and apply can
+// occur at this time.
+EvalStatus AndroidThingsPolicy::UpdateCanBeApplied(
+ EvaluationContext* ec,
+ State* state,
+ string* error,
+ ErrorCode* result,
+ chromeos_update_engine::InstallPlan* install_plan) const {
+ // Build a list of policies to consult. Note that each policy may modify the
+ // result structure, even if it signals kContinue.
+ ApiRestrictedDownloadsPolicyImpl api_restricted_downloads_policy;
+
+ vector<Policy const*> policies_to_consult = {
+ // Do not apply the update if all updates are restricted by the API.
+ &api_restricted_downloads_policy,
+ };
+
+ // Now that the list of policy implementations, and the order to consult them,
+ // as been setup, do that. If none of the policies make a definitive
+ // decisions about whether or not to check for updates, then allow the update
+ // check to happen.
+ EvalStatus status = ConsultPolicies(policies_to_consult,
+ &Policy::UpdateCanBeApplied,
+ ec,
+ state,
+ error,
+ result,
+ install_plan);
+ if (EvalStatus::kContinue != status) {
+ return status;
+ } else {
+ // The update can proceed.
+ LOG(INFO) << "Allowing update to be applied.";
+ *result = ErrorCode::kSuccess;
+ return EvalStatus::kSucceeded;
+ }
+}
+
+// Always returns |EvalStatus::kSucceeded|
+EvalStatus AndroidThingsPolicy::UpdateCanStart(EvaluationContext* ec,
+ State* state,
+ string* error,
+ UpdateDownloadParams* result,
+ UpdateState update_state) const {
+ // Update is good to go.
+ result->update_can_start = true;
+ return EvalStatus::kSucceeded;
+}
+
+// Always returns |EvalStatus::kSucceeded|
+EvalStatus AndroidThingsPolicy::UpdateDownloadAllowed(EvaluationContext* ec,
+ State* state,
+ string* error,
+ bool* result) const {
+ // By default, we allow updates.
+ *result = true;
+ return EvalStatus::kSucceeded;
+}
+
+// P2P is always disabled. Returns |result|==|false| and
+// |EvalStatus::kSucceeded|
+EvalStatus AndroidThingsPolicy::P2PEnabled(EvaluationContext* ec,
+ State* state,
+ string* error,
+ bool* result) const {
+ *result = false;
+ return EvalStatus::kSucceeded;
+}
+
+// This will return immediately with |EvalStatus::kSucceeded| and set
+// |result|==|false|
+EvalStatus AndroidThingsPolicy::P2PEnabledChanged(EvaluationContext* ec,
+ State* state,
+ string* error,
+ bool* result,
+ bool prev_result) const {
+ *result = false;
+ return EvalStatus::kSucceeded;
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/android_things_policy.h b/update_manager/android_things_policy.h
new file mode 100644
index 0000000..9fd8bc4
--- /dev/null
+++ b/update_manager/android_things_policy.h
@@ -0,0 +1,92 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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.
+//
+
+#ifndef UPDATE_ENGINE_UPDATE_MANAGER_ANDROID_THINGS_POLICY_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_ANDROID_THINGS_POLICY_H_
+
+#include <string>
+
+#include "update_engine/update_manager/next_update_check_policy_impl.h"
+#include "update_engine/update_manager/policy_utils.h"
+
+namespace chromeos_update_manager {
+
+// AndroidThingsPolicy implements the policy-related logic used in
+// AndroidThings.
+class AndroidThingsPolicy : public Policy {
+ public:
+ AndroidThingsPolicy() = default;
+ ~AndroidThingsPolicy() override = default;
+
+ // Policy overrides.
+ EvalStatus UpdateCheckAllowed(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ UpdateCheckParams* result) const override;
+
+ // Uses the |UpdateRestrictions| to determine if the download and apply can
+ // occur at this time.
+ EvalStatus UpdateCanBeApplied(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ chromeos_update_engine::ErrorCode* result,
+ chromeos_update_engine::InstallPlan* install_plan) const override;
+
+ // Always returns |EvalStatus::kSucceeded|
+ EvalStatus UpdateCanStart(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ UpdateDownloadParams* result,
+ UpdateState update_state) const override;
+
+ // Always returns |EvalStatus::kSucceeded|
+ EvalStatus UpdateDownloadAllowed(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ bool* result) const override;
+
+ // P2P is always disabled. Returns |result|==|false| and
+ // |EvalStatus::kSucceeded|
+ EvalStatus P2PEnabled(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ bool* result) const override;
+
+ // This will return immediately with |EvalStatus::kSucceeded| and set
+ // |result|==|false|
+ EvalStatus P2PEnabledChanged(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ bool* result,
+ bool prev_result) const override;
+
+ protected:
+ // Policy override.
+ std::string PolicyName() const override { return "AndroidThingsPolicy"; }
+
+ private:
+ friend class UmAndroidThingsPolicyTest;
+ FRIEND_TEST(UmAndroidThingsPolicyTest, UpdateCheckAllowedWaitsForTheTimeout);
+
+ static const NextUpdateCheckPolicyConstants kNextUpdateCheckPolicyConstants;
+
+ DISALLOW_COPY_AND_ASSIGN(AndroidThingsPolicy);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // UPDATE_ENGINE_UPDATE_MANAGER_ANDROID_THINGS_POLICY_H_
diff --git a/update_manager/android_things_policy_unittest.cc b/update_manager/android_things_policy_unittest.cc
new file mode 100644
index 0000000..8a50bc2
--- /dev/null
+++ b/update_manager/android_things_policy_unittest.cc
@@ -0,0 +1,188 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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 "update_engine/update_manager/android_things_policy.h"
+
+#include <memory>
+
+#include "update_engine/update_manager/next_update_check_policy_impl.h"
+#include "update_engine/update_manager/policy_test_utils.h"
+
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_engine::ErrorCode;
+using chromeos_update_engine::InstallPlan;
+
+namespace chromeos_update_manager {
+
+class UmAndroidThingsPolicyTest : public UmPolicyTestBase {
+ protected:
+ UmAndroidThingsPolicyTest() {
+ policy_ = std::make_unique<AndroidThingsPolicy>();
+ }
+
+ void SetUpDefaultState() override {
+ UmPolicyTestBase::SetUpDefaultState();
+
+ // For the purpose of the tests, this is an official build
+ fake_state_.system_provider()->var_is_official_build()->reset(
+ new bool(true));
+ // NOLINTNEXTLINE(readability/casting)
+ fake_state_.system_provider()->var_num_slots()->reset(new unsigned int(2));
+ }
+
+ // Configures the policy to return a desired value from UpdateCheckAllowed by
+ // faking the current wall clock time as needed. Restores the default state.
+ // This is used when testing policies that depend on this one.
+ virtual void SetUpdateCheckAllowed(bool allow_check) {
+ Time next_update_check;
+ CallMethodWithContext(&NextUpdateCheckTimePolicyImpl::NextUpdateCheckTime,
+ &next_update_check,
+ AndroidThingsPolicy::kNextUpdateCheckPolicyConstants);
+ SetUpDefaultState();
+ Time curr_time = next_update_check;
+ if (allow_check)
+ curr_time += TimeDelta::FromSeconds(1);
+ else
+ curr_time -= TimeDelta::FromSeconds(1);
+ fake_clock_.SetWallclockTime(curr_time);
+ }
+};
+
+TEST_F(UmAndroidThingsPolicyTest, UpdateCheckAllowedWaitsForTheTimeout) {
+ // We get the next update_check timestamp from the policy's private method
+ // and then we check the public method respects that value on the normal
+ // case.
+ Time next_update_check;
+ Time last_checked_time =
+ fake_clock_.GetWallclockTime() + TimeDelta::FromMinutes(1234);
+
+ LOG(INFO) << "last_checked_time: " << last_checked_time;
+ fake_state_.updater_provider()->var_last_checked_time()->reset(
+ new Time(last_checked_time));
+ CallMethodWithContext(&NextUpdateCheckTimePolicyImpl::NextUpdateCheckTime,
+ &next_update_check,
+ AndroidThingsPolicy::kNextUpdateCheckPolicyConstants);
+ LOG(INFO) << "Next check allowed at: " << next_update_check;
+
+ // Check that the policy blocks until the next_update_check is reached.
+ SetUpDefaultClock();
+ SetUpDefaultState();
+ fake_state_.updater_provider()->var_last_checked_time()->reset(
+ new Time(last_checked_time));
+ fake_clock_.SetWallclockTime(next_update_check - TimeDelta::FromSeconds(1));
+
+ UpdateCheckParams result;
+ ExpectPolicyStatus(
+ EvalStatus::kAskMeAgainLater, &Policy::UpdateCheckAllowed, &result);
+
+ SetUpDefaultClock();
+ SetUpDefaultState();
+ fake_state_.updater_provider()->var_last_checked_time()->reset(
+ new Time(last_checked_time));
+ fake_clock_.SetWallclockTime(next_update_check + TimeDelta::FromSeconds(1));
+ ExpectPolicyStatus(
+ EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
+ EXPECT_TRUE(result.updates_enabled);
+ EXPECT_FALSE(result.is_interactive);
+}
+
+TEST_F(UmAndroidThingsPolicyTest,
+ UpdateCheckAllowedUpdatesDisabledForUnofficialBuilds) {
+ // UpdateCheckAllowed should return kAskMeAgainLater if this is an unofficial
+ // build; we don't want periodic update checks on developer images.
+
+ fake_state_.system_provider()->var_is_official_build()->reset(
+ new bool(false));
+
+ UpdateCheckParams result;
+ ExpectPolicyStatus(
+ EvalStatus::kAskMeAgainLater, &Policy::UpdateCheckAllowed, &result);
+}
+
+TEST_F(UmAndroidThingsPolicyTest,
+ UpdateCheckAllowedUpdatesDisabledWhenNotEnoughSlotsAbUpdates) {
+ // UpdateCheckAllowed should return false (kSucceeded) if the image booted
+ // without enough slots to do A/B updates.
+
+ // NOLINTNEXTLINE(readability/casting)
+ fake_state_.system_provider()->var_num_slots()->reset(new unsigned int(1));
+
+ UpdateCheckParams result;
+ ExpectPolicyStatus(
+ EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
+ EXPECT_FALSE(result.updates_enabled);
+}
+
+TEST_F(UmAndroidThingsPolicyTest,
+ UpdateCheckAllowedForcedUpdateRequestedInteractive) {
+ // UpdateCheckAllowed should return true because a forced update request was
+ // signaled for an interactive update.
+
+ SetUpdateCheckAllowed(true);
+ fake_state_.updater_provider()->var_forced_update_requested()->reset(
+ new UpdateRequestStatus(UpdateRequestStatus::kInteractive));
+
+ UpdateCheckParams result;
+ ExpectPolicyStatus(
+ EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
+ EXPECT_TRUE(result.updates_enabled);
+ EXPECT_TRUE(result.is_interactive);
+}
+
+TEST_F(UmAndroidThingsPolicyTest,
+ UpdateCheckAllowedForcedUpdateRequestedPeriodic) {
+ // UpdateCheckAllowed should return true because a forced update request was
+ // signaled for a periodic check.
+
+ SetUpdateCheckAllowed(true);
+ fake_state_.updater_provider()->var_forced_update_requested()->reset(
+ new UpdateRequestStatus(UpdateRequestStatus::kPeriodic));
+
+ UpdateCheckParams result;
+ ExpectPolicyStatus(
+ EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
+ EXPECT_TRUE(result.updates_enabled);
+ EXPECT_FALSE(result.is_interactive);
+}
+
+TEST_F(UmAndroidThingsPolicyTest, UpdateCanBeAppliedOk) {
+ // UpdateCanBeApplied should return kSucceeded in the base case
+
+ InstallPlan plan;
+ ErrorCode result;
+ ExpectPolicyStatus(
+ EvalStatus::kSucceeded, &Policy::UpdateCanBeApplied, &result, &plan);
+
+ EXPECT_EQ(ErrorCode::kSuccess, result);
+}
+
+TEST_F(UmAndroidThingsPolicyTest, UpdateCanBeAppliedRestricted) {
+ // UpdateCanBeApplied should return kOmahaUpdateDeferredPerPolicy in
+ // when the restricted flag is set in the Updater.
+
+ fake_state_.updater_provider()->var_update_restrictions()->reset(
+ new UpdateRestrictions(UpdateRestrictions::kRestrictDownloading));
+
+ InstallPlan plan;
+ ErrorCode result;
+ ExpectPolicyStatus(
+ EvalStatus::kSucceeded, &Policy::UpdateCanBeApplied, &result, &plan);
+
+ EXPECT_EQ(ErrorCode::kOmahaUpdateDeferredPerPolicy, result);
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/api_restricted_downloads_policy_impl.cc b/update_manager/api_restricted_downloads_policy_impl.cc
new file mode 100644
index 0000000..d413cca
--- /dev/null
+++ b/update_manager/api_restricted_downloads_policy_impl.cc
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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 "update_engine/update_manager/api_restricted_downloads_policy_impl.h"
+
+using chromeos_update_engine::ErrorCode;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_manager {
+
+// Allow the API to restrict the downloading of updates.
+EvalStatus ApiRestrictedDownloadsPolicyImpl::UpdateCanBeApplied(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ ErrorCode* result,
+ chromeos_update_engine::InstallPlan* install_plan) const {
+ // Next, check to see if updates can be applied (in general).
+ const UpdateRestrictions* update_restrictions_p =
+ ec->GetValue(state->updater_provider()->var_update_restrictions());
+ if (update_restrictions_p) {
+ if (*update_restrictions_p & UpdateRestrictions::kRestrictDownloading) {
+ *result = ErrorCode::kOmahaUpdateDeferredPerPolicy;
+ return EvalStatus::kSucceeded;
+ }
+ }
+
+ // The API isn't restricting downloads, so implicitly allow them to happen
+ // but don't explicitly return success from this policy implementation.
+ return EvalStatus::kContinue;
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/api_restricted_downloads_policy_impl.h b/update_manager/api_restricted_downloads_policy_impl.h
new file mode 100644
index 0000000..21457a5
--- /dev/null
+++ b/update_manager/api_restricted_downloads_policy_impl.h
@@ -0,0 +1,51 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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.
+//
+
+#ifndef UPDATE_ENGINE_UPDATE_MANAGER_API_RESTRICTED_DOWNLOADS_POLICY_IMPL_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_API_RESTRICTED_DOWNLOADS_POLICY_IMPL_H_
+
+#include <string>
+
+#include "update_engine/update_manager/policy_utils.h"
+
+namespace chromeos_update_manager {
+
+// Allow the API to restrict the downloading of updates.
+class ApiRestrictedDownloadsPolicyImpl : public PolicyImplBase {
+ public:
+ ApiRestrictedDownloadsPolicyImpl() = default;
+ ~ApiRestrictedDownloadsPolicyImpl() override = default;
+
+ // Policy overrides.
+ EvalStatus UpdateCanBeApplied(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ chromeos_update_engine::ErrorCode* result,
+ chromeos_update_engine::InstallPlan* install_plan) const override;
+
+ protected:
+ std::string PolicyName() const override {
+ return "ApiRestrictedDownloadsPolicyImpl";
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ApiRestrictedDownloadsPolicyImpl);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // UPDATE_ENGINE_UPDATE_MANAGER_API_RESTRICTED_DOWNLOADS_POLICY_IMPL_H_
diff --git a/update_manager/boxed_value.cc b/update_manager/boxed_value.cc
index 9758d33..a437c02 100644
--- a/update_manager/boxed_value.cc
+++ b/update_manager/boxed_value.cc
@@ -176,4 +176,19 @@
return "Unknown";
}
+template <>
+string BoxedValue::ValuePrinter<UpdateRestrictions>(const void* value) {
+ const UpdateRestrictions* val =
+ reinterpret_cast<const UpdateRestrictions*>(value);
+
+ if (*val == UpdateRestrictions::kNone) {
+ return "None";
+ }
+ string retval = "Flags:";
+ if (*val & kRestrictDownloading) {
+ retval += " RestrictDownloading";
+ }
+ return retval;
+}
+
} // namespace chromeos_update_manager
diff --git a/update_manager/boxed_value_unittest.cc b/update_manager/boxed_value_unittest.cc
index 2a086a6..4aeaec8 100644
--- a/update_manager/boxed_value_unittest.cc
+++ b/update_manager/boxed_value_unittest.cc
@@ -21,6 +21,7 @@
#include <map>
#include <set>
#include <string>
+#include <utility>
#include <base/strings/stringprintf.h>
#include <base/time/time.h>
@@ -231,4 +232,14 @@
EXPECT_EQ("DeleterMarker:true", value.ToString());
}
+TEST(UmBoxedValueTest, UpdateRestrictionsToString) {
+ EXPECT_EQ(
+ "None",
+ BoxedValue(new UpdateRestrictions(UpdateRestrictions::kNone)).ToString());
+ EXPECT_EQ("Flags: RestrictDownloading",
+ BoxedValue(new UpdateRestrictions(
+ UpdateRestrictions::kRestrictDownloading))
+ .ToString());
+}
+
} // namespace chromeos_update_manager
diff --git a/update_manager/chromeos_policy.cc b/update_manager/chromeos_policy.cc
index e3893d3..b32b626 100644
--- a/update_manager/chromeos_policy.cc
+++ b/update_manager/chromeos_policy.cc
@@ -36,6 +36,7 @@
using chromeos_update_engine::ConnectionTethering;
using chromeos_update_engine::ConnectionType;
using chromeos_update_engine::ErrorCode;
+using chromeos_update_engine::InstallPlan;
using std::get;
using std::max;
using std::min;
@@ -78,6 +79,7 @@
case ErrorCode::kPayloadMismatchedType:
case ErrorCode::kUnsupportedMajorPayloadVersion:
case ErrorCode::kUnsupportedMinorPayloadVersion:
+ case ErrorCode::kPayloadTimestampError:
LOG(INFO) << "Advancing download URL due to error "
<< chromeos_update_engine::utils::ErrorCodeToString(err_code)
<< " (" << static_cast<int>(err_code) << ")";
@@ -134,7 +136,7 @@
case ErrorCode::kOmahaRequestXMLHasEntityDecl:
case ErrorCode::kFilesystemVerifierError:
case ErrorCode::kUserCanceled:
- case ErrorCode::kOmahaUpdateIgnoredOverCellular:
+ case ErrorCode::kUpdatedButNotActive:
LOG(INFO) << "Not changing URL index or failure count due to error "
<< chromeos_update_engine::utils::ErrorCodeToString(err_code)
<< " (" << static_cast<int>(err_code) << ")";
@@ -324,6 +326,15 @@
return EvalStatus::kSucceeded;
}
+EvalStatus ChromeOSPolicy::UpdateCanBeApplied(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ ErrorCode* result,
+ InstallPlan* install_plan) const {
+ *result = ErrorCode::kSuccess;
+ return EvalStatus::kSucceeded;
+}
+
EvalStatus ChromeOSPolicy::UpdateCanStart(
EvaluationContext* ec,
State* state,
diff --git a/update_manager/chromeos_policy.h b/update_manager/chromeos_policy.h
index b4370c4..283bedc 100644
--- a/update_manager/chromeos_policy.h
+++ b/update_manager/chromeos_policy.h
@@ -59,6 +59,13 @@
EvaluationContext* ec, State* state, std::string* error,
UpdateCheckParams* result) const override;
+ EvalStatus UpdateCanBeApplied(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ chromeos_update_engine::ErrorCode* result,
+ chromeos_update_engine::InstallPlan* install_plan) const override;
+
EvalStatus UpdateCanStart(
EvaluationContext* ec,
State* state,
diff --git a/update_manager/chromeos_policy_unittest.cc b/update_manager/chromeos_policy_unittest.cc
index 0c38700..63fa0f7 100644
--- a/update_manager/chromeos_policy_unittest.cc
+++ b/update_manager/chromeos_policy_unittest.cc
@@ -93,6 +93,7 @@
new bool(true));
fake_state_.system_provider()->var_is_oobe_complete()->reset(
new bool(true));
+ // NOLINTNEXTLINE(readability/casting)
fake_state_.system_provider()->var_num_slots()->reset(new unsigned int(2));
// Connection is wifi, untethered.
@@ -418,10 +419,11 @@
}
TEST_F(UmChromeOSPolicyTest,
- UpdateCheckAllowedUpdatesDisabledForRemovableBootDevice) {
+ UpdateCheckAllowedUpdatesDisabledWhenNotEnoughSlotsAbUpdates) {
// UpdateCheckAllowed should return false (kSucceeded) if the image booted
- // from a removable device.
+ // without enough slots to do A/B updates.
+ // NOLINTNEXTLINE(readability/casting)
fake_state_.system_provider()->var_num_slots()->reset(new unsigned int(1));
UpdateCheckParams result;
diff --git a/update_manager/default_policy.cc b/update_manager/default_policy.cc
index 9a5ce7e..5da1520 100644
--- a/update_manager/default_policy.cc
+++ b/update_manager/default_policy.cc
@@ -16,6 +16,9 @@
#include "update_engine/update_manager/default_policy.h"
+using chromeos_update_engine::ErrorCode;
+using chromeos_update_engine::InstallPlan;
+
namespace {
// A fixed minimum interval between consecutive allowed update checks. This
@@ -53,6 +56,15 @@
return EvalStatus::kAskMeAgainLater;
}
+EvalStatus DefaultPolicy::UpdateCanBeApplied(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ ErrorCode* result,
+ InstallPlan* install_plan) const {
+ *result = ErrorCode::kSuccess;
+ return EvalStatus::kSucceeded;
+}
+
EvalStatus DefaultPolicy::UpdateCanStart(
EvaluationContext* ec,
State* state,
diff --git a/update_manager/default_policy.h b/update_manager/default_policy.h
index 3f41178..136ca35 100644
--- a/update_manager/default_policy.h
+++ b/update_manager/default_policy.h
@@ -69,6 +69,13 @@
EvaluationContext* ec, State* state, std::string* error,
UpdateCheckParams* result) const override;
+ EvalStatus UpdateCanBeApplied(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ chromeos_update_engine::ErrorCode* result,
+ chromeos_update_engine::InstallPlan* install_plan) const override;
+
EvalStatus UpdateCanStart(
EvaluationContext* ec, State* state, std::string* error,
UpdateDownloadParams* result,
diff --git a/update_manager/enough_slots_ab_updates_policy_impl.cc b/update_manager/enough_slots_ab_updates_policy_impl.cc
new file mode 100644
index 0000000..70f15d4
--- /dev/null
+++ b/update_manager/enough_slots_ab_updates_policy_impl.cc
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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 "update_engine/update_manager/enough_slots_ab_updates_policy_impl.h"
+
+namespace chromeos_update_manager {
+
+// Do not perform any updates if booted from removable device. This decision
+// is final.
+EvalStatus EnoughSlotsAbUpdatesPolicyImpl::UpdateCheckAllowed(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ UpdateCheckParams* result) const {
+ const auto* num_slots_p =
+ ec->GetValue(state->system_provider()->var_num_slots());
+ if (num_slots_p == nullptr || *num_slots_p < 2) {
+ LOG(INFO) << "Not enough slots for A/B updates, disabling update checks.";
+ result->updates_enabled = false;
+ return EvalStatus::kSucceeded;
+ }
+ return EvalStatus::kContinue;
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/enough_slots_ab_updates_policy_impl.h b/update_manager/enough_slots_ab_updates_policy_impl.h
new file mode 100644
index 0000000..1d45389
--- /dev/null
+++ b/update_manager/enough_slots_ab_updates_policy_impl.h
@@ -0,0 +1,49 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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.
+//
+
+#ifndef UPDATE_ENGINE_UPDATE_MANAGER_ENOUGH_SLOTS_AB_UPDATES_POLICY_IMPL_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_ENOUGH_SLOTS_AB_UPDATES_POLICY_IMPL_H_
+
+#include <string>
+
+#include "update_engine/update_manager/policy_utils.h"
+
+namespace chromeos_update_manager {
+
+// Do not perform any updates if booted from removable device.
+class EnoughSlotsAbUpdatesPolicyImpl : public PolicyImplBase {
+ public:
+ EnoughSlotsAbUpdatesPolicyImpl() = default;
+ ~EnoughSlotsAbUpdatesPolicyImpl() override = default;
+
+ // Policy overrides.
+ EvalStatus UpdateCheckAllowed(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ UpdateCheckParams* result) const override;
+
+ protected:
+ std::string PolicyName() const override {
+ return "EnoughSlotsAbUpdatesPolicyImpl";
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(EnoughSlotsAbUpdatesPolicyImpl);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // UPDATE_ENGINE_UPDATE_MANAGER_ENOUGH_SLOTS_AB_UPDATES_POLICY_IMPL_H_
diff --git a/update_manager/fake_updater_provider.h b/update_manager/fake_updater_provider.h
index 44389f4..7295765 100644
--- a/update_manager/fake_updater_provider.h
+++ b/update_manager/fake_updater_provider.h
@@ -41,19 +41,15 @@
return &var_update_completed_time_;
}
- FakeVariable<double>* var_progress() override {
- return &var_progress_;
- }
+ FakeVariable<double>* var_progress() override { return &var_progress_; }
- FakeVariable<Stage>* var_stage() override {
- return &var_stage_;
- }
+ FakeVariable<Stage>* var_stage() override { return &var_stage_; }
FakeVariable<std::string>* var_new_version() override {
return &var_new_version_;
}
- FakeVariable<int64_t>* var_payload_size() override {
+ FakeVariable<uint64_t>* var_payload_size() override {
return &var_payload_size_;
}
@@ -65,9 +61,7 @@
return &var_new_channel_;
}
- FakeVariable<bool>* var_p2p_enabled() override {
- return &var_p2p_enabled_;
- }
+ FakeVariable<bool>* var_p2p_enabled() override { return &var_p2p_enabled_; }
FakeVariable<bool>* var_cellular_enabled() override {
return &var_cellular_enabled_;
@@ -85,42 +79,35 @@
return &var_forced_update_requested_;
}
+ FakeVariable<UpdateRestrictions>* var_update_restrictions() override {
+ return &var_update_restrictions_;
+ }
+
private:
- FakeVariable<base::Time>
- var_updater_started_time_{ // NOLINT(whitespace/braces)
- "updater_started_time", kVariableModePoll};
- FakeVariable<base::Time> var_last_checked_time_{ // NOLINT(whitespace/braces)
- "last_checked_time", kVariableModePoll};
- FakeVariable<base::Time>
- var_update_completed_time_{ // NOLINT(whitespace/braces)
- "update_completed_time", kVariableModePoll};
- FakeVariable<double> var_progress_{ // NOLINT(whitespace/braces)
- "progress", kVariableModePoll};
- FakeVariable<Stage> var_stage_{ // NOLINT(whitespace/braces)
- "stage", kVariableModePoll};
- FakeVariable<std::string> var_new_version_{ // NOLINT(whitespace/braces)
- "new_version", kVariableModePoll};
- FakeVariable<int64_t> var_payload_size_{ // NOLINT(whitespace/braces)
- "payload_size", kVariableModePoll};
- FakeVariable<std::string> var_curr_channel_{ // NOLINT(whitespace/braces)
- "curr_channel", kVariableModePoll};
- FakeVariable<std::string> var_new_channel_{ // NOLINT(whitespace/braces)
- "new_channel", kVariableModePoll};
- FakeVariable<bool> var_p2p_enabled_{// NOLINT(whitespace/braces)
- "p2p_enabled",
- kVariableModeAsync};
- FakeVariable<bool> var_cellular_enabled_{// NOLINT(whitespace/braces)
- "cellular_enabled",
+ FakeVariable<base::Time> var_updater_started_time_{"updater_started_time",
+ kVariableModePoll};
+ FakeVariable<base::Time> var_last_checked_time_{"last_checked_time",
+ kVariableModePoll};
+ FakeVariable<base::Time> var_update_completed_time_{"update_completed_time",
+ kVariableModePoll};
+ FakeVariable<double> var_progress_{"progress", kVariableModePoll};
+ FakeVariable<Stage> var_stage_{"stage", kVariableModePoll};
+ FakeVariable<std::string> var_new_version_{"new_version", kVariableModePoll};
+ FakeVariable<uint64_t> var_payload_size_{"payload_size", kVariableModePoll};
+ FakeVariable<std::string> var_curr_channel_{"curr_channel",
+ kVariableModePoll};
+ FakeVariable<std::string> var_new_channel_{"new_channel", kVariableModePoll};
+ FakeVariable<bool> var_p2p_enabled_{"p2p_enabled", kVariableModeAsync};
+ FakeVariable<bool> var_cellular_enabled_{"cellular_enabled",
kVariableModeAsync};
- FakeVariable<unsigned int>
- var_consecutive_failed_update_checks_{ // NOLINT(whitespace/braces)
- "consecutive_failed_update_checks", kVariableModePoll};
- FakeVariable<unsigned int>
- var_server_dictated_poll_interval_{ // NOLINT(whitespace/braces)
- "server_dictated_poll_interval", kVariableModePoll};
- FakeVariable<UpdateRequestStatus>
- var_forced_update_requested_{ // NOLINT(whitespace/braces)
- "forced_update_requested", kVariableModeAsync};
+ FakeVariable<unsigned int> var_consecutive_failed_update_checks_{
+ "consecutive_failed_update_checks", kVariableModePoll};
+ FakeVariable<unsigned int> var_server_dictated_poll_interval_{
+ "server_dictated_poll_interval", kVariableModePoll};
+ FakeVariable<UpdateRequestStatus> var_forced_update_requested_{
+ "forced_update_requested", kVariableModeAsync};
+ FakeVariable<UpdateRestrictions> var_update_restrictions_{
+ "update_restrictions", kVariableModePoll};
DISALLOW_COPY_AND_ASSIGN(FakeUpdaterProvider);
};
diff --git a/update_manager/interactive_update_policy_impl.cc b/update_manager/interactive_update_policy_impl.cc
new file mode 100644
index 0000000..df7f17b
--- /dev/null
+++ b/update_manager/interactive_update_policy_impl.cc
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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 "update_engine/update_manager/interactive_update_policy_impl.h"
+
+namespace chromeos_update_manager {
+
+// Check to see if an interactive update was requested.
+EvalStatus InteractiveUpdatePolicyImpl::UpdateCheckAllowed(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ UpdateCheckParams* result) const {
+ UpdaterProvider* const updater_provider = state->updater_provider();
+
+ // First, check to see if an interactive update was requested.
+ const UpdateRequestStatus* forced_update_requested_p =
+ ec->GetValue(updater_provider->var_forced_update_requested());
+ if (forced_update_requested_p != nullptr &&
+ *forced_update_requested_p != UpdateRequestStatus::kNone) {
+ result->is_interactive =
+ (*forced_update_requested_p == UpdateRequestStatus::kInteractive);
+ LOG(INFO) << "Forced update signaled ("
+ << (result->is_interactive ? "interactive" : "periodic")
+ << "), allowing update check.";
+ return EvalStatus::kSucceeded;
+ }
+ return EvalStatus::kContinue;
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/interactive_update_policy_impl.h b/update_manager/interactive_update_policy_impl.h
new file mode 100644
index 0000000..a431456
--- /dev/null
+++ b/update_manager/interactive_update_policy_impl.h
@@ -0,0 +1,49 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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.
+//
+
+#ifndef UPDATE_ENGINE_UPDATE_MANAGER_INTERACTIVE_UPDATE_POLICY_IMPL_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_INTERACTIVE_UPDATE_POLICY_IMPL_H_
+
+#include <string>
+
+#include "update_engine/update_manager/policy_utils.h"
+
+namespace chromeos_update_manager {
+
+// Check to see if an interactive update was requested.
+class InteractiveUpdatePolicyImpl : public PolicyImplBase {
+ public:
+ InteractiveUpdatePolicyImpl() = default;
+ ~InteractiveUpdatePolicyImpl() override = default;
+
+ // Policy overrides.
+ EvalStatus UpdateCheckAllowed(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ UpdateCheckParams* result) const override;
+
+ protected:
+ std::string PolicyName() const override {
+ return "InteractiveUpdatePolicyImpl";
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InteractiveUpdatePolicyImpl);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // UPDATE_ENGINE_UPDATE_MANAGER_OFFICIAL_BUILD_CHECK_POLICY_IMPL_H_
diff --git a/update_manager/mock_policy.h b/update_manager/mock_policy.h
index 14470e9..8060bf8 100644
--- a/update_manager/mock_policy.h
+++ b/update_manager/mock_policy.h
@@ -36,6 +36,11 @@
testing::_))
.WillByDefault(testing::Invoke(
&default_policy_, &DefaultPolicy::UpdateCheckAllowed));
+ ON_CALL(*this,
+ UpdateCanBeApplied(
+ testing::_, testing::_, testing::_, testing::_, testing::_))
+ .WillByDefault(testing::Invoke(&default_policy_,
+ &DefaultPolicy::UpdateCanBeApplied));
ON_CALL(*this, UpdateCanStart(testing::_, testing::_, testing::_,
testing::_, testing::_))
.WillByDefault(testing::Invoke(
@@ -61,6 +66,13 @@
EvalStatus(EvaluationContext*, State*, std::string*,
UpdateCheckParams*));
+ MOCK_CONST_METHOD5(UpdateCanBeApplied,
+ EvalStatus(EvaluationContext*,
+ State*,
+ std::string*,
+ chromeos_update_engine::ErrorCode*,
+ chromeos_update_engine::InstallPlan*));
+
MOCK_CONST_METHOD5(UpdateCanStart,
EvalStatus(EvaluationContext*, State*, std::string*,
UpdateDownloadParams*, UpdateState));
diff --git a/update_manager/next_update_check_policy_impl.cc b/update_manager/next_update_check_policy_impl.cc
new file mode 100644
index 0000000..6f9748e
--- /dev/null
+++ b/update_manager/next_update_check_policy_impl.cc
@@ -0,0 +1,150 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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 "update_engine/update_manager/next_update_check_policy_impl.h"
+
+#include <algorithm>
+
+#include "update_engine/common/utils.h"
+
+using base::Time;
+using base::TimeDelta;
+using std::max;
+using std::string;
+
+namespace chromeos_update_manager {
+
+NextUpdateCheckTimePolicyImpl::NextUpdateCheckTimePolicyImpl(
+ const NextUpdateCheckPolicyConstants& constants)
+ : policy_constants_(constants) {}
+
+EvalStatus NextUpdateCheckTimePolicyImpl::UpdateCheckAllowed(
+ EvaluationContext* ec,
+ State* state,
+ string* error,
+ UpdateCheckParams* result) const {
+ // Ensure that periodic update checks are timed properly.
+ Time next_update_check;
+
+ if (NextUpdateCheckTime(
+ ec, state, error, &next_update_check, policy_constants_) !=
+ EvalStatus::kSucceeded) {
+ return EvalStatus::kFailed;
+ }
+ if (!ec->IsWallclockTimeGreaterThan(next_update_check)) {
+ LOG(INFO) << "Periodic check interval not satisfied, blocking until "
+ << chromeos_update_engine::utils::ToString(next_update_check);
+ return EvalStatus::kAskMeAgainLater;
+ }
+
+ return EvalStatus::kContinue;
+}
+
+EvalStatus NextUpdateCheckTimePolicyImpl::NextUpdateCheckTime(
+ EvaluationContext* ec,
+ State* state,
+ string* error,
+ Time* next_update_check,
+ const NextUpdateCheckPolicyConstants& constants) {
+ UpdaterProvider* const updater_provider = state->updater_provider();
+
+ // Don't check for updates too often. We limit the update checks to once every
+ // some interval. The interval is kTimeoutInitialInterval the first time and
+ // kTimeoutPeriodicInterval for the subsequent update checks. If the update
+ // check fails, we increase the interval between the update checks
+ // exponentially until kTimeoutMaxBackoffInterval. Finally, to avoid having
+ // many chromebooks running update checks at the exact same time, we add some
+ // fuzz to the interval.
+ const Time* updater_started_time =
+ ec->GetValue(updater_provider->var_updater_started_time());
+ POLICY_CHECK_VALUE_AND_FAIL(updater_started_time, error);
+
+ const Time* last_checked_time =
+ ec->GetValue(updater_provider->var_last_checked_time());
+
+ const auto* seed = ec->GetValue(state->random_provider()->var_seed());
+ POLICY_CHECK_VALUE_AND_FAIL(seed, error);
+
+ PRNG prng(*seed);
+
+ // If this is the first attempt, compute and return an initial value.
+ if (last_checked_time == nullptr ||
+ *last_checked_time < *updater_started_time) {
+ *next_update_check = *updater_started_time +
+ FuzzedInterval(&prng,
+ constants.timeout_initial_interval,
+ constants.timeout_regular_fuzz);
+ return EvalStatus::kSucceeded;
+ }
+
+ // Check whether the server is enforcing a poll interval; if not, this value
+ // will be zero.
+ const unsigned int* server_dictated_poll_interval =
+ ec->GetValue(updater_provider->var_server_dictated_poll_interval());
+ POLICY_CHECK_VALUE_AND_FAIL(server_dictated_poll_interval, error);
+
+ int interval = *server_dictated_poll_interval;
+ int fuzz = 0;
+
+ // If no poll interval was dictated by server compute a back-off period,
+ // starting from a predetermined base periodic interval and increasing
+ // exponentially by the number of consecutive failed attempts.
+ if (interval == 0) {
+ const unsigned int* consecutive_failed_update_checks =
+ ec->GetValue(updater_provider->var_consecutive_failed_update_checks());
+ POLICY_CHECK_VALUE_AND_FAIL(consecutive_failed_update_checks, error);
+
+ interval = constants.timeout_periodic_interval;
+ unsigned int num_failures = *consecutive_failed_update_checks;
+ while (interval < constants.timeout_max_backoff_interval && num_failures) {
+ interval *= 2;
+ num_failures--;
+ }
+ }
+
+ // We cannot back off longer than the predetermined maximum interval.
+ if (interval > constants.timeout_max_backoff_interval)
+ interval = constants.timeout_max_backoff_interval;
+
+ // We cannot back off shorter than the predetermined periodic interval. Also,
+ // in this case set the fuzz to a predetermined regular value.
+ if (interval <= constants.timeout_periodic_interval) {
+ interval = constants.timeout_periodic_interval;
+ fuzz = constants.timeout_regular_fuzz;
+ }
+
+ // If not otherwise determined, defer to a fuzz of +/-(interval / 2).
+ if (fuzz == 0)
+ fuzz = interval;
+
+ *next_update_check =
+ *last_checked_time + FuzzedInterval(&prng, interval, fuzz);
+ return EvalStatus::kSucceeded;
+}
+
+TimeDelta NextUpdateCheckTimePolicyImpl::FuzzedInterval(PRNG* prng,
+ int interval,
+ int fuzz) {
+ DCHECK_GE(interval, 0);
+ DCHECK_GE(fuzz, 0);
+ int half_fuzz = fuzz / 2;
+ // This guarantees the output interval is non negative.
+ int interval_min = max(interval - half_fuzz, 0);
+ int interval_max = interval + half_fuzz;
+ return TimeDelta::FromSeconds(prng->RandMinMax(interval_min, interval_max));
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/next_update_check_policy_impl.h b/update_manager/next_update_check_policy_impl.h
new file mode 100644
index 0000000..291ea0f
--- /dev/null
+++ b/update_manager/next_update_check_policy_impl.h
@@ -0,0 +1,98 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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.
+//
+
+#ifndef UPDATE_ENGINE_UPDATE_MANAGER_NEXT_UPDATE_CHECK_POLICY_IMPL_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_NEXT_UPDATE_CHECK_POLICY_IMPL_H_
+
+#include <string>
+
+#include <base/time/time.h>
+
+#include "update_engine/update_manager/policy_utils.h"
+#include "update_engine/update_manager/prng.h"
+
+namespace chromeos_update_manager {
+
+// Constants that are provided to the policy implementation.
+struct NextUpdateCheckPolicyConstants {
+ // Default update check timeout interval/fuzz values used to compute the
+ // NextUpdateCheckTime(), in seconds. Actual fuzz is within +/- half of the
+ // indicated value.
+ int timeout_initial_interval;
+ int timeout_periodic_interval;
+ int timeout_max_backoff_interval;
+ int timeout_regular_fuzz;
+
+ // Maximum update attempt backoff interval and fuzz.
+ int attempt_backoff_max_interval_in_days;
+ int attempt_backoff_fuzz_in_hours;
+};
+
+// Ensure that periodic update checks are timed properly.
+class NextUpdateCheckTimePolicyImpl : public PolicyImplBase {
+ public:
+ explicit NextUpdateCheckTimePolicyImpl(
+ const NextUpdateCheckPolicyConstants& constants);
+
+ // Policy overrides.
+ EvalStatus UpdateCheckAllowed(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ UpdateCheckParams* result) const override;
+
+ // A private policy implementation returning the wallclock timestamp when
+ // the next update check should happen.
+ // TODO(garnold) We should probably change that to infer a monotonic
+ // timestamp, which will make the update check intervals more resilient to
+ // clock skews. Might require switching some of the variables exported by the
+ // UpdaterProvider to report monotonic time, as well.
+ //
+ // NOTE:
+ // Exposed as a public static so that it's logic can be used to test
+ // Policy implementations that utilize this fragment for their
+ // timing, without needing to list them all with FRIEND_TEST (so that
+ // those Policy implementations can exist without modifying this
+ // class's definition.
+ //
+ // The output value from this method (|next_update_check|), isn't
+ // available via the UpdateCheckParams |result| of the Policy
+ // method, and so this timing logic needs to be otherwise exposed.
+ static EvalStatus NextUpdateCheckTime(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ base::Time* next_update_check,
+ const NextUpdateCheckPolicyConstants& constants);
+
+ // Returns a TimeDelta based on the provided |interval| seconds +/- half
+ // |fuzz| seconds. The return value is guaranteed to be a non-negative
+ // TimeDelta.
+ static base::TimeDelta FuzzedInterval(PRNG* prng, int interval, int fuzz);
+
+ protected:
+ std::string PolicyName() const override {
+ return "NextUpdateCheckTimePolicyImpl";
+ }
+
+ private:
+ const NextUpdateCheckPolicyConstants policy_constants_;
+
+ DISALLOW_COPY_AND_ASSIGN(NextUpdateCheckTimePolicyImpl);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // UPDATE_ENGINE_UPDATE_MANAGER_NEXT_UPDATE_CHECK_POLICY_IMPL_H_
diff --git a/update_manager/next_update_check_policy_impl_unittest.cc b/update_manager/next_update_check_policy_impl_unittest.cc
new file mode 100644
index 0000000..58aff66
--- /dev/null
+++ b/update_manager/next_update_check_policy_impl_unittest.cc
@@ -0,0 +1,163 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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 "update_engine/update_manager/next_update_check_policy_impl.h"
+
+#include <memory>
+
+#include "update_engine/update_manager/policy_test_utils.h"
+
+using base::Time;
+using base::TimeDelta;
+using std::string;
+
+namespace chromeos_update_manager {
+
+const NextUpdateCheckPolicyConstants policy_test_constants = {
+ // these are specifically NOT the values used by real Policy
+ // implementations.
+ .timeout_initial_interval = 3 * 60,
+ .timeout_periodic_interval = 2 * 60 * 60,
+ .timeout_max_backoff_interval = 8 * 60 * 60,
+ .timeout_regular_fuzz = 5 * 60,
+ .attempt_backoff_max_interval_in_days = 12,
+ .attempt_backoff_fuzz_in_hours = 10,
+};
+
+class UmNextUpdateCheckTimePolicyImplTest : public UmPolicyTestBase {
+ protected:
+ UmNextUpdateCheckTimePolicyImplTest() {
+ policy_ =
+ std::make_unique<NextUpdateCheckTimePolicyImpl>(policy_test_constants);
+ }
+};
+
+TEST_F(UmNextUpdateCheckTimePolicyImplTest,
+ FirstCheckIsAtMostInitialIntervalAfterStart) {
+ Time next_update_check;
+
+ // Set the last update time so it'll appear as if this is a first update check
+ // in the lifetime of the current updater.
+ fake_state_.updater_provider()->var_last_checked_time()->reset(
+ new Time(fake_clock_.GetWallclockTime() - TimeDelta::FromMinutes(10)));
+
+ CallMethodWithContext(&NextUpdateCheckTimePolicyImpl::NextUpdateCheckTime,
+ &next_update_check,
+ policy_test_constants);
+
+ EXPECT_LE(fake_clock_.GetWallclockTime(), next_update_check);
+ EXPECT_GE(fake_clock_.GetWallclockTime() +
+ TimeDelta::FromSeconds(
+ policy_test_constants.timeout_initial_interval +
+ policy_test_constants.timeout_regular_fuzz / 2),
+ next_update_check);
+}
+
+TEST_F(UmNextUpdateCheckTimePolicyImplTest, RecurringCheckBaseIntervalAndFuzz) {
+ // Ensure that we're using the correct interval (kPeriodicInterval) and fuzz
+ // (ktimeout_regular_fuzz) as base values for period updates.
+ Time next_update_check;
+
+ CallMethodWithContext(&NextUpdateCheckTimePolicyImpl::NextUpdateCheckTime,
+ &next_update_check,
+ policy_test_constants);
+
+ EXPECT_LE(fake_clock_.GetWallclockTime() +
+ TimeDelta::FromSeconds(
+ policy_test_constants.timeout_periodic_interval -
+ policy_test_constants.timeout_regular_fuzz / 2),
+ next_update_check);
+ EXPECT_GE(fake_clock_.GetWallclockTime() +
+ TimeDelta::FromSeconds(
+ policy_test_constants.timeout_periodic_interval +
+ policy_test_constants.timeout_regular_fuzz / 2),
+ next_update_check);
+}
+
+TEST_F(UmNextUpdateCheckTimePolicyImplTest,
+ RecurringCheckBackoffIntervalAndFuzz) {
+ // Ensure that we're properly backing off and fuzzing in the presence of
+ // failed updates attempts.
+ Time next_update_check;
+
+ fake_state_.updater_provider()->var_consecutive_failed_update_checks()->reset(
+ new unsigned int{2});
+
+ ExpectStatus(EvalStatus::kSucceeded,
+ NextUpdateCheckTimePolicyImpl::NextUpdateCheckTime,
+ &next_update_check,
+ policy_test_constants);
+
+ int expected_interval = policy_test_constants.timeout_periodic_interval * 4;
+ EXPECT_LE(
+ fake_clock_.GetWallclockTime() +
+ TimeDelta::FromSeconds(expected_interval - expected_interval / 2),
+ next_update_check);
+ EXPECT_GE(
+ fake_clock_.GetWallclockTime() +
+ TimeDelta::FromSeconds(expected_interval + expected_interval / 2),
+ next_update_check);
+}
+
+TEST_F(UmNextUpdateCheckTimePolicyImplTest,
+ RecurringCheckServerDictatedPollInterval) {
+ // Policy honors the server provided check poll interval.
+ Time next_update_check;
+
+ const auto kInterval = policy_test_constants.timeout_periodic_interval * 4;
+ fake_state_.updater_provider()->var_server_dictated_poll_interval()->reset(
+ new unsigned int(kInterval)); // NOLINT(readability/casting)
+ // We should not be backing off in this case.
+ fake_state_.updater_provider()->var_consecutive_failed_update_checks()->reset(
+ new unsigned int(2)); // NOLINT(readability/casting)
+
+ ExpectStatus(EvalStatus::kSucceeded,
+ &NextUpdateCheckTimePolicyImpl::NextUpdateCheckTime,
+ &next_update_check,
+ policy_test_constants);
+
+ EXPECT_LE(fake_clock_.GetWallclockTime() +
+ TimeDelta::FromSeconds(kInterval - kInterval / 2),
+ next_update_check);
+ EXPECT_GE(fake_clock_.GetWallclockTime() +
+ TimeDelta::FromSeconds(kInterval + kInterval / 2),
+ next_update_check);
+}
+
+TEST_F(UmNextUpdateCheckTimePolicyImplTest, ExponentialBackoffIsCapped) {
+ Time next_update_check;
+
+ fake_state_.updater_provider()->var_consecutive_failed_update_checks()->reset(
+ new unsigned int(100)); // NOLINT(readability/casting)
+
+ ExpectStatus(EvalStatus::kSucceeded,
+ &NextUpdateCheckTimePolicyImpl::NextUpdateCheckTime,
+ &next_update_check,
+ policy_test_constants);
+
+ EXPECT_LE(fake_clock_.GetWallclockTime() +
+ TimeDelta::FromSeconds(
+ policy_test_constants.timeout_max_backoff_interval -
+ policy_test_constants.timeout_max_backoff_interval / 2),
+ next_update_check);
+ EXPECT_GE(fake_clock_.GetWallclockTime() +
+ TimeDelta::FromSeconds(
+ policy_test_constants.timeout_max_backoff_interval +
+ policy_test_constants.timeout_max_backoff_interval / 2),
+ next_update_check);
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/official_build_check_policy_impl.cc b/update_manager/official_build_check_policy_impl.cc
new file mode 100644
index 0000000..096f7bf
--- /dev/null
+++ b/update_manager/official_build_check_policy_impl.cc
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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 "update_engine/update_manager/official_build_check_policy_impl.h"
+
+namespace chromeos_update_manager {
+
+// Unofficial builds should not perform periodic update checks.
+EvalStatus OnlyUpdateOfficialBuildsPolicyImpl::UpdateCheckAllowed(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ UpdateCheckParams* result) const {
+ const bool* is_official_build_p =
+ ec->GetValue(state->system_provider()->var_is_official_build());
+ if (is_official_build_p != nullptr && !(*is_official_build_p)) {
+ LOG(INFO) << "Unofficial build, blocking periodic update checks.";
+ return EvalStatus::kAskMeAgainLater;
+ }
+ return EvalStatus::kContinue;
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/official_build_check_policy_impl.h b/update_manager/official_build_check_policy_impl.h
new file mode 100644
index 0000000..6257209
--- /dev/null
+++ b/update_manager/official_build_check_policy_impl.h
@@ -0,0 +1,49 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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.
+//
+
+#ifndef UPDATE_ENGINE_UPDATE_MANAGER_OFFICIAL_BUILD_CHECK_POLICY_IMPL_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_OFFICIAL_BUILD_CHECK_POLICY_IMPL_H_
+
+#include <string>
+
+#include "update_engine/update_manager/policy_utils.h"
+
+namespace chromeos_update_manager {
+
+// Unofficial builds should not perform periodic update checks.
+class OnlyUpdateOfficialBuildsPolicyImpl : public PolicyImplBase {
+ public:
+ OnlyUpdateOfficialBuildsPolicyImpl() = default;
+ ~OnlyUpdateOfficialBuildsPolicyImpl() override = default;
+
+ // Policy overrides.
+ EvalStatus UpdateCheckAllowed(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ UpdateCheckParams* result) const override;
+
+ protected:
+ std::string PolicyName() const override {
+ return "OnlyUpdateOfficialBuildsPolicyImpl";
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OnlyUpdateOfficialBuildsPolicyImpl);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // UPDATE_ENGINE_UPDATE_MANAGER_OFFICIAL_BUILD_CHECK_POLICY_IMPL_H_
diff --git a/update_manager/policy.cc b/update_manager/policy.cc
index 151c225..5f79a68 100644
--- a/update_manager/policy.cc
+++ b/update_manager/policy.cc
@@ -30,6 +30,8 @@
return "kSucceeded";
case EvalStatus::kAskMeAgainLater:
return "kAskMeAgainLater";
+ case EvalStatus::kContinue:
+ return "kContinue";
}
return "Invalid";
}
diff --git a/update_manager/policy.h b/update_manager/policy.h
index fae1494..b60c4da 100644
--- a/update_manager/policy.h
+++ b/update_manager/policy.h
@@ -22,6 +22,7 @@
#include <vector>
#include "update_engine/common/error_code.h"
+#include "update_engine/payload_consumer/install_plan.h"
#include "update_engine/update_manager/evaluation_context.h"
#include "update_engine/update_manager/state.h"
@@ -32,6 +33,7 @@
kFailed,
kSucceeded,
kAskMeAgainLater,
+ kContinue,
};
std::string ToString(EvalStatus status);
@@ -204,6 +206,9 @@
if (reinterpret_cast<typeof(&Policy::UpdateCheckAllowed)>(
policy_method) == &Policy::UpdateCheckAllowed)
return class_name + "UpdateCheckAllowed";
+ if (reinterpret_cast<typeof(&Policy::UpdateCanBeApplied)>(policy_method) ==
+ &Policy::UpdateCanBeApplied)
+ return class_name + "UpdateCanBeApplied";
if (reinterpret_cast<typeof(&Policy::UpdateCanStart)>(
policy_method) == &Policy::UpdateCanStart)
return class_name + "UpdateCanStart";
@@ -235,6 +240,17 @@
EvaluationContext* ec, State* state, std::string* error,
UpdateCheckParams* result) const = 0;
+ // UpdateCanBeApplied returns whether the given |install_plan| can be acted
+ // on at this time. The reason for not applying is returned in |result|.
+ // The Policy may modify the passed-in |install_plan|, based on the
+ // implementation in the Policy and values provided by the EvaluationContext.
+ virtual EvalStatus UpdateCanBeApplied(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ chromeos_update_engine::ErrorCode* result,
+ chromeos_update_engine::InstallPlan* install_plan) const = 0;
+
// Returns EvalStatus::kSucceeded if either an update can start being
// processed, or the attempt needs to be aborted. In cases where the update
// needs to wait for some condition to be satisfied, but none of the values
diff --git a/update_manager/policy_test_utils.cc b/update_manager/policy_test_utils.cc
new file mode 100644
index 0000000..fbfcb82
--- /dev/null
+++ b/update_manager/policy_test_utils.cc
@@ -0,0 +1,110 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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 "update_engine/update_manager/policy_test_utils.h"
+
+#include <memory>
+#include <tuple>
+#include <vector>
+
+#include "update_engine/update_manager/next_update_check_policy_impl.h"
+
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_engine::ErrorCode;
+using std::string;
+using std::tuple;
+using std::vector;
+
+namespace chromeos_update_manager {
+
+void UmPolicyTestBase::SetUp() {
+ loop_.SetAsCurrent();
+ SetUpDefaultClock();
+ eval_ctx_ = new EvaluationContext(&fake_clock_, TimeDelta::FromSeconds(5));
+ SetUpDefaultState();
+}
+
+void UmPolicyTestBase::TearDown() {
+ EXPECT_FALSE(loop_.PendingTasks());
+}
+
+// Sets the clock to fixed values.
+void UmPolicyTestBase::SetUpDefaultClock() {
+ fake_clock_.SetMonotonicTime(Time::FromInternalValue(12345678L));
+ fake_clock_.SetWallclockTime(Time::FromInternalValue(12345678901234L));
+}
+
+void UmPolicyTestBase::SetUpDefaultState() {
+ fake_state_.updater_provider()->var_updater_started_time()->reset(
+ new Time(fake_clock_.GetWallclockTime()));
+ fake_state_.updater_provider()->var_last_checked_time()->reset(
+ new Time(fake_clock_.GetWallclockTime()));
+ fake_state_.updater_provider()->var_consecutive_failed_update_checks()->reset(
+ new unsigned int(0)); // NOLINT(readability/casting)
+ fake_state_.updater_provider()->var_server_dictated_poll_interval()->reset(
+ new unsigned int(0)); // NOLINT(readability/casting)
+ fake_state_.updater_provider()->var_forced_update_requested()->reset(
+ new UpdateRequestStatus{UpdateRequestStatus::kNone});
+
+ // Chosen by fair dice roll. Guaranteed to be random.
+ fake_state_.random_provider()->var_seed()->reset(new uint64_t(4));
+}
+
+// Returns a default UpdateState structure:
+UpdateState UmPolicyTestBase::GetDefaultUpdateState(
+ TimeDelta first_seen_period) {
+ Time first_seen_time = fake_clock_.GetWallclockTime() - first_seen_period;
+ UpdateState update_state = UpdateState();
+
+ // This is a non-interactive check returning a delta payload, seen for the
+ // first time (|first_seen_period| ago). Clearly, there were no failed
+ // attempts so far.
+ update_state.is_interactive = false;
+ update_state.is_delta_payload = false;
+ update_state.first_seen = first_seen_time;
+ update_state.num_checks = 1;
+ update_state.num_failures = 0;
+ update_state.failures_last_updated = Time(); // Needs to be zero.
+ // There's a single HTTP download URL with a maximum of 10 retries.
+ update_state.download_urls = vector<string>{"http://fake/url/"};
+ update_state.download_errors_max = 10;
+ // Download was never attempted.
+ update_state.last_download_url_idx = -1;
+ update_state.last_download_url_num_errors = 0;
+ // There were no download errors.
+ update_state.download_errors = vector<tuple<int, ErrorCode, Time>>();
+ // P2P is not disabled by Omaha.
+ update_state.p2p_downloading_disabled = false;
+ update_state.p2p_sharing_disabled = false;
+ // P2P was not attempted.
+ update_state.p2p_num_attempts = 0;
+ update_state.p2p_first_attempted = Time();
+ // No active backoff period, backoff is not disabled by Omaha.
+ update_state.backoff_expiry = Time();
+ update_state.is_backoff_disabled = false;
+ // There is no active scattering wait period (max 7 days allowed) nor check
+ // threshold (none allowed).
+ update_state.scatter_wait_period = TimeDelta();
+ update_state.scatter_check_threshold = 0;
+ update_state.scatter_wait_period_max = TimeDelta::FromDays(7);
+ update_state.scatter_check_threshold_min = 0;
+ update_state.scatter_check_threshold_max = 0;
+
+ return update_state;
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/policy_test_utils.h b/update_manager/policy_test_utils.h
new file mode 100644
index 0000000..5b93f7b
--- /dev/null
+++ b/update_manager/policy_test_utils.h
@@ -0,0 +1,99 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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.
+//
+
+#ifndef UPDATE_ENGINE_UPDATE_MANAGER_POLICY_TEST_UTILS_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_POLICY_TEST_UTILS_H_
+
+#include <memory>
+#include <string>
+
+#include <base/time/time.h>
+#include <brillo/message_loops/fake_message_loop.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/fake_clock.h"
+#include "update_engine/update_manager/evaluation_context.h"
+#include "update_engine/update_manager/fake_state.h"
+#include "update_engine/update_manager/policy_utils.h"
+
+namespace chromeos_update_manager {
+
+class UmPolicyTestBase : public ::testing::Test {
+ protected:
+ UmPolicyTestBase() = default;
+
+ void SetUp() override;
+
+ void TearDown() override;
+
+ // Sets the clock to fixed values.
+ virtual void SetUpDefaultClock();
+
+ // Sets up the default state in fake_state_. override to add Policy-specific
+ // items, but only after calling this class's implementation.
+ virtual void SetUpDefaultState();
+
+ // Returns a default UpdateState structure:
+ virtual UpdateState GetDefaultUpdateState(base::TimeDelta first_seen_period);
+
+ // Runs the passed |method| after resetting the EvaluationContext and expects
+ // it to return the |expected| return value.
+ template <typename T, typename R, typename... Args>
+ void ExpectStatus(EvalStatus expected, T method, R* result, Args... args) {
+ std::string error = "<None>";
+ eval_ctx_->ResetEvaluation();
+ EXPECT_EQ(expected,
+ (*method)(eval_ctx_.get(), &fake_state_, &error, result, args...))
+ << "Returned error: " << error
+ << "\nEvaluation context: " << eval_ctx_->DumpContext();
+ }
+
+ // Runs the passed |method| after resetting the EvaluationContext, in order
+ // to use the method to get a value for other testing (doesn't validate the
+ // return value, just returns it).
+ template <typename T, typename R, typename... Args>
+ EvalStatus CallMethodWithContext(T method, R* result, Args... args) {
+ std::string error = "<None>";
+ eval_ctx_->ResetEvaluation();
+ return (*method)(eval_ctx_.get(), &fake_state_, &error, result, args...);
+ }
+
+ // Runs the passed |policy_method| on the framework policy and expects it to
+ // return the |expected| return value.
+ template <typename T, typename R, typename... Args>
+ void ExpectPolicyStatus(EvalStatus expected,
+ T policy_method,
+ R* result,
+ Args... args) {
+ std::string error = "<None>";
+ eval_ctx_->ResetEvaluation();
+ EXPECT_EQ(expected,
+ (policy_.get()->*policy_method)(
+ eval_ctx_.get(), &fake_state_, &error, result, args...))
+ << "Returned error: " << error
+ << "\nEvaluation context: " << eval_ctx_->DumpContext();
+ }
+
+ brillo::FakeMessageLoop loop_{nullptr};
+ chromeos_update_engine::FakeClock fake_clock_;
+ FakeState fake_state_;
+ scoped_refptr<EvaluationContext> eval_ctx_;
+ std::unique_ptr<Policy> policy_;
+};
+
+} // namespace chromeos_update_manager
+
+#endif // UPDATE_ENGINE_UPDATE_MANAGER_POLICY_TEST_UTILS_H_
diff --git a/update_manager/policy_utils.h b/update_manager/policy_utils.h
index 960987e..eaf9ee9 100644
--- a/update_manager/policy_utils.h
+++ b/update_manager/policy_utils.h
@@ -17,6 +17,9 @@
#ifndef UPDATE_ENGINE_UPDATE_MANAGER_POLICY_UTILS_H_
#define UPDATE_ENGINE_UPDATE_MANAGER_POLICY_UTILS_H_
+#include <string>
+#include <vector>
+
#include "update_engine/update_manager/policy.h"
// Checks that the passed pointer value is not null, returning kFailed on the
@@ -35,4 +38,84 @@
} \
} while (false)
+namespace chromeos_update_manager {
+
+// Call the passed-in Policy method on a series of Policy implementations, until
+// one of them renders a decision by returning a value other than
+// |EvalStatus::kContinue|.
+template <typename T, typename R, typename... Args>
+EvalStatus ConsultPolicies(const std::vector<Policy const*> policies,
+ T policy_method,
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ R* result,
+ Args... args) {
+ for (auto policy : policies) {
+ EvalStatus status =
+ (policy->*policy_method)(ec, state, error, result, args...);
+ if (status != EvalStatus::kContinue) {
+ LOG(INFO) << "decision by " << policy->PolicyRequestName(policy_method);
+ return status;
+ }
+ }
+ return EvalStatus::kContinue;
+}
+
+// Base class implementation that returns |EvalStatus::kContinue| for all
+// decisions, to be used as a base-class for various Policy facets that only
+// pertain to certain situations. This might be better folded into Policy
+// instead of using pure-virtual methods on that class.
+class PolicyImplBase : public Policy {
+ public:
+ // Policy overrides.
+ EvalStatus UpdateCheckAllowed(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ UpdateCheckParams* result) const override {
+ return EvalStatus::kContinue;
+ };
+
+ EvalStatus UpdateCanBeApplied(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ chromeos_update_engine::ErrorCode* result,
+ chromeos_update_engine::InstallPlan* install_plan) const override {
+ return EvalStatus::kContinue;
+ };
+
+ EvalStatus UpdateCanStart(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ UpdateDownloadParams* result,
+ UpdateState update_state) const override {
+ return EvalStatus::kContinue;
+ };
+
+ EvalStatus UpdateDownloadAllowed(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ bool* result) const override {
+ return EvalStatus::kContinue;
+ };
+
+ EvalStatus P2PEnabled(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ bool* result) const override {
+ return EvalStatus::kContinue;
+ };
+
+ EvalStatus P2PEnabledChanged(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ bool* result,
+ bool prev_result) const override {
+ return EvalStatus::kContinue;
+ };
+};
+
+} // namespace chromeos_update_manager
+
#endif // UPDATE_ENGINE_UPDATE_MANAGER_POLICY_UTILS_H_
diff --git a/update_manager/real_shill_provider_unittest.cc b/update_manager/real_shill_provider_unittest.cc
index af674d0..6506923 100644
--- a/update_manager/real_shill_provider_unittest.cc
+++ b/update_manager/real_shill_provider_unittest.cc
@@ -94,7 +94,9 @@
now_exp.minute = 5;
now_exp.second = 33;
now_exp.millisecond = 675;
- return Time::FromLocalExploded(now_exp);
+ Time time;
+ ignore_result(Time::FromLocalExploded(now_exp, &time));
+ return time;
}
Time ConnChangedTime() {
diff --git a/update_manager/real_time_provider.cc b/update_manager/real_time_provider.cc
index ca3acad..db26816 100644
--- a/update_manager/real_time_provider.cc
+++ b/update_manager/real_time_provider.cc
@@ -43,7 +43,10 @@
Time::Exploded now_exp;
clock_->GetWallclockTime().LocalExplode(&now_exp);
now_exp.hour = now_exp.minute = now_exp.second = now_exp.millisecond = 0;
- return new Time(Time::FromLocalExploded(now_exp));
+ Time* now = new Time();
+ bool success = Time::FromLocalExploded(now_exp, now);
+ DCHECK(success);
+ return now;
}
private:
diff --git a/update_manager/real_time_provider_unittest.cc b/update_manager/real_time_provider_unittest.cc
index 0e1ef34..f8db30b 100644
--- a/update_manager/real_time_provider_unittest.cc
+++ b/update_manager/real_time_provider_unittest.cc
@@ -51,7 +51,9 @@
now_exp.minute = 5;
now_exp.second = 33;
now_exp.millisecond = 675;
- return Time::FromLocalExploded(now_exp);
+ Time time;
+ ignore_result(Time::FromLocalExploded(now_exp, &time));
+ return time;
}
FakeClock fake_clock_;
@@ -66,7 +68,8 @@
exploded.minute = 0;
exploded.second = 0;
exploded.millisecond = 0;
- const Time expected = Time::FromLocalExploded(exploded);
+ Time expected;
+ ignore_result(Time::FromLocalExploded(exploded, &expected));
fake_clock_.SetWallclockTime(now);
UmTestUtils::ExpectVariableHasValue(expected, provider_->var_curr_date());
diff --git a/update_manager/real_updater_provider.cc b/update_manager/real_updater_provider.cc
index 1a3e65a..050bd42 100644
--- a/update_manager/real_updater_provider.cc
+++ b/update_manager/real_updater_provider.cc
@@ -25,10 +25,12 @@
#include <base/time/time.h>
#include <update_engine/dbus-constants.h>
+#include "update_engine/client_library/include/update_engine/update_status.h"
#include "update_engine/common/clock_interface.h"
#include "update_engine/common/prefs.h"
#include "update_engine/omaha_request_params.h"
#include "update_engine/update_attempter.h"
+#include "update_engine/update_status_utils.h"
using base::StringPrintf;
using base::Time;
@@ -36,6 +38,8 @@
using chromeos_update_engine::OmahaRequestParams;
using chromeos_update_engine::SystemState;
using std::string;
+using update_engine::UpdateAttemptFlags;
+using update_engine::UpdateEngineStatus;
namespace chromeos_update_manager {
@@ -60,27 +64,32 @@
class GetStatusHelper {
public:
GetStatusHelper(SystemState* system_state, string* errmsg) {
- is_success_ = system_state->update_attempter()->GetStatus(
- &last_checked_time_, &progress_, &update_status_, &new_version_,
- &payload_size_);
- if (!is_success_ && errmsg)
+ is_success_ =
+ system_state->update_attempter()->GetStatus(&update_engine_status_);
+ if (!is_success_ && errmsg) {
*errmsg = "Failed to get a status update from the update engine";
+ }
}
inline bool is_success() { return is_success_; }
- inline int64_t last_checked_time() { return last_checked_time_; }
- inline double progress() { return progress_; }
- inline const string& update_status() { return update_status_; }
- inline const string& new_version() { return new_version_; }
- inline int64_t payload_size() { return payload_size_; }
+ inline int64_t last_checked_time() {
+ return update_engine_status_.last_checked_time;
+ }
+ inline double progress() { return update_engine_status_.progress; }
+ inline const string update_status() {
+ return chromeos_update_engine::UpdateStatusToString(
+ update_engine_status_.status);
+ }
+ inline const string& new_version() {
+ return update_engine_status_.new_version;
+ }
+ inline uint64_t payload_size() {
+ return update_engine_status_.new_size_bytes;
+ }
private:
bool is_success_;
- int64_t last_checked_time_;
- double progress_;
- string update_status_;
- string new_version_;
- int64_t payload_size_;
+ UpdateEngineStatus update_engine_status_;
};
// A variable reporting the time when a last update check was issued.
@@ -196,24 +205,18 @@
};
// A variable reporting the size of the update being processed in bytes.
-class PayloadSizeVariable : public UpdaterVariableBase<int64_t> {
+class PayloadSizeVariable : public UpdaterVariableBase<uint64_t> {
public:
PayloadSizeVariable(const string& name, SystemState* system_state)
- : UpdaterVariableBase<int64_t>(name, kVariableModePoll, system_state) {}
+ : UpdaterVariableBase<uint64_t>(name, kVariableModePoll, system_state) {}
private:
- const int64_t* GetValue(TimeDelta /* timeout */, string* errmsg) override {
+ const uint64_t* GetValue(TimeDelta /* timeout */, string* errmsg) override {
GetStatusHelper raw(system_state(), errmsg);
if (!raw.is_success())
return nullptr;
- if (raw.payload_size() < 0) {
- if (errmsg)
- *errmsg = string("Invalid payload size: %" PRId64, raw.payload_size());
- return nullptr;
- }
-
- return new int64_t(raw.payload_size());
+ return new uint64_t(raw.payload_size());
}
DISALLOW_COPY_AND_ASSIGN(PayloadSizeVariable);
@@ -414,40 +417,66 @@
DISALLOW_COPY_AND_ASSIGN(ForcedUpdateRequestedVariable);
};
+// A variable returning the current update restrictions that are in effect.
+class UpdateRestrictionsVariable
+ : public UpdaterVariableBase<UpdateRestrictions> {
+ public:
+ UpdateRestrictionsVariable(const string& name, SystemState* system_state)
+ : UpdaterVariableBase<UpdateRestrictions>(
+ name, kVariableModePoll, system_state) {}
+
+ private:
+ const UpdateRestrictions* GetValue(TimeDelta /* timeout */,
+ string* /* errmsg */) override {
+ UpdateAttemptFlags attempt_flags =
+ system_state()->update_attempter()->GetCurrentUpdateAttemptFlags();
+ UpdateRestrictions restriction_flags = UpdateRestrictions::kNone;
+ // Don't blindly copy the whole value, test and set bits that should
+ // transfer from one set of flags to the other.
+ if (attempt_flags & UpdateAttemptFlags::kFlagRestrictDownload) {
+ restriction_flags = static_cast<UpdateRestrictions>(
+ restriction_flags | UpdateRestrictions::kRestrictDownloading);
+ }
+
+ return new UpdateRestrictions(restriction_flags);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(UpdateRestrictionsVariable);
+};
+
// RealUpdaterProvider methods.
RealUpdaterProvider::RealUpdaterProvider(SystemState* system_state)
- : system_state_(system_state),
- var_updater_started_time_("updater_started_time",
- system_state->clock()->GetWallclockTime()),
- var_last_checked_time_(
- new LastCheckedTimeVariable("last_checked_time", system_state_)),
- var_update_completed_time_(
- new UpdateCompletedTimeVariable("update_completed_time",
- system_state_)),
- var_progress_(new ProgressVariable("progress", system_state_)),
- var_stage_(new StageVariable("stage", system_state_)),
- var_new_version_(new NewVersionVariable("new_version", system_state_)),
- var_payload_size_(new PayloadSizeVariable("payload_size", system_state_)),
- var_curr_channel_(new CurrChannelVariable("curr_channel", system_state_)),
- var_new_channel_(new NewChannelVariable("new_channel", system_state_)),
- var_p2p_enabled_(
- new BooleanPrefVariable("p2p_enabled", system_state_->prefs(),
- chromeos_update_engine::kPrefsP2PEnabled,
- false)),
- var_cellular_enabled_(
- new BooleanPrefVariable(
- "cellular_enabled", system_state_->prefs(),
- chromeos_update_engine::kPrefsUpdateOverCellularPermission,
- false)),
- var_consecutive_failed_update_checks_(
- new ConsecutiveFailedUpdateChecksVariable(
- "consecutive_failed_update_checks", system_state_)),
- var_server_dictated_poll_interval_(
- new ServerDictatedPollIntervalVariable(
- "server_dictated_poll_interval", system_state_)),
- var_forced_update_requested_(
- new ForcedUpdateRequestedVariable(
- "forced_update_requested", system_state_)) {}
-
+ : system_state_(system_state),
+ var_updater_started_time_("updater_started_time",
+ system_state->clock()->GetWallclockTime()),
+ var_last_checked_time_(
+ new LastCheckedTimeVariable("last_checked_time", system_state_)),
+ var_update_completed_time_(new UpdateCompletedTimeVariable(
+ "update_completed_time", system_state_)),
+ var_progress_(new ProgressVariable("progress", system_state_)),
+ var_stage_(new StageVariable("stage", system_state_)),
+ var_new_version_(new NewVersionVariable("new_version", system_state_)),
+ var_payload_size_(new PayloadSizeVariable("payload_size", system_state_)),
+ var_curr_channel_(new CurrChannelVariable("curr_channel", system_state_)),
+ var_new_channel_(new NewChannelVariable("new_channel", system_state_)),
+ var_p2p_enabled_(
+ new BooleanPrefVariable("p2p_enabled",
+ system_state_->prefs(),
+ chromeos_update_engine::kPrefsP2PEnabled,
+ false)),
+ var_cellular_enabled_(new BooleanPrefVariable(
+ "cellular_enabled",
+ system_state_->prefs(),
+ chromeos_update_engine::kPrefsUpdateOverCellularPermission,
+ false)),
+ var_consecutive_failed_update_checks_(
+ new ConsecutiveFailedUpdateChecksVariable(
+ "consecutive_failed_update_checks", system_state_)),
+ var_server_dictated_poll_interval_(new ServerDictatedPollIntervalVariable(
+ "server_dictated_poll_interval", system_state_)),
+ var_forced_update_requested_(new ForcedUpdateRequestedVariable(
+ "forced_update_requested", system_state_)),
+ var_update_restrictions_(new UpdateRestrictionsVariable(
+ "update_restrictions", system_state_)) {}
} // namespace chromeos_update_manager
diff --git a/update_manager/real_updater_provider.h b/update_manager/real_updater_provider.h
index b99bcc5..5e3e27b 100644
--- a/update_manager/real_updater_provider.h
+++ b/update_manager/real_updater_provider.h
@@ -64,7 +64,7 @@
return var_new_version_.get();
}
- Variable<int64_t>* var_payload_size() override {
+ Variable<uint64_t>* var_payload_size() override {
return var_payload_size_.get();
}
@@ -96,6 +96,10 @@
return var_forced_update_requested_.get();
}
+ Variable<UpdateRestrictions>* var_update_restrictions() override {
+ return var_update_restrictions_.get();
+ }
+
private:
// A pointer to the update engine's system state aggregator.
chromeos_update_engine::SystemState* system_state_;
@@ -107,7 +111,7 @@
std::unique_ptr<Variable<double>> var_progress_;
std::unique_ptr<Variable<Stage>> var_stage_;
std::unique_ptr<Variable<std::string>> var_new_version_;
- std::unique_ptr<Variable<int64_t>> var_payload_size_;
+ std::unique_ptr<Variable<uint64_t>> var_payload_size_;
std::unique_ptr<Variable<std::string>> var_curr_channel_;
std::unique_ptr<Variable<std::string>> var_new_channel_;
std::unique_ptr<Variable<bool>> var_p2p_enabled_;
@@ -115,6 +119,7 @@
std::unique_ptr<Variable<unsigned int>> var_consecutive_failed_update_checks_;
std::unique_ptr<Variable<unsigned int>> var_server_dictated_poll_interval_;
std::unique_ptr<Variable<UpdateRequestStatus>> var_forced_update_requested_;
+ std::unique_ptr<Variable<UpdateRestrictions>> var_update_restrictions_;
DISALLOW_COPY_AND_ASSIGN(RealUpdaterProvider);
};
diff --git a/update_manager/real_updater_provider_unittest.cc b/update_manager/real_updater_provider_unittest.cc
index 14eb30b..b653885 100644
--- a/update_manager/real_updater_provider_unittest.cc
+++ b/update_manager/real_updater_provider_unittest.cc
@@ -38,9 +38,11 @@
using chromeos_update_engine::OmahaRequestParams;
using std::string;
using std::unique_ptr;
+using testing::_;
+using testing::DoAll;
using testing::Return;
using testing::SetArgPointee;
-using testing::_;
+using update_engine::UpdateAttemptFlags;
namespace {
@@ -55,7 +57,9 @@
now_exp.minute = 5;
now_exp.second = 33;
now_exp.millisecond = 675;
- return Time::FromLocalExploded(now_exp);
+ Time time;
+ ignore_result(Time::FromLocalExploded(now_exp, &time));
+ return time;
}
// Rounds down a timestamp to the nearest second. This is useful when faking
@@ -64,7 +68,29 @@
Time::Exploded exp;
time.LocalExplode(&exp);
exp.millisecond = 0;
- return Time::FromLocalExploded(exp);
+ Time rounded_time;
+ ignore_result(Time::FromLocalExploded(exp, &rounded_time));
+ return rounded_time;
+}
+
+ACTION_P(ActionSetUpdateEngineStatusLastCheckedTime, time) {
+ arg0->last_checked_time = time;
+};
+
+ACTION_P(ActionSetUpdateEngineStatusProgress, progress) {
+ arg0->progress = progress;
+};
+
+ACTION_P(ActionSetUpdateEngineStatusStatus, status) {
+ arg0->status = status;
+}
+
+ACTION_P(ActionSetUpdateEngineStatusNewVersion, new_version) {
+ arg0->new_version = new_version;
+}
+
+ACTION_P(ActionSetUpdateEngineStatusNewSizeBytes, new_size_bytes) {
+ arg0->new_size_bytes = new_size_bytes;
}
} // namespace
@@ -116,225 +142,189 @@
}
TEST_F(UmRealUpdaterProviderTest, GetLastCheckedTimeOkay) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<0>(FixedTime().ToTimeT()), Return(true)));
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(
+ ActionSetUpdateEngineStatusLastCheckedTime(FixedTime().ToTimeT()),
+ Return(true)));
UmTestUtils::ExpectVariableHasValue(RoundedToSecond(FixedTime()),
provider_->var_last_checked_time());
}
TEST_F(UmRealUpdaterProviderTest, GetLastCheckedTimeFailNoValue) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
.WillOnce(Return(false));
UmTestUtils::ExpectVariableNotSet(provider_->var_last_checked_time());
}
TEST_F(UmRealUpdaterProviderTest, GetProgressOkayMin) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(0.0), Return(true)));
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(ActionSetUpdateEngineStatusProgress(0.0), Return(true)));
UmTestUtils::ExpectVariableHasValue(0.0, provider_->var_progress());
}
TEST_F(UmRealUpdaterProviderTest, GetProgressOkayMid) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(0.3), Return(true)));
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(ActionSetUpdateEngineStatusProgress(0.3), Return(true)));
UmTestUtils::ExpectVariableHasValue(0.3, provider_->var_progress());
}
TEST_F(UmRealUpdaterProviderTest, GetProgressOkayMax) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(1.0), Return(true)));
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(ActionSetUpdateEngineStatusProgress(1.0), Return(true)));
UmTestUtils::ExpectVariableHasValue(1.0, provider_->var_progress());
}
TEST_F(UmRealUpdaterProviderTest, GetProgressFailNoValue) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
.WillOnce(Return(false));
UmTestUtils::ExpectVariableNotSet(provider_->var_progress());
}
TEST_F(UmRealUpdaterProviderTest, GetProgressFailTooSmall) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(-2.0), Return(true)));
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(ActionSetUpdateEngineStatusProgress(-2.0), Return(true)));
UmTestUtils::ExpectVariableNotSet(provider_->var_progress());
}
TEST_F(UmRealUpdaterProviderTest, GetProgressFailTooBig) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(2.0), Return(true)));
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(ActionSetUpdateEngineStatusProgress(2.0), Return(true)));
UmTestUtils::ExpectVariableNotSet(provider_->var_progress());
}
TEST_F(UmRealUpdaterProviderTest, GetStageOkayIdle) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<2>(update_engine::kUpdateStatusIdle),
- Return(true)));
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(
+ ActionSetUpdateEngineStatusStatus(update_engine::UpdateStatus::IDLE),
+ Return(true)));
UmTestUtils::ExpectVariableHasValue(Stage::kIdle, provider_->var_stage());
}
TEST_F(UmRealUpdaterProviderTest, GetStageOkayCheckingForUpdate) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(
- SetArgPointee<2>(update_engine::kUpdateStatusCheckingForUpdate),
- Return(true)));
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(ActionSetUpdateEngineStatusStatus(
+ update_engine::UpdateStatus::CHECKING_FOR_UPDATE),
+ Return(true)));
UmTestUtils::ExpectVariableHasValue(Stage::kCheckingForUpdate,
provider_->var_stage());
}
TEST_F(UmRealUpdaterProviderTest, GetStageOkayUpdateAvailable) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(
- SetArgPointee<2>(update_engine::kUpdateStatusUpdateAvailable),
- Return(true)));
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(ActionSetUpdateEngineStatusStatus(
+ update_engine::UpdateStatus::UPDATE_AVAILABLE),
+ Return(true)));
UmTestUtils::ExpectVariableHasValue(Stage::kUpdateAvailable,
provider_->var_stage());
}
TEST_F(UmRealUpdaterProviderTest, GetStageOkayDownloading) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<2>(update_engine::kUpdateStatusDownloading),
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(ActionSetUpdateEngineStatusStatus(
+ update_engine::UpdateStatus::DOWNLOADING),
Return(true)));
UmTestUtils::ExpectVariableHasValue(Stage::kDownloading,
provider_->var_stage());
}
TEST_F(UmRealUpdaterProviderTest, GetStageOkayVerifying) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<2>(update_engine::kUpdateStatusVerifying),
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(ActionSetUpdateEngineStatusStatus(
+ update_engine::UpdateStatus::VERIFYING),
Return(true)));
UmTestUtils::ExpectVariableHasValue(Stage::kVerifying,
provider_->var_stage());
}
TEST_F(UmRealUpdaterProviderTest, GetStageOkayFinalizing) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<2>(update_engine::kUpdateStatusFinalizing),
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(ActionSetUpdateEngineStatusStatus(
+ update_engine::UpdateStatus::FINALIZING),
Return(true)));
UmTestUtils::ExpectVariableHasValue(Stage::kFinalizing,
provider_->var_stage());
}
TEST_F(UmRealUpdaterProviderTest, GetStageOkayUpdatedNeedReboot) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(
- SetArgPointee<2>(update_engine::kUpdateStatusUpdatedNeedReboot),
- Return(true)));
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(ActionSetUpdateEngineStatusStatus(
+ update_engine::UpdateStatus::UPDATED_NEED_REBOOT),
+ Return(true)));
UmTestUtils::ExpectVariableHasValue(Stage::kUpdatedNeedReboot,
provider_->var_stage());
}
TEST_F(UmRealUpdaterProviderTest, GetStageOkayReportingErrorEvent) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(
- SetArgPointee<2>(update_engine::kUpdateStatusReportingErrorEvent),
- Return(true)));
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(ActionSetUpdateEngineStatusStatus(
+ update_engine::UpdateStatus::REPORTING_ERROR_EVENT),
+ Return(true)));
UmTestUtils::ExpectVariableHasValue(Stage::kReportingErrorEvent,
provider_->var_stage());
}
TEST_F(UmRealUpdaterProviderTest, GetStageOkayAttemptingRollback) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(
- SetArgPointee<2>(update_engine::kUpdateStatusAttemptingRollback),
- Return(true)));
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(ActionSetUpdateEngineStatusStatus(
+ update_engine::UpdateStatus::ATTEMPTING_ROLLBACK),
+ Return(true)));
UmTestUtils::ExpectVariableHasValue(Stage::kAttemptingRollback,
provider_->var_stage());
}
TEST_F(UmRealUpdaterProviderTest, GetStageFailNoValue) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
.WillOnce(Return(false));
UmTestUtils::ExpectVariableNotSet(provider_->var_stage());
}
-TEST_F(UmRealUpdaterProviderTest, GetStageFailUnknown) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<2>("FooUpdateEngineState"),
- Return(true)));
- UmTestUtils::ExpectVariableNotSet(provider_->var_stage());
-}
-
-TEST_F(UmRealUpdaterProviderTest, GetStageFailEmpty) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<2>(""), Return(true)));
- UmTestUtils::ExpectVariableNotSet(provider_->var_stage());
-}
-
TEST_F(UmRealUpdaterProviderTest, GetNewVersionOkay) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<3>("1.2.0"), Return(true)));
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(
+ DoAll(ActionSetUpdateEngineStatusNewVersion("1.2.0"), Return(true)));
UmTestUtils::ExpectVariableHasValue(string("1.2.0"),
provider_->var_new_version());
}
TEST_F(UmRealUpdaterProviderTest, GetNewVersionFailNoValue) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
.WillOnce(Return(false));
UmTestUtils::ExpectVariableNotSet(provider_->var_new_version());
}
TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeOkayZero) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<4>(static_cast<int64_t>(0)), Return(true)));
- UmTestUtils::ExpectVariableHasValue(static_cast<int64_t>(0),
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(
+ ActionSetUpdateEngineStatusNewSizeBytes(static_cast<uint64_t>(0)),
+ Return(true)));
+ UmTestUtils::ExpectVariableHasValue(static_cast<uint64_t>(0),
provider_->var_payload_size());
}
TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeOkayArbitrary) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<4>(static_cast<int64_t>(567890)),
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(ActionSetUpdateEngineStatusNewSizeBytes(
+ static_cast<uint64_t>(567890)),
Return(true)));
- UmTestUtils::ExpectVariableHasValue(static_cast<int64_t>(567890),
+ UmTestUtils::ExpectVariableHasValue(static_cast<uint64_t>(567890),
provider_->var_payload_size());
}
TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeOkayTwoGigabytes) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<4>(static_cast<int64_t>(1) << 31),
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
+ .WillOnce(DoAll(ActionSetUpdateEngineStatusNewSizeBytes(
+ static_cast<uint64_t>(1) << 31),
Return(true)));
- UmTestUtils::ExpectVariableHasValue(static_cast<int64_t>(1) << 31,
+ UmTestUtils::ExpectVariableHasValue(static_cast<uint64_t>(1) << 31,
provider_->var_payload_size());
}
TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeFailNoValue) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetStatus(_))
.WillOnce(Return(false));
UmTestUtils::ExpectVariableNotSet(provider_->var_payload_size());
}
-TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeFailNegative) {
- EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
- GetStatus(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<4>(static_cast<int64_t>(-1024)),
- Return(true)));
- UmTestUtils::ExpectVariableNotSet(provider_->var_payload_size());
-}
-
TEST_F(UmRealUpdaterProviderTest, GetCurrChannelOkay) {
const string kChannelName("foo-channel");
OmahaRequestParams request_params(&fake_sys_state_);
@@ -440,4 +430,20 @@
kPollInterval, provider_->var_server_dictated_poll_interval());
}
+TEST_F(UmRealUpdaterProviderTest, GetUpdateRestrictions) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetCurrentUpdateAttemptFlags())
+ .WillRepeatedly(Return(UpdateAttemptFlags::kFlagRestrictDownload |
+ UpdateAttemptFlags::kFlagNonInteractive));
+ UmTestUtils::ExpectVariableHasValue(UpdateRestrictions::kRestrictDownloading,
+ provider_->var_update_restrictions());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetUpdateRestrictionsNone) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetCurrentUpdateAttemptFlags())
+ .WillRepeatedly(Return(UpdateAttemptFlags::kNone));
+ UmTestUtils::ExpectVariableHasValue(UpdateRestrictions::kNone,
+ provider_->var_update_restrictions());
+}
} // namespace chromeos_update_manager
diff --git a/update_manager/update_manager.cc b/update_manager/update_manager.cc
index 8e9b221..25f3216 100644
--- a/update_manager/update_manager.cc
+++ b/update_manager/update_manager.cc
@@ -16,7 +16,11 @@
#include "update_engine/update_manager/update_manager.h"
+#ifdef __ANDROID__
+#include "update_engine/update_manager/android_things_policy.h"
+#else
#include "update_engine/update_manager/chromeos_policy.h"
+#endif // __ANDROID__
#include "update_engine/update_manager/state.h"
namespace chromeos_update_manager {
@@ -28,9 +32,11 @@
evaluation_timeout_(evaluation_timeout),
expiration_timeout_(expiration_timeout),
weak_ptr_factory_(this) {
- // TODO(deymo): Make it possible to replace this policy with a different
- // implementation with a build-time flag.
+#ifdef __ANDROID__
+ policy_.reset(new AndroidThingsPolicy());
+#else
policy_.reset(new ChromeOSPolicy());
+#endif // __ANDROID__
}
UpdateManager::~UpdateManager() {
diff --git a/update_manager/update_manager_unittest.cc b/update_manager/update_manager_unittest.cc
index 03f1610..c2766ea 100644
--- a/update_manager/update_manager_unittest.cc
+++ b/update_manager/update_manager_unittest.cc
@@ -67,7 +67,9 @@
now_exp.minute = 5;
now_exp.second = 33;
now_exp.millisecond = 675;
- return Time::FromLocalExploded(now_exp);
+ Time time;
+ ignore_result(Time::FromLocalExploded(now_exp, &time));
+ return time;
}
} // namespace
diff --git a/update_manager/updater_provider.h b/update_manager/updater_provider.h
index 8048d38..cb62623 100644
--- a/update_manager/updater_provider.h
+++ b/update_manager/updater_provider.h
@@ -44,6 +44,12 @@
kPeriodic,
};
+// These enum values are a bit-field.
+enum UpdateRestrictions : int {
+ kNone,
+ kRestrictDownloading = (1 << 0),
+};
+
// Provider for Chrome OS update related information.
class UpdaterProvider : public Provider {
public:
@@ -79,7 +85,7 @@
// A variable returning the update payload size. The payload size is
// guaranteed to be non-negative.
- virtual Variable<int64_t>* var_payload_size() = 0;
+ virtual Variable<uint64_t>* var_payload_size() = 0;
// A variable returning the current channel.
virtual Variable<std::string>* var_curr_channel() = 0;
@@ -105,6 +111,10 @@
// scheduled update.
virtual Variable<UpdateRequestStatus>* var_forced_update_requested() = 0;
+ // A variable that returns the update restriction flags that are set
+ // for all updates.
+ virtual Variable<UpdateRestrictions>* var_update_restrictions() = 0;
+
protected:
UpdaterProvider() {}
diff --git a/update_metadata.proto b/update_metadata.proto
index ad377c7..b5d6c59 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -288,4 +288,8 @@
// array can have more than two partitions if needed, and they are identified
// by the partition name.
repeated PartitionUpdate partitions = 13;
+
+ // The maximum timestamp of the OS allowed to apply this payload.
+ // Can be used to prevent downgrading the OS.
+ optional int64 max_timestamp = 14;
}
diff --git a/update_status_utils.cc b/update_status_utils.cc
index 5de3381..ff039b8 100644
--- a/update_status_utils.cc
+++ b/update_status_utils.cc
@@ -30,8 +30,6 @@
return update_engine::kUpdateStatusCheckingForUpdate;
case UpdateStatus::UPDATE_AVAILABLE:
return update_engine::kUpdateStatusUpdateAvailable;
- case UpdateStatus::NEED_PERMISSION_TO_UPDATE:
- return update_engine::kUpdateStatusNeedPermissionToUpdate;
case UpdateStatus::DOWNLOADING:
return update_engine::kUpdateStatusDownloading;
case UpdateStatus::VERIFYING:
@@ -63,9 +61,6 @@
} else if (s == update_engine::kUpdateStatusUpdateAvailable) {
*status = UpdateStatus::UPDATE_AVAILABLE;
return true;
- } else if (s == update_engine::kUpdateStatusNeedPermissionToUpdate) {
- *status = UpdateStatus::NEED_PERMISSION_TO_UPDATE;
- return true;
} else if (s == update_engine::kUpdateStatusDownloading) {
*status = UpdateStatus::DOWNLOADING;
return true;
diff --git a/utils_android.cc b/utils_android.cc
index 38d62ea..393e65a 100644
--- a/utils_android.cc
+++ b/utils_android.cc
@@ -16,7 +16,6 @@
#include "update_engine/utils_android.h"
-#include <cutils/properties.h>
#include <fs_mgr.h>
using std::string;