Servicify unzipping and use it in the component updater.

Bug: 792066
Cq-Include-Trybots: master.tryserver.chromium.mac:ios-simulator-cronet;master.tryserver.chromium.mac:ios-simulator-full-configs
Change-Id: I910f5dde8cb7c071c4aeb6e3a0db04b301db1063
Reviewed-on: https://chromium-review.googlesource.com/809264
Reviewed-by: Colin Blundell <[email protected]>
Reviewed-by: Tom Sepez <[email protected]>
Reviewed-by: Ilya Sherman <[email protected]>
Reviewed-by: Nico Weber <[email protected]>
Reviewed-by: Rohit Rao <[email protected]>
Reviewed-by: Jay Civelli <[email protected]>
Reviewed-by: Cait Phillips <[email protected]>
Reviewed-by: Sorin Jianu <[email protected]>
Reviewed-by: Ken Rockot <[email protected]>
Commit-Queue: Joshua Pawlicki <[email protected]>
Cr-Commit-Position: refs/heads/master@{#538109}
diff --git a/components/component_updater/component_installer_unittest.cc b/components/component_updater/component_installer_unittest.cc
index 476ed96a..a1f6717 100644
--- a/components/component_updater/component_installer_unittest.cc
+++ b/components/component_updater/component_installer_unittest.cc
@@ -217,9 +217,10 @@
 }
 
 void ComponentInstallerTest::Unpack(const base::FilePath& crx_path) {
+  auto config = base::MakeRefCounted<TestConfigurator>();
   auto component_unpacker = base::MakeRefCounted<ComponentUnpacker>(
       std::vector<uint8_t>(std::begin(kSha256Hash), std::end(kSha256Hash)),
-      crx_path, nullptr, nullptr);
+      crx_path, nullptr, config->CreateServiceManagerConnector());
   component_unpacker->Unpack(base::BindOnce(
       &ComponentInstallerTest::UnpackComplete, base::Unretained(this)));
   RunThreads();
diff --git a/components/filesystem/public/interfaces/types.mojom b/components/filesystem/public/interfaces/types.mojom
index c3abf6c..7c8fff8 100644
--- a/components/filesystem/public/interfaces/types.mojom
+++ b/components/filesystem/public/interfaces/types.mojom
@@ -79,6 +79,8 @@
 const uint32 kFlagAppend = 0x80;
 // Deletes the file when closed.
 const uint32 kDeleteOnClose = 0x2000;
+// May change modified time of the file (used on Windows only).
+const uint32 kFlagWriteAttributes = 0x4000;
 
 // File types.
 //
