[go: nahoru, domu]

Clean up skottie support build flag.

There are 2 issues with the current paradigm:
1) #if !defined(OS_ANDROID) is sprinkled throughout the compositor code.
Beyond the stylistic aspect of this, the inconsistency (some Skottie
code is guarded and some is not) makes it hard to maintain. It is
difficult to tell what should be guarded and what should not.
2) Instead of duplicating !OS_ANDROID throughout the code, there
should be a dedicated build flag for skottie support.

The way this is being solved is to hide all dependencies on the Skottie
library within SkottieWrapper. On platforms that don't support
Skottie (Android currently), a dummy SkottieWrapper file is linked. This
prevents the Chromium code from being polluted with #ifdefs and still
omitting Skottie on certain builds. The whole thing is controlled by
a GN flag.

Note this CL does not introduce any functional changes whatsoever.

Bug: b:203584064
Test: cc_unittests
Change-Id: I4db2ff5997f49b8a974c57da3f494435c90b98d7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3244467
Commit-Queue: Eric Sum <esum@google.com>
Reviewed-by: vmpstr <vmpstr@chromium.org>
Reviewed-by: Florin Malita <fmalita@chromium.org>
Cr-Commit-Position: refs/heads/main@{#936026}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 5b2ccbb..8390b690 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -746,9 +746,6 @@
     "paint/paint_worklet_input_unittest.cc",
     "paint/scoped_raster_flags_unittest.cc",
     "paint/skia_paint_canvas_unittest.cc",
-    "paint/skottie_mru_resource_provider_unittest.cc",
-    "paint/skottie_resource_metadata_unittest.cc",
-    "paint/skottie_wrapper_unittest.cc",
     "paint/solid_color_analyzer_unittest.cc",
     "paint/transfer_cache_unittest.cc",
     "raster/playback_image_provider_unittest.cc",
@@ -831,9 +828,14 @@
     "test/run_all_unittests.cc",
   ]
 
-  if (!is_android) {
+  if (skia_support_skottie) {
     data = [ "//components/viz/test/data/" ]
-    sources += [ "paint/skottie_transfer_cache_entry_unittest.cc" ]
+    sources += [
+      "paint/skottie_mru_resource_provider_unittest.cc",
+      "paint/skottie_resource_metadata_unittest.cc",
+      "paint/skottie_transfer_cache_entry_unittest.cc",
+      "paint/skottie_wrapper_unittest.cc",
+    ]
   }
 
   deps = [
@@ -860,6 +862,7 @@
     "//media",
     "//mojo/core/embedder",
     "//mojo/public/cpp/bindings",
+    "//skia:buildflags",
     "//skia:skcms",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/cc/paint/BUILD.gn b/cc/paint/BUILD.gn
index 7784167..bf374f7 100644
--- a/cc/paint/BUILD.gn
+++ b/cc/paint/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//cc/cc.gni")
+import("//skia/features.gni")
 import("//testing/libfuzzer/fuzzer_test.gni")
 
 cc_component("paint") {
@@ -81,11 +82,10 @@
     "skia_paint_canvas.h",
     "skia_paint_image_generator.cc",
     "skia_paint_image_generator.h",
-    "skottie_mru_resource_provider.cc",
-    "skottie_mru_resource_provider.h",
     "skottie_resource_metadata.cc",
     "skottie_resource_metadata.h",
-    "skottie_wrapper.cc",
+    "skottie_transfer_cache_entry.cc",
+    "skottie_transfer_cache_entry.h",
     "skottie_wrapper.h",
     "solid_color_analyzer.cc",
     "solid_color_analyzer.h",
@@ -120,11 +120,20 @@
     "//ui/gfx/ipc/color",
   ]
 
-  if (!is_android) {
+  if (skia_support_skottie) {
+    # All source files that depend on the actual Skottie module within Skia
+    # should go here. If a source file is Skottie-related but depends only on
+    # Chromium and/or "common" Skia dependencies, it is fine to include that
+    # in the main "sources" list. Note that ultimately, all dependencies on
+    # the Skottie library should be contained in some way/shape/form within
+    # skottie_wrapper_impl.cc
     sources += [
-      "skottie_transfer_cache_entry.cc",
-      "skottie_transfer_cache_entry.h",
+      "skottie_mru_resource_provider.cc",
+      "skottie_mru_resource_provider.h",
+      "skottie_wrapper_impl.cc",
     ]
+  } else {
+    sources += [ "skottie_wrapper_stub.cc" ]
   }
 }
 
diff --git a/cc/paint/DEPS b/cc/paint/DEPS
index b76d368..873400ee 100644
--- a/cc/paint/DEPS
+++ b/cc/paint/DEPS
@@ -5,6 +5,7 @@
   "+cc/base",
   "+cc/debug",
   "+cc/paint",
+  "+skia/buildflags.h",
 ]
 
 specific_include_rules = {
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index 257f290..6791ff1 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -10,7 +10,6 @@
 #include <vector>
 
 #include "base/stl_util.h"
-#include "build/build_config.h"
 #include "cc/paint/decoded_draw_image.h"
 #include "cc/paint/display_item_list.h"
 #include "cc/paint/image_provider.h"
@@ -691,19 +690,12 @@
                                 const PaintFlags* flags_to_serialize,
                                 const SkM44& current_ctm,
                                 const SkM44& original_ctm) {
-#if defined(OS_ANDROID)
-  // Skottie is not used in android, so to keep apk size small it is excluded
-  // from the build.
-  NOTREACHED();
-  return 0u;
-#else
   auto* op = static_cast<const DrawSkottieOp*>(base_op);
   PaintOpWriter helper(memory, size, options);
   helper.Write(op->dst);
   helper.Write(SkFloatToScalar(op->t));
   helper.Write(op->skottie);
   return helper.size();
-#endif  // OS_ANDROID
 }
 
 size_t DrawTextBlobOp::Serialize(const PaintOp* base_op,
@@ -1214,11 +1206,6 @@
                                     void* output,
                                     size_t output_size,
                                     const DeserializeOptions& options) {
-#if defined(OS_ANDROID)
-  // Skottie is not used on Android. To keep apk size small the skottie library
-  // is excluded from the binary.
-  return nullptr;
-#else
   DCHECK_GE(output_size, sizeof(DrawSkottieOp));
   DrawSkottieOp* op = new (output) DrawSkottieOp;
 
@@ -1237,7 +1224,6 @@
   }
   UpdateTypeAndSkip(op);
   return op;
-#endif  // OS_ANDROID
 }
 
 PaintOp* DrawTextBlobOp::Deserialize(const volatile void* input,
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc
index b44b9e19..5c6307d 100644
--- a/cc/paint/paint_op_buffer_unittest.cc
+++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -10,7 +10,6 @@
 #include "base/cxx17_backports.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/stringprintf.h"
-#include "build/build_config.h"
 #include "cc/paint/decoded_draw_image.h"
 #include "cc/paint/display_item_list.h"
 #include "cc/paint/image_provider.h"
@@ -29,6 +28,7 @@
 #include "cc/test/test_paint_worklet_input.h"
 #include "cc/test/test_skcanvas.h"
 #include "cc/test/transfer_cache_test_helper.h"
+#include "skia/buildflags.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkMaskFilter.h"
 #include "third_party/skia/include/effects/SkColorMatrixFilter.h"
@@ -1223,21 +1223,23 @@
     CreateDiscardablePaintImage(gfx::Size(50, 50)),
 };
 
