summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/action_processor.cc4
-rw-r--r--common/action_processor.h7
-rw-r--r--common/http_fetcher_unittest.cc120
-rw-r--r--common/libcurl_http_fetcher.cc223
-rw-r--r--common/libcurl_http_fetcher.h14
-rw-r--r--mock_proxy_resolver.h38
-rw-r--r--payload_consumer/download_action.cc8
-rw-r--r--payload_consumer/download_action.h12
-rw-r--r--update_attempter_android.cc22
9 files changed, 294 insertions, 154 deletions
diff --git a/common/action_processor.cc b/common/action_processor.cc
index 2618e4e7..3549e080 100644
--- a/common/action_processor.cc
+++ b/common/action_processor.cc
@@ -60,6 +60,10 @@ void ActionProcessor::StopProcessing() {
<< (suspended_ ? " while suspended" : "");
current_action_ = nullptr;
suspended_ = false;
+ // Delete all the actions before calling the delegate.
+ for (auto action : actions_)
+ action->SetProcessor(nullptr);
+ actions_.clear();
if (delegate_)
delegate_->ProcessingStopped(this);
}
diff --git a/common/action_processor.h b/common/action_processor.h
index 050eee97..c9c179ea 100644
--- a/common/action_processor.h
+++ b/common/action_processor.h
@@ -51,8 +51,9 @@ class ActionProcessor {
virtual void StartProcessing();
// Aborts processing. If an Action is running, it will have
- // TerminateProcessing() called on it. The Action that was running
- // will be lost and must be re-enqueued if this Processor is to use it.
+ // TerminateProcessing() called on it. The Action that was running and all the
+ // remaining actions will be lost and must be re-enqueued if this Processor is
+ // to use it.
void StopProcessing();
// Suspend the processing. If an Action is running, it will have the
@@ -63,7 +64,7 @@ class ActionProcessor {
void SuspendProcessing();
// Resume the suspended processing. If the ActionProcessor is not suspended
- // or not running on the first place this method performs no action.
+ // or not running in the first place this method performs no action.
void ResumeProcessing();
// Returns true iff the processing was started but not yet completed nor
diff --git a/common/http_fetcher_unittest.cc b/common/http_fetcher_unittest.cc
index aaa538c0..54509583 100644
--- a/common/http_fetcher_unittest.cc
+++ b/common/http_fetcher_unittest.cc
@@ -48,6 +48,7 @@
#include "update_engine/common/multi_range_http_fetcher.h"
#include "update_engine/common/test_utils.h"
#include "update_engine/common/utils.h"
+#include "update_engine/mock_proxy_resolver.h"
#include "update_engine/proxy_resolver.h"
using brillo::MessageLoop;
@@ -56,6 +57,10 @@ using std::pair;
using std::string;
using std::unique_ptr;
using std::vector;
+using testing::DoAll;
+using testing::Return;
+using testing::SaveArg;
+using testing::_;
namespace {
@@ -193,14 +198,19 @@ class AnyHttpFetcherTest {
AnyHttpFetcherTest() {}
virtual ~AnyHttpFetcherTest() {}
- virtual HttpFetcher* NewLargeFetcher(size_t num_proxies) = 0;
+ virtual HttpFetcher* NewLargeFetcher(ProxyResolver* proxy_resolver) = 0;
+ HttpFetcher* NewLargeFetcher(size_t num_proxies) {
+ proxy_resolver_.set_num_proxies(num_proxies);
+ return NewLargeFetcher(&proxy_resolver_);
+ }
HttpFetcher* NewLargeFetcher() {
return NewLargeFetcher(1);
}
- virtual HttpFetcher* NewSmallFetcher(size_t num_proxies) = 0;
+ virtual HttpFetcher* NewSmallFetcher(ProxyResolver* proxy_resolver) = 0;
HttpFetcher* NewSmallFetcher() {
- return NewSmallFetcher(1);
+ proxy_resolver_.set_num_proxies(1);
+ return NewSmallFetcher(&proxy_resolver_);
}
virtual string BigUrl(in_port_t port) const { return kUnusedUrl; }
@@ -227,25 +237,16 @@ class MockHttpFetcherTest : public AnyHttpFetcherTest {
public:
// Necessary to unhide the definition in the base class.
using AnyHttpFetcherTest::NewLargeFetcher;
- HttpFetcher* NewLargeFetcher(size_t num_proxies) override {
+ HttpFetcher* NewLargeFetcher(ProxyResolver* proxy_resolver) override {
brillo::Blob big_data(1000000);
- CHECK_GT(num_proxies, 0u);
- proxy_resolver_.set_num_proxies(num_proxies);
return new MockHttpFetcher(
- big_data.data(),
- big_data.size(),
- reinterpret_cast<ProxyResolver*>(&proxy_resolver_));
+ big_data.data(), big_data.size(), proxy_resolver);
}
// Necessary to unhide the definition in the base class.
using AnyHttpFetcherTest::NewSmallFetcher;
- HttpFetcher* NewSmallFetcher(size_t num_proxies) override {
- CHECK_GT(num_proxies, 0u);
- proxy_resolver_.set_num_proxies(num_proxies);
- return new MockHttpFetcher(
- "x",
- 1,
- reinterpret_cast<ProxyResolver*>(&proxy_resolver_));
+ HttpFetcher* NewSmallFetcher(ProxyResolver* proxy_resolver) override {
+ return new MockHttpFetcher("x", 1, proxy_resolver);
}
bool IsMock() const override { return true; }
@@ -260,12 +261,9 @@ class LibcurlHttpFetcherTest : public AnyHttpFetcherTest {
public:
// Necessary to unhide the definition in the base class.
using AnyHttpFetcherTest::NewLargeFetcher;
- HttpFetcher* NewLargeFetcher(size_t num_proxies) override {
- CHECK_GT(num_proxies, 0u);
- proxy_resolver_.set_num_proxies(num_proxies);
- LibcurlHttpFetcher *ret = new
- LibcurlHttpFetcher(reinterpret_cast<ProxyResolver*>(&proxy_resolver_),
- &fake_hardware_);
+ HttpFetcher* NewLargeFetcher(ProxyResolver* proxy_resolver) override {
+ LibcurlHttpFetcher* ret =
+ new LibcurlHttpFetcher(proxy_resolver, &fake_hardware_);
// Speed up test execution.
ret->set_idle_seconds(1);
ret->set_retry_seconds(1);
@@ -275,8 +273,8 @@ class LibcurlHttpFetcherTest : public AnyHttpFetcherTest {
// Necessary to unhide the definition in the base class.
using AnyHttpFetcherTest::NewSmallFetcher;
- HttpFetcher* NewSmallFetcher(size_t num_proxies) override {
- return NewLargeFetcher(num_proxies);
+ HttpFetcher* NewSmallFetcher(ProxyResolver* proxy_resolver) override {
+ return NewLargeFetcher(proxy_resolver);
}
string BigUrl(in_port_t port) const override {
@@ -307,14 +305,9 @@ class MultiRangeHttpFetcherTest : public LibcurlHttpFetcherTest {
public:
// Necessary to unhide the definition in the base class.
using AnyHttpFetcherTest::NewLargeFetcher;
- HttpFetcher* NewLargeFetcher(size_t num_proxies) override {
- CHECK_GT(num_proxies, 0u);
- proxy_resolver_.set_num_proxies(num_proxies);
- ProxyResolver* resolver =
- reinterpret_cast<ProxyResolver*>(&proxy_resolver_);
- MultiRangeHttpFetcher *ret =
- new MultiRangeHttpFetcher(
- new LibcurlHttpFetcher(resolver, &fake_hardware_));
+ HttpFetcher* NewLargeFetcher(ProxyResolver* proxy_resolver) override {
+ MultiRangeHttpFetcher* ret = new MultiRangeHttpFetcher(
+ new LibcurlHttpFetcher(proxy_resolver, &fake_hardware_));
ret->ClearRanges();
ret->AddRange(0);
// Speed up test execution.
@@ -326,8 +319,8 @@ class MultiRangeHttpFetcherTest : public LibcurlHttpFetcherTest {
// Necessary to unhide the definition in the base class.
using AnyHttpFetcherTest::NewSmallFetcher;
- HttpFetcher* NewSmallFetcher(size_t num_proxies) override {
- return NewLargeFetcher(num_proxies);
+ HttpFetcher* NewSmallFetcher(ProxyResolver* proxy_resolver) override {
+ return NewLargeFetcher(proxy_resolver);
}
bool IsMulti() const override { return true; }
@@ -517,26 +510,51 @@ void UnpausingTimeoutCallback(PausingHttpFetcherTestDelegate* delegate,
} // namespace
TYPED_TEST(HttpFetcherTest, PauseTest) {
- {
- PausingHttpFetcherTestDelegate delegate;
- unique_ptr<HttpFetcher> fetcher(this->test_.NewLargeFetcher());
- delegate.paused_ = false;
- delegate.fetcher_ = fetcher.get();
- fetcher->set_delegate(&delegate);
+ PausingHttpFetcherTestDelegate delegate;
+ unique_ptr<HttpFetcher> fetcher(this->test_.NewLargeFetcher());
+ delegate.paused_ = false;
+ delegate.fetcher_ = fetcher.get();
+ fetcher->set_delegate(&delegate);
- unique_ptr<HttpServer> server(this->test_.CreateServer());
- ASSERT_TRUE(server->started_);
+ unique_ptr<HttpServer> server(this->test_.CreateServer());
+ ASSERT_TRUE(server->started_);
- MessageLoop::TaskId callback_id;
- callback_id = this->loop_.PostDelayedTask(
- FROM_HERE,
- base::Bind(&UnpausingTimeoutCallback, &delegate, &callback_id),
- base::TimeDelta::FromMilliseconds(200));
- fetcher->BeginTransfer(this->test_.BigUrl(server->GetPort()));
+ MessageLoop::TaskId callback_id;
+ callback_id = this->loop_.PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&UnpausingTimeoutCallback, &delegate, &callback_id),
+ base::TimeDelta::FromMilliseconds(200));
+ fetcher->BeginTransfer(this->test_.BigUrl(server->GetPort()));
- this->loop_.Run();
- EXPECT_TRUE(this->loop_.CancelTask(callback_id));
- }
+ this->loop_.Run();
+ EXPECT_TRUE(this->loop_.CancelTask(callback_id));
+}
+
+// This test will pause the fetcher while the download is not yet started
+// because it is waiting for the proxy to be resolved.
+TYPED_TEST(HttpFetcherTest, PauseWhileResolvingProxyTest) {
+ if (this->test_.IsMock())
+ return;
+ MockProxyResolver mock_resolver;
+ unique_ptr<HttpFetcher> fetcher(this->test_.NewLargeFetcher(&mock_resolver));
+
+ // Saved arguments from the proxy call.
+ ProxiesResolvedFn proxy_callback = nullptr;
+ void* proxy_data = nullptr;
+
+ EXPECT_CALL(mock_resolver, GetProxiesForUrl("http://fake_url", _, _))
+ .WillOnce(DoAll(
+ SaveArg<1>(&proxy_callback), SaveArg<2>(&proxy_data), Return(true)));
+ fetcher->BeginTransfer("http://fake_url");
+ testing::Mock::VerifyAndClearExpectations(&mock_resolver);
+
+ // Pausing and unpausing while resolving the proxy should not affect anything.
+ fetcher->Pause();
+ fetcher->Unpause();
+ fetcher->Pause();
+ // Proxy resolver comes back after we paused the fetcher.
+ ASSERT_TRUE(proxy_callback);
+ (*proxy_callback)({1, kNoProxy}, proxy_data);
}
namespace {
diff --git a/common/libcurl_http_fetcher.cc b/common/libcurl_http_fetcher.cc
index 761b74e1..789f46ef 100644
--- a/common/libcurl_http_fetcher.cc
+++ b/common/libcurl_http_fetcher.cc
@@ -99,6 +99,7 @@ void LibcurlHttpFetcher::ResumeTransfer(const string& url) {
curl_handle_ = curl_easy_init();
CHECK(curl_handle_);
+ ignore_failure_ = false;
CHECK(HasProxy());
bool is_direct = (GetCurrentProxy() == kNoProxy);
@@ -291,6 +292,12 @@ void LibcurlHttpFetcher::ProxiesResolved() {
http_response_code_ = 0;
terminate_requested_ = false;
sent_byte_ = false;
+
+ // If we are paused, we delay these two operations until Unpause is called.
+ if (transfer_paused_) {
+ restart_transfer_on_unpause_ = true;
+ return;
+ }
ResumeTransfer(url_);
CurlPerformOnce();
}
@@ -325,96 +332,110 @@ void LibcurlHttpFetcher::CurlPerformOnce() {
return;
}
}
- if (0 == running_handles) {
- GetHttpResponseCode();
- if (http_response_code_) {
- LOG(INFO) << "HTTP response code: " << http_response_code_;
- no_network_retry_count_ = 0;
- } else {
- LOG(ERROR) << "Unable to get http response code.";
- }
- // we're done!
- CleanUp();
-
- // TODO(petkov): This temporary code tries to deal with the case where the
- // update engine performs an update check while the network is not ready
- // (e.g., right after resume). Longer term, we should check if the network
- // is online/offline and return an appropriate error code.
- if (!sent_byte_ &&
- http_response_code_ == 0 &&
- no_network_retry_count_ < no_network_max_retries_) {
- no_network_retry_count_++;
- MessageLoop::current()->PostDelayedTask(
+ // If the transfer completes while paused, we should ignore the failure once
+ // the fetcher is unpaused.
+ if (running_handles == 0 && transfer_paused_ && !ignore_failure_) {
+ LOG(INFO) << "Connection closed while paused, ignoring failure.";
+ ignore_failure_ = true;
+ }
+
+ if (running_handles != 0 || transfer_paused_) {
+ // There's either more work to do or we are paused, so we just keep the
+ // file descriptors to watch up to date and exit, until we are done with the
+ // work and we are not paused.
+ SetupMessageLoopSources();
+ return;
+ }
+
+ // At this point, the transfer was completed in some way (error, connection
+ // closed or download finished).
+
+ GetHttpResponseCode();
+ if (http_response_code_) {
+ LOG(INFO) << "HTTP response code: " << http_response_code_;
+ no_network_retry_count_ = 0;
+ } else {
+ LOG(ERROR) << "Unable to get http response code.";
+ }
+
+ // we're done!
+ CleanUp();
+
+ // TODO(petkov): This temporary code tries to deal with the case where the
+ // update engine performs an update check while the network is not ready
+ // (e.g., right after resume). Longer term, we should check if the network
+ // is online/offline and return an appropriate error code.
+ if (!sent_byte_ &&
+ http_response_code_ == 0 &&
+ no_network_retry_count_ < no_network_max_retries_) {
+ no_network_retry_count_++;
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&LibcurlHttpFetcher::RetryTimeoutCallback,
+ base::Unretained(this)),
+ TimeDelta::FromSeconds(kNoNetworkRetrySeconds));
+ LOG(INFO) << "No HTTP response, retry " << no_network_retry_count_;
+ } else if ((!sent_byte_ && !IsHttpResponseSuccess()) ||
+ IsHttpResponseError()) {
+ // The transfer completed w/ error and we didn't get any bytes.
+ // If we have another proxy to try, try that.
+ //
+ // TODO(garnold) in fact there are two separate cases here: one case is an
+ // other-than-success return code (including no return code) and no
+ // received bytes, which is necessary due to the way callbacks are
+ // currently processing error conditions; the second is an explicit HTTP
+ // error code, where some data may have been received (as in the case of a
+ // semi-successful multi-chunk fetch). This is a confusing behavior and
+ // should be unified into a complete, coherent interface.
+ LOG(INFO) << "Transfer resulted in an error (" << http_response_code_
+ << "), " << bytes_downloaded_ << " bytes downloaded";
+
+ PopProxy(); // Delete the proxy we just gave up on.
+
+ if (HasProxy()) {
+ // We have another proxy. Retry immediately.
+ LOG(INFO) << "Retrying with next proxy setting";
+ MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&LibcurlHttpFetcher::RetryTimeoutCallback,
- base::Unretained(this)),
- TimeDelta::FromSeconds(kNoNetworkRetrySeconds));
- LOG(INFO) << "No HTTP response, retry " << no_network_retry_count_;
- return;
+ base::Unretained(this)));
+ } else {
+ // Out of proxies. Give up.
+ LOG(INFO) << "No further proxies, indicating transfer complete";
+ if (delegate_)
+ delegate_->TransferComplete(this, false); // signal fail
}
-
- if ((!sent_byte_ && !IsHttpResponseSuccess()) || IsHttpResponseError()) {
- // The transfer completed w/ error and we didn't get any bytes.
- // If we have another proxy to try, try that.
- //
- // TODO(garnold) in fact there are two separate cases here: one case is an
- // other-than-success return code (including no return code) and no
- // received bytes, which is necessary due to the way callbacks are
- // currently processing error conditions; the second is an explicit HTTP
- // error code, where some data may have been received (as in the case of a
- // semi-successful multi-chunk fetch). This is a confusing behavior and
- // should be unified into a complete, coherent interface.
- LOG(INFO) << "Transfer resulted in an error (" << http_response_code_
- << "), " << bytes_downloaded_ << " bytes downloaded";
-
- PopProxy(); // Delete the proxy we just gave up on.
-
- if (HasProxy()) {
- // We have another proxy. Retry immediately.
- LOG(INFO) << "Retrying with next proxy setting";
- MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&LibcurlHttpFetcher::RetryTimeoutCallback,
- base::Unretained(this)));
- } else {
- // Out of proxies. Give up.
- LOG(INFO) << "No further proxies, indicating transfer complete";
- if (delegate_)
- delegate_->TransferComplete(this, false); // signal fail
- }
- } else if ((transfer_size_ >= 0) && (bytes_downloaded_ < transfer_size_)) {
+ } else if ((transfer_size_ >= 0) && (bytes_downloaded_ < transfer_size_)) {
+ if (!ignore_failure_)
retry_count_++;
- LOG(INFO) << "Transfer interrupted after downloading "
- << bytes_downloaded_ << " of " << transfer_size_ << " bytes. "
- << transfer_size_ - bytes_downloaded_ << " bytes remaining "
- << "after " << retry_count_ << " attempt(s)";
-
- if (retry_count_ > max_retry_count_) {
- LOG(INFO) << "Reached max attempts (" << retry_count_ << ")";
- if (delegate_)
- delegate_->TransferComplete(this, false); // signal fail
- } else {
- // Need to restart transfer
- LOG(INFO) << "Restarting transfer to download the remaining bytes";
- MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::Bind(&LibcurlHttpFetcher::RetryTimeoutCallback,
- base::Unretained(this)),
- TimeDelta::FromSeconds(retry_seconds_));
- }
+ LOG(INFO) << "Transfer interrupted after downloading "
+ << bytes_downloaded_ << " of " << transfer_size_ << " bytes. "
+ << transfer_size_ - bytes_downloaded_ << " bytes remaining "
+ << "after " << retry_count_ << " attempt(s)";
+
+ if (retry_count_ > max_retry_count_) {
+ LOG(INFO) << "Reached max attempts (" << retry_count_ << ")";
+ if (delegate_)
+ delegate_->TransferComplete(this, false); // signal fail
} else {
- LOG(INFO) << "Transfer completed (" << http_response_code_
- << "), " << bytes_downloaded_ << " bytes downloaded";
- if (delegate_) {
- bool success = IsHttpResponseSuccess();
- delegate_->TransferComplete(this, success);
- }
+ // Need to restart transfer
+ LOG(INFO) << "Restarting transfer to download the remaining bytes";
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&LibcurlHttpFetcher::RetryTimeoutCallback,
+ base::Unretained(this)),
+ TimeDelta::FromSeconds(retry_seconds_));
}
} else {
- // set up callback
- SetupMessageLoopSources();
+ LOG(INFO) << "Transfer completed (" << http_response_code_
+ << "), " << bytes_downloaded_ << " bytes downloaded";
+ if (delegate_) {
+ bool success = IsHttpResponseSuccess();
+ delegate_->TransferComplete(this, success);
+ }
}
+ ignore_failure_ = false;
}
size_t LibcurlHttpFetcher::LibcurlWrite(void *ptr, size_t size, size_t nmemb) {
@@ -449,15 +470,43 @@ size_t LibcurlHttpFetcher::LibcurlWrite(void *ptr, size_t size, size_t nmemb) {
}
void LibcurlHttpFetcher::Pause() {
+ if (transfer_paused_) {
+ LOG(ERROR) << "Fetcher already paused.";
+ return;
+ }
+ transfer_paused_ = true;
+ if (!transfer_in_progress_) {
+ // If pause before we started a connection, we don't need to notify curl
+ // about that, we will simply not start the connection later.
+ return;
+ }
CHECK(curl_handle_);
- CHECK(transfer_in_progress_);
CHECK_EQ(curl_easy_pause(curl_handle_, CURLPAUSE_ALL), CURLE_OK);
}
void LibcurlHttpFetcher::Unpause() {
+ if (!transfer_paused_) {
+ LOG(ERROR) << "Resume attempted when fetcher not paused.";
+ return;
+ }
+ transfer_paused_ = false;
+ if (restart_transfer_on_unpause_) {
+ restart_transfer_on_unpause_ = false;
+ ResumeTransfer(url_);
+ CurlPerformOnce();
+ return;
+ }
+ if (!transfer_in_progress_) {
+ // If resumed before starting the connection, there's no need to notify
+ // anybody. We will simply start the connection once it is time.
+ return;
+ }
CHECK(curl_handle_);
- CHECK(transfer_in_progress_);
CHECK_EQ(curl_easy_pause(curl_handle_, CURLPAUSE_CONT), CURLE_OK);
+ // Since the transfer is in progress, we need to dispatch a CurlPerformOnce()
+ // now to let the connection continue, otherwise it would be called by the
+ // TimeoutCallback but with a delay.
+ CurlPerformOnce();
}
// This method sets up callbacks with the MessageLoop.
@@ -537,7 +586,7 @@ void LibcurlHttpFetcher::SetupMessageLoopSources() {
// Set up a timeout callback for libcurl.
if (timeout_id_ == MessageLoop::kTaskIdNull) {
- LOG(INFO) << "Setting up timeout source: " << idle_seconds_ << " seconds.";
+ VLOG(1) << "Setting up timeout source: " << idle_seconds_ << " seconds.";
timeout_id_ = MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&LibcurlHttpFetcher::TimeoutCallback,
@@ -547,6 +596,10 @@ void LibcurlHttpFetcher::SetupMessageLoopSources() {
}
void LibcurlHttpFetcher::RetryTimeoutCallback() {
+ if (transfer_paused_) {
+ restart_transfer_on_unpause_ = true;
+ return;
+ }
ResumeTransfer(url_);
CurlPerformOnce();
}
@@ -599,6 +652,8 @@ void LibcurlHttpFetcher::CleanUp() {
curl_multi_handle_ = nullptr;
}
transfer_in_progress_ = false;
+ transfer_paused_ = false;
+ restart_transfer_on_unpause_ = false;
}
void LibcurlHttpFetcher::GetHttpResponseCode() {
diff --git a/common/libcurl_http_fetcher.h b/common/libcurl_http_fetcher.h
index 66dbb18c..218e6cba 100644
--- a/common/libcurl_http_fetcher.h
+++ b/common/libcurl_http_fetcher.h
@@ -132,9 +132,9 @@ class LibcurlHttpFetcher : public HttpFetcher {
// Calls into curl_multi_perform to let libcurl do its work. Returns after
// curl_multi_perform is finished, which may actually be after more than
// one call to curl_multi_perform. This method will set up the message
- // loop with sources for future work that libcurl will do.
+ // loop with sources for future work that libcurl will do, if any, or complete
+ // the transfer and finish the action if no work left to do.
// This method will not block.
- // Returns true if we should resume immediately after this call.
void CurlPerformOnce();
// Sets up message loop sources as needed by libcurl. This is generally
@@ -192,6 +192,16 @@ class LibcurlHttpFetcher : public HttpFetcher {
brillo::MessageLoop::TaskId timeout_id_{brillo::MessageLoop::kTaskIdNull};
bool transfer_in_progress_{false};
+ bool transfer_paused_{false};
+
+ // Whether it should ignore transfer failures for the purpose of retrying the
+ // connection.
+ bool ignore_failure_{false};
+
+ // Whether we should restart the transfer once Unpause() is called. This can
+ // be caused because either the connection dropped while pause or the proxy
+ // was resolved and we never started the transfer in the first place.
+ bool restart_transfer_on_unpause_{false};
// The transfer size. -1 if not known.
off_t transfer_size_{0};
diff --git a/mock_proxy_resolver.h b/mock_proxy_resolver.h
new file mode 100644
index 00000000..0595f5ab
--- /dev/null
+++ b/mock_proxy_resolver.h
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2016 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_PROXY_RESOLVER_H_
+#define UPDATE_ENGINE_MOCK_PROXY_RESOLVER_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "update_engine/proxy_resolver.h"
+
+namespace chromeos_update_engine {
+
+class MockProxyResolver : public ProxyResolver {
+ public:
+ MOCK_METHOD3(GetProxiesForUrl,
+ bool(const std::string& url,
+ ProxiesResolvedFn callback,
+ void* data));
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_MOCK_PROXY_RESOLVER_H_
diff --git a/payload_consumer/download_action.cc b/payload_consumer/download_action.cc
index 6a71fdbe..fdbbd72e 100644
--- a/payload_consumer/download_action.cc
+++ b/payload_consumer/download_action.cc
@@ -232,6 +232,14 @@ void DownloadAction::PerformAction() {
http_fetcher_->BeginTransfer(install_plan_.download_url);
}
+void DownloadAction::SuspendAction() {
+ http_fetcher_->Pause();
+}
+
+void DownloadAction::ResumeAction() {
+ http_fetcher_->Unpause();
+}
+
void DownloadAction::TerminateProcessing() {
if (writer_) {
writer_->Close();
diff --git a/payload_consumer/download_action.h b/payload_consumer/download_action.h
index d000c670..fc320688 100644
--- a/payload_consumer/download_action.h
+++ b/payload_consumer/download_action.h
@@ -68,6 +68,9 @@ class PrefsInterface;
class DownloadAction : public InstallPlanAction,
public HttpFetcherDelegate {
public:
+ // Debugging/logging
+ static std::string StaticType() { return "DownloadAction"; }
+
// Takes ownership of the passed in HttpFetcher. Useful for testing.
// A good calling pattern is:
// DownloadAction(prefs, boot_contol, hardware, system_state,
@@ -78,8 +81,13 @@ class DownloadAction : public InstallPlanAction,
SystemState* system_state,
HttpFetcher* http_fetcher);
~DownloadAction() override;
+
+ // InstallPlanAction overrides.
void PerformAction() override;
+ void SuspendAction() override;
+ void ResumeAction() override;
void TerminateProcessing() override;
+ std::string Type() const override { return StaticType(); }
// Testing
void SetTestFileWriter(FileWriter* writer) {
@@ -88,10 +96,6 @@ class DownloadAction : public InstallPlanAction,
int GetHTTPResponseCode() { return http_fetcher_->http_response_code(); }
- // Debugging/logging
- static std::string StaticType() { return "DownloadAction"; }
- std::string Type() const override { return StaticType(); }
-
// HttpFetcherDelegate methods (see http_fetcher.h)
void ReceivedBytes(HttpFetcher* fetcher,
const void* bytes, size_t length) override;
diff --git a/update_attempter_android.cc b/update_attempter_android.cc
index c10b0803..6f88ee75 100644
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -173,6 +173,7 @@ bool UpdateAttempterAndroid::ApplyPayload(
SetupDownload();
cpu_limiter_.StartLimiter();
SetStatusAndNotify(UpdateStatus::UPDATE_AVAILABLE);
+ ongoing_update_ = true;
// 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.
@@ -181,23 +182,24 @@ bool UpdateAttempterAndroid::ApplyPayload(
}
bool UpdateAttempterAndroid::SuspendUpdate(brillo::ErrorPtr* error) {
- // TODO(deymo): Implement suspend/resume.
- return LogAndSetError(error, FROM_HERE, "Suspend/resume not implemented");
+ if (!ongoing_update_)
+ return LogAndSetError(error, FROM_HERE, "No ongoing update to suspend.");
+ processor_->SuspendProcessing();
+ return true;
}
bool UpdateAttempterAndroid::ResumeUpdate(brillo::ErrorPtr* error) {
- // TODO(deymo): Implement suspend/resume.
- return LogAndSetError(error, FROM_HERE, "Suspend/resume not implemented");
+ if (!ongoing_update_)
+ return LogAndSetError(error, FROM_HERE, "No ongoing update to resume.");
+ processor_->ResumeProcessing();
+ return true;
}
bool UpdateAttempterAndroid::CancelUpdate(brillo::ErrorPtr* error) {
- if (status_ == UpdateStatus::IDLE ||
- status_ == UpdateStatus::UPDATED_NEED_REBOOT) {
+ if (!ongoing_update_)
return LogAndSetError(error, FROM_HERE, "No ongoing update to cancel.");
- }
-
- // TODO(deymo): Implement cancel.
- return LogAndSetError(error, FROM_HERE, "Cancel not implemented");
+ processor_->StopProcessing();
+ return true;
}
bool UpdateAttempterAndroid::ResetStatus(brillo::ErrorPtr* error) {