diff --git a/components/unzip_service/BUILD.gn b/components/unzip_service/BUILD.gn
new file mode 100644
index 0000000..ea7e53f
--- /dev/null
+++ b/components/unzip_service/BUILD.gn
@@ -0,0 +1,32 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//services/service_manager/public/cpp/service.gni")
+import("//services/service_manager/public/service_manifest.gni")
+
+source_set("lib") {
+  sources = [
+    "unzip_service.cc",
+    "unzip_service.h",
+    "unzipper_impl.cc",
+    "unzipper_impl.h",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/public/cpp/bindings",
+    "//third_party/zlib/google:zip",
+  ]
+
+  public_deps = [
+    "//components/filesystem/public/interfaces",
+    "//components/unzip_service/public/interfaces",
+    "//services/service_manager/public/cpp",
+  ]
+}
+
+service_manifest("manifest") {
+  name = "unzip_service"
+  source = "manifest.json"
+}
diff --git a/components/unzip_service/DEPS b/components/unzip_service/DEPS
new file mode 100644
index 0000000..116747a
--- /dev/null
+++ b/components/unzip_service/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+  "+components/filesystem",
+  "+mojo/public",
+  "+services/service_manager/public",
+  "+third_party/zlib/google",
+]
diff --git a/components/unzip_service/OWNERS b/components/unzip_service/OWNERS
new file mode 100644
index 0000000..bd74fe69
--- /dev/null
+++ b/components/unzip_service/OWNERS
@@ -0,0 +1,4 @@
[email protected]
[email protected]
+per-file manifest.json=set noparent
+per-file manifest.json=file://ipc/SECURITY_OWNERS
diff --git a/components/unzip_service/manifest.json b/components/unzip_service/manifest.json
new file mode 100644
index 0000000..b21cb37
--- /dev/null
+++ b/components/unzip_service/manifest.json
@@ -0,0 +1,15 @@
+{
+  "name": "unzip_service",
+  "display_name": "Unzip Service",
+  "sandbox_type": "utility",
+  "interface_provider_specs": {
+    "service_manager:connector": {
+      "provides": {
+        "unzip_file": [ "unzip::mojom::Unzipper" ]
+       },
+       "requires": {
+         "service_manager": [ "service_manager:all_users" ]
+       }
+     }
+  }
+}
diff --git a/components/unzip_service/public/cpp/BUILD.gn b/components/unzip_service/public/cpp/BUILD.gn
new file mode 100644
index 0000000..83bc8ae
--- /dev/null
+++ b/components/unzip_service/public/cpp/BUILD.gn
@@ -0,0 +1,18 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+source_set("cpp") {
+  sources = [
+    "unzip.cc",
+    "unzip.h",
+  ]
+
+  public_deps = [
+    "//components/filesystem:lib",
+    "//components/unzip_service/public/interfaces",
+    "//services/service_manager/public/cpp",
+  ]
+}
diff --git a/components/unzip_service/public/cpp/unzip.cc b/components/unzip_service/public/cpp/unzip.cc
new file mode 100644
index 0000000..f30e8c6
--- /dev/null
+++ b/components/unzip_service/public/cpp/unzip.cc
@@ -0,0 +1,94 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/unzip_service/public/cpp/unzip.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string16.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "components/filesystem/directory_impl.h"
+#include "components/filesystem/lock_table.h"
+#include "components/unzip_service/public/interfaces/constants.mojom.h"
+#include "components/unzip_service/public/interfaces/unzipper.mojom.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "services/service_manager/public/cpp/connector.h"
+
+namespace unzip {
+
+namespace {
+
+class UnzipParams : public base::RefCounted<UnzipParams> {
+ public:
+  UnzipParams(mojom::UnzipperPtr unzipper, UnzipCallback callback)
+      : unzipper_(std::move(unzipper)), callback_(std::move(callback)) {}
+
+  mojom::UnzipperPtr* unzipper() { return &unzipper_; }
+
+  UnzipCallback TakeCallback() { return std::move(callback_); }
+
+ private:
+  friend class base::RefCounted<UnzipParams>;
+
+  ~UnzipParams() = default;
+
+  // The UnzipperPtr is stored so it does not get deleted before the callback
+  // runs.
+  mojom::UnzipperPtr unzipper_;
+
+  UnzipCallback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(UnzipParams);
+};
+
+void UnzipDone(scoped_refptr<UnzipParams> params, bool success) {
+  UnzipCallback cb = params->TakeCallback();
+  if (!cb.is_null())
+    std::move(cb).Run(success);
+}
+
+}  // namespace
+
+void Unzip(service_manager::Connector* connector,
+           const base::FilePath& zip_path,
+           const base::FilePath& output_dir,
+           UnzipCallback callback) {
+  DCHECK(!callback.is_null());
+
+  base::File zip_file(zip_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+
+  if (!zip_file.IsValid()) {
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), false));
+    return;
+  }
+
+  filesystem::mojom::DirectoryPtr directory_ptr;
+  mojo::MakeStrongBinding(
+      std::make_unique<filesystem::DirectoryImpl>(output_dir, nullptr, nullptr),
+      mojo::MakeRequest(&directory_ptr));
+
+  mojom::UnzipperPtr unzipper;
+  connector->BindInterface(mojom::kServiceName, mojo::MakeRequest(&unzipper));
+
+  // |callback| is shared between the connection error handler and the Unzip
+  // call using a refcounted UnzipParams object that owns |callback|.
+  auto unzip_params = base::MakeRefCounted<UnzipParams>(std::move(unzipper),
+                                                        std::move(callback));
+
+  unzip_params->unzipper()->set_connection_error_handler(
+      base::BindRepeating(&UnzipDone, unzip_params, false));
+  (*unzip_params->unzipper())
+      ->Unzip(std::move(zip_file), std::move(directory_ptr),
+              base::BindOnce(&UnzipDone, unzip_params));
+}
+
+}  // namespace unzip
diff --git a/components/unzip_service/public/cpp/unzip.h b/components/unzip_service/public/cpp/unzip.h
new file mode 100644
index 0000000..14b4b1f0
--- /dev/null
+++ b/components/unzip_service/public/cpp/unzip.h
@@ -0,0 +1,30 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UNZIP_SERVICE_PUBLIC_CPP_UNZIP_H_
+#define COMPONENTS_UNZIP_SERVICE_PUBLIC_CPP_UNZIP_H_
+
+#include "base/callback_forward.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace service_manager {
+class Connector;
+}
+
+namespace unzip {
+
+// Unzips |zip_file| into |output_dir|.
+using UnzipCallback = base::OnceCallback<void(bool result)>;
+// TODO(waffles): Unzip can only be called on blockable threads (never UI).
+void Unzip(service_manager::Connector* connector,
+           const base::FilePath& zip_file,
+           const base::FilePath& output_dir,
+           UnzipCallback result_callback);
+
+}  // namespace unzip
+
+#endif  // COMPONENTS_UNZIP_SERVICE_PUBLIC_CPP_UNZIP_H_
diff --git a/components/unzip_service/public/interfaces/BUILD.gn b/components/unzip_service/public/interfaces/BUILD.gn
new file mode 100644
index 0000000..d048a589
--- /dev/null
+++ b/components/unzip_service/public/interfaces/BUILD.gn
@@ -0,0 +1,23 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+  sources = [
+    "unzipper.mojom",
+  ]
+
+  public_deps = [
+    ":constants",
+    "//components/filesystem/public/interfaces",
+    "//mojo/common:common_custom_types",
+  ]
+}
+
+mojom("constants") {
+  sources = [
+    "constants.mojom",
+  ]
+}
diff --git a/components/unzip_service/public/interfaces/OWNERS b/components/unzip_service/public/interfaces/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/components/unzip_service/public/interfaces/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/components/unzip_service/public/interfaces/README.md b/components/unzip_service/public/interfaces/README.md
new file mode 100644
index 0000000..2c3d6a0
--- /dev/null
+++ b/components/unzip_service/public/interfaces/README.md
@@ -0,0 +1 @@
+Mojo service that exposes an interface that can be used to unzip ZIP files.
diff --git a/components/unzip_service/public/interfaces/constants.mojom b/components/unzip_service/public/interfaces/constants.mojom
new file mode 100644
index 0000000..6cf09d156
--- /dev/null
+++ b/components/unzip_service/public/interfaces/constants.mojom
@@ -0,0 +1,7 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module unzip.mojom;
+
+const string kServiceName = "unzip_service";
diff --git a/components/unzip_service/public/interfaces/unzipper.mojom b/components/unzip_service/public/interfaces/unzipper.mojom
new file mode 100644
index 0000000..19e2153
--- /dev/null
+++ b/components/unzip_service/public/interfaces/unzipper.mojom
@@ -0,0 +1,15 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module unzip.mojom;
+
+import "mojo/common/file.mojom";
+import "components/filesystem/public/interfaces/directory.mojom";
+
+interface Unzipper {
+  // Unzip |zip_file| into |output_dir|.
+  // Returns true on success, false otherwise.
+  Unzip(mojo.common.mojom.File zip_file, filesystem.mojom.Directory output_dir)
+      => (bool result);
+};
diff --git a/components/unzip_service/unzip_service.cc b/components/unzip_service/unzip_service.cc
new file mode 100644
index 0000000..dc38d52
--- /dev/null
+++ b/components/unzip_service/unzip_service.cc
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/unzip_service/unzip_service.h"
+
+#include "build/build_config.h"
+#include "components/unzip_service/unzipper_impl.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace unzip {
+
+namespace {
+
+void OnUnzipRequest(service_manager::ServiceContextRefFactory* ref_factory,
+                    unzip::mojom::UnzipperRequest request) {
+  mojo::MakeStrongBinding(
+      std::make_unique<UnzipperImpl>(ref_factory->CreateRef()),
+      std::move(request));
+}
+
+}  // namespace
+
+UnzipService::UnzipService() = default;
+
+UnzipService::~UnzipService() = default;
+
+std::unique_ptr<service_manager::Service> UnzipService::CreateService() {
+  return std::make_unique<UnzipService>();
+}
+
+void UnzipService::OnStart() {
+  ref_factory_ = std::make_unique<service_manager::ServiceContextRefFactory>(
+      context()->CreateQuitClosure());
+  registry_.AddInterface(
+      base::BindRepeating(&OnUnzipRequest, ref_factory_.get()));
+}
+
+void UnzipService::OnBindInterface(
+    const service_manager::BindSourceInfo& source_info,
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle interface_pipe) {
+  registry_.BindInterface(interface_name, std::move(interface_pipe));
+}
+
+}  //  namespace unzip
diff --git a/components/unzip_service/unzip_service.h b/components/unzip_service/unzip_service.h
new file mode 100644
index 0000000..bca9f89a
--- /dev/null
+++ b/components/unzip_service/unzip_service.h
@@ -0,0 +1,39 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UNZIP_SERVICE_UNZIP_SERVICE_H_
+#define COMPONENTS_UNZIP_SERVICE_UNZIP_SERVICE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+#include "services/service_manager/public/cpp/service_context.h"
+#include "services/service_manager/public/cpp/service_context_ref.h"
+
+namespace unzip {
+
+class UnzipService : public service_manager::Service {
+ public:
+  UnzipService();
+  ~UnzipService() override;
+  // Factory method for creating the service.
+  static std::unique_ptr<service_manager::Service> CreateService();
+  // Lifescycle events that occur after the service has started to spinup.
+  void OnStart() override;
+  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
+                       const std::string& interface_name,
+                       mojo::ScopedMessagePipeHandle interface_pipe) override;
+
+ private:
+  // State needed to manage service lifecycle and lifecycle of bound clients.
+  std::unique_ptr<service_manager::ServiceContextRefFactory> ref_factory_;
+  service_manager::BinderRegistry registry_;
+  DISALLOW_COPY_AND_ASSIGN(UnzipService);
+};
+
+}  // namespace unzip
+
+#endif  // COMPONENTS_UNZIP_SERVICE_UNZIP_SERVICE_H_
diff --git a/components/unzip_service/unzipper_impl.cc b/components/unzip_service/unzipper_impl.cc
new file mode 100644
index 0000000..09001109
--- /dev/null
+++ b/components/unzip_service/unzipper_impl.cc
@@ -0,0 +1,107 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/unzip_service/unzipper_impl.h"
+
+#include "base/compiler_specific.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "third_party/zlib/google/zip.h"
+#include "third_party/zlib/google/zip_reader.h"
+
+namespace unzip {
+
+namespace {
+
+// A writer delegate that reports errors instead of writing.
+class DudWriterDelegate : public zip::WriterDelegate {
+ public:
+  DudWriterDelegate() {}
+  ~DudWriterDelegate() override {}
+
+  // WriterDelegate methods:
+  bool PrepareOutput() override { return false; }
+  bool WriteBytes(const char* data, int num_bytes) override { return false; }
+  void SetTimeModified(const base::Time& time) override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DudWriterDelegate);
+};
+
+std::string PathToMojoString(const base::FilePath& path) {
+#if defined(OS_WIN)
+  return base::WideToUTF8(path.value());
+#else
+  return path.value();
+#endif
+}
+
+// Modifies output_dir to point to the final directory.
+bool CreateDirectory(filesystem::mojom::DirectoryPtr* output_dir,
+                     const base::FilePath& path) {
+  filesystem::mojom::FileError err = filesystem::mojom::FileError::OK;
+  return (*output_dir)
+             ->OpenDirectory(PathToMojoString(path), nullptr,
+                             filesystem::mojom::kFlagOpenAlways, &err) &&
+         err == filesystem::mojom::FileError::OK;
+}
+
+std::unique_ptr<zip::WriterDelegate> MakeFileWriterDelegateNoParent(
+    filesystem::mojom::DirectoryPtr* output_dir,
+    const base::FilePath& path) {
+  auto file = std::make_unique<base::File>();
+  filesystem::mojom::FileError err;
+  if (!(*output_dir)
+           ->OpenFileHandle(PathToMojoString(path),
+                            filesystem::mojom::kFlagCreate |
+                                filesystem::mojom::kFlagWrite |
+                                filesystem::mojom::kFlagWriteAttributes,
+                            &err, file.get()) ||
+      err != filesystem::mojom::FileError::OK) {
+    return std::make_unique<DudWriterDelegate>();
+  }
+  return std::make_unique<zip::FileWriterDelegate>(std::move(file));
+}
+
+std::unique_ptr<zip::WriterDelegate> MakeFileWriterDelegate(
+    filesystem::mojom::DirectoryPtr* output_dir,
+    const base::FilePath& path) {
+  if (path == path.BaseName())
+    return MakeFileWriterDelegateNoParent(output_dir, path);
+  filesystem::mojom::DirectoryPtr parent;
+  filesystem::mojom::FileError err;
+  if (!(*output_dir)
+           ->OpenDirectory(PathToMojoString(path.DirName()),
+                           mojo::MakeRequest(&parent),
+                           filesystem::mojom::kFlagOpenAlways, &err) ||
+      err != filesystem::mojom::FileError::OK) {
+    return std::make_unique<DudWriterDelegate>();
+  }
+  return MakeFileWriterDelegateNoParent(&parent, path.BaseName());
+}
+
+bool FilterNoFiles(const base::FilePath& unused) {
+  return true;
+}
+
+}  // namespace
+
+UnzipperImpl::UnzipperImpl(
+    std::unique_ptr<service_manager::ServiceContextRef> service_ref)
+    : service_ref_(std::move(service_ref)) {}
+
+UnzipperImpl::~UnzipperImpl() = default;
+
+void UnzipperImpl::Unzip(base::File zip_file,
+                         filesystem::mojom::DirectoryPtr output_dir,
+                         UnzipCallback callback) {
+  DCHECK(zip_file.IsValid());
+  std::move(callback).Run(zip::UnzipWithFilterAndWriters(
+      zip_file.GetPlatformFile(),
+      base::BindRepeating(&MakeFileWriterDelegate, &output_dir),
+      base::BindRepeating(&CreateDirectory, &output_dir),
+      base::BindRepeating(&FilterNoFiles), false));
+}
+
+}  // namespace unzip
diff --git a/components/unzip_service/unzipper_impl.h b/components/unzip_service/unzipper_impl.h
new file mode 100644
index 0000000..6bb6d34
--- /dev/null
+++ b/components/unzip_service/unzipper_impl.h
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UNZIP_SERVICE_UNZIPPER_IMPL_H_
+#define COMPONENTS_UNZIP_SERVICE_UNZIPPER_IMPL_H_
+
+#include <memory>
+
+#include "base/files/file.h"
+#include "base/macros.h"
+#include "components/unzip_service/public/interfaces/unzipper.mojom.h"
+#include "services/service_manager/public/cpp/service_context_ref.h"
+
+namespace unzip {
+
+class UnzipperImpl : public mojom::Unzipper {
+ public:
+  explicit UnzipperImpl(
+      std::unique_ptr<service_manager::ServiceContextRef> service_ref);
+  ~UnzipperImpl() override;
+
+ private:
+  // unzip::mojom::Unzipper:
+  void Unzip(base::File zip_file,
+             filesystem::mojom::DirectoryPtr output_dir,
+             UnzipCallback callback) override;
+
+  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
+
+  DISALLOW_COPY_AND_ASSIGN(UnzipperImpl);
+};
+
+}  // namespace unzip
+
+#endif  // COMPONENTS_UNZIP_SERVICE_UNZIPPER_IMPL_H_
diff --git a/components/update_client/BUILD.gn b/components/update_client/BUILD.gn
index 675c7be0..c912252 100644
--- a/components/update_client/BUILD.gn
+++ b/components/update_client/BUILD.gn
@@ -69,12 +69,12 @@
     "//components/data_use_measurement/core",
     "//components/patch_service/public/cpp",
     "//components/prefs",