-#if defined(OS_ANDROID)
-bool kIsSkottieSupported = false;
-#else
+#if BUILDFLAG(SKIA_SUPPORT_SKOTTIE)
 bool kIsSkottieSupported = true;
-#endif
 
 std::vector<scoped_refptr<SkottieWrapper>> test_skotties = {
     CreateSkottie(gfx::Size(10, 20), 4), CreateSkottie(gfx::Size(100, 40), 5),
     CreateSkottie(gfx::Size(80, 70), 6)};
-
 std::vector<float> test_skottie_floats = {0, 0.1f, 1.f};
-
 std::vector<SkRect> test_skottie_rects = {SkRect::MakeXYWH(10, 20, 30, 40),
                                           SkRect::MakeXYWH(0, 5, 10, 20),
                                           SkRect::MakeXYWH(6, 0, 3, 50)};
+#else
+bool kIsSkottieSupported = false;
+
+std::vector<scoped_refptr<SkottieWrapper>> test_skotties;
+std::vector<float> test_skottie_floats;
+std::vector<SkRect> test_skottie_rects;
+#endif
 
 // Writes as many ops in |buffer| as can fit in |output_size| to |output|.
 // Records the numbers of bytes written for each op.
@@ -2768,19 +2770,6 @@
   }
 }
 
-TEST(PaintOpBufferTest, BoundingRect_DrawSkottieOp) {
-  PaintOpBuffer buffer;
-  PushDrawSkottieOps(&buffer);
-
-  SkRect rect;
-  for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
-    auto* op = static_cast<DrawSkottieOp*>(base_op);
-
-    ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
-    EXPECT_EQ(rect, op->dst.makeSorted());
-  }
-}
-
 TEST(PaintOpBufferTest, BoundingRect_DrawTextBlobOp) {
   PaintOpBuffer buffer;
   PushDrawTextBlobOps(&buffer);
@@ -3422,7 +3411,20 @@
   EXPECT_TRUE(*rect_op->flags.getShader() == *flags.getShader());
 }
 