+    "//components/unzip_service/public/cpp",
     "//components/version_info:version_info",
     "//courgette:courgette_lib",
     "//crypto",
     "//net",
     "//third_party/libxml",
-    "//third_party/zlib/google:zip",
     "//url",
   ]
   libs = []
@@ -97,12 +97,16 @@
   public_deps = [
     ":update_client",
   ]
+
   deps = [
     "//base",
     "//components/patch_service:lib",
     "//components/prefs",
+    "//components/unzip_service:lib",
+    "//mojo/public/cpp/bindings",
     "//net:test_support",
-    "//services/service_manager/public/cpp/test:test_support",
+    "//services/service_manager/public/cpp",
+    "//services/service_manager/public/mojom",
     "//testing/gmock",
     "//testing/gtest",
     "//url",
diff --git a/components/update_client/DEPS b/components/update_client/DEPS
index 30e7de9..64005e1a 100644
--- a/components/update_client/DEPS
+++ b/components/update_client/DEPS
@@ -4,10 +4,12 @@
   "+components/data_use_measurement/core",
   "+components/patch_service",
   "+components/prefs",
+  "+components/unzip_service",
   "+components/version_info",
   "+courgette",
   "+crypto",
   "+libxml",
+  "+mojo",
   "+net",
   "+services/service_manager/public",
   "+third_party/libxml",
diff --git a/components/update_client/action_runner.cc b/components/update_client/action_runner.cc
index be8706c4..ded657e 100644
--- a/components/update_client/action_runner.cc
+++ b/components/update_client/action_runner.cc
@@ -19,6 +19,7 @@
 #include "components/update_client/configurator.h"
 #include "components/update_client/task_traits.h"
 #include "components/update_client/update_client.h"
+#include "components/update_client/update_engine.h"
 
 namespace update_client {
 
@@ -36,11 +37,14 @@
   run_complete_ = std::move(run_complete);
 
   base::CreateSequencedTaskRunnerWithTraits(kTaskTraits)
-      ->PostTask(FROM_HERE,
-                 base::BindOnce(&ActionRunner::Unpack, base::Unretained(this)));
+      ->PostTask(
+          FROM_HERE,
+          base::BindOnce(&ActionRunner::Unpack, base::Unretained(this),
+                         component_.config()->CreateServiceManagerConnector()));
 }
 
-void ActionRunner::Unpack() {
+void ActionRunner::Unpack(
+    std::unique_ptr<service_manager::Connector> connector) {
   const auto& installer = component_.crx_component().installer;
 
   base::FilePath file_path;
@@ -48,8 +52,8 @@
 
   // Contains the key hash of the CRX this object is allowed to run.
   const auto key_hash = component_.config()->GetRunActionKeyHash();
-  auto unpacker = base::MakeRefCounted<ComponentUnpacker>(key_hash, file_path,
-                                                          installer, nullptr);
+  auto unpacker = base::MakeRefCounted<ComponentUnpacker>(
+      key_hash, file_path, installer, std::move(connector));
   unpacker->Unpack(
       base::BindOnce(&ActionRunner::UnpackComplete, base::Unretained(this)));
 }
diff --git a/components/update_client/action_runner.h b/components/update_client/action_runner.h
index 877d9c2..b63571c 100644
--- a/components/update_client/action_runner.h
+++ b/components/update_client/action_runner.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <memory>
 #include <utility>
 #include <vector>
 
@@ -38,7 +39,7 @@
   void Run(Callback run_complete);
 
  private:
-  void Unpack();
+  void Unpack(std::unique_ptr<service_manager::Connector> connector);
   void UnpackComplete(const ComponentUnpacker::Result& result);
 
   void RunCommand(const base::CommandLine& cmdline);
diff --git a/components/update_client/component_unpacker.cc b/components/update_client/component_unpacker.cc
index eae811e..9e27a11 100644
--- a/components/update_client/component_unpacker.cc
+++ b/components/update_client/component_unpacker.cc
@@ -21,10 +21,10 @@
 #include "base/strings/stringprintf.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "components/crx_file/crx_verifier.h"
+#include "components/unzip_service/public/cpp/unzip.h"
 #include "components/update_client/component_patcher.h"
 #include "components/update_client/update_client.h"
 #include "components/update_client/update_client_errors.h"
-#include "third_party/zlib/google/zip.h"
 
 namespace update_client {
 
@@ -45,13 +45,9 @@
 
 ComponentUnpacker::~ComponentUnpacker() {}
 
-bool ComponentUnpacker::UnpackInternal() {
-  return Verify() && Unzip() && BeginPatching();
-}
-
 void ComponentUnpacker::Unpack(Callback callback) {
   callback_ = std::move(callback);
-  if (!UnpackInternal())
+  if (!Verify() || !BeginUnzipping())
     EndUnpacking();
 }
 
@@ -76,7 +72,7 @@
   return true;
 }
 
-bool ComponentUnpacker::Unzip() {
+bool ComponentUnpacker::BeginUnzipping() {
   // Mind the reference to non-const type, passed as an argument below.
   base::FilePath& destination = is_delta_ ? unpack_diff_path_ : unpack_path_;
   if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
@@ -86,13 +82,20 @@
     return false;
   }
   VLOG(1) << "Unpacking in: " << destination.value();
-  if (!zip::Unzip(path_, destination)) {
+  unzip::Unzip(connector_.get(), path_, destination,
+               base::BindOnce(&ComponentUnpacker::EndUnzipping, this));
+  return true;
+}
+
+void ComponentUnpacker::EndUnzipping(bool result) {
+  if (!result) {
     VLOG(1) << "Unzipping failed.";
     error_ = UnpackerError::kUnzipFailed;
-    return false;
+    EndUnpacking();
+    return;
   }
   VLOG(1) << "Unpacked successfully";
-  return true;
+  BeginPatching();
 }
 
 bool ComponentUnpacker::BeginPatching() {
diff --git a/components/update_client/component_unpacker.h b/components/update_client/component_unpacker.h
index 8a305dd..0fe5bcc9 100644
--- a/components/update_client/component_unpacker.h
+++ b/components/update_client/component_unpacker.h
@@ -96,16 +96,15 @@
 
   virtual ~ComponentUnpacker();
 
-  bool UnpackInternal();
-
   // The first step of unpacking is to verify the file. Returns false if an
   // error is encountered, the file is malformed, or the file is incorrectly
   // signed.
   bool Verify();
 
-  // The second step of unpacking is to unzip. Returns false if an error
-  // occurs as part of unzipping.
-  bool Unzip();
+  // The second step of unpacking is to unzip. Returns false if an early error
+  // is encountered.
+  bool BeginUnzipping();
+  void EndUnzipping(bool error);
 
   // The third step is to optionally patch files - this is a no-op for full
   // (non-differential) updates. This step is asynchronous. Returns false if an
diff --git a/components/update_client/component_unpacker_unittest.cc b/components/update_client/component_unpacker_unittest.cc
index 3ba343c8..636ff68 100644
--- a/components/update_client/component_unpacker_unittest.cc
+++ b/components/update_client/component_unpacker_unittest.cc
@@ -100,10 +100,12 @@
 }
 
 TEST_F(ComponentUnpackerTest, UnpackFullCrx) {
+  auto config = base::MakeRefCounted<TestConfigurator>();
   scoped_refptr<ComponentUnpacker> component_unpacker =
       base::MakeRefCounted<ComponentUnpacker>(
           std::vector<uint8_t>(std::begin(jebg_hash), std::end(jebg_hash)),
-          test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"), nullptr, nullptr);
+          test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"), nullptr,
+          config->CreateServiceManagerConnector());
   component_unpacker->Unpack(base::BindOnce(
       &ComponentUnpackerTest::UnpackComplete, base::Unretained(this)));
   RunThreads();
diff --git a/components/update_client/test_configurator.cc b/components/update_client/test_configurator.cc
index 2fde1dd5..1e28dd8 100644
--- a/components/update_client/test_configurator.cc
+++ b/components/update_client/test_configurator.cc
@@ -10,11 +10,20 @@
 #include "base/version.h"
 #include "components/patch_service/file_patcher_impl.h"
 #include "components/patch_service/patch_service.h"
+#include "components/patch_service/public/interfaces/constants.mojom.h"
 #include "components/patch_service/public/interfaces/file_patcher.mojom.h"
 #include "components/prefs/pref_service.h"
+#include "components/unzip_service/public/interfaces/constants.mojom.h"
+#include "components/unzip_service/public/interfaces/unzipper.mojom.h"
+#include "components/unzip_service/unzip_service.h"
+#include "components/unzip_service/unzipper_impl.h"
 #include "components/update_client/activity_data_service.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
 #include "net/url_request/url_request_test_util.h"
 #include "services/service_manager/public/cpp/connector.h"
+#include "services/service_manager/public/cpp/service.h"
+#include "services/service_manager/public/cpp/service_context.h"
+#include "services/service_manager/public/mojom/connector.mojom.h"
 #include "url/gurl.h"
 
 namespace update_client {
@@ -28,6 +37,93 @@
   return urls;
 }
 
+class TestConnector : public service_manager::mojom::Connector {
+ public:
+  TestConnector()
+      : patch_context_(std::make_unique<patch::PatchService>(),
+                       mojo::MakeRequest(&patch_ptr_)),
+        unzip_context_(std::make_unique<unzip::UnzipService>(),
+                       mojo::MakeRequest(&unzip_ptr_)) {
+    const service_manager::Identity service_id("TestConnector");
+    patch_ptr_->OnStart(service_id,
+                        base::BindOnce(&TestConnector::OnStartCallback,
+                                       base::Unretained(this)));
+    unzip_ptr_->OnStart(service_id,
+                        base::BindOnce(&TestConnector::OnStartCallback,
+                                       base::Unretained(this)));
+  }
+
+  ~TestConnector() override = default;
+
+  void BindInterface(const service_manager::Identity& target,
+                     const std::string& interface_name,
+                     mojo::ScopedMessagePipeHandle interface_pipe,
+                     BindInterfaceCallback callback) override {
+    service_manager::mojom::ServicePtr* service_ptr = nullptr;
+    if (interface_name == "patch::mojom::FilePatcher") {
+      service_ptr = &patch_ptr_;
+    } else if (interface_name == "unzip::mojom::Unzipper") {
+      service_ptr = &unzip_ptr_;
+    } else {
+      LOG(ERROR) << "Requested " << interface_name << " from the TestConnector"
+                 << ", but no such instance was bound.";
+      return;
+    }
+    (*service_ptr)
+        ->OnBindInterface(service_manager::BindSourceInfo(
+                              service_manager::Identity("TestConnector"),
+                              service_manager::CapabilitySet()),
+                          interface_name, std::move(interface_pipe),
+                          base::BindRepeating(&base::DoNothing));
+    std::move(callback).Run(service_manager::mojom::ConnectResult::SUCCEEDED,
+                            service_manager::Identity());
+  }
+
+  void StartService(const service_manager::Identity& target,
+                    StartServiceCallback callback) override {
+    NOTREACHED();
+  }
+
+  void QueryService(const service_manager::Identity& target,
+                    QueryServiceCallback callback) override {
+    NOTREACHED();
+  }
+
+  void StartServiceWithProcess(
+      const service_manager::Identity& identity,
+      mojo::ScopedMessagePipeHandle service,
+      service_manager::mojom::PIDReceiverRequest pid_receiver_request,
+      StartServiceWithProcessCallback callback) override {
+    NOTREACHED();
+  }
+
+  void Clone(service_manager::mojom::ConnectorRequest request) override {
+    bindings_.AddBinding(this, std::move(request));
+  }
+
+  void FilterInterfaces(
+      const std::string& spec,
+      const service_manager::Identity& source,
+      service_manager::mojom::InterfaceProviderRequest source_request,
+      service_manager::mojom::InterfaceProviderPtr target) override {
+    NOTREACHED();
+  }
+
+ private:
+  void OnStartCallback(
+      service_manager::mojom::ConnectorRequest request,
+      service_manager::mojom::ServiceControlAssociatedRequest control_request) {
+  }
+
+  service_manager::mojom::ServicePtr patch_ptr_;
+  service_manager::mojom::ServicePtr unzip_ptr_;
+  service_manager::ServiceContext patch_context_;
+  service_manager::ServiceContext unzip_context_;
+  mojo::BindingSet<service_manager::mojom::Connector> bindings_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestConnector);
+};
+
 }  // namespace
 
 TestConfigurator::TestConfigurator()
@@ -36,12 +132,13 @@
       ondemand_time_(0),
       enabled_cup_signing_(false),
       enabled_component_updates_(true),
-      connector_factory_(
-          service_manager::TestConnectorFactory::CreateForUniqueService(
-              std::make_unique<patch::PatchService>())),
-      connector_(connector_factory_->CreateConnector()),
+      connector_mojo_(base::MakeUnique<TestConnector>()),
       context_(base::MakeRefCounted<net::TestURLRequestContextGetter>(
-          base::ThreadTaskRunnerHandle::Get())) {}
+          base::ThreadTaskRunnerHandle::Get())) {
+  service_manager::mojom::ConnectorPtr proxy;
+  connector_mojo_->Clone(mojo::MakeRequest(&proxy));
+  connector_ = base::MakeUnique<service_manager::Connector>(std::move(proxy));
+}
 
 TestConfigurator::~TestConfigurator() {
 }
diff --git a/components/update_client/test_configurator.h b/components/update_client/test_configurator.h
index 9d12aa6..f4fc0db8 100644
--- a/components/update_client/test_configurator.h
+++ b/components/update_client/test_configurator.h
@@ -15,7 +15,6 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "components/update_client/configurator.h"
-#include "services/service_manager/public/cpp/test/test_connector_factory.h"
 #include "url/gurl.h"
 
 class PrefService;
@@ -26,8 +25,10 @@
 }  // namespace net
 
 namespace service_manager {
+namespace mojom {
 class Connector;
 }
+}  // namespace service_manager
 
 namespace update_client {
 
@@ -122,7 +123,7 @@
   GURL update_check_url_;
   GURL ping_url_;
 
-  std::unique_ptr<service_manager::TestConnectorFactory> connector_factory_;
+  std::unique_ptr<service_manager::mojom::Connector> connector_mojo_;
   std::unique_ptr<service_manager::Connector> connector_;
   scoped_refptr<net::TestURLRequestContextGetter> context_;
 
diff --git a/components/update_client/update_client_unittest.cc b/components/update_client/update_client_unittest.cc
index 9a8b533..efca391 100644
--- a/components/update_client/update_client_unittest.cc
+++ b/components/update_client/update_client_unittest.cc
@@ -3272,9 +3272,11 @@
     base::RunLoop runloop;
     base::OnceClosure quit_closure = runloop.QuitClosure();
 
+    auto config = base::MakeRefCounted<TestConfigurator>();
     scoped_refptr<ComponentUnpacker> component_unpacker = new ComponentUnpacker(
         std::vector<uint8_t>(std::begin(gjpm_hash), std::end(gjpm_hash)),
-        TestFilePath("runaction_test_win.crx3"), nullptr, nullptr);
+        TestFilePath("runaction_test_win.crx3"), nullptr,
+        config->CreateServiceManagerConnector());
 
     component_unpacker->Unpack(base::BindOnce(
         [](base::FilePath* unpack_path, base::OnceClosure quit_closure,