-#if !defined(OS_ANDROID)
+#if BUILDFLAG(SKIA_SUPPORT_SKOTTIE)
+TEST(PaintOpBufferTest, BoundingRect_DrawSkottieOp) {
+  PaintOpBuffer buffer;
+  PushDrawSkottieOps(&buffer);
+
+  SkRect rect;
+  for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
+    auto* op = static_cast<DrawSkottieOp*>(base_op);
+
+    ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
+    EXPECT_EQ(rect, op->dst.makeSorted());
+  }
+}
+
 // Skottie-specific deserialization failure case.
 TEST(PaintOpBufferTest,
      DrawSkottieOpSerializationFailureFromUnPrivilegedProcess) {
@@ -3458,7 +3460,7 @@
       memory.get(), serializer.written(), d_options);
   ASSERT_FALSE(deserialized_buffer);
 }
-#endif  // !defined(OS_ANDROID
+#endif  // BUILDFLAG(SKIA_SUPPORT_SKOTTIE)
 
 TEST(PaintOpBufferTest, CustomData) {
   // Basic tests: size, move, comparison.
diff --git a/cc/paint/paint_op_reader.cc b/cc/paint/paint_op_reader.cc
index 63afe24..b4c1b61 100644
--- a/cc/paint/paint_op_reader.cc
+++ b/cc/paint/paint_op_reader.cc
@@ -25,6 +25,8 @@
 #include "cc/paint/paint_op_buffer.h"
 #include "cc/paint/paint_shader.h"
 #include "cc/paint/shader_transfer_cache_entry.h"
+#include "cc/paint/skottie_transfer_cache_entry.h"
+#include "cc/paint/skottie_wrapper.h"
 #include "cc/paint/transfer_cache_deserialize_helper.h"
 #include "components/crash/core/common/crash_key.h"
 #include "third_party/skia/include/core/SkPath.h"
@@ -33,11 +35,6 @@
 #include "third_party/skia/include/core/SkTextBlob.h"
 #include "third_party/skia/src/core/SkRemoteGlyphCache.h"
 
-#if !defined(OS_ANDROID)
-#include "cc/paint/skottie_transfer_cache_entry.h"
-#include "cc/paint/skottie_wrapper.h"
-#endif
-
 namespace cc {
 namespace {
 
@@ -728,9 +725,6 @@
   ReadData(sizeof(gpu::Mailbox::Name), (*mailbox).name);
 }
 
-// Android does not use skottie. Remove below section to keep binary size to a
-// minimum.
-#if !defined(OS_ANDROID)
 void PaintOpReader::Read(scoped_refptr<SkottieWrapper>* skottie) {
   if (!options_.is_privileged) {
     valid_ = false;
@@ -761,7 +755,6 @@
   memory_ += bytes_to_skip;
   remaining_bytes_ -= bytes_to_skip;
 }
-#endif  // !defined(OS_ANDROID)
 
 void PaintOpReader::AlignMemory(size_t alignment) {
   size_t padding = base::bits::AlignUp(memory_, alignment) - memory_;
diff --git a/cc/paint/paint_op_reader.h b/cc/paint/paint_op_reader.h
index 1782eae..201cdfde 100644
--- a/cc/paint/paint_op_reader.h
+++ b/cc/paint/paint_op_reader.h
@@ -6,7 +6,6 @@
 #define CC_PAINT_PAINT_OP_READER_H_
 
 #include "base/memory/scoped_refptr.h"
-#include "build/build_config.h"
 #include "cc/paint/paint_export.h"
 #include "cc/paint/paint_filter.h"
 #include "cc/paint/paint_op_writer.h"
@@ -79,9 +78,7 @@
   void Read(SkYUVAInfo::Subsampling* subsampling);
   void Read(gpu::Mailbox* mailbox);
 
-#if !defined(OS_ANDROID)
   void Read(scoped_refptr<SkottieWrapper>* skottie);
-#endif
 
   void Read(SkClipOp* op) { ReadEnum<SkClipOp, SkClipOp::kMax_EnumValue>(op); }
   void Read(PaintCanvas::AnnotationType* type) {
diff --git a/cc/paint/paint_op_writer.cc b/cc/paint/paint_op_writer.cc
index 1f1182ff..52d9e42 100644
--- a/cc/paint/paint_op_writer.cc
+++ b/cc/paint/paint_op_writer.cc
@@ -14,6 +14,8 @@
 #include "cc/paint/paint_flags.h"
 #include "cc/paint/paint_op_buffer_serializer.h"
 #include "cc/paint/paint_shader.h"
+#include "cc/paint/skottie_transfer_cache_entry.h"
+#include "cc/paint/skottie_wrapper.h"
 #include "cc/paint/transfer_cache_serialize_helper.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "third_party/skia/include/core/SkSerialProcs.h"
@@ -22,11 +24,6 @@
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/geometry/skia_conversions.h"
 
-#if !defined(OS_ANDROID)
-#include "cc/paint/skottie_transfer_cache_entry.h"
-#include "cc/paint/skottie_wrapper.h"
-#endif
-
 namespace cc {
 namespace {
 constexpr size_t kSkiaAlignment = 4u;
@@ -282,9 +279,6 @@
   WriteImage(decoded_draw_image);
 }
 
-// Android does not use skottie. Remove below section to keep binary size to a
-// minimum.
-#if !defined(OS_ANDROID)
 void PaintOpWriter::Write(scoped_refptr<SkottieWrapper> skottie) {
   uint32_t id = skottie->id();
   Write(id);
@@ -309,7 +303,6 @@
   memory_ += bytes_written;
   remaining_bytes_ -= bytes_written;
 }
-#endif  // !defined(OS_ANDROID)
 
 void PaintOpWriter::WriteImage(const DecodedDrawImage& decoded_draw_image) {
   if (!decoded_draw_image.mailbox().IsZero()) {
diff --git a/cc/paint/paint_op_writer.h b/cc/paint/paint_op_writer.h
index 4b19a65..fdd65d7a 100644
--- a/cc/paint/paint_op_writer.h
+++ b/cc/paint/paint_op_writer.h
@@ -5,7 +5,6 @@
 #ifndef CC_PAINT_PAINT_OP_WRITER_H_
 #define CC_PAINT_PAINT_OP_WRITER_H_
 
-#include "build/build_config.h"
 #include "cc/paint/paint_canvas.h"
 #include "cc/paint/paint_export.h"
 #include "cc/paint/paint_filter.h"
@@ -120,10 +119,8 @@
   // image.
   void Write(const DrawImage& draw_image, SkSize* scale_adjustment);
 
-#if !defined(OS_ANDROID)
   // Serializes the given |skottie| vector graphic.
   void Write(scoped_refptr<SkottieWrapper> skottie);
-#endif
 
  private:
   template <typename T>
diff --git a/cc/paint/skottie_transfer_cache_entry_unittest.cc b/cc/paint/skottie_transfer_cache_entry_unittest.cc
index 58884a1..8ee012f 100644
--- a/cc/paint/skottie_transfer_cache_entry_unittest.cc
+++ b/cc/paint/skottie_transfer_cache_entry_unittest.cc
@@ -12,6 +12,7 @@
 #include "cc/paint/skottie_wrapper.h"
 #include "cc/test/lottie_test_data.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkSize.h"
 
 namespace cc {
 
diff --git a/cc/paint/skottie_wrapper.cc b/cc/paint/skottie_wrapper.cc
deleted file mode 100644
index 5a223a4..0000000
--- a/cc/paint/skottie_wrapper.cc
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2018 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 "cc/paint/skottie_wrapper.h"
-
-#include <utility>
-#include <vector>
-
-#include "base/hash/hash.h"
-#include "base/logging.h"
-#include "base/trace_event/trace_event.h"
-
-namespace cc {
-namespace {
-
-// Directs logs from the skottie animation builder to //base/logging. Without
-// this, errors/warnings from the animation builder get silently dropped.
-class SkottieLogWriter : public skottie::Logger {
- public:
-  void log(Level level, const char message[], const char* json) override {
-    static constexpr char kSkottieLogPrefix[] = "[Skottie] \"";
-    static constexpr char kSkottieLogSuffix[] = "\"";
-    switch (level) {
-      case Level::kWarning:
-        LOG(WARNING) << kSkottieLogPrefix << message << kSkottieLogSuffix;
-        break;
-      case Level::kError:
-        LOG(ERROR) << kSkottieLogPrefix << message << kSkottieLogSuffix;
-        break;
-    }
-  }
-};
-
-}  // namespace
-
-// static
-scoped_refptr<SkottieWrapper> SkottieWrapper::CreateSerializable(
-    std::vector<uint8_t> data) {
-  base::span<const uint8_t> data_span(data);
-  return base::WrapRefCounted<SkottieWrapper>(
-      new SkottieWrapper(data_span, std::move(data)));
-}
-
-// static
-scoped_refptr<SkottieWrapper> SkottieWrapper::CreateNonSerializable(
-    base::span<const uint8_t> data) {
-  return base::WrapRefCounted<SkottieWrapper>(
-      new SkottieWrapper(data, /*owned_data=*/std::vector<uint8_t>()));
-}
-
-SkottieWrapper::SkottieWrapper(base::span<const uint8_t> data,
-                               std::vector<uint8_t> owned_data)
-    : mru_resource_provider_(sk_make_sp<SkottieMRUResourceProvider>()),
-      animation_(
-          skottie::Animation::Builder()
-              .setLogger(sk_make_sp<SkottieLogWriter>())
-              .setResourceProvider(skresources::CachingResourceProvider::Make(
-                  mru_resource_provider_))
-              .make(reinterpret_cast<const char*>(data.data()), data.size())),
-      raw_data_(std::move(owned_data)),
-      id_(base::FastHash(data)),
-      // The underlying assumption here is that skottie::Animation::Builder
-      // loads image assets on initialization rather than doing so lazily at
-      // render() time. This is the case currently, and there will be unit test
-      // failures if this does not hold at some point in the future.
-      image_asset_metadata_(mru_resource_provider_->GetImageAssetMetadata()),
-      image_assets_(mru_resource_provider_->GetImageAssetMap()) {}
-
-SkottieWrapper::~SkottieWrapper() = default;
-
-const SkottieResourceMetadataMap& SkottieWrapper::GetImageAssetMetadata()
-    const {
-  return image_asset_metadata_;
-}
-
-bool SkottieWrapper::SetImageForAsset(SkottieResourceIdHash asset_id_hash,
-                                      sk_sp<SkImage> image,
-                                      SkSamplingOptions sampling) {
-  auto asset_iter = image_assets_.find(asset_id_hash);
-  if (asset_iter == image_assets_.end()) {
-    LOG(ERROR) << "Failed to set image for unknown asset with id: "
-               << asset_id_hash;
-    return false;
-  }
-  SkottieMRUResourceProvider::FrameData frame_data;
-  frame_data.image = std::move(image);
-  frame_data.sampling = sampling;
-  SkottieMRUResourceProvider::ImageAsset& asset = *asset_iter->second;
-  asset.SetCurrentFrameData(std::move(frame_data));
-  return true;
-}
-
-void SkottieWrapper::Draw(SkCanvas* canvas, float t, const SkRect& rect) {
-  base::AutoLock lock(lock_);
-  animation_->seek(t);
-  animation_->render(canvas, &rect);
-}
-
-base::span<const uint8_t> SkottieWrapper::raw_data() const {
-  DCHECK(raw_data_.size());
-  return base::as_bytes(base::make_span(raw_data_.data(), raw_data_.size()));
-}
-
-}  // namespace cc
diff --git a/cc/paint/skottie_wrapper.h b/cc/paint/skottie_wrapper.h
index 02321b0..fecbfe7 100644
--- a/cc/paint/skottie_wrapper.h
+++ b/cc/paint/skottie_wrapper.h
@@ -10,23 +10,25 @@
 
 #include "base/containers/span.h"
 #include "base/memory/ref_counted.h"
-#include "base/synchronization/lock.h"
 #include "cc/paint/paint_export.h"
-#include "cc/paint/skottie_mru_resource_provider.h"
 #include "cc/paint/skottie_resource_metadata.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkSamplingOptions.h"
-#include "third_party/skia/modules/skottie/include/Skottie.h"
 
 class SkCanvas;
 class SkImage;
 struct SkRect;
+struct SkSize;
 
 namespace cc {
 
 // A wrapper over Skia's Skottie object that can be shared by multiple
 // SkiaVectorAnimation objects. This class is thread safe when performing a draw
 // on an SkCanvas.
+//
+// The API intentionally does not have any dependencies on the Skottie public
+// header files. This is to facilitate a "dummy" implementation for builds where
+// the Skottie library should not be compiled/linked into the final binary.
 class CC_PAINT_EXPORT SkottieWrapper
     : public base::RefCountedThreadSafe<SkottieWrapper> {
  public:
@@ -40,17 +42,15 @@
       base::span<const uint8_t> data);
 
   SkottieWrapper(const SkottieWrapper&) = delete;
-
   SkottieWrapper& operator=(const SkottieWrapper&) = delete;
 
-  // Returns true if the |animation_| object initialized is a valid skottie
-  // animation.
-  bool is_valid() const { return !!animation_; }
+  // Returns true if this object contains a valid skottie animation.
+  virtual bool is_valid() const = 0;
 
   // Returns the set of all image assets in the animation and their
   // corresponding metadata. The returned map is effectively immutable; it
   // does not change during SkottieWrapper's lifetime.
-  const SkottieResourceMetadataMap& GetImageAssetMetadata() const;
+  virtual const SkottieResourceMetadataMap& GetImageAssetMetadata() const = 0;
 
   // Sets the |image| to use for the asset in the animation with the given
   // |asset_id_hash| (use GetImageAssetMetadata() to get all available assets).
@@ -77,42 +77,26 @@
   // for that currently.
   //
   // Must be called from the same thread as Draw().
-  bool SetImageForAsset(SkottieResourceIdHash asset_id_hash,
-                        sk_sp<SkImage> image,
-                        SkSamplingOptions sampling = SkSamplingOptions());
+  virtual bool SetImageForAsset(SkottieResourceIdHash asset_id_hash,
+                                sk_sp<SkImage> image,
+                                SkSamplingOptions sampling) = 0;
 
   // A thread safe call that will draw an image with bounds |rect| for the
   // frame at normalized time instant |t| onto the |canvas|.
-  void Draw(SkCanvas* canvas, float t, const SkRect& rect);
+  virtual void Draw(SkCanvas* canvas, float t, const SkRect& rect) = 0;
 
-  float duration() const { return animation_->duration(); }
-  SkSize size() const { return animation_->size(); }
+  virtual float duration() const = 0;
+  virtual SkSize size() const = 0;
 
-  base::span<const uint8_t> raw_data() const;
-  uint32_t id() const { return id_; }
+  virtual base::span<const uint8_t> raw_data() const = 0;
+  virtual uint32_t id() const = 0;
+
+ protected:
+  SkottieWrapper() = default;
+  virtual ~SkottieWrapper() = default;
 
  private:
   friend class base::RefCountedThreadSafe<SkottieWrapper>;
-
-  SkottieWrapper(base::span<const uint8_t> data,
-                 std::vector<uint8_t> owned_data);
-  ~SkottieWrapper();
-
-  base::Lock lock_;
-  const sk_sp<SkottieMRUResourceProvider> mru_resource_provider_;
-  sk_sp<skottie::Animation> animation_;
-
-  // The raw byte data is stored for serialization across OOP-R. This is only
-  // valid if serialization was enabled at construction.
-  const std::vector<uint8_t> raw_data_;
-
-  // Unique id generated for a given animation. This will be unique per
-  // animation file. 2 animation objects from the same source file will have the
-  // same value.
-  const uint32_t id_;
-
-  const SkottieResourceMetadataMap image_asset_metadata_;
-  const SkottieMRUResourceProvider::ImageAssetMap image_assets_;
 };
 
 }  // namespace cc
diff --git a/cc/paint/skottie_wrapper_impl.cc b/cc/paint/skottie_wrapper_impl.cc
new file mode 100644
index 0000000..19722fc
--- /dev/null
+++ b/cc/paint/skottie_wrapper_impl.cc
@@ -0,0 +1,142 @@
+// Copyright 2021 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 "cc/paint/skottie_wrapper.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/hash/hash.h"
+#include "base/logging.h"
+#include "base/synchronization/lock.h"
+#include "base/trace_event/trace_event.h"
+#include "cc/paint/skottie_mru_resource_provider.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/modules/skottie/include/Skottie.h"
+#include "third_party/skia/modules/skresources/include/SkResources.h"
+
+namespace cc {
+namespace {
+
+// Directs logs from the skottie animation builder to //base/logging. Without
+// this, errors/warnings from the animation builder get silently dropped.
+class SkottieLogWriter : public skottie::Logger {
+ public:
+  void log(Level level, const char message[], const char* json) override {
+    static constexpr char kSkottieLogPrefix[] = "[Skottie] \"";
+    static constexpr char kSkottieLogSuffix[] = "\"";
+    switch (level) {
+      case Level::kWarning:
+        LOG(WARNING) << kSkottieLogPrefix << message << kSkottieLogSuffix;
+        break;
+      case Level::kError:
+        LOG(ERROR) << kSkottieLogPrefix << message << kSkottieLogSuffix;
+        break;
+    }
+  }
+};
+
+class SkottieWrapperImpl : public SkottieWrapper {
+ public:
+  SkottieWrapperImpl(base::span<const uint8_t> data,
+                     std::vector<uint8_t> owned_data)
+      : mru_resource_provider_(sk_make_sp<SkottieMRUResourceProvider>()),
+        animation_(
+            skottie::Animation::Builder()
+                .setLogger(sk_make_sp<SkottieLogWriter>())
+                .setResourceProvider(skresources::CachingResourceProvider::Make(
+                    mru_resource_provider_))
+                .make(reinterpret_cast<const char*>(data.data()), data.size())),
+        raw_data_(std::move(owned_data)),
+        id_(base::FastHash(data)),
+        // The underlying assumption here is that skottie::Animation::Builder
+        // loads image assets on initialization rather than doing so lazily at
+        // render() time. This is the case currently, and there will be unit
+        // test failures if this does not hold at some point in the future.
+        image_asset_metadata_(mru_resource_provider_->GetImageAssetMetadata()),
+        image_assets_(mru_resource_provider_->GetImageAssetMap()) {}
+
+  SkottieWrapperImpl(const SkottieWrapperImpl&) = delete;
+  SkottieWrapperImpl& operator=(const SkottieWrapperImpl&) = delete;
+
+  // SkottieWrapper implementation:
+  bool is_valid() const override { return !!animation_; }
+
+  const SkottieResourceMetadataMap& GetImageAssetMetadata() const override {
+    return image_asset_metadata_;
+  }
+
+  bool SetImageForAsset(SkottieResourceIdHash asset_id_hash,
+                        sk_sp<SkImage> image,
+                        SkSamplingOptions sampling) override {
+    auto asset_iter = image_assets_.find(asset_id_hash);
+    if (asset_iter == image_assets_.end()) {
+      LOG(ERROR) << "Failed to set image for unknown asset with id: "
+                 << asset_id_hash;
+      return false;
+    }
+    SkottieMRUResourceProvider::FrameData frame_data;
+    frame_data.image = std::move(image);
+    frame_data.sampling = sampling;
+    SkottieMRUResourceProvider::ImageAsset& asset = *asset_iter->second;
+    asset.SetCurrentFrameData(std::move(frame_data));
+    return true;
+  }
+
+  void Draw(SkCanvas* canvas, float t, const SkRect& rect) override {
+    base::AutoLock lock(lock_);
+    animation_->seek(t);
+    animation_->render(canvas, &rect);
+  }
+
+  float duration() const override { return animation_->duration(); }
+
+  SkSize size() const override { return animation_->size(); }
+
+  base::span<const uint8_t> raw_data() const override {
+    DCHECK(raw_data_.size());
+    return base::as_bytes(base::make_span(raw_data_.data(), raw_data_.size()));
+  }
+
+  uint32_t id() const override { return id_; }
+
+ private:
+  friend class base::RefCountedThreadSafe<SkottieWrapperImpl>;
+
+  ~SkottieWrapperImpl() override = default;
+
+  base::Lock lock_;
+  const sk_sp<SkottieMRUResourceProvider> mru_resource_provider_;
+  sk_sp<skottie::Animation> animation_;
+
+  // The raw byte data is stored for serialization across OOP-R. This is only
+  // valid if serialization was enabled at construction.
+  const std::vector<uint8_t> raw_data_;
+
+  // Unique id generated for a given animation. This will be unique per
+  // animation file. 2 animation objects from the same source file will have the
+  // same value.
+  const uint32_t id_;
+
+  const SkottieResourceMetadataMap image_asset_metadata_;
+  const SkottieMRUResourceProvider::ImageAssetMap image_assets_;
+};
+
+}  // namespace
+
+// static
+scoped_refptr<SkottieWrapper> SkottieWrapper::CreateSerializable(
+    std::vector<uint8_t> data) {
+  base::span<const uint8_t> data_span(data);
+  return base::MakeRefCounted<SkottieWrapperImpl>(data_span, std::move(data));
+}
+
+// static
+scoped_refptr<SkottieWrapper> SkottieWrapper::CreateNonSerializable(
+    base::span<const uint8_t> data) {
+  return base::MakeRefCounted<SkottieWrapperImpl>(
+      data, /*owned_data=*/std::vector<uint8_t>());
+}
+
+}  // namespace cc
diff --git a/cc/paint/skottie_wrapper_stub.cc b/cc/paint/skottie_wrapper_stub.cc
new file mode 100644
index 0000000..b298d71
--- /dev/null
+++ b/cc/paint/skottie_wrapper_stub.cc
@@ -0,0 +1,29 @@
+// Copyright 2021 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 "cc/paint/skottie_wrapper.h"
+
+#include "base/check.h"
+
+namespace cc {
+
+// This stub source file is only built on platforms that don't support skottie.
+// Skottie code paths should not be taken at all on these platforms, so
+// a concrete SkottieWrapper implementation is not required.
+
+// static
+scoped_refptr<SkottieWrapper> SkottieWrapper::CreateSerializable(
+    std::vector<uint8_t> data) {
+  CHECK(false) << "Skottie is not supported on this platform";
+  return nullptr;
+}
+
+// static
+scoped_refptr<SkottieWrapper> SkottieWrapper::CreateNonSerializable(
+    base::span<const uint8_t> data) {
+  CHECK(false) << "Skottie is not supported on this platform";
+  return nullptr;
+}
+
+}  // namespace cc
diff --git a/cc/paint/skottie_wrapper_unittest.cc b/cc/paint/skottie_wrapper_unittest.cc
index 91855f55..efdab31 100644
--- a/cc/paint/skottie_wrapper_unittest.cc
+++ b/cc/paint/skottie_wrapper_unittest.cc
@@ -19,6 +19,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/include/core/SkSize.h"
 
 namespace cc {
 namespace {
@@ -112,17 +113,21 @@
   ::testing::NiceMock<MockCanvas> canvas;
   EXPECT_TRUE(skottie->SetImageForAsset(
       HashSkottieResourceId("image_0"),
-      CreateBitmapImage(gfx::Size(100, 100)).GetSwSkImage()));
+      CreateBitmapImage(gfx::Size(100, 100)).GetSwSkImage(),
+      SkSamplingOptions()));
   EXPECT_TRUE(skottie->SetImageForAsset(
       HashSkottieResourceId("image_1"),
-      CreateBitmapImage(gfx::Size(100, 100)).GetSwSkImage()));
+      CreateBitmapImage(gfx::Size(100, 100)).GetSwSkImage(),
+      SkSamplingOptions()));
   skottie->Draw(&canvas, /*t=*/0.1, SkRect::MakeWH(500, 500));
   EXPECT_TRUE(skottie->SetImageForAsset(
       HashSkottieResourceId("image_0"),
-      CreateBitmapImage(gfx::Size(200, 200)).GetSwSkImage()));
+      CreateBitmapImage(gfx::Size(200, 200)).GetSwSkImage(),
+      SkSamplingOptions()));
   EXPECT_TRUE(skottie->SetImageForAsset(
       HashSkottieResourceId("image_1"),
-      CreateBitmapImage(gfx::Size(200, 200)).GetSwSkImage()));
+      CreateBitmapImage(gfx::Size(200, 200)).GetSwSkImage(),
+      SkSamplingOptions()));
   skottie->Draw(&canvas, /*t=*/0.2, SkRect::MakeWH(500, 500));
 }
 
@@ -134,7 +139,8 @@
   ASSERT_TRUE(skottie->is_valid());
   EXPECT_FALSE(skottie->SetImageForAsset(
       HashSkottieResourceId("unknown-asset"),
-      CreateBitmapImage(gfx::Size(100, 100)).GetSwSkImage()));
+      CreateBitmapImage(gfx::Size(100, 100)).GetSwSkImage(),
+      SkSamplingOptions()));
 }
 
 }  // namespace
diff --git a/cc/paint/transfer_cache_entry.cc b/cc/paint/transfer_cache_entry.cc
index 5a6dda6..edd4e2b 100644
--- a/cc/paint/transfer_cache_entry.cc
+++ b/cc/paint/transfer_cache_entry.cc
@@ -7,14 +7,10 @@
 #include <memory>
 
 #include "base/notreached.h"
-#include "build/build_config.h"
 #include "cc/paint/image_transfer_cache_entry.h"
 #include "cc/paint/raw_memory_transfer_cache_entry.h"
 #include "cc/paint/shader_transfer_cache_entry.h"
-
-#if !defined(OS_ANDROID)
 #include "cc/paint/skottie_transfer_cache_entry.h"
-#endif
 
 namespace cc {
 
@@ -30,11 +26,7 @@
       // CreateLocalEntry and is never serialized/deserialized.
       return nullptr;
     case TransferCacheEntryType::kSkottie:
-#if !defined(OS_ANDROID)
       return std::make_unique<ServiceSkottieTransferCacheEntry>();
-#else
-      return nullptr;
-#endif
   }
 
   return nullptr;
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 38653fc..74e33d7 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -25,16 +25,17 @@
 
 skia_support_gpu = !is_ios
 skia_support_pdf = !is_ios && enable_basic_printing
-skia_support_skottie = true
 
 declare_args() {
   enable_skia_wuffs_gif = true
 }
 
-# Generate a buildflag header for compile-time checking of Dawn support.
 buildflag_header("buildflags") {
   header = "buildflags.h"
-  flags = [ "SKIA_USE_DAWN=$skia_use_dawn" ]
+  flags = [
+    "SKIA_SUPPORT_SKOTTIE=$skia_support_skottie",
+    "SKIA_USE_DAWN=$skia_use_dawn",
+  ]
 }
 
 # External-facing config for dependent code.
diff --git a/skia/features.gni b/skia/features.gni
index 5675938..bf567a5 100644
--- a/skia/features.gni
+++ b/skia/features.gni
@@ -12,3 +12,8 @@
   # path for D3D12 is available.
   enable_skia_dawn_gtests = false
 }
+
+# Skottie is not used on Android. To keep apk size small the skottie library is
+# excluded from the binary. At the time this comment was written, it adds ~200KB
+# to the APK.
+skia_support_skottie = !is_android
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index b3bdbcc..97f9f3f 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -13,6 +13,7 @@
 import("//build/config/sanitizers/sanitizers.gni")
 import("//build/config/ui.gni")
 import("//build/util/branding.gni")
+import("//skia/features.gni")
 import("//testing/libfuzzer/fuzzer_test.gni")
 import("//testing/test.gni")
 import("//tools/grit/grit_rule.gni")
@@ -1181,7 +1182,7 @@
     data += [ "$root_out_dir/ui_unittests Framework.framework/" ]
   }
 
-  if (!is_ios) {
+  if (skia_support_skottie && !is_ios) {
     sources += [ "//ui/lottie/animation_unittest.cc" ]
     deps += [ "//ui/lottie" ]
   }