Import Cobalt 22.lts.3.306081
diff --git a/src/.codespellignorelines b/src/.codespellignorelines
new file mode 100644
index 0000000..4e787e0
--- /dev/null
+++ b/src/.codespellignorelines
@@ -0,0 +1 @@
+    vp9WhiteList.get("Technicolor").add("STING");
diff --git a/src/.pre-commit-config.yaml b/src/.pre-commit-config.yaml
index 7bc0f93..e78b053 100644
--- a/src/.pre-commit-config.yaml
+++ b/src/.pre-commit-config.yaml
@@ -21,6 +21,7 @@
     hooks:
     -   id: codespell
         name: Spell Check
+        args: [-x, .codespellignorelines]
 
 -   repo: local
     hooks:
diff --git a/src/BUILD.gn b/src/BUILD.gn
index 6900b8c..54b1b89 100644
--- a/src/BUILD.gn
+++ b/src/BUILD.gn
@@ -12,10 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# executable("hello_world") {
-#   sources = ["hello_world.cc"]
-# }
-
 group("gn_all") {
   testonly = true
 
diff --git a/src/cobalt/black_box_tests/black_box_tests.py b/src/cobalt/black_box_tests/black_box_tests.py
index 271b2cc..9468917 100644
--- a/src/cobalt/black_box_tests/black_box_tests.py
+++ b/src/cobalt/black_box_tests/black_box_tests.py
@@ -153,7 +153,8 @@
                proxy_port=None,
                test_name=None,
                wpt_http_port=None,
-               device_ips=None):
+               device_ips=None,
+               device_id=None):
     # Setup global variables used by test cases.
     global _launcher_params
     _launcher_params = command_line.CreateLauncherParams()
@@ -167,9 +168,17 @@
       wpt_http_port = str(self.GetUnusedPort([server_binding_address]))
     global _wpt_http_port
     _wpt_http_port = wpt_http_port
+    # TODO: Remove generation of --dev_servers_listen_ip once executable will
+    # be able to bind correctly with incomplete support of IPv6
+    if device_id and IsValidIpAddress(device_id):
+      _launcher_params.target_params.append(
+          '--dev_servers_listen_ip={}'.format(device_id))
+    elif IsValidIpAddress(server_binding_address):
+      _launcher_params.target_params.append(
+          '--dev_servers_listen_ip={}'.format(server_binding_address))
     _launcher_params.target_params.append(
-        '--web-platform-test-server=http://web-platform.test:%s' %
-        wpt_http_port)
+        '--web-platform-test-server=http://web-platform.test:{}'.format(
+            wpt_http_port))
 
     # Port used to create the proxy server. If not specified, a random free
     # port is used.
@@ -258,6 +267,29 @@
         sock[1].close()
 
 
+def IsValidIpAddress(address):
+  """Checks if address is valid IP address."""
+  return IsValidIpv4Address(address) or IsValidIpv6Address(address)
+
+
+def IsValidIpv4Address(address):
+  """Checks if address is valid IPv4 address."""
+  try:
+    socket.inet_pton(socket.AF_INET, address)
+    return True
+  except socket.error:  # not a valid address
+    return False
+
+
+def IsValidIpv6Address(address):
+  """Checks if address is valid IPv6 address."""
+  try:
+    socket.inet_pton(socket.AF_INET6, address)
+    return True
+  except socket.error:  # not a valid address
+    return False
+
+
 def main():
   parser = argparse.ArgumentParser()
   parser.add_argument('-v', '--verbose', required=False, action='store_true')
@@ -289,10 +321,15 @@
             'server. If not specified, a random free port is'
             'used.'))
   parser.add_argument(
+      '--device_id',
+      default=None,
+      help=('ID of test device to connect. If specified, it will be passed '
+            'as --dev_servers_listen_ip param on the test device.'))
+  parser.add_argument(
       '--device_ips',
       default=None,
       nargs='*',
-      help=('IPs of test devices that will be allowed to connect. If not'
+      help=('IPs of test devices that will be allowed to connect. If not '
             'specified, all IPs will be allowed to connect.'))
   args, _ = parser.parse_known_args()
 
@@ -300,7 +337,8 @@
 
   test_object = BlackBoxTests(args.server_binding_address, args.proxy_address,
                               args.proxy_port, args.test_name,
-                              args.wpt_http_port, args.device_ips)
+                              args.wpt_http_port, args.device_ips,
+                              args.device_id)
   sys.exit(test_object.Run())
 
 
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 05e4364..303a70b 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -839,7 +839,21 @@
 #if SB_IS(EVERGREEN)
   if (SbSystemGetExtension(kCobaltExtensionInstallationManagerName) &&
       !command_line->HasSwitch(switches::kDisableUpdaterModule)) {
-    updater_module_.reset(new updater::UpdaterModule(network_module_.get()));
+    uint64_t update_check_delay_sec =
+        cobalt::updater::kDefaultUpdateCheckDelaySeconds;
+    if (command_line->HasSwitch(browser::switches::kUpdateCheckDelaySeconds)) {
+      std::string seconds_value = command_line->GetSwitchValueASCII(
+          browser::switches::kUpdateCheckDelaySeconds);
+      if (!base::StringToUint64(seconds_value, &update_check_delay_sec)) {
+        LOG(WARNING) << "Invalid delay specified for the update check: "
+                     << seconds_value << ". Using default value: "
+                     << cobalt::updater::kDefaultUpdateCheckDelaySeconds;
+        update_check_delay_sec =
+            cobalt::updater::kDefaultUpdateCheckDelaySeconds;
+      }
+    }
+    updater_module_.reset(new updater::UpdaterModule(network_module_.get(),
+                                                     update_check_delay_sec));
   }
 #endif
   browser_module_.reset(new BrowserModule(
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 44fc15e..ea42569 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -409,6 +409,15 @@
 #endif  // ENABLE_DEBUGGER
     }
   }
+
+  if (command_line->HasSwitch(switches::kDisableMediaEncryptionSchemes)) {
+    std::string encryption_schemes = command_line->GetSwitchValueASCII(
+        switches::kDisableMediaEncryptionSchemes);
+    if (!encryption_schemes.empty()) {
+      can_play_type_handler_->SetDisabledMediaEncryptionSchemes(
+          encryption_schemes);
+    }
+  }
 #endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 
   if (application_state_ == base::kApplicationStateStarted ||
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index 4241402..01ff75d 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -71,6 +71,15 @@
     "example, setting the value to \"avc;hvc\" will disable any h264 and h265 "
     "playbacks.";
 
+const char kDisableMediaEncryptionSchemes[] =
+    "disable_media_encryption_schemes";
+const char kDisableMediaEncryptionSchemesHelp[] =
+    "Disables the semicolon-separated list of encryption schemes that will be "
+    "treated as unsupported for encrypted media playback. Used for debugging "
+    "and testing purposes. For example, setting the value to \"cenc;cbcs\" "
+    "will disable any cenc and cbcs DRM playbacks. Accepted values: \"cenc\", "
+    "\"cbcs\", \"cbcs-1-9\".";
+
 const char kDisableRasterizerCaching[] = "disable_rasterizer_caching";
 const char kDisableRasterizerCachingHelp[] =
     "Disables caching of rasterized render tree nodes; caching improves "
@@ -410,6 +419,11 @@
     "the URL used to launch Cobalt, then the value of "
     "'fallback_splash_screen_url' will be used.";
 
+const char kUpdateCheckDelaySeconds[] = "update_check_delay_seconds";
+const char kUpdateCheckDelaySecondsHelp[] =
+    "Number of seconds to delay the first Cobalt Evergreen check for updates."
+    "The default value is 60 seconds.";
+
 const char kVersion[] = "version";
 const char kVersionHelp[] = "Prints the current version of Cobalt";
 
@@ -437,6 +451,7 @@
         {kDisableImageAnimations, kDisableImageAnimationsHelp},
         {kForceDeterministicRendering, kForceDeterministicRenderingHelp},
         {kDisableMediaCodecs, kDisableMediaCodecsHelp},
+        {kDisableMediaEncryptionSchemes, kDisableMediaEncryptionSchemesHelp},
         {kDisableRasterizerCaching, kDisableRasterizerCachingHelp},
         {kDisableSignIn, kDisableSignInHelp},
         {kDisableSplashScreenOnReloads, kDisableSplashScreenOnReloadsHelp},
@@ -493,6 +508,7 @@
         {kSoftwareSurfaceCacheSizeInBytes,
          kSoftwareSurfaceCacheSizeInBytesHelp},
         {kFallbackSplashScreenURL, kFallbackSplashScreenURLHelp},
+        {kUpdateCheckDelaySeconds, kUpdateCheckDelaySecondsHelp},
         {kVersion, kVersionHelp}, {kViewport, kViewportHelp},
         {kVideoPlaybackRateMultiplier, kVideoPlaybackRateMultiplierHelp},
   };
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index d07aa1e..fa7f6a4 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -41,6 +41,8 @@
 extern const char kForceDeterministicRendering[];
 extern const char kDisableMediaCodecs[];
 extern const char kDisableMediaCodecsHelp[];
+extern const char kDisableMediaEncryptionSchemes[];
+extern const char kDisableMediaEncryptionSchemesHelp[];
 extern const char kDisableRasterizerCaching[];
 extern const char kDisableSignIn[];
 extern const char kDisableSignInHelp[];
@@ -151,6 +153,8 @@
 extern const char kFallbackSplashScreenURLHelp[];
 extern const char kFallbackSplashScreenTopics[];
 extern const char kFallbackSplashScreenTopicsHelp[];
+extern const char kUpdateCheckDelaySeconds[];
+extern const char kUpdateCheckDelaySecondsHelp[];
 extern const char kVersion[];
 extern const char kVersionHelp[];
 extern const char kViewport[];
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index a003887..bd615ed 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-305573
\ No newline at end of file
+306081
\ No newline at end of file
diff --git a/src/cobalt/build/gyp_utils.py b/src/cobalt/build/gyp_utils.py
index fc61343..022cb74 100644
--- a/src/cobalt/build/gyp_utils.py
+++ b/src/cobalt/build/gyp_utils.py
@@ -34,13 +34,11 @@
 
 
 def CheckRevInfo(key, cwd=None):
-  cwd = cwd if cwd else '.'
-  git_prefix = ['git', '-C', cwd]
-  git_get_remote_args = git_prefix + ['config', '--get', 'remote.origin.url']
-  remote = subprocess.check_output(git_get_remote_args).strip()
+  git_get_remote_args = ['git', 'config', '--get', 'remote.origin.url']
+  remote = subprocess.check_output(git_get_remote_args, cwd=cwd).strip()
 
-  git_get_revision_args = git_prefix + ['rev-parse', 'HEAD']
-  revision = subprocess.check_output(git_get_revision_args).strip()
+  git_get_revision_args = ['git', 'rev-parse', 'HEAD']
+  revision = subprocess.check_output(git_get_revision_args, cwd=cwd).strip()
   return {key: '{}@{}'.format(remote, revision)}
 
 
@@ -70,7 +68,11 @@
     try:
       repos.update(CheckRevInfo(rel_path, cwd=path))
     except subprocess.CalledProcessError as e:
-      logging.warning('Failed to get revision information for subrepo: %s', e)
+      logging.warning('Failed to get revision information for subrepo %s: %s',
+                      rel_path, e)
+      continue
+    except OSError as e:
+      logging.info('%s. Subrepository %s not found.', e, rel_path)
       continue
 
   return repos
diff --git a/src/cobalt/doc/deep_links.md b/src/cobalt/doc/deep_links.md
new file mode 100644
index 0000000..69080ca
--- /dev/null
+++ b/src/cobalt/doc/deep_links.md
@@ -0,0 +1,135 @@
+# Cobalt Deep Links
+
+- [Cobalt Deep Links](#cobalt-deep-links)
+  - [Deep Links](#deep-links)
+  - [Web API](#web-api)
+  - [Platform (Starboard) API](#platform-starboard-api)
+  - [Behavior details](#behavior-details)
+## Deep Links
+
+For Cobalt, a deep link is a string that can be sent from the platform to an
+application running in Cobalt. Generally, it can be used as any string value
+signal, but typically deep links are used to specify a view, page, or content
+to be shown by the application. While these strings typically are URI formatted
+values, when deep link strings are received by Cobalt they are forwarded to the
+running application without separate validation or modification.
+
+Applications should interpret received deep links as superseding previous deep
+links. Deep links received by Cobalt in rapid succession are not guaranteed to
+all be delivered to the application. On a busy or slow device, intermediate
+deep links can be dropped before they are delivered to the application.
+
+The startup URL passed to Cobalt determines which application Cobalt will load.
+Web deep links intended as a signal to the application should not be sent to
+Cobalt as a startup URL because that would result in a different application
+being loaded. Since a deep link is a string that may originate from an
+untrusted source on the device, it should not be used directly to determine
+what application Cobalt will load.
+
+Deep links are made visible to applications by Cobalt with a Web API that is
+separate from the Location interface web API. Cobalt will never directly
+navigate as a result of a received deep link, even if the link matches the
+current application location, for example with a query or fragment identifier.
+Applications that wish to navigate as a result of incoming deep links should do
+so explicitly when they are received.
+
+## Web API
+
+The deep link Web API consists of two parts: The
+`h5vcc.runtime.initialDeepLink` property and `h5vcc.runtime.onDeepLink` event
+target.
+
+Applications can read the value of `initialDeepLink`, and/or they can use
+`h5vcc.runtime.onDeepLink.addListener(foo)` to register callback functions to
+be called when deep links are received.
+
+A deep link is considered 'consumed' when it is read from `initialDeepLink`, or
+when it is reported to a callback function registered to `onDeepLink`.
+
+The IDL for this Cobalt specific interface can be found in cobalt/h5vcc, and is
+repeated below.
+
+```
+interface H5vccRuntime {
+  readonly attribute DOMString initialDeepLink;
+  readonly attribute H5vccDeepLinkEventTarget onDeepLink;
+}
+interface H5vccDeepLinkEventTarget {
+  void addListener(H5vccDeepLinkEventCallback callback);
+};
+callback H5vccDeepLinkEventCallback = void(DOMString link);
+interface H5vcc {
+  readonly attribute H5vccRuntime runtime;
+}
+```
+
+## Platform (Starboard) API
+
+Deep links can be passed into Cobalt in two ways:
+ * As the 'Startup Link':
+   * When Cobalt is first started, a deep link can be passed in with the
+     initial event (either `kSbEventTypePreload` or `kSbEventTypeStart`). This
+     can be achieved by calling `Application::SetStartLink` or by using and
+     overload of `Application::Run` that has the 'link_data' parameter to start
+     Cobalt. The value passed in there is then passed into Cobalt via the
+     'link' member of the SbEventStartData event parameter, constructed in
+     `Application::CreateInitialEvent`.
+ * As a 'Deep Link Event':
+   * At any time while Cobalt is running, it can be sent as the string value
+     passed with a kSbEventTypeLink event. The `Application::Link` method can
+     be called to inject such an event.
+
+On many platforms, the 'Startup Link' value can also be set with the `--link`
+command-line parameter (See `kLinkSwitch` in `Application::Run`).
+
+The `Application` class mentioned above can be found at
+`starboard/shared/starboard/application.cc`.
+
+## Behavior details
+
+Both the 'Startup Link' and 'Deep Link Event' values are treated the same by
+Cobalt: A 'Startup Link' is treated as a 'Deep Link Event' that was received
+immediately at startup. For the application, it is transparent whether a deep
+link was received as a 'Startup Link' or arrived from a 'Deep Link Event'. Deep
+link values of either type are made available as soon as they are known, with
+the same Web API interface.
+
+The most recently received deep link is remembered by Cobalt until it is
+consumed by the application. This includes deep links received by Cobalt while
+the application is still being fetched and loaded, including during page
+redirects or reloads, and after the application is loaded, if it has not
+consumed the deep link.
+
+Deep link values are considered consumed when the application either reads them
+from the `initialDeepLink` attribute or receives them in a callback to an
+`onDeepLink` listener. An application can use either or both reads of
+`initialDeepLink` or `onDeepLink` listeners to consume the most recently
+received deep link value.
+
+Calls to `onDeepLink` listeners are done as soon as deep links are available.
+Specifically, they can be called before `document.onreadystatechange`,
+`document.onload` & `window.onload` event handlers are called. As a result,
+deep link values can be consumed in synchronously loaded JavaScript that
+executes before the `document.onload` event.
+
+Until the first `onDeepLink` listener is added, the `initialDeepLink` property
+will return the most recently received deep link value. When an `onDeepLink`
+listener is added, the `initialDeepLink` value will no longer be updated, even
+when additional deep link events are received subsequently.
+
+An application can decide to never register an `onDeepLink` listener and poll
+the `initialDeepLink` value instead. This will then always return the value of
+the most recently received deep link.
+
+An application can decide to register an `onDeepLink` listener without reading
+the `initialDeepLink` value. Upon registering, the most recently received deep
+link, which may be the 'Startup Link' or from a 'Deep Link Event', will be
+reported to the listener.
+
+If a deep link value is consumed, it will not be made available again if the
+page is navigated (e.g. redirected or reloaded). When a deep link is consumed
+before a page redirect or reload, the deep link will not be repeated later.
+
+If a deep link value is not consumed, it will be made available again if the
+page is navigated (e.g. redirected or reloaded). A deep link will not be lost
+if a page redirect or reload is done without consuming it.
diff --git a/src/cobalt/h5vcc/h5vcc.gyp b/src/cobalt/h5vcc/h5vcc.gyp
index 6486986..ce59392 100644
--- a/src/cobalt/h5vcc/h5vcc.gyp
+++ b/src/cobalt/h5vcc/h5vcc.gyp
@@ -110,6 +110,9 @@
             'h5vcc_updater.cc',
             'h5vcc_updater.h',
           ],
+         'dependencies': [
+           '<(DEPTH)/cobalt/updater/updater.gyp:updater',
+         ],
         }],
       ],
     },
diff --git a/src/cobalt/h5vcc/h5vcc_updater.cc b/src/cobalt/h5vcc/h5vcc_updater.cc
index c4d7bbf..97162bb 100644
--- a/src/cobalt/h5vcc/h5vcc_updater.cc
+++ b/src/cobalt/h5vcc/h5vcc_updater.cc
@@ -14,6 +14,10 @@
 
 #include "cobalt/h5vcc/h5vcc_updater.h"
 
+#if SB_IS(EVERGREEN)
+#include "cobalt/updater/utils.h"
+#endif
+
 namespace {
 
 const uint16 kInvalidInstallationIndex = 1000;
@@ -23,6 +27,7 @@
 namespace cobalt {
 namespace h5vcc {
 
+#if SB_IS(EVERGREEN)
 std::string H5vccUpdater::GetUpdaterChannel() const {
   if (!updater_module_) {
     return "";
@@ -66,5 +71,10 @@
   return index == -1 ? kInvalidInstallationIndex : static_cast<uint16>(index);
 }
 
+std::string H5vccUpdater::GetLibrarySha256(uint16 index) const {
+  return cobalt::updater::GetLibrarySha256(index);
+}
+
+#endif  // SB_IS(EVERGREEN)
 }  // namespace h5vcc
 }  // namespace cobalt
diff --git a/src/cobalt/h5vcc/h5vcc_updater.h b/src/cobalt/h5vcc/h5vcc_updater.h
index 841b893..8b869ff 100644
--- a/src/cobalt/h5vcc/h5vcc_updater.h
+++ b/src/cobalt/h5vcc/h5vcc_updater.h
@@ -46,6 +46,8 @@
 
   uint16 GetInstallationIndex() const;
 
+  std::string GetLibrarySha256(uint16 index) const;
+
 #else
   H5vccUpdater() {}
 #endif
diff --git a/src/cobalt/h5vcc/h5vcc_updater.idl b/src/cobalt/h5vcc/h5vcc_updater.idl
index 4a81664..d71d603 100644
--- a/src/cobalt/h5vcc/h5vcc_updater.idl
+++ b/src/cobalt/h5vcc/h5vcc_updater.idl
@@ -24,4 +24,7 @@
   void resetInstallations();
 
   unsigned short getInstallationIndex();
+
+  DOMString getLibrarySha256(unsigned short index);
+
 };
diff --git a/src/cobalt/media/base/sbplayer_pipeline.cc b/src/cobalt/media/base/sbplayer_pipeline.cc
index 3fa6b43..52e44a4 100644
--- a/src/cobalt/media/base/sbplayer_pipeline.cc
+++ b/src/cobalt/media/base/sbplayer_pipeline.cc
@@ -935,8 +935,8 @@
 
   CallSeekCB(DECODER_ERROR_NOT_SUPPORTED,
              "SbPlayerPipeline::CreateUrlPlayer failed to create a valid "
-             "StarboardPlayer:" +
-                 (error_message.empty() ? "" : " Error: " + error_message));
+             "StarboardPlayer: \'" +
+                 error_message + "\'");
 }
 
 void SbPlayerPipeline::SetDrmSystem(SbDrmSystem drm_system) {
@@ -1037,8 +1037,8 @@
 
   CallSeekCB(DECODER_ERROR_NOT_SUPPORTED,
              "SbPlayerPipeline::CreatePlayer failed to create a valid "
-             "StarboardPlayer:" +
-                 (error_message.empty() ? "" : " Error: " + error_message));
+             "StarboardPlayer: \'" +
+                 error_message + "\'");
 }
 
 void SbPlayerPipeline::OnDemuxerInitialized(PipelineStatus status) {
@@ -1441,15 +1441,18 @@
   if (player_) {
     player_->Resume(window);
     if (!player_->IsValid()) {
+      std::string error_message;
       {
         base::AutoLock auto_lock(lock_);
+        error_message = player_->GetPlayerCreationErrorMessage();
         player_.reset();
       }
       // TODO: Determine if CallSeekCB() may be used here, as |seek_cb_| may be
       // available if the app is suspended before a seek is completed.
       CallErrorCB(DECODER_ERROR_NOT_SUPPORTED,
-                  "SbPlayerPipeline::ResumeTask failed: "
-                  "player_->IsValid() is false.");
+                  "SbPlayerPipeline::ResumeTask failed to create a valid "
+                  "StarboardPlayer: \'" +
+                      error_message + "\'");
       return;
     }
   }
diff --git a/src/cobalt/media/can_play_type_handler.h b/src/cobalt/media/can_play_type_handler.h
index 00b94f1..5ed65d7 100644
--- a/src/cobalt/media/can_play_type_handler.h
+++ b/src/cobalt/media/can_play_type_handler.h
@@ -30,6 +30,8 @@
   virtual SbMediaSupportType CanPlayAdaptive(
       const std::string& mime_type, const std::string& key_system) const = 0;
   virtual void SetDisabledMediaCodecs(const std::string& codecs) = 0;
+  virtual void SetDisabledMediaEncryptionSchemes(
+      const std::string& disabled_encryption_schemes) = 0;
 
  protected:
   CanPlayTypeHandler() {}
diff --git a/src/cobalt/media/decoder_buffer_allocator.cc b/src/cobalt/media/decoder_buffer_allocator.cc
index 7c83911..9515da1 100644
--- a/src/cobalt/media/decoder_buffer_allocator.cc
+++ b/src/cobalt/media/decoder_buffer_allocator.cc
@@ -37,10 +37,6 @@
 // be different.
 const std::size_t kSmallAllocationThreshold = 512;
 
-bool IsLargeAllocation(std::size_t size) {
-  return size > kSmallAllocationThreshold;
-}
-
 }  // namespace
 
 DecoderBufferAllocator::DecoderBufferAllocator()
@@ -65,8 +61,9 @@
   // set yet. Use 0 (unbounded) until |video_codec_| is updated in
   // UpdateVideoConfig().
   int max_capacity = 0;
-  reuse_allocator_.reset(new ReuseAllocator(
-      &fallback_allocator_, initial_capacity_, allocation_unit_, max_capacity));
+  reuse_allocator_.reset(new nb::BidirectionalFitReuseAllocator(
+      &fallback_allocator_, initial_capacity_, kSmallAllocationThreshold,
+      allocation_unit_, max_capacity));
   DLOG(INFO) << "Allocated " << initial_capacity_
              << " bytes for media buffer pool as its initial buffer, with max"
              << " capacity set to " << max_capacity;
@@ -109,9 +106,9 @@
       max_capacity = SbMediaGetMaxBufferCapacity(
           video_codec_, resolution_width_, resolution_height_, bits_per_pixel_);
     }
-    reuse_allocator_.reset(new ReuseAllocator(&fallback_allocator_,
-                                              initial_capacity_,
-                                              allocation_unit_, max_capacity));
+    reuse_allocator_.reset(new nb::BidirectionalFitReuseAllocator(
+        &fallback_allocator_, initial_capacity_, kSmallAllocationThreshold,
+        allocation_unit_, max_capacity));
     DLOG(INFO) << "Allocated " << initial_capacity_
                << " bytes for media buffer pool, with max capacity set to "
                << max_capacity;
@@ -189,48 +186,6 @@
              << reuse_allocator_->GetCapacity();
 }
 
-DecoderBufferAllocator::ReuseAllocator::ReuseAllocator(
-    Allocator* fallback_allocator, std::size_t initial_capacity,
-    std::size_t allocation_increment, std::size_t max_capacity)
-    : BidirectionalFitReuseAllocator(fallback_allocator, initial_capacity,
-                                     kSmallAllocationThreshold,
-                                     allocation_increment, max_capacity) {}
-
-DecoderBufferAllocator::ReuseAllocator::FreeBlockSet::iterator
-DecoderBufferAllocator::ReuseAllocator::FindBestFreeBlock(
-    std::size_t size, std::size_t alignment, intptr_t context,
-    FreeBlockSet::iterator begin, FreeBlockSet::iterator end,
-    bool* allocate_from_front) {
-  DCHECK(allocate_from_front);
-
-  auto free_block_iter =
-      FindFreeBlock(size, alignment, begin, end, allocate_from_front);
-  if (free_block_iter != end) {
-    return free_block_iter;
-  }
-
-  *allocate_from_front = size > kSmallAllocationThreshold;
-  *allocate_from_front = context == 1;
-  if (*allocate_from_front) {
-    for (FreeBlockSet::iterator it = begin; it != end; ++it) {
-      if (it->CanFulfill(1, alignment)) {
-        return it;
-      }
-    }
-
-    return end;
-  }
-
-  FreeBlockSet::reverse_iterator rbegin(end);
-  FreeBlockSet::reverse_iterator rend(begin);
-  for (FreeBlockSet::reverse_iterator it = rbegin; it != rend; ++it) {
-    if (it->CanFulfill(1, alignment)) {
-      return --it.base();
-    }
-  }
-  return end;
-}
-
 std::size_t DecoderBufferAllocator::GetAllocatedMemory() const {
   if (!using_memory_pool_) {
     return sbmemory_bytes_used_.load();
diff --git a/src/cobalt/media/decoder_buffer_allocator.h b/src/cobalt/media/decoder_buffer_allocator.h
index 0504a6e..e78e6af 100644
--- a/src/cobalt/media/decoder_buffer_allocator.h
+++ b/src/cobalt/media/decoder_buffer_allocator.h
@@ -46,17 +46,6 @@
   size_t GetMaximumMemoryCapacity() const override;
 
  private:
-  class ReuseAllocator : public nb::BidirectionalFitReuseAllocator {
-   public:
-    ReuseAllocator(Allocator* fallback_allocator, std::size_t initial_capacity,
-                   std::size_t allocation_increment, std::size_t max_capacity);
-
-    FreeBlockSet::iterator FindBestFreeBlock(
-        std::size_t size, std::size_t alignment, intptr_t context,
-        FreeBlockSet::iterator begin, FreeBlockSet::iterator end,
-        bool* allocate_from_front) override;
-  };
-
   // Update the Allocation record, and return false if allocation exceeds the
   // max buffer capacity, or true otherwise.
   bool UpdateAllocationRecord() const;
@@ -68,7 +57,7 @@
 
   starboard::Mutex mutex_;
   nb::StarboardMemoryAllocator fallback_allocator_;
-  std::unique_ptr<ReuseAllocator> reuse_allocator_;
+  std::unique_ptr<nb::BidirectionalFitReuseAllocator> reuse_allocator_;
 
   SbMediaVideoCodec video_codec_ = kSbMediaVideoCodecNone;
   int resolution_width_ = -1;
diff --git a/src/cobalt/media/media_module.cc b/src/cobalt/media/media_module.cc
index b70b045..3f6671d 100644
--- a/src/cobalt/media/media_module.cc
+++ b/src/cobalt/media/media_module.cc
@@ -14,6 +14,9 @@
 
 #include "cobalt/media/media_module.h"
 
+#include <algorithm>
+#include <string>
+#include <utility>
 #include <vector>
 
 #include "base/bind.h"
@@ -37,6 +40,57 @@
 
 namespace {
 
+// TODO: Determine if ExtractCodecs() and ExtractEncryptionScheme() can be
+// combined and simplified.
+static std::vector<std::string> ExtractCodecs(const std::string& mime_type) {
+  std::vector<std::string> codecs;
+  std::vector<std::string> components = base::SplitString(
+      mime_type, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  LOG_IF(WARNING, components.empty())
+      << "argument mime type \"" << mime_type << "\" is not valid.";
+  // The first component is the type/subtype pair. We want to iterate over the
+  // remaining components to search for the codecs.
+  auto iter = components.begin() + 1;
+  for (; iter != components.end(); ++iter) {
+    std::vector<std::string> name_and_value = base::SplitString(
+        *iter, "=", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+    if (name_and_value.size() != 2) {
+      LOG(WARNING) << "parameter for mime_type \"" << mime_type
+                   << "\" is not valid.";
+      continue;
+    }
+    if (name_and_value[0] == "codecs") {
+      ParseCodecString(name_and_value[1], &codecs, /* strip= */ false);
+      return codecs;
+    }
+  }
+  return codecs;
+}
+
+static std::string ExtractEncryptionScheme(const std::string& key_system) {
+  std::vector<std::string> components = base::SplitString(
+      key_system, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+
+  auto iter = components.begin();
+  for (; iter != components.end(); ++iter) {
+    std::vector<std::string> name_and_value = base::SplitString(
+        *iter, "=", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+    if (name_and_value.size() != 1 && name_and_value.size() != 2) {
+      LOG(WARNING) << "parameter for key_system \"" << key_system
+                   << "\" is not valid.";
+      continue;
+    }
+    if (name_and_value[0] == "encryptionscheme") {
+      if (name_and_value.size() < 2) {
+        return "";
+      }
+      base::RemoveChars(name_and_value[1], "\"", &name_and_value[1]);
+      return name_and_value[1];
+    }
+  }
+  return "";
+}
+
 class CanPlayTypeHandlerStarboard : public CanPlayTypeHandler {
  public:
   void SetDisabledMediaCodecs(
@@ -48,6 +102,15 @@
               << "\" from console/command line.";
   }
 
+  void SetDisabledMediaEncryptionSchemes(
+      const std::string& disabled_encryption_schemes) override {
+    disabled_encryption_schemes_ =
+        base::SplitString(disabled_encryption_schemes, ";",
+                          base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+    LOG(INFO) << "Disabled encryption scheme(s) \""
+              << disabled_encryption_schemes << "\" from command line.";
+  }
+
   SbMediaSupportType CanPlayProgressive(
       const std::string& mime_type) const override {
     // |mime_type| is something like:
@@ -72,31 +135,6 @@
   }
 
  private:
-  std::vector<std::string> ExtractCodecs(const std::string& mime_type) const {
-    std::vector<std::string> codecs;
-    std::vector<std::string> components = base::SplitString(
-        mime_type, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-    LOG_IF(WARNING, components.empty())
-        << "argument mime type \"" << mime_type << "\" is not valid.";
-    // The first component is the type/subtype pair. We want to iterate over the
-    // remaining components to search for the codecs.
-    auto iter = components.begin() + 1;
-    for (; iter != components.end(); ++iter) {
-      std::vector<std::string> name_and_value = base::SplitString(
-          *iter, "=", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-      if (name_and_value.size() != 2) {
-        LOG(WARNING) << "parameter for mime_type \"" << mime_type
-                     << "\" is not valid.";
-        continue;
-      }
-      if (name_and_value[0] == "codecs") {
-        ParseCodecString(name_and_value[1], &codecs, /* strip= */ false);
-        return codecs;
-      }
-    }
-    return codecs;
-  }
-
   SbMediaSupportType CanPlayType(const std::string& mime_type,
                                  const std::string& key_system) const {
     if (!disabled_media_codecs_.empty()) {
@@ -111,6 +149,17 @@
         }
       }
     }
+
+    if (!disabled_encryption_schemes_.empty()) {
+      std::string encryption_scheme = ExtractEncryptionScheme(key_system);
+      if (std::find(disabled_encryption_schemes_.begin(),
+                    disabled_encryption_schemes_.end(),
+                    encryption_scheme) != disabled_encryption_schemes_.end()) {
+        LOG(INFO) << "Encryption scheme (" << encryption_scheme
+                  << ") is disabled via console/command line.";
+        return kSbMediaSupportTypeNotSupported;
+      }
+    }
     SbMediaSupportType type =
         SbMediaCanPlayMimeAndKeySystem(mime_type.c_str(), key_system.c_str());
     return type;
@@ -118,6 +167,9 @@
 
   // List of disabled media codecs that will be treated as unsupported.
   std::vector<std::string> disabled_media_codecs_;
+  // List of disabled DRM encryption schemes that will be treated as
+  // unsupported.
+  std::vector<std::string> disabled_encryption_schemes_;
 };
 
 }  // namespace
diff --git a/src/cobalt/site/docs/codelabs/cobalt_extensions/codelab.md b/src/cobalt/site/docs/codelabs/cobalt_extensions/codelab.md
index 8fc180c..76eda31 100644
--- a/src/cobalt/site/docs/codelabs/cobalt_extensions/codelab.md
+++ b/src/cobalt/site/docs/codelabs/cobalt_extensions/codelab.md
@@ -108,7 +108,7 @@
 because it makes rebasing to future versions of Cobalt more difficult but has
 been possible because porters have historically built **both** Cobalt and
 Starboard. However, Cobalt is moving toward Evergreen
-(<a href="https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/doc/evergreen/cobalt_evergreen_overview.md">overview</a>),
+(<a href="https://cobalt.googlesource.com/cobalt/+/refs/heads/master/starboard/doc/evergreen/cobalt_evergreen_overview.md">overview</a>),
 an architecture that enables automatic Cobalt updates on devices by separating a
 Google-built, Cobalt core shared library from the partner-built Starboard layer
 and Cobalt loader app. Because Cobalt core code is built by Google, custom
diff --git a/src/cobalt/site/docs/development/setup-android.md b/src/cobalt/site/docs/development/setup-android.md
index f999d81..b2ddb55 100644
--- a/src/cobalt/site/docs/development/setup-android.md
+++ b/src/cobalt/site/docs/development/setup-android.md
@@ -224,9 +224,7 @@
 run that on a device, it needs to be packaged into an APK, which is done by the
 associated "deploy" target (e.g. nplb_deploy). The Starboard test runner does
 all this for you, so just use that to build and run tests. For example, to
-build and run "devel" NPLB on an ARM64 device, from the top 'src' directory
-(if you've unnested the 'src' directory, just run this from your top-level
-directory):
+build and run "devel" NPLB on an ARM64 device, from the top-level directory:
 
 ```
 starboard/tools/testing/test_runner.py -p android-arm64 -c devel -b -r -t nplb
diff --git a/src/cobalt/site/docs/development/setup-linux.md b/src/cobalt/site/docs/development/setup-linux.md
index 014394d..288711b 100644
--- a/src/cobalt/site/docs/development/setup-linux.md
+++ b/src/cobalt/site/docs/development/setup-linux.md
@@ -57,23 +57,17 @@
 
 ### Set up Developer Tools
 
-Cobalt's developer tools require a different file structure which we are in the
-process of moving to. For now, if you want to use these tools, you must unnest
-the `src/` directory like so:
+1.  Enter your new `cobalt` directory:
 
-```
-$ cd cobalt
-$ mv src/* ./
-$ mv src/.* ./
-```
+    ```
+    $ cd cobalt
+    ```
 
-Once you do that, you'll be able to follow the following two steps to have C++
-and Python linting and formatting as well as other helpful checks enabled. Keep
-in mind that after doing this, you'll want to run following commands in the
-top-level directory instead of the `src/` subdirectory.
-
-Git will track this as a large change, we recommend that you create a commit
-for it and rebase that commit of our upstream continuously for now.
+<aside class="note">
+<b>Note:</b> Pre-commit is only available on branches later than 22.lts.1+,
+including trunk. The below commands will fail on 22.lts.1+ and earlier branches.
+For earlier branches, run `cd src` and move on to the next section.
+</aside>
 
 1.  Create a Python 3 virtual environment for working on Cobalt (feel free to use `virtualenvwrapper` instead):
 
@@ -87,15 +81,14 @@
 
     ```
     $ pre-commit install -t post-checkout -t pre-commit -t pre-push --allow-missing-config
-    $ git checkout -b <my-branch-name> origin/COBALT
+    $ git checkout -b <my-branch-name> origin/master
     ```
 
 ## Build and Run Cobalt
 
-1.  Build the code by navigating to the `src` directory in your new
-    `cobalt` directory and running the following command. You must
-    specify a platform when running this command. On Ubuntu Linux, the
-    canonical platform is `linux-x64x11`.
+1.  Build the code running the following command in the top-level `cobalt`
+    directory. You must specify a platform when running this command. On Ubuntu
+    Linux, the canonical platform is `linux-x64x11`.
 
     You can also use the `-C` command-line flag to specify a `build_type`.
     Valid build types are `debug`, `devel`, `qa`, and `gold`. If you
@@ -109,7 +102,7 @@
     <aside class="note"><b>Important:</b> You need to rerun gyp_cobalt every
     time a change is made to a `.gyp` file.</aside>
 
-1.  Compile the code from the `src/` directory:
+1.  Compile the code from the `cobalt/` directory:
 
     ```
     $ ninja -C out/<platform>_<build_type> <target_name>
@@ -155,7 +148,7 @@
 
     The flags in the following table are frequently used, and the full set
     of flags that this command supports are in <code><a
-    href="https://cobalt.googlesource.com/cobalt/+/master/src/cobalt/browser/switches.cc">cobalt/browser/switches.cc</a></code>.
+    href="https://cobalt.googlesource.com/cobalt/+/master/cobalt/browser/switches.cc">cobalt/browser/switches.cc</a></code>.
 
     <table class="details responsive">
       <tr>
diff --git a/src/cobalt/site/docs/development/setup-raspi.md b/src/cobalt/site/docs/development/setup-raspi.md
index 0e58a7d..48ca4cc 100644
--- a/src/cobalt/site/docs/development/setup-raspi.md
+++ b/src/cobalt/site/docs/development/setup-raspi.md
@@ -106,19 +106,14 @@
 
 ## Build, install, and run Cobalt for Raspberry Pi
 
-<aside class="note">
-<b>Note:</b> If you've unnested the 'src' directory, build and compile the code
-in your top-level checkout of Cobalt instead of the 'src' subdirectory.
-</aside>
-
-1.  Build the code by navigating to the src directory in your cobalt directory and run the
-    following command :
+1.  Build the code by navigating to the `cobalt` directory and run the
+    following command:
 
     ```
     $ cobalt/build/gyp_cobalt raspi-2
     ```
 
-1.  Compile the code from the `src/` directory:
+1.  Compile the code from the `cobalt/` directory:
 
     ```
     $ ninja -C out/raspi-2_debug cobalt
diff --git a/src/cobalt/site/docs/gen/starboard/build/doc/gn_migrate_stub_to_platform.md b/src/cobalt/site/docs/gen/starboard/build/doc/gn_migrate_stub_to_platform.md
index f717d32..12c9356 100644
--- a/src/cobalt/site/docs/gen/starboard/build/doc/gn_migrate_stub_to_platform.md
+++ b/src/cobalt/site/docs/gen/starboard/build/doc/gn_migrate_stub_to_platform.md
@@ -17,14 +17,14 @@
 Here are the steps to do your migration:
 
 1.  [Copy stub files over to your platform and build them](#copy-stub-files-over-to-your-platform-and-build-them).
-2.  [Replace stub toolchain with your platform's toolchain](#replace-stub-toolchain-with-your-platforms-toolchain).
-3.  [Replace stub configuration with your platform's configuration](#replace-stub-configuration-with-your-platforms-configuration).
-4.  [Replace stubbed starboard_platform target sources with your platform's
-    sources](#replace-stubbed-starboardplatform-sources-with-your-platforms-sources).
+2.  [Replace stub toolchain with toolchain for your platform](#replace-stub-toolchain-with-toolchain-for-your-platform).
+3.  [Replace stub configuration with configuration for your platform](#replace-stub-configuration-with-configuration-for-your-platform).
+4.  [Replace stubbed starboard_platform sources with sources for your platform](#replace-stubbed-starboard_platform-sources-with-sources-for-your-platform).
 
-After each step, you should be able to build the starboard_platform target.
-For example, you would build raspi2 starboard_platform target with the following
+After each step, you should be able to build the starboard_platform target. For
+example, you would build raspi2 starboard_platform target with the following
 commands:
+
 ```
 $gn gen out/raspi-2gn_devel --args='target_platform="raspi-2" build_type="devel"'
 $ninja -C out/raspi-2gn_devel/ starboard
@@ -47,41 +47,43 @@
     *   starboard/stub/toolchain/BUILD.gn >
         starboard/YOUR_PLATFORM/toolchain/BUILD.gn
 2.  Add your platform path to starboard/build/platforms.gni as referenced
-    [here](../migrating_gyp_to_gn.md#adding-your-platform-to-starboard)
+    [here](./migrating_gyp_to_gn.md#adding-your-platform-to-starboard)
 3.  Resolve any errors which come up for missing/incorrect file paths. Then, you
     should be able to build your platform target with the stubbed out files
     suggested in the above section.
 
-### Replace Stub Toolchain with Your Platform's Toolchain
+### Replace Stub Toolchain with Toolchain for Your Platform
 
-Follow instructions [here](../migrating_gyp_to_gn.md#migrating-a-toolchain) for
+Follow instructions [here](./migrating_gyp_to_gn.md#migrating-a-toolchain) for
 migrating the toolchain. Resolve errors and build the starboard_platform target
 with the stubbed files.
 
-### Replace Stub Configuration with Your Platform's Configuration
+### Replace Stub Configuration with Configuration for Your Platform
 
 This involves migrating the compiler flags and build variables as referenced
-[here](../migrating_gyp_to_gn.md#migrating-a-platform).
+[here](./migrating_gyp_to_gn.md#migrating-a-platform).
 
 > **Highly recommended** \
-> It’s good to turn off the `treat_warnings_as_errors flag` until you can compile
-> the starboard_platform target with the platform files.
-> If this flag is not disabled you might run into a lot of
-> warnings turned errors and it might take time to solve all those errors.
-> Meanwhile you won't be in a buildable state which might make it uncertain as to
-> how much progress you are actually making.
-> For disabling the flag you can pass that as an argument to gn.
-> Here's an example for disabling the flag for raspi2:
+> It’s good to turn off the `treat_warnings_as_errors flag` until you can
+> compile the starboard_platform target with the platform files. If this flag is
+> not disabled you might run into a lot of warnings turned errors and it might
+> take time to solve all those errors. Meanwhile you won't be in a buildable
+> state which might make it uncertain as to how much progress you are actually
+> making. For disabling the flag you can pass that as an argument to gn. Here's
+> an example for disabling the flag for raspi2:
+>
 > ```
-> $gn gen out/raspi-2gn_devel --args='target_platform="raspi-2" build_type="devel" treat_warnings_as_errors=false'
+> $gn gen out/raspi-2gn_devel
+> --args='target_platform="raspi-2" build_type="devel"
+> treat_warnings_as_errors=false'
 > ```
 
 Resolve errors and build the starboard_platform target with the stubbed files.
 
-### Replace Stubbed starboard_platform Sources with Your Platform's Sources
+### Replace Stubbed starboard_platform Sources with Sources for Your Platform
 
 This involves adding files for the starboard_platform target as suggested
-[here](../migrating_gyp_to_gn.md#migrating-a-platform).
+[here](./migrating_gyp_to_gn.md#migrating-a-platform).
 
 While building any target, follow the recommendation above of building the
 target with `treat_warnings_as_errors=false`.
@@ -118,15 +120,16 @@
     *   Are the compiler flags for this file the same as in GYP ?
 
         > To compare flags for GYP vs GN refer
-        > [section](../migrating_gyp_to_gn.md#validating-a-target). To check if
+        > [section](./migrating_gyp_to_gn.md#validating-a-target). To check if
         > the variables/flags you are compiling have changed since GYP, refer
-        > [page](../migration_changes.md).
+        > [page](./migration_changes.md).
 
     *   Have you passed in the default arguments for your platform correctly?
 
         > Default variables such as `target_cpu`, `target_os` and others can be
         > overridden by passing arguments to gn while building. Here's an
         > example of passing the default argument `target_cpu` for raspi2:
+        >
         > ```
         > $gn gen out/raspi-2gn_devel --args='target_platform="raspi-2" build_type="devel" target_cpu="arm"'
         > ```
diff --git a/src/cobalt/site/docs/gen/starboard/build/doc/migrating_gyp_to_gn.md b/src/cobalt/site/docs/gen/starboard/build/doc/migrating_gyp_to_gn.md
index 07b4968..3a9c87e 100644
--- a/src/cobalt/site/docs/gen/starboard/build/doc/migrating_gyp_to_gn.md
+++ b/src/cobalt/site/docs/gen/starboard/build/doc/migrating_gyp_to_gn.md
@@ -83,7 +83,8 @@
 ```
 
 You also may need to remove default configs. The default configs are listed in
-[BUILDCONFIG.gn](../config/BUILDCONFIG.gn). You remove a config like so:
+[BUILDCONFIG.gn](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/starboard/build/config/BUILDCONFIG.gn).
+You remove a config like so:
 
 ```
 static_library("foo") {
@@ -148,8 +149,9 @@
 
 Instead of implicitly searching directories for certain files like GYP did, we
 explicitly enumerate our ports and their locations.
-[platforms.gni](../platforms.gni) contains all of this information, and you'll
-need to add your platform to that list following the same format.
+[platforms.gni](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/starboard/build/platforms.gni)
+contains all of this information, and you'll need to add your platform to that
+list following the same format.
 
 ### Migrating a Family of Platforms
 
@@ -175,10 +177,10 @@
 
 You may define a toolchain from scratch following the [reference][gn_toolchain],
 or you can use the
-[gcc/clang templates](../../../build/toolchain/gcc_toolchain.gni) provided.
-Almost all of the reference platforms use these templates, so look to those as
-examples for how to use it correctly. Here's the linux-x64x11
-[toolchain/BUILD.gn file](../../linux/x64x11/toolchain/BUILD.gn).
+[gcc/clang templates](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/build/toolchain/gcc_toolchain.gni)
+provided. Almost all of the reference platforms use these templates, so look to
+those as examples for how to use it correctly. Here's the linux-x64x11
+[toolchain/BUILD.gn file](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/starboard/linux/x64x11/toolchain/BUILD.gn).
 
 ## Checking Your Migration
 
@@ -203,29 +205,36 @@
 ```
 
 If this was equivalent to a GYP target, you can compare the ninja compilation
-databases by using [format_ninja.py](../../../tools/format_ninja.py) and a
-comparison tool, i.e. [meld](https://meldmerge.org/). This will allow you to see
-any changes in commands, i.e. with flags or otherwise.
+databases by using
+[format_ninja.py](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/tools/format_ninja.py)
+and a comparison tool, i.e. [meld](https://meldmerge.org/). This will allow you
+to see any changes in commands, i.e. with flags or otherwise.
 
 The following differences for ninja flags between GYP and GN don't cause any
 issues:
 
-1. The name of the intermediate .o, .d files is different in both cases: Here is
-   an example while compiling the same source file
-   ```
-   starboard/common/new.cc
-   ```
-   GYP generates:
-   ```
-   obj/starboard/common/common.new.cc.o
-   ```
-   GN generates:
-   ```
-   obj/starboard/common/common/new.o
-   ```
-2. The `-x` flag for specifying language is not present in GN migration.
-   For example GYP specifies `-x c` flag while building c language files for
-   certain targets. This flag is not specified while building any GN targets.
+1.  The name of the intermediate .o, .d files is different in both cases: Here
+    is an example while compiling the same source file
+
+    ```
+    starboard/common/new.cc
+    ```
+
+    GYP generates:
+
+    ```
+    obj/starboard/common/common.new.cc.o
+    ```
+
+    GN generates:
+
+    ```
+    obj/starboard/common/common/new.o
+    ```
+
+1.  The `-x` flag for specifying language is not present in GN migration. For
+    example GYP specifies `-x c` flag while building c language files for
+    certain targets. This flag is not specified while building any GN targets.
 
 ### Validating a Platform
 
@@ -240,7 +249,7 @@
 
 ### Step by Step Stub to Your Platform Migration Guide
 
-This [document](../gn_migrate_stub_to_platform.md) outlines a step by step
+This [document](./gn_migrate_stub_to_platform.md) outlines a step by step
 process for converting the stub platform's GN files to GN files that will be
 able to be built for your platform.
 
diff --git a/src/cobalt/site/docs/gen/starboard/build/doc/migration_changes.md b/src/cobalt/site/docs/gen/starboard/build/doc/migration_changes.md
index 38d8477..83f3dfa 100644
--- a/src/cobalt/site/docs/gen/starboard/build/doc/migration_changes.md
+++ b/src/cobalt/site/docs/gen/starboard/build/doc/migration_changes.md
@@ -19,6 +19,7 @@
 `has_drm_system_extension`                | `is_internal_build` (true/false)                     | (global)
 `has_cdm`                                 | `is_internal_build` (true/false)                     | (global)
 `has_private_system_properties`           | `is_internal_build` (true/false)                     | (global)
+`sb_pedantic_warnings` (0/1)              | `has_pedantic_warnings` (true/false)                 | (global, see "Compiler Options" note)
 `sb_deploy_output_dir`                    | `sb_install_output_dir`                              | `//starboard/build/config/base_configuration.gni`
 `sb_evergreen` (0/1)                      | `sb_is_evergreen` (true/false)                       | `//starboard/build/config/base_configuration.gni`
 `sb_evergreen_compatible` (0/1)           | `sb_is_evergreen_compatible` (true/false)            | `//starboard/build/config/base_configuration.gni`
@@ -42,10 +43,8 @@
 `optimize_target_for_speed` (1) | `"//starboard/build/config:speed"`                    | Optimizations
 `compiler_flags_*_speed`        | `speed_config_path`                                   | Optimizations
 `compiler_flags_*_size`         | `size_config_path`                                    | Optimizations
-`sb_pedantic_warnings`          | `pedantic_warnings_config_path`                       | Compiler Options
-`sb_pedantic_warnings`          | `no_pedantic_warnings_config_path`                    | Compiler Options
 
-Notes:
+## Notes:
 
 *   *Starboard Implementation:* If your platform defined
     `STARBOARD_IMPLENTATION` in its implementation, you would now add the above
@@ -60,13 +59,12 @@
     correct ones for `speed_config_path` and `size_config_path` in your
     platform's `platform_configuration/configuration.gni` file.
 
-*   *Compiler Options:* Cobalt compiles some targets with stricter settings
-    than others, depending on the platform. Before these targets would opt into
-    the stricter settings by settings `sb_pedantic_warnings: 1` in their
-    `variables` section. Now they will add the appropriate config like so:
-    `configs += [ "//starboard/build/config:pedantic_warnings" ]` and remove
-    the default: `configs -= [ "//starboard/build/config:no_pedantic_warnings"
-    ]`. The additional config that is used to compile these targets is
-    specified with the `pedantic_warnings_config_path` and
-    `no_pedantic_warnings_config_path` variables in your platform's
-    `platform_configuration/configuration.gni` file.
+*   *Compiler Options:* Cobalt compiles some targets with stricter,
+    platform-dependent settings than others. Before these targets would opt into
+    the stricter settings by setting `sb_pedantic_warnings: 1` in their
+    `variables` section. Now targets will be compiled with pedantic warnings if
+    the target sets `has_pedantic_warnings=true`. The additional config that is
+    used to compile these targets is specified with the
+    `pedantic_warnings_config_path` and `no_pedantic_warnings_config_path`
+    variables in your platform's `platform_configuration/configuration.gni`
+    file.
diff --git a/src/cobalt/site/docs/gen/starboard/doc/c99.md b/src/cobalt/site/docs/gen/starboard/doc/c99.md
index 3aed66d..2c5f7a5 100644
--- a/src/cobalt/site/docs/gen/starboard/doc/c99.md
+++ b/src/cobalt/site/docs/gen/starboard/doc/c99.md
@@ -35,13 +35,63 @@
 * tolower
 * toupper
 ### <math.h>
+* acos
+* acosf
+* asin
+* asinf
+* atan
+* atan2
+* atan2f
+* atanf
+* cbrt
+* cbrtf
+* ceil
+* ceilf
+* cos
+* cosf
+* div
+* erf
+* erff
+* exp
+* expf
+* exp2f
 * fabs
 * floor
+* floorf
+* fmod
+* fmodf
+* frexp
 * isfinite
 * isnan
+* labs
+* llround
+* llroundf
+* log
+* log10
+* log10f
+* log2
+* log2f
+* ldexp
+* lrint
+* lrintf
+* modf
+* nearbyint
+* nearbyintf
+* nextafter
+* nextafterf
 * pow
+* powf
+* round
+* roundf
+* scalbn
+* sin
+* sinf
 * sqrt
 * sqrtf
+* tan
+* tanf
+* trunc
+* truncf
 ### <stdlib.h>
 * abs
 * atoi
@@ -70,6 +120,9 @@
 * strrchr
 * strstr
 * strspn
+### <tgmath.h>
+* ceill
+* logl
 ### <wchar.h>
 * wcscat
 * wcschr
diff --git a/src/cobalt/site/docs/gen/starboard/doc/evergreen/cobalt_evergreen_overview.md b/src/cobalt/site/docs/gen/starboard/doc/evergreen/cobalt_evergreen_overview.md
index 4e07d8e..8d770ca 100644
--- a/src/cobalt/site/docs/gen/starboard/doc/evergreen/cobalt_evergreen_overview.md
+++ b/src/cobalt/site/docs/gen/starboard/doc/evergreen/cobalt_evergreen_overview.md
@@ -507,16 +507,17 @@
 
 ### Fonts
 The system font directory `kSbSystemPathFontDirectory` should be configured to
-point to the `standard` (23MB) or the `limited` (3.1MB) cobalt font packages. An
-easy way to do that is to use the `kSbSystemPathContentDirectory` to contain
-the system font directory and setting the `cobalt_font_package` to `standard` or
-`limited` in your port.
+point to either the system fonts on the device or the Cobalt `standard` (23MB)
+or the Cobalt `limited` (3.1MB) font packages. An easy way to use the Cobalt
+fonts is to set `kSbSystemPathFontDirectory` to point to
+`kSbSystemPathContentDirectory/fonts` and configure `cobalt_font_package` to
+`standard` or `limited` in your port.
 
 Cobalt Evergreen (built by Google), will by default use the `empty` font
 package to minimize storage requirements. A separate
 `cobalt_font_package` variable is set to `empty` in the Evergreen platform.
 
-On Raspberry Pi this is:
+On Raspberry Pi the Cobalt fonts are configured the following way:
 
 `empty` set of fonts under:
 ```
diff --git a/src/cobalt/site/docs/starboard/porting.md b/src/cobalt/site/docs/starboard/porting.md
index aee5b59..c6e635a 100644
--- a/src/cobalt/site/docs/starboard/porting.md
+++ b/src/cobalt/site/docs/starboard/porting.md
@@ -71,9 +71,9 @@
 Add the following directories to the source tree for the `<family-name>`
 that you selected in step 1:
 
-*   `src/third_party/starboard/<family-name>/`
+*   `third_party/starboard/<family-name>/`
 
-*   `src/third_party/starboard/<family-name>/shared/`
+*   `third_party/starboard/<family-name>/shared/`
 
     This subdirectory contains code that is shared between architectures
     within a product family. For example, if BobBox devices run on many
@@ -82,15 +82,15 @@
     Starboard function implementation, BobCo can put that function in the
     `shared` directory to make it accessible in every binary variant.
 
-*   `src/third_party/starboard/<family-name>/<binary-variant>/`
+*   `third_party/starboard/<family-name>/<binary-variant>/`
 
     You should create one directory for _each_ `<binary-variant>`. So, for
     example, BobCo could create the following directories:
 
-    *   `src/third_party/starboard/bobbox/shared/`
-    *   `src/third_party/starboard/bobbox/armeb/`
-    *   `src/third_party/starboard/bobbox/armel/`
-    *   `src/third_party/starboard/bobbox/armel/gles/`
+    *   `third_party/starboard/bobbox/shared/`
+    *   `third_party/starboard/bobbox/armeb/`
+    *   `third_party/starboard/bobbox/armel/`
+    *   `third_party/starboard/bobbox/armel/gles/`
 
 Again, functions that work for all of the configurations would go in the
 `shared` directory. Functions that work for all little-endian devices would go
@@ -110,7 +110,7 @@
 *   `thread_types_public.h`
 
 We recommend that you copy the files from the Stub reference implementation,
-located at `src/starboard/stub/` to your `binary-variant` directories.
+located at `starboard/stub/` to your `binary-variant` directories.
 In this approach, you will essentially start with a clean slate of stub
 interfaces that need to be modified to work with your platform.
 
@@ -122,8 +122,8 @@
 command for each `binary-variant` directory:
 
 ```sh
-cp -R src/starboard/stub
-      src/third_party/starboard/<family-name>/<binary-variant>
+cp -R starboard/stub
+      third_party/starboard/<family-name>/<binary-variant>
 ```
 
 After copying these files, you should be able to compile Cobalt and link it
@@ -304,20 +304,20 @@
 The `starboard_platform.gyp` file points to all of the source files included
 in your new Starboard implementation. If you are starting with a copy of the
 Stub implementation, then that file will initially include a lot of files
-from the `src/starboard/shared/stub/` directory. Similarly, if you are starting
+from the `starboard/shared/stub/` directory. Similarly, if you are starting
 starting with a copy of the Desktop Linux port, then that file will initially
-point to files in the `src/starboard/shared/posix` directory.
+point to files in the `starboard/shared/posix` directory.
 
 The modules are defined so that each one has a set of functions, and each
 function is defined in its own file with a consistent naming convention.
 For example, the `SbSystemBreakIntoDebugger()` function is defined in the
 `system_break_into_debugger.cc` file. The list of files in the
-`src/starboard/shared/stub/` directory represents an authoritative list of
+`starboard/shared/stub/` directory represents an authoritative list of
 supported functions.
 
 Function-by-function and module-by-module, you can now replace stub
 implementations with either custom implementations or with other ported
-implementations from the `src/starboard/shared/` directory until you have
+implementations from the `starboard/shared/` directory until you have
 gone through all of the modules. As you do, update the
 `starboard_platform.gyp` file to identify the appropriate source files.
 
diff --git a/src/cobalt/site/docs/starboard/testing.md b/src/cobalt/site/docs/starboard/testing.md
index fd2f502..16eea52 100644
--- a/src/cobalt/site/docs/starboard/testing.md
+++ b/src/cobalt/site/docs/starboard/testing.md
@@ -21,11 +21,11 @@
 
 ## Test Organization
 
-NPLB tests can be found in the `src/starboard/nplb/` directory and are broken
+NPLB tests can be found in the `starboard/nplb/` directory and are broken
 out into files by Starboard module and function:
 
 ```
-src/starboard/nplb/<module>_<function>_test.cc
+starboard/nplb/<module>_<function>_test.cc
 ```
 
 Although each test may incidentally test other functions, there is an attempt
@@ -36,4 +36,4 @@
 For example, the `SbSocketSendTo` and `SbSocketReceiveFrom` are tested
 together, ensuring that the API is self-consistent on both sides of a
 connection. Therefore, only one set of tests exist to cover those use cases,
-in `src/starboard/nplb/socket_receive_from_test.cc`.
+in `starboard/nplb/socket_receive_from_test.cc`.
diff --git a/src/cobalt/updater/configurator.cc b/src/cobalt/updater/configurator.cc
index b8df78b..d26d9a4 100644
--- a/src/cobalt/updater/configurator.cc
+++ b/src/cobalt/updater/configurator.cc
@@ -64,6 +64,7 @@
       network_fetcher_factory_(
           base::MakeRefCounted<NetworkFetcherFactoryCobalt>(network_module)),
       patch_factory_(base::MakeRefCounted<PatcherFactory>()) {
+  LOG(INFO) << "Configurator::Configurator";
   const std::string persisted_channel =
       persisted_data_->GetUpdaterChannel(GetAppGuid());
   if (persisted_channel.empty()) {
@@ -75,7 +76,7 @@
     user_agent_string_ = network_module->GetUserAgent();
   }
 }
-Configurator::~Configurator() = default;
+Configurator::~Configurator() { LOG(INFO) << "Configurator::~Configurator"; }
 
 int Configurator::InitialDelay() const { return 0; }
 
diff --git a/src/cobalt/updater/network_fetcher.cc b/src/cobalt/updater/network_fetcher.cc
index 1dfbc7c..c70b636 100644
--- a/src/cobalt/updater/network_fetcher.cc
+++ b/src/cobalt/updater/network_fetcher.cc
@@ -52,7 +52,7 @@
 }
 
 // Returns the integral value of a header of the server response or -1 if
-// if the header is not available or a conversion error has occured.
+// if the header is not available or a conversion error has occurred.
 int64_t GetInt64Header(const net::HttpResponseHeaders* headers,
                        const char* header_name) {
   if (!headers) {
@@ -68,9 +68,13 @@
 namespace updater {
 
 NetworkFetcher::NetworkFetcher(const network::NetworkModule* network_module)
-    : network_module_(network_module) {}
+    : network_module_(network_module) {
+  LOG(INFO) << "cobalt::updater::NetworkFetcher::NetworkFetcher";
+}
 
-NetworkFetcher::~NetworkFetcher() {}
+NetworkFetcher::~NetworkFetcher() {
+  LOG(INFO) << "cobalt::updater::NetworkFetcher::~NetworkFetcher";
+}
 
 void NetworkFetcher::PostRequest(
     const GURL& url, const std::string& post_data,
@@ -80,8 +84,9 @@
     PostRequestCompleteCallback post_request_complete_callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  SB_LOG(INFO) << "PostRequest url = " << url;
-  SB_LOG(INFO) << "PostRequest post_data = " << post_data;
+  LOG(INFO) << "cobalt::updater::NetworkFetcher::PostRequest";
+  LOG(INFO) << "PostRequest url = " << url;
+  LOG(INFO) << "PostRequest post_data = " << post_data;
 
   response_started_callback_ = std::move(response_started_callback);
   progress_callback_ = std::move(progress_callback);
@@ -111,8 +116,9 @@
     DownloadToFileCompleteCallback download_to_file_complete_callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  SB_LOG(INFO) << "DownloadToFile url = " << url;
-  SB_LOG(INFO) << "DownloadToFile file_path = " << file_path;
+  LOG(INFO) << "cobalt::updater::NetworkFetcher::DownloadToFile";
+  LOG(INFO) << "DownloadToFile url = " << url;
+  LOG(INFO) << "DownloadToFile file_path = " << file_path;
 
   response_started_callback_ = std::move(response_started_callback);
   progress_callback_ = std::move(progress_callback);
@@ -129,15 +135,15 @@
   url_fetcher_->Start();
 }
 
-void NetworkFetcher::CancelDownloadToFile() {
+void NetworkFetcher::Cancel() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  SB_LOG(INFO) << "Canceling DownloadToFile";
+  LOG(INFO) << "cobalt::updater::NetworkFetcher::Cancel";
   url_fetcher_.reset();
 }
 
 void NetworkFetcher::OnURLFetchResponseStarted(const net::URLFetcher* source) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  LOG(INFO) << "cobalt::updater::NetworkFetcher::OnURLFetchResponseStarted";
   std::move(response_started_callback_)
       .Run(source->GetURL(), source->GetResponseCode(),
            source->GetResponseHeaders()
@@ -147,6 +153,7 @@
 
 void NetworkFetcher::OnURLFetchComplete(const net::URLFetcher* source) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  LOG(INFO) << "cobalt::updater::NetworkFetcher::OnURLFetchComplete";
   const net::URLRequestStatus& status = source->GetStatus();
   const int response_code = source->GetResponseCode();
   if (url_fetcher_type_ == kUrlFetcherTypePostRequest) {
@@ -169,6 +176,7 @@
                                                 int64_t current, int64_t total,
                                                 int64_t current_network_bytes) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  LOG(INFO) << "cobalt::updater::NetworkFetcher::OnURLFetchDownloadProgress";
 
   progress_callback_.Run(current);
 }
@@ -195,6 +203,7 @@
 
 void NetworkFetcher::OnPostRequestComplete(const net::URLFetcher* source,
                                            const int status_error) {
+  LOG(INFO) << "cobalt::updater::NetworkFetcher::OnPostRequestComplete";
   std::unique_ptr<std::string> response_body(new std::string);
   auto* download_data_writer =
       base::polymorphic_downcast<loader::URLFetcherStringWriter*>(
@@ -204,11 +213,10 @@
   }
 
   if (response_body->empty()) {
-    SB_LOG(ERROR) << "PostRequest got empty response.";
+    LOG(ERROR) << "PostRequest got empty response.";
   }
 
-  SB_LOG(INFO) << "OnPostRequestComplete response_body = "
-               << *response_body.get();
+  LOG(INFO) << "OnPostRequestComplete response_body = " << *response_body.get();
 
   net::HttpResponseHeaders* response_headers = source->GetResponseHeaders();
   std::move(post_request_complete_callback_)
@@ -221,11 +229,12 @@
 
 void NetworkFetcher::OnDownloadToFileComplete(const net::URLFetcher* source,
                                               const int status_error) {
+  LOG(INFO) << "cobalt::updater::NetworkFetcher::OnDownloadToFileComplete";
   base::FilePath response_file;
   if (!source->GetResponseAsFilePath(true, &response_file)) {
-    SB_LOG(ERROR) << "DownloadToFile failed to get response from a file";
+    LOG(ERROR) << "DownloadToFile failed to get response from a file";
   }
-  SB_LOG(INFO) << "OnDownloadToFileComplete response_file = " << response_file;
+  LOG(INFO) << "OnDownloadToFileComplete response_file = " << response_file;
 
   std::move(download_to_file_complete_callback_)
       .Run(response_file, status_error,
@@ -236,8 +245,9 @@
 
 NetworkFetcher::ReturnWrapper NetworkFetcher::HandleError(
     const std::string& message) {
+  LOG(ERROR) << "cobalt::updater::NetworkFetcher::HandleError message="
+             << message;
   url_fetcher_.reset();
-  SB_LOG(ERROR) << message;
   return ReturnWrapper();
 }
 
diff --git a/src/cobalt/updater/network_fetcher.h b/src/cobalt/updater/network_fetcher.h
index 17b8909..c32f74d 100644
--- a/src/cobalt/updater/network_fetcher.h
+++ b/src/cobalt/updater/network_fetcher.h
@@ -69,7 +69,7 @@
                       ProgressCallback progress_callback,
                       DownloadToFileCompleteCallback
                           download_to_file_complete_callback) override;
-  void CancelDownloadToFile() override;
+  void Cancel() override;
 
   // net::URLFetcherDelegate interface.
   void OnURLFetchResponseStarted(const net::URLFetcher* source) override;
diff --git a/src/cobalt/updater/prefs.cc b/src/cobalt/updater/prefs.cc
index 5745632..3e195bb 100644
--- a/src/cobalt/updater/prefs.cc
+++ b/src/cobalt/updater/prefs.cc
@@ -1,6 +1,16 @@
-// Copyright 2019 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.
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
 
 #include "cobalt/updater/prefs.h"
 
@@ -27,17 +37,17 @@
       static_cast<const CobaltExtensionInstallationManagerApi*>(
           SbSystemGetExtension(kCobaltExtensionInstallationManagerName));
   if (!installation_api) {
-    SB_LOG(ERROR) << "Failed to get installation manager api.";
+    LOG(ERROR) << "Failed to get installation manager api.";
     return nullptr;
   }
   char app_key[IM_EXT_MAX_APP_KEY_LENGTH];
   if (installation_api->GetAppKey(app_key, IM_EXT_MAX_APP_KEY_LENGTH) ==
       IM_EXT_ERROR) {
-    SB_LOG(ERROR) << "Failed to get app key.";
+    LOG(ERROR) << "Failed to get app key.";
     return nullptr;
   }
 
-  SB_LOG(INFO) << "Updater: prefs app_key=" << app_key;
+  LOG(INFO) << "Updater: prefs app_key=" << app_key;
   PrefServiceFactory pref_service_factory;
   std::string file_name = "prefs_";
   file_name += app_key;
diff --git a/src/cobalt/updater/unzipper.cc b/src/cobalt/updater/unzipper.cc
index 876eea6..1855b3a 100644
--- a/src/cobalt/updater/unzipper.cc
+++ b/src/cobalt/updater/unzipper.cc
@@ -1,6 +1,16 @@
-// Copyright 2019 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.
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
 
 #include "cobalt/updater/unzipper.h"
 
@@ -25,10 +35,10 @@
     std::move(callback).Run(zip::Unzip(zip_path, output_path));
     SbTimeMonotonic time_unzip_took =
         SbTimeGetMonotonicNow() - time_before_unzip;
-    SB_LOG(INFO) << "Unzip file path = " << zip_path;
-    SB_LOG(INFO) << "output_path = " << output_path;
-    SB_LOG(INFO) << "Unzip took " << time_unzip_took / kSbTimeMillisecond
-                 << " milliseconds.";
+    LOG(INFO) << "Unzip file path = " << zip_path;
+    LOG(INFO) << "output_path = " << output_path;
+    LOG(INFO) << "Unzip took " << time_unzip_took / kSbTimeMillisecond
+              << " milliseconds.";
   }
 };
 
diff --git a/src/cobalt/updater/updater.cc b/src/cobalt/updater/updater.cc
index 8f77701..9d8da63 100644
--- a/src/cobalt/updater/updater.cc
+++ b/src/cobalt/updater/updater.cc
@@ -94,7 +94,8 @@
   network::NetworkModule::Options network_options;
   network_module.reset(new network::NetworkModule(network_options));
 
-  updater_module.reset(new updater::UpdaterModule(network_module.get()));
+  updater_module.reset(new updater::UpdaterModule(
+      network_module.get(), kDefaultUpdateCheckDelaySeconds));
 
   return 0;
 }
diff --git a/src/cobalt/updater/updater_module.cc b/src/cobalt/updater/updater_module.cc
index 3941cb6..2cf09b5 100644
--- a/src/cobalt/updater/updater_module.cc
+++ b/src/cobalt/updater/updater_module.cc
@@ -26,10 +26,12 @@
 #include "base/run_loop.h"
 #include "base/stl_util.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/task/post_task.h"
 #include "base/task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/version.h"
+#include "cobalt/browser/switches.h"
 #include "cobalt/extension/installation_manager.h"
 #include "cobalt/updater/crash_client.h"
 #include "cobalt/updater/crash_reporter.h"
@@ -81,7 +83,10 @@
 namespace cobalt {
 namespace updater {
 
+const uint64_t kDefaultUpdateCheckDelaySeconds = 60;
+
 void Observer::OnEvent(Events event, const std::string& id) {
+  LOG(INFO) << "Observer::OnEvent id=" << id;
   std::string status;
   if (update_client_->GetCrxUpdateState(id, &crx_update_item_)) {
     auto status_iterator =
@@ -112,35 +117,51 @@
     status = "No status available";
   }
   updater_configurator_->SetUpdaterStatus(status);
-  SB_LOG(INFO) << "Updater status is " << status;
+  LOG(INFO) << "Updater status is " << status;
 }
 
-UpdaterModule::UpdaterModule(network::NetworkModule* network_module)
-    : updater_thread_("updater"), network_module_(network_module) {
-  updater_thread_.StartWithOptions(
+UpdaterModule::UpdaterModule(network::NetworkModule* network_module,
+                             uint64_t update_check_delay_sec)
+    : network_module_(network_module),
+      update_check_delay_sec_(update_check_delay_sec) {
+  LOG(INFO) << "UpdaterModule::UpdaterModule";
+  updater_thread_.reset(new base::Thread("Updater"));
+  updater_thread_->StartWithOptions(
       base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
 
   DETACH_FROM_THREAD(thread_checker_);
   // Initialize the underlying update client.
   is_updater_running_ = true;
-  updater_thread_.task_runner()->PostTask(
+  updater_thread_->task_runner()->PostTask(
       FROM_HERE,
       base::Bind(&UpdaterModule::Initialize, base::Unretained(this)));
+
+  // Mark the current installation as successful.
+  updater_thread_->task_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(&UpdaterModule::MarkSuccessful, base::Unretained(this)));
 }
 
 UpdaterModule::~UpdaterModule() {
+  LOG(INFO) << "UpdaterModule::~UpdaterModule";
   if (is_updater_running_) {
     is_updater_running_ = false;
-    updater_thread_.task_runner()->PostBlockingTask(
+    updater_thread_->task_runner()->PostBlockingTask(
         FROM_HERE,
         base::Bind(&UpdaterModule::Finalize, base::Unretained(this)));
   }
+
+  // Upon destruction the thread will allow all queued tasks to complete before
+  // the thread is terminated. The thread is destroyed before returning from
+  // this destructor to prevent one of the thread's tasks from accessing member
+  // fields after they are destroyed.
+  updater_thread_.reset();
 }
 
 void UpdaterModule::Suspend() {
   if (is_updater_running_) {
     is_updater_running_ = false;
-    updater_thread_.task_runner()->PostBlockingTask(
+    updater_thread_->task_runner()->PostBlockingTask(
         FROM_HERE,
         base::Bind(&UpdaterModule::Finalize, base::Unretained(this)));
   }
@@ -149,7 +170,7 @@
 void UpdaterModule::Resume() {
   if (!is_updater_running_) {
     is_updater_running_ = true;
-    updater_thread_.task_runner()->PostTask(
+    updater_thread_->task_runner()->PostTask(
         FROM_HERE,
         base::Bind(&UpdaterModule::Initialize, base::Unretained(this)));
   }
@@ -165,13 +186,17 @@
   update_client_->AddObserver(updater_observer_.get());
 
   // Schedule the first update check.
-  updater_thread_.task_runner()->PostDelayedTask(
+
+  LOG(INFO) << "Scheduling UpdateCheck with delay " << update_check_delay_sec_
+            << " seconds";
+  updater_thread_->task_runner()->PostDelayedTask(
       FROM_HERE, base::Bind(&UpdaterModule::Update, base::Unretained(this)),
-      base::TimeDelta::FromMinutes(1));
+      base::TimeDelta::FromSeconds(update_check_delay_sec_));
 }
 
 void UpdaterModule::Finalize() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  LOG(INFO) << "UpdaterModule::Finalize begin";
   update_client_->RemoveObserver(updater_observer_.get());
   updater_observer_.reset();
   update_client_->Stop();
@@ -186,27 +211,28 @@
   }
 
   updater_configurator_ = nullptr;
+  LOG(INFO) << "UpdaterModule::Finalize end";
 }
 
 void UpdaterModule::MarkSuccessful() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  LOG(INFO) << "UpdaterModule::MarkSuccessful";
 
   auto installation_manager =
       static_cast<const CobaltExtensionInstallationManagerApi*>(
           SbSystemGetExtension(kCobaltExtensionInstallationManagerName));
   if (!installation_manager) {
-    SB_LOG(ERROR) << "Updater failed to get installation manager extension.";
+    LOG(ERROR) << "Updater failed to get installation manager extension.";
     return;
   }
   int index = installation_manager->GetCurrentInstallationIndex();
   if (index == IM_EXT_ERROR) {
-    SB_LOG(ERROR) << "Updater failed to get current installation index.";
+    LOG(ERROR) << "Updater failed to get current installation index.";
     return;
   }
   if (installation_manager->MarkInstallationSuccessful(index) !=
       IM_EXT_SUCCESS) {
-    SB_LOG(ERROR)
-        << "Updater failed to mark the current installation successful";
+    LOG(ERROR) << "Updater failed to mark the current installation successful";
   }
 }
 
@@ -221,7 +247,7 @@
 
   const base::Version manifest_version(GetCurrentEvergreenVersion());
   if (!manifest_version.IsValid()) {
-    SB_LOG(ERROR) << "Updater failed to get the current update version.";
+    LOG(ERROR) << "Updater failed to get the current update version.";
     return;
   }
 
@@ -250,11 +276,6 @@
           },
           base::Bind(base::DoNothing::Repeatedly())));
 
-  // Mark the current installation as successful.
-  updater_thread_.task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&UpdaterModule::MarkSuccessful, base::Unretained(this)));
-
   IncrementUpdateCheckCount();
 
   int kNextUpdateCheckHours = 0;
@@ -267,7 +288,7 @@
     // hours.
     kNextUpdateCheckHours = 24;
   }
-  updater_thread_.task_runner()->PostDelayedTask(
+  updater_thread_->task_runner()->PostDelayedTask(
       FROM_HERE, base::Bind(&UpdaterModule::Update, base::Unretained(this)),
       base::TimeDelta::FromHours(kNextUpdateCheckHours));
 }
@@ -280,26 +301,41 @@
 }
 
 std::string UpdaterModule::GetUpdaterChannel() const {
+  LOG(INFO) << "UpdaterModule::GetUpdaterChannel";
   auto config = updater_configurator_;
-  if (!config) return "";
+  if (!config) {
+    LOG(ERROR) << "UpdaterModule::GetUpdaterChannel: missing config";
+    return "";
+  }
 
-  return config->GetChannel();
+  std::string channel = config->GetChannel();
+  LOG(INFO) << "UpdaterModule::GetUpdaterChannel channel=" << channel;
+  return channel;
 }
 
 void UpdaterModule::SetUpdaterChannel(const std::string& updater_channel) {
+  LOG(INFO) << "UpdaterModule::SetUpdaterChannel updater_channel="
+            << updater_channel;
   auto config = updater_configurator_;
   if (config) config->SetChannel(updater_channel);
 }
 
 std::string UpdaterModule::GetUpdaterStatus() const {
+  LOG(INFO) << "UpdaterModule::GetUpdaterStatus";
   auto config = updater_configurator_;
-  if (!config) return "";
+  if (!config) {
+    LOG(ERROR) << "UpdaterModule::GetUpdaterStatus: missing configuration";
+    return "";
+  }
 
-  return config->GetUpdaterStatus();
+  std::string updater_status = config->GetUpdaterStatus();
+  LOG(INFO) << "UpdaterModule::GetUpdaterStatus updater_status="
+            << updater_status;
+  return updater_status;
 }
 
 void UpdaterModule::RunUpdateCheck() {
-  updater_thread_.task_runner()->PostTask(
+  updater_thread_->task_runner()->PostTask(
       FROM_HERE, base::Bind(&UpdaterModule::Update, base::Unretained(this)));
 }
 
@@ -308,21 +344,21 @@
       static_cast<const CobaltExtensionInstallationManagerApi*>(
           SbSystemGetExtension(kCobaltExtensionInstallationManagerName));
   if (!installation_manager) {
-    SB_LOG(ERROR) << "Updater failed to get installation manager extension.";
+    LOG(ERROR) << "Updater failed to get installation manager extension.";
     return;
   }
   if (installation_manager->Reset() == IM_EXT_ERROR) {
-    SB_LOG(ERROR) << "Updater failed to reset installations.";
+    LOG(ERROR) << "Updater failed to reset installations.";
     return;
   }
   base::FilePath product_data_dir;
   if (!GetProductDirectoryPath(&product_data_dir)) {
-    SB_LOG(ERROR) << "Updater failed to get product directory path.";
+    LOG(ERROR) << "Updater failed to get product directory path.";
     return;
   }
   if (!starboard::SbFileDeleteRecursive(product_data_dir.value().c_str(),
                                         true)) {
-    SB_LOG(ERROR) << "Updater failed to clean the product directory.";
+    LOG(ERROR) << "Updater failed to clean the product directory.";
     return;
   }
 }
@@ -332,12 +368,12 @@
       static_cast<const CobaltExtensionInstallationManagerApi*>(
           SbSystemGetExtension(kCobaltExtensionInstallationManagerName));
   if (!installation_manager) {
-    SB_LOG(ERROR) << "Updater failed to get installation manager extension.";
+    LOG(ERROR) << "Updater failed to get installation manager extension.";
     return -1;
   }
   int index = installation_manager->GetCurrentInstallationIndex();
   if (index == IM_EXT_ERROR) {
-    SB_LOG(ERROR) << "Updater failed to get current installation index.";
+    LOG(ERROR) << "Updater failed to get current installation index.";
     return -1;
   }
   return index;
diff --git a/src/cobalt/updater/updater_module.h b/src/cobalt/updater/updater_module.h
index fa4abc0..353215e 100644
--- a/src/cobalt/updater/updater_module.h
+++ b/src/cobalt/updater/updater_module.h
@@ -53,8 +53,9 @@
 };
 
 // Mapping a component state to an updater status.
-const std::map<ComponentState, UpdaterStatus> component_to_updater_status_map =
-    {
+// clang-format off
+const std::map<ComponentState, UpdaterStatus> component_to_updater_status_map = {
+        // clang-format on
         {ComponentState::kNew, UpdaterStatus::kNewUpdate},
         {ComponentState::kChecking, UpdaterStatus::kChecking},
         {ComponentState::kCanUpdate, UpdaterStatus::kUpdateAvailable},
@@ -88,6 +89,9 @@
     {UpdaterStatus::kRun, "Transitioning..."},
 };
 
+// Default number of seconds to delay the first update check.
+extern const uint64_t kDefaultUpdateCheckDelaySeconds;
+
 // An interface that observes the updater. It provides notifications when the
 // updater changes status.
 class Observer : public update_client::UpdateClient::Observer {
@@ -101,7 +105,7 @@
             SbSystemGetExtension(kCobaltExtensionUpdaterNotificationName));
     if (updater_notification_ext &&
         strcmp(updater_notification_ext->name,
-                           kCobaltExtensionUpdaterNotificationName) == 0 &&
+               kCobaltExtensionUpdaterNotificationName) == 0 &&
         updater_notification_ext->version >= 1) {
       updater_notification_ext_ = updater_notification_ext;
     } else {
@@ -127,7 +131,8 @@
 // checks run according to a schedule defined by the Cobalt application.
 class UpdaterModule {
  public:
-  explicit UpdaterModule(network::NetworkModule* network_module);
+  explicit UpdaterModule(network::NetworkModule* network_module,
+                         uint64_t update_check_delay_sec);
   ~UpdaterModule();
 
   void Suspend();
@@ -147,13 +152,14 @@
   int GetInstallationIndex() const;
 
  private:
-  base::Thread updater_thread_;
+  std::unique_ptr<base::Thread> updater_thread_;
   scoped_refptr<update_client::UpdateClient> update_client_;
   std::unique_ptr<Observer> updater_observer_;
   network::NetworkModule* network_module_;
   scoped_refptr<Configurator> updater_configurator_;
   int update_check_count_ = 0;
   bool is_updater_running_;
+  uint64_t update_check_delay_sec_ = kDefaultUpdateCheckDelaySeconds;
 
   THREAD_CHECKER(thread_checker_);
 
diff --git a/src/cobalt/updater/utils.cc b/src/cobalt/updater/utils.cc
index 5baad0a..decc274 100644
--- a/src/cobalt/updater/utils.cc
+++ b/src/cobalt/updater/utils.cc
@@ -4,6 +4,7 @@
 
 #include "cobalt/updater/utils.h"
 
+#include <memory>
 #include <vector>
 
 #include "base/base_paths.h"
@@ -11,10 +12,13 @@
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/path_service.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "cobalt/extension/installation_manager.h"
 #include "components/update_client/utils.h"
+#include "crypto/secure_hash.h"
+#include "crypto/sha2.h"
 #include "starboard/configuration_constants.h"
 #include "starboard/string.h"
 #include "starboard/system.h"
@@ -61,8 +65,8 @@
 #if SB_API_VERSION >= 12
   if (!SbSystemGetPath(kSbSystemPathStorageDirectory, storage_dir.data(),
                        kSbFileMaxPath)) {
-    SB_LOG(ERROR) << "GetProductDirectoryPath: Failed to get "
-                     "kSbSystemPathStorageDirectory";
+    LOG(ERROR) << "GetProductDirectoryPath: Failed to get "
+                  "kSbSystemPathStorageDirectory";
     return false;
   }
 #else
@@ -97,7 +101,7 @@
   std::vector<char> system_path_content_dir(kSbFileMaxPath);
   if (!SbSystemGetPath(kSbSystemPathContentDirectory,
                        system_path_content_dir.data(), kSbFileMaxPath)) {
-    SB_LOG(ERROR) << "Failed to get system path content directory";
+    LOG(ERROR) << "Failed to get system path content directory";
     return "";
   }
   // Get the parent directory of the system_path_content_dir, and read the
@@ -108,8 +112,8 @@
           .DirName());
 
   if (!version.IsValid()) {
-    SB_LOG(ERROR) << "Failed to get the Evergreen version. Defaulting to "
-                  << kDefaultManifestVersion << ".";
+    LOG(ERROR) << "Failed to get the Evergreen version. Defaulting to "
+               << kDefaultManifestVersion << ".";
     return std::string(kDefaultManifestVersion);
   }
   return version.GetString();
@@ -120,23 +124,23 @@
       static_cast<const CobaltExtensionInstallationManagerApi*>(
           SbSystemGetExtension(kCobaltExtensionInstallationManagerName));
   if (!installation_manager) {
-    SB_LOG(ERROR) << "Failed to get installation manager extension, getting "
-                     "the Evergreen version of the loaded installation.";
+    LOG(ERROR) << "Failed to get installation manager extension, getting "
+                  "the Evergreen version of the loaded installation.";
     return GetLoadedInstallationEvergreenVersion();
   }
   // Get the update version from the manifest file under the current
   // installation path.
   int index = installation_manager->GetCurrentInstallationIndex();
   if (index == IM_EXT_ERROR) {
-    SB_LOG(ERROR) << "Failed to get current installation index, getting the "
-                     "Evergreen version of the currently loaded installation.";
+    LOG(ERROR) << "Failed to get current installation index, getting the "
+                  "Evergreen version of the currently loaded installation.";
     return GetLoadedInstallationEvergreenVersion();
   }
   std::vector<char> installation_path(kSbFileMaxPath);
   if (installation_manager->GetInstallationPath(
           index, installation_path.data(), kSbFileMaxPath) == IM_EXT_ERROR) {
-    SB_LOG(ERROR) << "Failed to get installation path, getting the Evergreen "
-                     "version of the currently loaded installation.";
+    LOG(ERROR) << "Failed to get installation path, getting the Evergreen "
+                  "version of the currently loaded installation.";
     return GetLoadedInstallationEvergreenVersion();
   }
 
@@ -144,12 +148,81 @@
       std::string(installation_path.begin(), installation_path.end())));
 
   if (!version.IsValid()) {
-    SB_LOG(ERROR) << "Failed to get the Evergreen version. Defaulting to "
-                  << kDefaultManifestVersion << ".";
+    LOG(ERROR) << "Failed to get the Evergreen version. Defaulting to "
+               << kDefaultManifestVersion << ".";
     return std::string(kDefaultManifestVersion);
   }
   return version.GetString();
 }
 
+std::string GetLibrarySha256(int index) {
+  base::FilePath filepath;
+  auto installation_manager =
+      static_cast<const CobaltExtensionInstallationManagerApi*>(
+          SbSystemGetExtension(kCobaltExtensionInstallationManagerName));
+  if (!installation_manager && index == 0) {
+    // Evergreen Lite
+    std::vector<char> system_path_content_dir(kSbFileMaxPath);
+    if (!SbSystemGetPath(kSbSystemPathContentDirectory,
+                         system_path_content_dir.data(), kSbFileMaxPath)) {
+      SB_LOG(ERROR)
+          << "GetLibrarySha256: Failed to get system path content directory";
+      return "";
+    }
+
+    filepath = base::FilePath(std::string(system_path_content_dir.begin(),
+                                          system_path_content_dir.end()))
+                   .DirName();
+  } else if (!installation_manager && index != 0) {
+    SB_LOG(ERROR) << "GetLibrarySha256: Evergreen lite supports only slot 0";
+    return "";
+  } else {
+    // Evergreen Full
+    std::vector<char> installation_path(kSbFileMaxPath);
+    if (installation_manager->GetInstallationPath(
+            index, installation_path.data(), kSbFileMaxPath) == IM_EXT_ERROR) {
+      SB_LOG(ERROR) << "GetLibrarySha256: Failed to get installation path";
+      return "";
+    }
+
+    filepath = base::FilePath(installation_path.data());
+  }
+
+  filepath = filepath.AppendASCII("lib").AppendASCII("libcobalt.so");
+  base::File source_file(filepath,
+                         base::File::FLAG_OPEN | base::File::FLAG_READ);
+  if (!source_file.IsValid()) {
+    SB_LOG(ERROR) << "GetLibrarySha256(): Unable to open source file: "
+                  << filepath.value();
+    return "";
+  }
+
+  const size_t kBufferSize = 32768;
+  std::vector<char> buffer(kBufferSize);
+  uint8_t actual_hash[crypto::kSHA256Length] = {0};
+  std::unique_ptr<crypto::SecureHash> hasher(
+      crypto::SecureHash::Create(crypto::SecureHash::SHA256));
+
+  while (true) {
+    int bytes_read = source_file.ReadAtCurrentPos(&buffer[0], buffer.size());
+    if (bytes_read < 0) {
+      SB_LOG(ERROR) << "GetLibrarySha256(): error reading from: "
+                    << filepath.value();
+
+      return "";
+    }
+
+    if (bytes_read == 0) {
+      break;
+    }
+
+    hasher->Update(&buffer[0], bytes_read);
+  }
+
+  hasher->Finish(actual_hash, sizeof(actual_hash));
+
+  return base::HexEncode(actual_hash, sizeof(actual_hash));
+}
+
 }  // namespace updater
 }  // namespace cobalt
diff --git a/src/cobalt/updater/utils.h b/src/cobalt/updater/utils.h
index 5efa2db..a0b5008 100644
--- a/src/cobalt/updater/utils.h
+++ b/src/cobalt/updater/utils.h
@@ -29,6 +29,10 @@
 // Read the Evergreen version of the installation dir.
 base::Version ReadEvergreenVersion(base::FilePath installation_dir);
 
+// Returns the hash of the libcobalt.so binary for the installation
+// at slot |index|.
+std::string GetLibrarySha256(int index);
+
 }  // namespace updater
 }  // namespace cobalt
 
diff --git a/src/cobalt/version.h b/src/cobalt/version.h
index b338e71..67c2448 100644
--- a/src/cobalt/version.h
+++ b/src/cobalt/version.h
@@ -35,6 +35,6 @@
 //                  release is cut.
 //.
 
-#define COBALT_VERSION "22.lts.2"
+#define COBALT_VERSION "22.lts.3"
 
 #endif  // COBALT_VERSION_H_
diff --git a/src/components/prefs/pref_service.cc b/src/components/prefs/pref_service.cc
index 624a78f..a59c614 100644
--- a/src/components/prefs/pref_service.cc
+++ b/src/components/prefs/pref_service.cc
@@ -97,6 +97,9 @@
       user_pref_store_(std::move(user_prefs)),
       read_error_callback_(std::move(read_error_callback)),
       pref_registry_(std::move(pref_registry)) {
+#if defined(STARBOARD)
+  LOG(INFO) << "PrefService::PrefService";
+#endif
   pref_notifier_->SetPrefService(this);
 
   DCHECK(pref_registry_);
@@ -106,6 +109,9 @@
 }
 
 PrefService::~PrefService() {
+#if defined(STARBOARD)
+  LOG(INFO) << "PrefService::~PrefService";
+#endif
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // TODO(crbug.com/942491, 946668, 945772) The following code collects
diff --git a/src/components/update_client/cobalt_slot_management.cc b/src/components/update_client/cobalt_slot_management.cc
index cc8856e..0c601d7 100644
--- a/src/components/update_client/cobalt_slot_management.cc
+++ b/src/components/update_client/cobalt_slot_management.cc
@@ -14,6 +14,8 @@
 
 #include "components/update_client/cobalt_slot_management.h"
 
+#include <vector>
+
 #include "base/values.h"
 #include "cobalt/updater/utils.h"
 #include "components/update_client/utils.h"
@@ -29,9 +31,9 @@
   std::string bad_app_key_file_path =
       starboard::loader_app::GetBadAppKeyFilePath(installation_path, app_key);
   SB_DCHECK(!bad_app_key_file_path.empty());
-  SB_LOG(INFO) << "bad_app_key_file_path: " << bad_app_key_file_path;
-  SB_LOG(INFO) << "bad_app_key_file_path SbFileExists: "
-               << SbFileExists(bad_app_key_file_path.c_str());
+  LOG(INFO) << "bad_app_key_file_path: " << bad_app_key_file_path;
+  LOG(INFO) << "bad_app_key_file_path SbFileExists: "
+            << SbFileExists(bad_app_key_file_path.c_str());
   return !bad_app_key_file_path.empty() &&
          SbFileExists(bad_app_key_file_path.c_str());
 }
@@ -41,21 +43,21 @@
 
 bool CobaltSlotManagement::Init(
     const CobaltExtensionInstallationManagerApi* installation_api) {
-  SB_LOG(INFO) << "CobaltSlotManagement::Init";
+  LOG(INFO) << "CobaltSlotManagement::Init";
 
   installation_api_ = installation_api;
 
   // Make sure the index is reset
   installation_index_ = IM_EXT_INVALID_INDEX;
   if (!installation_api_) {
-    SB_LOG(ERROR) << "Failed to get installation manager";
+    LOG(ERROR) << "Failed to get installation manager";
     return false;
   }
 
   char app_key[IM_EXT_MAX_APP_KEY_LENGTH];
   if (installation_api_->GetAppKey(app_key, IM_EXT_MAX_APP_KEY_LENGTH) ==
       IM_EXT_ERROR) {
-    SB_LOG(ERROR) << "Failed to get app key.";
+    LOG(ERROR) << "Failed to get app key.";
     return false;
   }
   app_key_ = app_key;
@@ -64,10 +66,10 @@
 
 bool CobaltSlotManagement::SelectSlot(base::FilePath* dir) {
   SB_DCHECK(installation_api_);
-  SB_LOG(INFO) << "CobaltSlotManagement::SelectSlot";
+  LOG(INFO) << "CobaltSlotManagement::SelectSlot";
   int max_slots = installation_api_->GetMaxNumberInstallations();
   if (max_slots == IM_EXT_ERROR) {
-    SB_LOG(ERROR) << "Failed to get max number of slots";
+    LOG(ERROR) << "Failed to get max number of slots";
     return false;
   }
 
@@ -78,14 +80,14 @@
 
   // Iterate over all writeable slots - index >= 1.
   for (int i = 1; i < max_slots; i++) {
-    SB_LOG(INFO) << "CobaltSlotManagement::SelectSlot iterating slot=" << i;
+    LOG(INFO) << "CobaltSlotManagement::SelectSlot iterating slot=" << i;
     std::vector<char> installation_path(kSbFileMaxPath);
     if (installation_api_->GetInstallationPath(i, installation_path.data(),
                                                installation_path.size()) ==
         IM_EXT_ERROR) {
-      SB_LOG(ERROR) << "CobaltSlotManagement::SelectSlot: Failed to get "
-                       "installation path for slot="
-                    << i;
+      LOG(ERROR) << "CobaltSlotManagement::SelectSlot: Failed to get "
+                    "installation path for slot="
+                 << i;
       continue;
     }
 
@@ -103,31 +105,29 @@
     base::Version version =
         cobalt::updater::ReadEvergreenVersion(installation_dir);
     if (!version.IsValid()) {
-      SB_LOG(INFO)
-          << "CobaltSlotManagement::SelectSlot installed version invalid";
+      LOG(INFO) << "CobaltSlotManagement::SelectSlot installed version invalid";
       if (!DrainFileDraining(installation_dir.value().c_str(), "")) {
-        SB_LOG(INFO) << "CobaltSlotManagement::SelectSlot not draining";
+        LOG(INFO) << "CobaltSlotManagement::SelectSlot not draining";
         // Found empty slot.
         slot_candidate = i;
         slot_candidate_path = installation_dir;
         break;
       } else {
         // There is active draining from another updater so bail out.
-        SB_LOG(ERROR) << "CobaltSlotManagement::SelectSlot bailing out";
+        LOG(ERROR) << "CobaltSlotManagement::SelectSlot bailing out";
         return false;
       }
     } else if ((!slot_candidate_version.IsValid() ||
                 slot_candidate_version > version)) {
       if (!DrainFileDraining(installation_dir.value().c_str(), "")) {
         // Found a slot with older version that's not draining.
-        SB_LOG(INFO) << "CobaltSlotManagement::SelectSlot slot candidate: "
-                     << i;
+        LOG(INFO) << "CobaltSlotManagement::SelectSlot slot candidate: " << i;
         slot_candidate_version = version;
         slot_candidate = i;
         slot_candidate_path = installation_dir;
       } else {
         // There is active draining from another updater so bail out.
-        SB_LOG(ERROR) << "CobaltSlotManagement::SelectSlot bailing out";
+        LOG(ERROR) << "CobaltSlotManagement::SelectSlot bailing out";
         return false;
       }
     }
@@ -138,7 +138,7 @@
 
   if (installation_index_ == -1 ||
       !DrainFileTryDrain(dir->value().c_str(), app_key_.c_str())) {
-    SB_LOG(ERROR)
+    LOG(ERROR)
         << "CobaltSlotManagement::SelectSlot unable to find a slot, candidate="
         << installation_index_;
     return false;
@@ -148,19 +148,19 @@
 
 bool CobaltSlotManagement::ConfirmSlot(const base::FilePath& dir) {
   SB_DCHECK(installation_api_);
-  SB_LOG(INFO) << "CobaltSlotManagement::ConfirmSlot ";
+  LOG(INFO) << "CobaltSlotManagement::ConfirmSlot ";
   if (!DrainFileRankAndCheck(dir.value().c_str(), app_key_.c_str())) {
-    SB_LOG(INFO) << "CobaltSlotManagement::ConfirmSlot: failed to lock slot ";
+    LOG(INFO) << "CobaltSlotManagement::ConfirmSlot: failed to lock slot ";
     return false;
   }
 
   // TODO: Double check the installed_version.
 
   // Use the installation slot
-  SB_LOG(INFO) << "Resetting the slot: " << installation_index_;
+  LOG(INFO) << "Resetting the slot: " << installation_index_;
   if (installation_api_->ResetInstallation(installation_index_) ==
       IM_EXT_ERROR) {
-    SB_LOG(INFO) << "CobaltSlotManagement::ConfirmSlot: failed to reset slot ";
+    LOG(INFO) << "CobaltSlotManagement::ConfirmSlot: failed to reset slot ";
     return false;
   }
 
@@ -189,13 +189,13 @@
       starboard::loader_app::GetGoodAppKeyFilePath(dir, app_key);
   SB_CHECK(!good_app_key_file_path.empty());
   if (!starboard::loader_app::CreateAppKeyFile(good_app_key_file_path)) {
-    SB_LOG(WARNING) << "Failed to create good app key file";
+    LOG(WARNING) << "Failed to create good app key file";
   }
   DrainFileRemove(dir.c_str(), app_key.c_str());
   int ret =
       installation_api->RequestRollForwardToInstallation(installation_index);
   if (ret == IM_EXT_ERROR) {
-    SB_LOG(ERROR) << "Failed to request roll forward.";
+    LOG(ERROR) << "Failed to request roll forward.";
     return false;
   }
   return true;
@@ -210,13 +210,13 @@
   char app_key[IM_EXT_MAX_APP_KEY_LENGTH];
   if (installation_api->GetAppKey(app_key, IM_EXT_MAX_APP_KEY_LENGTH) ==
       IM_EXT_ERROR) {
-    SB_LOG(ERROR) << "CobaltQuickUpdate: Failed to get app key.";
+    LOG(ERROR) << "CobaltQuickUpdate: Failed to get app key.";
     return true;
   }
 
   int max_slots = installation_api->GetMaxNumberInstallations();
   if (max_slots == IM_EXT_ERROR) {
-    SB_LOG(ERROR) << "CobaltQuickUpdate: Failed to get max number of slots.";
+    LOG(ERROR) << "CobaltQuickUpdate: Failed to get max number of slots.";
     return true;
   }
 
@@ -227,14 +227,14 @@
 
   // Iterate over all writeable slots - index >= 1.
   for (int i = 1; i < max_slots; i++) {
-    SB_LOG(INFO) << "CobaltQuickInstallation: iterating slot=" << i;
+    LOG(INFO) << "CobaltQuickInstallation: iterating slot=" << i;
     // Get the path to new installation.
     std::vector<char> installation_path(kSbFileMaxPath);
     if (installation_api->GetInstallationPath(i, installation_path.data(),
                                               installation_path.size()) ==
         IM_EXT_ERROR) {
-      SB_LOG(ERROR) << "CobaltQuickInstallation: Failed to get "
-                    << "installation path for slot=" << i;
+      LOG(ERROR) << "CobaltQuickInstallation: Failed to get "
+                 << "installation path for slot=" << i;
       continue;
     }
 
@@ -248,7 +248,7 @@
         cobalt::updater::ReadEvergreenVersion(installation_dir);
 
     if (!installed_version.IsValid()) {
-      SB_LOG(WARNING) << "CobaltQuickInstallation: invalid version ";
+      LOG(WARNING) << "CobaltQuickInstallation: invalid version ";
       continue;
     } else if (slot_candidate_version < installed_version &&
                current_version < installed_version &&
@@ -260,7 +260,7 @@
       // draining, and no bad file of current app exists, and a good file
       // exists. The final candidate is the newest version of the valid
       // candidates.
-      SB_LOG(INFO) << "CobaltQuickInstallation: slot candidate: " << i;
+      LOG(INFO) << "CobaltQuickInstallation: slot candidate: " << i;
       slot_candidate_version = installed_version;
       slot_candidate = i;
     }
@@ -269,11 +269,11 @@
   if (slot_candidate != -1) {
     if (installation_api->RequestRollForwardToInstallation(slot_candidate) !=
         IM_EXT_ERROR) {
-      SB_LOG(INFO) << "CobaltQuickInstallation: quick update succeeded.";
+      LOG(INFO) << "CobaltQuickInstallation: quick update succeeded.";
       return true;
     }
   }
-  SB_LOG(WARNING) << "CobaltQuickInstallation: quick update failed.";
+  LOG(WARNING) << "CobaltQuickInstallation: quick update failed.";
   return false;
 }
 
diff --git a/src/components/update_client/cobalt_slot_management_test.cc b/src/components/update_client/cobalt_slot_management_test.cc
index f12f060..55256de 100644
--- a/src/components/update_client/cobalt_slot_management_test.cc
+++ b/src/components/update_client/cobalt_slot_management_test.cc
@@ -149,7 +149,7 @@
   base::FilePath dir;
   cobalt_slot_management.SelectSlot(&dir);
   ASSERT_TRUE(DrainFileDraining(dir.value().c_str(), kTestAppKey1));
-  SB_LOG(INFO) << "dir=" << dir;
+  LOG(INFO) << "dir=" << dir;
 
   ASSERT_TRUE(base::EndsWith(dir.value(), "installation_2",
                              base::CompareCase::SENSITIVE));
@@ -169,7 +169,7 @@
   ASSERT_TRUE(cobalt_slot_management.Init(api_));
   base::FilePath dir;
   ASSERT_TRUE(cobalt_slot_management.SelectSlot(&dir));
-  SB_LOG(INFO) << "dir=" << dir;
+  LOG(INFO) << "dir=" << dir;
 
   ASSERT_TRUE(base::EndsWith(dir.value(), "installation_1",
                              base::CompareCase::SENSITIVE));
diff --git a/src/components/update_client/component.cc b/src/components/update_client/component.cc
index 421dc40..d9158fc 100644
--- a/src/components/update_client/component.cc
+++ b/src/components/update_client/component.cc
@@ -15,6 +15,9 @@
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task/post_task.h"
+#if defined(STARBOARD)
+#include "base/threading/thread_id_name_manager.h"
+#endif
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "components/update_client/action_runner.h"
@@ -81,6 +84,10 @@
     InstallOnBlockingTaskRunnerCompleteCallback callback,
     const base::FilePath& unpack_path,
     const CrxInstaller::Result& result) {
+#if defined(STARBOARD)
+    LOG(INFO) << "InstallComplete thread_name="
+              << base::ThreadIdNameManager::GetInstance()->GetNameForCurrentThread();
+#endif
   base::PostTaskWithTraits(
       FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
       base::BindOnce(
@@ -88,7 +95,10 @@
              InstallOnBlockingTaskRunnerCompleteCallback callback,
              const base::FilePath& unpack_path,
              const CrxInstaller::Result& result) {
-
+#if defined(STARBOARD)
+            LOG(INFO) << "Closure kicked off from InstallComplete thread_name="
+              << base::ThreadIdNameManager::GetInstance()->GetNameForCurrentThread();
+#endif
 // For Cobalt, don't delete the unpack_path, which is not a temp directory.
 // Cobalt uses a dedicated installation slot obtained from the Installation
 // Manager.
@@ -117,6 +127,11 @@
     InstallOnBlockingTaskRunnerCompleteCallback callback) {
   DCHECK(base::DirectoryExists(unpack_path));
 
+#if defined(STARBOARD)
+  LOG(INFO) << "InstallOnBlockingTaskRunner thread_name="
+              << base::ThreadIdNameManager::GetInstance()->GetNameForCurrentThread();
+#endif
+
 #if !defined(STARBOARD)
   // Acquire the ownership of the |unpack_path|.
   base::ScopedTempDir unpack_path_owner;
@@ -141,11 +156,11 @@
       static_cast<const CobaltExtensionInstallationManagerApi*>(
           SbSystemGetExtension(kCobaltExtensionInstallationManagerName));
   if (!installation_api) {
-    SB_LOG(ERROR) << "Failed to get installation manager api.";
+    LOG(ERROR) << "Failed to get installation manager api.";
     // TODO: add correct error code.
     install_error = InstallError::GENERIC_ERROR;
   } else if (installation_index == IM_EXT_INVALID_INDEX) {
-    SB_LOG(ERROR) << "Installation index is invalid.";
+    LOG(ERROR) << "Installation index is invalid.";
     // TODO: add correct error code.
     install_error = InstallError::GENERIC_ERROR;
   } else {
@@ -187,8 +202,9 @@
     scoped_refptr<CrxInstaller> installer,
     InstallOnBlockingTaskRunnerCompleteCallback callback,
     const ComponentUnpacker::Result& result) {
-
 #if defined(STARBOARD)
+  LOG(INFO) << "UnpackCompleteOnBlockingTaskRunner thread_name="
+              << base::ThreadIdNameManager::GetInstance()->GetNameForCurrentThread();
   base::DeleteFile(crx_path, false);
 #else
   update_client::DeleteFileAndEmptyParentDirectory(crx_path);
@@ -245,6 +261,10 @@
     scoped_refptr<Patcher> patcher_,
     crx_file::VerifierFormat crx_format,
     InstallOnBlockingTaskRunnerCompleteCallback callback) {
+#if defined(STARBOARD)
+  LOG(INFO) << "StartInstallOnBlockingTaskRunner thread_name="
+              << base::ThreadIdNameManager::GetInstance()->GetNameForCurrentThread();
+#endif
   auto unpacker = base::MakeRefCounted<ComponentUnpacker>(
       pk_hash, crx_path, installer, std::move(unzipper_), std::move(patcher_),
       crx_format);
@@ -274,9 +294,17 @@
 Component::Component(const UpdateContext& update_context, const std::string& id)
     : id_(id),
       state_(std::make_unique<StateNew>(this)),
-      update_context_(update_context) {}
+      update_context_(update_context) {
+#if defined(STARBOARD)
+  LOG(INFO) << "Component::Component";
+#endif
+}
 
-Component::~Component() {}
+Component::~Component() {
+#if defined(STARBOARD)
+  LOG(INFO) << "Component::~Component";
+#endif
+}
 
 scoped_refptr<Configurator> Component::config() const {
   return update_context_.config;
@@ -293,7 +321,9 @@
 void Component::Handle(CallbackHandleComplete callback_handle_complete) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(state_);
-
+#if defined(STARBOARD)
+  LOG(INFO) << "Component::Handle";
+#endif
   callback_handle_complete_ = std::move(callback_handle_complete);
 
   state_->Handle(
@@ -309,6 +339,10 @@
 
 void Component::ChangeState(std::unique_ptr<State> next_state) {
   DCHECK(thread_checker_.CalledOnValidThread());
+#if defined(STARBOARD)
+  LOG(INFO) << "Component::ChangeState next_state="
+    << ((next_state)? next_state->state_name(): "nullptr");
+#endif
 
   previous_state_ = state();
   if (next_state)
@@ -533,6 +567,9 @@
 
 void Component::State::Handle(CallbackNextState callback_next_state) {
   DCHECK(thread_checker_.CalledOnValidThread());
+#if defined(STARBOARD)
+  LOG(INFO) << "Component::State::Handle";
+#endif
 
   callback_next_state_ = std::move(callback_next_state);
 
@@ -542,7 +579,7 @@
 #if defined(STARBOARD)
 void Component::State::Cancel() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  // Further work may be needed to ensure cancelation during any state results
+  // Further work may be needed to ensure cancellation during any state results
   // in a clear result and no memory leaks.
 }
 #endif
@@ -550,6 +587,10 @@
 void Component::State::TransitionState(std::unique_ptr<State> next_state) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(next_state);
+#if defined(STARBOARD)
+  LOG(INFO) << "Component::State::TransitionState next_state="
+    << ((next_state)? next_state->state_name(): "nullptr");
+#endif
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
@@ -628,7 +669,7 @@
                                   base::Unretained(metadata), component.id_,
                                   config->GetChannel()));
   } else {
-    SB_LOG(WARNING) << "Failed to get the persisted data store to write the "
+    LOG(WARNING) << "Failed to get the persisted data store to write the "
                        "updater channel.";
   }
 #endif
@@ -972,6 +1013,10 @@
 }
 
 void Component::StateUpdating::DoHandle() {
+#if defined(STARBOARD)
+  LOG(INFO) << "Component::StateUpdating::DoHandle() thread_name="
+              << base::ThreadIdNameManager::GetInstance()->GetNameForCurrentThread();
+#endif
   DCHECK(thread_checker_.CalledOnValidThread());
 
   const auto& component = Component::State::component();
diff --git a/src/components/update_client/component.h b/src/components/update_client/component.h
index f6cc035..83b8a35 100644
--- a/src/components/update_client/component.h
+++ b/src/components/update_client/component.h
@@ -169,6 +169,41 @@
 
     ComponentState state() const { return state_; }
 
+#if defined(STARBOARD)
+    std::string state_name() {
+      switch (state_) {
+        case ComponentState::kNew:
+          return "New";
+        case ComponentState::kChecking:
+          return "Checking";
+        case ComponentState::kCanUpdate:
+          return "CanUpdate";
+        case ComponentState::kDownloadingDiff:
+          return "DownloadingDiff";
+        case ComponentState::kDownloading:
+          return "Downloaded";
+        case ComponentState::kUpdatingDiff:
+          return "UpdatingDiff";
+        case ComponentState::kUpdating:
+          return "Updating";
+        case ComponentState::kUpdated:
+          return "Updated";
+        case ComponentState::kUpToDate:
+          return  "UpToDate";
+        case ComponentState::kUpdateError:
+          return "UpdateError";
+        case ComponentState::kUninstalled:
+          return "Uninstalled";
+        case ComponentState::kRun:
+          return "Run";
+        case ComponentState::kLastStatus:
+          return "LastStatus";
+        default:
+          return "Unknown";
+      }
+    }
+#endif
+
    protected:
     // Initiates the transition to the new state.
     void TransitionState(std::unique_ptr<State> new_state);
diff --git a/src/components/update_client/component_unpacker.cc b/src/components/update_client/component_unpacker.cc
index 15dbf50..f76cb6a 100644
--- a/src/components/update_client/component_unpacker.cc
+++ b/src/components/update_client/component_unpacker.cc
@@ -72,7 +72,7 @@
       result != crx_file::VerifierResult::OK_DELTA) {
     error_ = UnpackerError::kInvalidFile;
     extended_error_ = static_cast<int>(result);
-    SB_LOG(INFO) << "Verification failed. Verifier error = " << extended_error_;
+    LOG(INFO) << "Verification failed. Verifier error = " << extended_error_;
     return false;
   }
   is_delta_ = result == crx_file::VerifierResult::OK_DELTA;
diff --git a/src/components/update_client/crx_downloader.cc b/src/components/update_client/crx_downloader.cc
index 8f2bffa..fb18952 100644
--- a/src/components/update_client/crx_downloader.cc
+++ b/src/components/update_client/crx_downloader.cc
@@ -12,6 +12,9 @@
 #include "base/logging.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
+#if defined(STARBOARD)
+#include "base/threading/thread_id_name_manager.h"
+#endif
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #if defined(OS_WIN)
@@ -60,9 +63,17 @@
 
 CrxDownloader::CrxDownloader(std::unique_ptr<CrxDownloader> successor)
     : main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
-      successor_(std::move(successor)) {}
+      successor_(std::move(successor)) {
+#if defined(STARBOARD)
+  LOG(INFO) << "CrxDownloader::CrxDownloader";
+#endif
+}
 
-CrxDownloader::~CrxDownloader() {}
+CrxDownloader::~CrxDownloader() {
+#if defined(STARBOARD)
+  LOG(INFO) << "CrxDownloader::~CrxDownloader";
+#endif
+}
 
 void CrxDownloader::set_progress_callback(
     const ProgressCallback& progress_callback) {
@@ -87,6 +98,9 @@
 void CrxDownloader::StartDownloadFromUrl(const GURL& url,
                                          const std::string& expected_hash,
                                          DownloadCallback download_callback) {
+#if defined(STARBOARD)
+  LOG(INFO) << "CrxDownloader::StartDownloadFromUrl: url=" << url;
+#endif
   std::vector<GURL> urls;
   urls.push_back(url);
   StartDownload(urls, expected_hash, std::move(download_callback));
@@ -122,6 +136,7 @@
 
 #if defined(STARBOARD)
 void CrxDownloader::CancelDownload() {
+  LOG(INFO) << "CrxDownloader::CancelDownload";
   DoCancelDownload();
 }
 #endif
@@ -131,7 +146,9 @@
     const Result& result,
     const DownloadMetrics& download_metrics) {
   DCHECK(thread_checker_.CalledOnValidThread());
-
+#if defined(STARBOARD)
+  LOG(INFO) << "CrxDownloader::OnDownloadComplete";
+#endif
   if (!result.error)
     base::PostTaskWithTraits(
         FROM_HERE, kTaskTraits,
@@ -161,7 +178,10 @@
   DCHECK_EQ(0, result.error);
   DCHECK_EQ(0, download_metrics.error);
   DCHECK(is_handled);
-
+#if defined(STARBOARD)
+  LOG(INFO) << "CrxDownloader::VerifyResponse thread_name="
+    << base::ThreadIdNameManager::GetInstance()->GetNameForCurrentThread();
+#endif
   if (VerifyFileHash256(result.response, expected_hash_)) {
     download_metrics_.push_back(download_metrics);
     main_task_runner()->PostTask(
@@ -196,6 +216,10 @@
   DCHECK(result.response.empty());
   DCHECK_NE(0, download_metrics.error);
 
+#if defined(STARBOARD)
+  LOG(INFO) << "CrxDownloader::HandleDownloadError";
+#endif
+
   download_metrics_.push_back(download_metrics);
 
 #if defined(STARBOARD)
diff --git a/src/components/update_client/crx_downloader_unittest.cc b/src/components/update_client/crx_downloader_unittest.cc
index e0cb5f9..1ff4634 100644
--- a/src/components/update_client/crx_downloader_unittest.cc
+++ b/src/components/update_client/crx_downloader_unittest.cc
@@ -22,6 +22,7 @@
 #include "components/update_client/utils.h"
 #include "net/base/net_errors.h"
 #if defined(STARBOARD)
+#include "components/update_client/test_configurator.h"
 #include "net/url_request/test_url_request_interceptor.h"
 #include "net/url_request/url_request_test_util.h"
 #else
@@ -72,17 +73,20 @@
 
   void DownloadProgress(int crx_context);
 
+#if !defined(STARBOARD)
   int GetInterceptorCount() { return interceptor_count_; }
 
   void AddResponse(const GURL& url,
                    const base::FilePath& file_path,
                    int net_error);
-
+#endif
  protected:
   std::unique_ptr<CrxDownloader> crx_downloader_;
-
+#if defined(STARBOARD)
+  std::unique_ptr<GetInterceptor> get_interceptor_;
+#else
   network::TestURLLoaderFactory test_url_loader_factory_;
-
+#endif
   CrxDownloader::DownloadCallback callback_;
   CrxDownloader::ProgressCallback progress_callback_;
 
@@ -94,16 +98,22 @@
   // These members are updated by DownloadProgress.
   int num_progress_calls_;
 
+#if !defined(STARBOARD)
   // Accumulates the number of loads triggered.
   int interceptor_count_ = 0;
+#endif
 
   // A magic value for the context to be used in the tests.
   static const int kExpectedContext = 0xaabb;
 
  private:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
+#if defined(STARBOARD)
+  scoped_refptr<net::TestURLRequestContextGetter> context_;
+#else
   scoped_refptr<network::SharedURLLoaderFactory>
       test_shared_url_loader_factory_;
+#endif
   base::OnceClosure quit_closure_;
 };
 
@@ -121,18 +131,40 @@
       num_progress_calls_(0),
       scoped_task_environment_(
           base::test::ScopedTaskEnvironment::MainThreadType::IO),
+#if defined(STARBOARD)
+      context_(base::MakeRefCounted<net::TestURLRequestContextGetter>(
+          base::ThreadTaskRunnerHandle::Get())) {
+}
+
+CrxDownloaderTest::~CrxDownloaderTest() {
+  context_ = nullptr;
+
+  // The GetInterceptor requires the message loop to run to destruct correctly.
+  get_interceptor_.reset();
+  RunThreadsUntilIdle();
+}
+#else
       test_shared_url_loader_factory_(
           base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-              &test_url_loader_factory_)) {}
+              &test_url_loader_factory_)) {
+}
 
 CrxDownloaderTest::~CrxDownloaderTest() {}
-
+#endif
 void CrxDownloaderTest::SetUp() {
   num_download_complete_calls_ = 0;
   download_complete_result_ = CrxDownloader::Result();
   num_progress_calls_ = 0;
 
-  // Do not use the background downloader in these tests.
+// Do not use the background downloader in these tests.
+#if defined(STARBOARD)
+  auto config = base::MakeRefCounted<TestConfigurator>();
+  crx_downloader_ = CrxDownloader::Create(false, config);
+  crx_downloader_->set_progress_callback(progress_callback_);
+
+  get_interceptor_ = std::make_unique<GetInterceptor>(
+      base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get());
+#else
   crx_downloader_ = CrxDownloader::Create(
       false, base::MakeRefCounted<NetworkFetcherChromiumFactory>(
                  test_shared_url_loader_factory_));
@@ -140,6 +172,7 @@
 
   test_url_loader_factory_.SetInterceptor(base::BindLambdaForTesting(
       [&](const network::ResourceRequest& request) { interceptor_count_++; }));
+#endif
 }
 
 void CrxDownloaderTest::TearDown() {
@@ -162,7 +195,7 @@
 void CrxDownloaderTest::DownloadProgress(int crx_context) {
   ++num_progress_calls_;
 }
-
+#if !defined(STARBOARD)
 void CrxDownloaderTest::AddResponse(const GURL& url,
                                     const base::FilePath& file_path,
                                     int net_error) {
@@ -182,7 +215,7 @@
       url, network::ResourceResponseHead(), std::string(),
       network::URLLoaderCompletionStatus(net_error));
 }
-
+#endif
 void CrxDownloaderTest::RunThreads() {
   base::RunLoop runloop;
   quit_closure_ = runloop.QuitClosure();
@@ -236,14 +269,19 @@
       GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
 
   const base::FilePath test_file(MakeTestFilePath(kTestFileName));
+#if defined(STARBOARD)
+  get_interceptor_->SetResponse(expected_crx_url, test_file);
+#else
   AddResponse(expected_crx_url, test_file, net::OK);
-
+#endif
   crx_downloader_->StartDownloadFromUrl(
       expected_crx_url, std::string(hash_jebg), std::move(callback_));
   RunThreads();
-
+#if defined(STARBOARD)
+  EXPECT_EQ(1, get_interceptor_->GetHitCount());
+#else
   EXPECT_EQ(1, GetInterceptorCount());
-
+#endif
   EXPECT_EQ(1, num_download_complete_calls_);
   EXPECT_EQ(kExpectedContext, crx_context_);
   EXPECT_EQ(0, download_complete_result_.error);
@@ -262,17 +300,22 @@
       GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
 
   const base::FilePath test_file(MakeTestFilePath(kTestFileName));
+#if defined(STARBOARD)
+  get_interceptor_->SetResponse(expected_crx_url, test_file);
+#else
   AddResponse(expected_crx_url, test_file, net::OK);
-
+#endif
   crx_downloader_->StartDownloadFromUrl(
       expected_crx_url,
       std::string(
           "813c59747e139a608b3b5fc49633affc6db574373f309f156ea6d27229c0b3f9"),
       std::move(callback_));
   RunThreads();
-
+#if defined(STARBOARD)
+  EXPECT_EQ(1, get_interceptor_->GetHitCount());
+#else
   EXPECT_EQ(1, GetInterceptorCount());
-
+#endif
   EXPECT_EQ(1, num_download_complete_calls_);
   EXPECT_EQ(kExpectedContext, crx_context_);
   EXPECT_EQ(static_cast<int>(CrxDownloaderError::BAD_HASH),
@@ -289,8 +332,11 @@
       GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
 
   const base::FilePath test_file(MakeTestFilePath(kTestFileName));
+#if defined(STARBOARD)
+  get_interceptor_->SetResponse(expected_crx_url, test_file);
+#else
   AddResponse(expected_crx_url, test_file, net::OK);
-
+#endif
   std::vector<GURL> urls;
   urls.push_back(expected_crx_url);
   urls.push_back(expected_crx_url);
@@ -299,8 +345,11 @@
                                  std::move(callback_));
   RunThreads();
 
+#if defined(STARBOARD)
+  EXPECT_EQ(1, get_interceptor_->GetHitCount());
+#else
   EXPECT_EQ(1, GetInterceptorCount());
-
+#endif
   EXPECT_EQ(1, num_download_complete_calls_);
   EXPECT_EQ(kExpectedContext, crx_context_);
   EXPECT_EQ(0, download_complete_result_.error);
@@ -320,9 +369,14 @@
       GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc.crx");
 
   const base::FilePath test_file(MakeTestFilePath(kTestFileName));
+#if defined(STARBOARD)
+  get_interceptor_->SetResponse(expected_crx_url, test_file);
+  get_interceptor_->SetResponse(no_file_url,
+                                base::FilePath(FILE_PATH_LITERAL("no-file")));
+#else
   AddResponse(expected_crx_url, test_file, net::OK);
   AddResponse(no_file_url, base::FilePath(), net::ERR_FILE_NOT_FOUND);
-
+#endif
   std::vector<GURL> urls;
   urls.push_back(no_file_url);
   urls.push_back(expected_crx_url);
@@ -331,8 +385,11 @@
                                  std::move(callback_));
   RunThreads();
 
+#if defined(STARBOARD)
+  EXPECT_EQ(2, get_interceptor_->GetHitCount());
+#else
   EXPECT_EQ(2, GetInterceptorCount());
-
+#endif
   EXPECT_EQ(1, num_download_complete_calls_);
   EXPECT_EQ(kExpectedContext, crx_context_);
   EXPECT_EQ(0, download_complete_result_.error);
@@ -365,8 +422,14 @@
       GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc.crx");
 
   const base::FilePath test_file(MakeTestFilePath(kTestFileName));
+#if defined(STARBOARD)
+  get_interceptor_->SetResponse(expected_crx_url, test_file);
+  get_interceptor_->SetResponse(no_file_url,
+                                base::FilePath(FILE_PATH_LITERAL("no-file")));
+#else
   AddResponse(expected_crx_url, test_file, net::OK);
   AddResponse(no_file_url, base::FilePath(), net::ERR_FILE_NOT_FOUND);
+#endif
 
   std::vector<GURL> urls;
   urls.push_back(expected_crx_url);
@@ -376,8 +439,11 @@
                                  std::move(callback_));
   RunThreads();
 
+#if defined(STARBOARD)
+  EXPECT_EQ(1, get_interceptor_->GetHitCount());
+#else
   EXPECT_EQ(1, GetInterceptorCount());
-
+#endif
   EXPECT_EQ(1, num_download_complete_calls_);
   EXPECT_EQ(kExpectedContext, crx_context_);
   EXPECT_EQ(0, download_complete_result_.error);
@@ -396,7 +462,12 @@
   const GURL expected_crx_url =
       GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
 
+#if defined(STARBOARD)
+  get_interceptor_->SetResponse(expected_crx_url,
+                                base::FilePath(FILE_PATH_LITERAL("no-file")));
+#else
   AddResponse(expected_crx_url, base::FilePath(), net::ERR_FILE_NOT_FOUND);
+#endif
 
   std::vector<GURL> urls;
   urls.push_back(expected_crx_url);
@@ -406,8 +477,11 @@
                                  std::move(callback_));
   RunThreads();
 
+#if defined(STARBOARD)
+  EXPECT_EQ(2, get_interceptor_->GetHitCount());
+#else
   EXPECT_EQ(2, GetInterceptorCount());
-
+#endif
   EXPECT_EQ(1, num_download_complete_calls_);
   EXPECT_EQ(kExpectedContext, crx_context_);
   EXPECT_NE(0, download_complete_result_.error);
diff --git a/src/components/update_client/net/network_impl_cobalt.cc b/src/components/update_client/net/network_impl_cobalt.cc
index 02acd0f..feada05 100644
--- a/src/components/update_client/net/network_impl_cobalt.cc
+++ b/src/components/update_client/net/network_impl_cobalt.cc
@@ -53,7 +53,7 @@
 }
 
 // Returns the integral value of a header of the server response or -1 if
-// if the header is not available or a conversion error has occured.
+// if the header is not available or a conversion error has occurred.
 int64_t GetInt64Header(const net::HttpResponseHeaders* headers,
                        const char* header_name) {
   if (!headers) {
@@ -82,8 +82,8 @@
     PostRequestCompleteCallback post_request_complete_callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  SB_LOG(INFO) << "PostRequest url = " << url;
-  SB_LOG(INFO) << "PostRequest post_data = " << post_data;
+  LOG(INFO) << "PostRequest url = " << url;
+  LOG(INFO) << "PostRequest post_data = " << post_data;
 
   response_started_callback_ = std::move(response_started_callback);
   progress_callback_ = std::move(progress_callback);
@@ -114,8 +114,8 @@
     DownloadToFileCompleteCallback download_to_file_complete_callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  SB_LOG(INFO) << "DownloadToFile url = " << url;
-  SB_LOG(INFO) << "DownloadToFile file_path = " << file_path;
+  LOG(INFO) << "DownloadToFile url = " << url;
+  LOG(INFO) << "DownloadToFile file_path = " << file_path;
 
   response_started_callback_ = std::move(response_started_callback);
   progress_callback_ = std::move(progress_callback);
@@ -132,6 +132,13 @@
   url_fetcher_->Start();
 }
 
+void NetworkFetcherCobaltImpl::Cancel() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  LOG(INFO) << "Cancel";
+  url_fetcher_.reset();
+}
+
 void NetworkFetcherCobaltImpl::OnURLFetchResponseStarted(
     const net::URLFetcher* source) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -206,11 +213,10 @@
   }
 
   if (response_body->empty()) {
-    SB_LOG(ERROR) << "PostRequest got empty response.";
+    LOG(ERROR) << "PostRequest got empty response.";
   }
 
-  SB_LOG(INFO) << "OnPostRequestComplete response_body = "
-               << *response_body.get();
+  LOG(INFO) << "OnPostRequestComplete response_body = " << *response_body.get();
 
   net::HttpResponseHeaders* response_headers = source->GetResponseHeaders();
   std::move(post_request_complete_callback_)
@@ -226,9 +232,9 @@
     const int status_error) {
   base::FilePath response_file;
   if (!source->GetResponseAsFilePath(true, &response_file)) {
-    SB_LOG(ERROR) << "DownloadToFile failed to get response from a file";
+    LOG(ERROR) << "DownloadToFile failed to get response from a file";
   }
-  SB_LOG(INFO) << "OnDownloadToFileComplete response_file = " << response_file;
+  LOG(INFO) << "OnDownloadToFileComplete response_file = " << response_file;
 
   std::move(download_to_file_complete_callback_)
       .Run(response_file, status_error,
@@ -240,7 +246,7 @@
 NetworkFetcherCobaltImpl::ReturnWrapper NetworkFetcherCobaltImpl::HandleError(
     const std::string& message) {
   url_fetcher_.reset();
-  SB_LOG(ERROR) << message;
+  LOG(ERROR) << message;
   return ReturnWrapper();
 }
 
diff --git a/src/components/update_client/net/network_impl_cobalt.h b/src/components/update_client/net/network_impl_cobalt.h
index c30f3ad..742e8ea 100644
--- a/src/components/update_client/net/network_impl_cobalt.h
+++ b/src/components/update_client/net/network_impl_cobalt.h
@@ -71,6 +71,7 @@
                       ProgressCallback progress_callback,
                       DownloadToFileCompleteCallback
                           download_to_file_complete_callback) override;
+  void Cancel() override;
 
   // net::URLFetcherDelegate interface.
   void OnURLFetchResponseStarted(const net::URLFetcher* source) override;
diff --git a/src/components/update_client/network.h b/src/components/update_client/network.h
index dec108f..cf884b5 100644
--- a/src/components/update_client/network.h
+++ b/src/components/update_client/network.h
@@ -64,7 +64,7 @@
       ProgressCallback progress_callback,
       DownloadToFileCompleteCallback download_to_file_complete_callback) = 0;
 #if defined(STARBOARD)
-  virtual void CancelDownloadToFile() = 0;
+  virtual void Cancel() = 0;
 #endif
 
  protected:
diff --git a/src/components/update_client/ping_manager.cc b/src/components/update_client/ping_manager.cc
index 3c2a60c..25c2c25 100644
--- a/src/components/update_client/ping_manager.cc
+++ b/src/components/update_client/ping_manager.cc
@@ -32,6 +32,12 @@
 const int kErrorNoEvents = -1;
 const int kErrorNoUrl = -2;
 
+// When building for STARBOARD add the PingSender to the update_client namespace
+// as we keep a reference to it in PingManager.
+#if defined(STARBOARD)
+}
+#endif
+
 // An instance of this class can send only one ping.
 class PingSender : public base::RefCountedThreadSafe<PingSender> {
  public:
@@ -39,6 +45,10 @@
   explicit PingSender(scoped_refptr<Configurator> config);
   void SendPing(const Component& component, Callback callback);
 
+#if defined(STARBOARD)
+  void Cancel();
+#endif
+
  protected:
   virtual ~PingSender();
 
@@ -66,6 +76,10 @@
 void PingSender::SendPing(const Component& component, Callback callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
+#if defined(STARBOARD)
+  LOG(INFO) << "PingSender::SendPing";
+#endif
+
   if (component.events().empty()) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(std::move(callback), kErrorNoEvents, ""));
@@ -103,27 +117,62 @@
       false, base::BindOnce(&PingSender::SendPingComplete, this));
 }
 
+#if defined(STARBOARD)
+void PingSender::Cancel() {
+  LOG(INFO) << "PingSender::Cancel";
+  if (request_sender_.get()) {
+    request_sender_->Cancel();
+  }
+}
+#endif
+
 void PingSender::SendPingComplete(int error,
                                   const std::string& response,
                                   int retry_after_sec) {
+  LOG(INFO) << "PingSender::SendPingComplete";
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   std::move(callback_).Run(error, response);
 }
 
+#if !defined(STARBOARD)
 }  // namespace
+#endif
 
 PingManager::PingManager(scoped_refptr<Configurator> config)
-    : config_(config) {}
+    : config_(config) {
+#if defined(STARBOARD)
+  LOG(INFO) << "PingManager::PingManager";
+#endif
+}
+
+#if defined(STARBOARD)
+void PingManager::Cancel() {
+  LOG(INFO) << "PingManager::Cancel";
+  if (ping_sender_.get()) {
+    ping_sender_->Cancel();
+  }
+}
+#endif
 
 PingManager::~PingManager() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+#if defined(STARBOARD)
+  LOG(INFO) << "PingManager::~PingManager";
+#endif
 }
 
 void PingManager::SendPing(const Component& component, Callback callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
+#if defined(STARBOARD)
+  LOG(INFO) << "PingManager::SendPing";
+
+  ping_sender_ = base::MakeRefCounted<PingSender>(config_);
+  ping_sender_->SendPing(component, std::move(callback));
+#else
   auto ping_sender = base::MakeRefCounted<PingSender>(config_);
   ping_sender->SendPing(component, std::move(callback));
+#endif
 }
 
 }  // namespace update_client
diff --git a/src/components/update_client/ping_manager.h b/src/components/update_client/ping_manager.h
index 24040e6..d049788 100644
--- a/src/components/update_client/ping_manager.h
+++ b/src/components/update_client/ping_manager.h
@@ -17,6 +17,10 @@
 class Configurator;
 class Component;
 
+#if defined(STARBOARD)
+class PingSender;
+#endif
+
 class PingManager : public base::RefCountedThreadSafe<PingManager> {
  public:
   // |error| is 0 if the ping was sent successfully, otherwise |error| contains
@@ -31,6 +35,9 @@
   // be discarded if it has not been sent for any reason.
   virtual void SendPing(const Component& component, Callback callback);
 
+#if defined(STARBOARD)
+  virtual void Cancel();
+#endif
  protected:
   virtual ~PingManager();
 
@@ -40,6 +47,10 @@
   THREAD_CHECKER(thread_checker_);
   const scoped_refptr<Configurator> config_;
 
+#if defined(STARBOARD)
+  scoped_refptr<PingSender> ping_sender_;
+#endif
+
   DISALLOW_COPY_AND_ASSIGN(PingManager);
 };
 
diff --git a/src/components/update_client/request_sender.cc b/src/components/update_client/request_sender.cc
index a68df98..2f3d878 100644
--- a/src/components/update_client/request_sender.cc
+++ b/src/components/update_client/request_sender.cc
@@ -45,6 +45,9 @@
     bool use_signing,
     RequestSenderCallback request_sender_callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
+#if defined(STARBOARD)
+  LOG(INFO) << "RequestSender::Send";
+#endif
 
   urls_ = urls;
   request_extra_headers_ = request_extra_headers;
@@ -101,10 +104,22 @@
                      base::Unretained(this), url));
 }
 
+#if defined(STARBOARD)
+void RequestSender::Cancel() {
+  LOG(INFO) << "RequestSender::Cancel";
+  if (network_fetcher_.get()) {
+    network_fetcher_->Cancel();
+  }
+}
+#endif
+
 void RequestSender::SendInternalComplete(int error,
                                          const std::string& response_body,
                                          const std::string& response_etag,
                                          int retry_after_sec) {
+#if defined(STARBOARD)
+  LOG(INFO) << "RequestSender::SendInternalComplete";
+#endif
   if (!error) {
     if (!use_signing_) {
       base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -152,6 +167,9 @@
     const std::string& header_etag,
     int64_t xheader_retry_after_sec) {
   DCHECK(thread_checker_.CalledOnValidThread());
+#if defined(STARBOARD)
+  LOG(INFO) << "RequestSender::OnNetworkFetcherComplete";
+#endif
 
   VLOG(1) << "request completed from url: " << original_url.spec();
 
diff --git a/src/components/update_client/request_sender.h b/src/components/update_client/request_sender.h
index 7b9e21b..1936047 100644
--- a/src/components/update_client/request_sender.h
+++ b/src/components/update_client/request_sender.h
@@ -58,6 +58,10 @@
       bool use_signing,
       RequestSenderCallback request_sender_callback);
 
+#if defined(STARBOARD)
+  void Cancel();
+#endif
+
  private:
   // Combines the |url| and |query_params| parameters.
   static GURL BuildUpdateUrl(const GURL& url, const std::string& query_params);
diff --git a/src/components/update_client/task_update.cc b/src/components/update_client/task_update.cc
index 0129e4c..b4894b7 100644
--- a/src/components/update_client/task_update.cc
+++ b/src/components/update_client/task_update.cc
@@ -22,14 +22,28 @@
       is_foreground_(is_foreground),
       ids_(ids),
       crx_data_callback_(std::move(crx_data_callback)),
-      callback_(std::move(callback)) {}
+      callback_(std::move(callback))
+#if defined(STARBOARD)
+      , is_completed_(false)
+#endif
+{
+#if defined(STARBOARD)
+    LOG(INFO) << "TaskUpdate::TaskUpdate";
+#endif
+}
 
 TaskUpdate::~TaskUpdate() {
   DCHECK(thread_checker_.CalledOnValidThread());
+#if defined(STARBOARD)
+  LOG(INFO) << "TaskUpdate::~TaskUpdate";
+#endif
 }
 
 void TaskUpdate::Run() {
   DCHECK(thread_checker_.CalledOnValidThread());
+#if defined(STARBOARD)
+  LOG(INFO) << "TaskUpdate::Run begin";
+#endif
 
   if (ids_.empty()) {
     TaskComplete(Error::INVALID_ARGUMENT);
@@ -40,16 +54,19 @@
   update_engine_->Update(is_foreground_, ids_, std::move(crx_data_callback_),
                          base::BindOnce(&TaskUpdate::TaskComplete, this),
                          cancelation_closure_);
+  LOG(INFO) << "TaskUpdate::Run end";
 #else
   update_engine_->Update(is_foreground_, ids_, std::move(crx_data_callback_),
                          base::BindOnce(&TaskUpdate::TaskComplete, this));
 #endif
+
 }
 
 void TaskUpdate::Cancel() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
 #if defined(STARBOARD)
+  LOG(INFO) << "TaskUpdate::Cancel";
   if (cancelation_closure_) {  // The engine's picked up the task.
     std::move(cancelation_closure_).Run();
   }
@@ -64,6 +81,18 @@
 
 void TaskUpdate::TaskComplete(Error error) {
   DCHECK(thread_checker_.CalledOnValidThread());
+#if defined(STARBOARD)
+  LOG(INFO) << "TaskUpdate::TaskComplete";
+
+  // The callback is defined as OnceCallback and should not
+  // be called multiple times.
+  if(is_completed_) {
+    LOG(INFO) << "TaskUpdate::TaskComplete already called";
+    return;
+  }
+
+  is_completed_ = true;
+#endif
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback_),
diff --git a/src/components/update_client/task_update.h b/src/components/update_client/task_update.h
index 74c0a6a..2697c35 100644
--- a/src/components/update_client/task_update.h
+++ b/src/components/update_client/task_update.h
@@ -59,6 +59,7 @@
   Callback callback_;
 #if defined(STARBOARD)
   base::OnceClosure cancelation_closure_;
+  bool is_completed_;
 #endif
 
   DISALLOW_COPY_AND_ASSIGN(TaskUpdate);
diff --git a/src/components/update_client/test_configurator.h b/src/components/update_client/test_configurator.h
index c3fbda1..6a9e5eb 100644
--- a/src/components/update_client/test_configurator.h
+++ b/src/components/update_client/test_configurator.h
@@ -85,7 +85,7 @@
  public:
   TestConfigurator();
 
-  // Overrrides for Configurator.
+  // Overrides for Configurator.
   int InitialDelay() const override;
   int NextCheckDelay() const override;
   int OnDemandDelay() const override;
@@ -127,8 +127,14 @@
   void SetAppGuid(const std::string& app_guid);
 
 #if defined(STARBOARD)
+  // TODO: add unit tests for updater channels and status
   void SetChannel(const std::string& channel) override {}
   void CompareAndSwapChannelChanged(int old_value, int new_value) override {}
+  std::string GetUpdaterStatus() const override { return ""; }
+  void SetUpdaterStatus(const std::string& status) override {}
+
+  std::string GetPreviousUpdaterStatus() const override { return ""; }
+  void SetPreviousUpdaterStatus(const std::string& status) override {}
 #else
   network::TestURLLoaderFactory* test_url_loader_factory() {
     return &test_url_loader_factory_;
diff --git a/src/components/update_client/update_checker.cc b/src/components/update_client/update_checker.cc
index 1e7eed4..4951abf 100644
--- a/src/components/update_client/update_checker.cc
+++ b/src/components/update_client/update_checker.cc
@@ -20,6 +20,9 @@
 #include "base/strings/stringprintf.h"
 #include "base/task/post_task.h"
 #include "base/threading/thread_checker.h"
+#if defined(STARBOARD)
+#include "base/threading/thread_id_name_manager.h"
+#endif
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #if defined(STARBOARD)
@@ -98,6 +101,9 @@
       const IdToComponentPtrMap& components,
       const base::flat_map<std::string, std::string>& additional_attributes,
       bool enabled_component_updates);
+#if defined(STARBOARD)
+  void Cancel();
+#endif
   void OnRequestSenderComplete(int error,
                                const std::string& response,
                                int retry_after_sec);
@@ -121,10 +127,17 @@
 
 UpdateCheckerImpl::UpdateCheckerImpl(scoped_refptr<Configurator> config,
                                      PersistedData* metadata)
-    : config_(config), metadata_(metadata) {}
+    : config_(config), metadata_(metadata) {
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateCheckerImpl::UpdateCheckerImpl";
+#endif
+}
 
 UpdateCheckerImpl::~UpdateCheckerImpl() {
   DCHECK(thread_checker_.CalledOnValidThread());
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateCheckerImpl::~UpdateCheckerImpl";
+#endif
 }
 
 void UpdateCheckerImpl::CheckForUpdates(
@@ -135,6 +148,9 @@
     bool enabled_component_updates,
     UpdateCheckCallback update_check_callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateCheckerImpl::CheckForUpdates";
+#endif
 
   ids_checked_ = ids_checked;
   update_check_callback_ = std::move(update_check_callback);
@@ -150,6 +166,11 @@
 
 // This function runs on the blocking pool task runner.
 void UpdateCheckerImpl::ReadUpdaterStateAttributes() {
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateCheckerImpl::ReadUpdaterStateAttributes current_thread="
+    << base::ThreadIdNameManager::GetInstance()->GetNameForCurrentThread();
+#endif
+
 #if defined(OS_WIN)
   // On Windows, the Chrome and the updater install modes are matched by design.
   updater_state_attributes_ =
@@ -168,6 +189,9 @@
     const base::flat_map<std::string, std::string>& additional_attributes,
     bool enabled_component_updates) {
   DCHECK(thread_checker_.CalledOnValidThread());
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateCheckerImpl::CheckForUpdatesHelper";
+#endif
 
   auto urls(config_->UpdateUrl());
   if (IsEncryptionRequired(components))
@@ -219,7 +243,7 @@
         static_cast<const CobaltExtensionInstallationManagerApi*>(
             SbSystemGetExtension(kCobaltExtensionInstallationManagerName));
     if (!installation_api) {
-      SB_LOG(ERROR) << "Failed to get installation manager extension.";
+      LOG(ERROR) << "Failed to get installation manager extension.";
       return;
     }
 
@@ -274,6 +298,15 @@
 #endif
 }
 
+#if defined(STARBOARD)
+void UpdateCheckerImpl::Cancel() {
+  LOG(INFO) << "UpdateCheckerImpl::Cancel";
+  if (request_sender_.get()) {
+    request_sender_->Cancel();
+  }
+}
+#endif
+
 void UpdateCheckerImpl::OnRequestSenderComplete(int error,
                                                 const std::string& response,
                                                 int retry_after_sec) {
diff --git a/src/components/update_client/update_checker.h b/src/components/update_client/update_checker.h
index 9a53357..78eb968 100644
--- a/src/components/update_client/update_checker.h
+++ b/src/components/update_client/update_checker.h
@@ -51,6 +51,10 @@
       bool enabled_component_updates,
       UpdateCheckCallback update_check_callback) = 0;
 
+#if defined(STARBOARD)
+  virtual void Cancel() = 0;
+#endif
+
   static std::unique_ptr<UpdateChecker> Create(
       scoped_refptr<Configurator> config,
       PersistedData* persistent);
diff --git a/src/components/update_client/update_checker_unittest.cc b/src/components/update_client/update_checker_unittest.cc
index de84eb9..7905805 100644
--- a/src/components/update_client/update_checker_unittest.cc
+++ b/src/components/update_client/update_checker_unittest.cc
@@ -369,7 +369,7 @@
   EXPECT_EQ(GURL("http://localhost/download/"), result.crx_urls.front());
   EXPECT_STREQ("this", result.action_run.c_str());
 
-  // Check the DDOS protection header values.
+// Check the DDOS protection header values.
 #if defined(STARBOARD)
   const auto extra_request_headers = post_interceptor_->GetRequests()[0].second;
 #else
@@ -740,8 +740,12 @@
 
   if (is_foreground_) {
     {
+#if defined(STARBOARD)
+      auto post_interceptor = post_interceptor_;
+#else
       auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
           config_->test_url_loader_factory());
+#endif
       EXPECT_TRUE(post_interceptor->ExpectRequest(
           std::make_unique<PartialMatch>("updatecheck"),
           test_file("updatecheck_reply_1.json")));
@@ -758,8 +762,12 @@
       EXPECT_FALSE(app.FindKey("installedby"));
     }
     {
+#if defined(STARBOARD)
+      auto post_interceptor = post_interceptor_;
+#else
       auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
           config_->test_url_loader_factory());
+#endif
       EXPECT_TRUE(post_interceptor->ExpectRequest(
           std::make_unique<PartialMatch>("updatecheck"),
           test_file("updatecheck_reply_1.json")));
@@ -783,8 +791,12 @@
 
   DCHECK(!is_foreground_);
   {
+#if defined(STARBOARD)
+    auto post_interceptor = post_interceptor_;
+#else
     auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
         config_->test_url_loader_factory());
+#endif
     EXPECT_TRUE(post_interceptor->ExpectRequest(
         std::make_unique<PartialMatch>("updatecheck"),
         test_file("updatecheck_reply_1.json")));
@@ -800,8 +812,12 @@
     EXPECT_FALSE(app.FindKey("installsource"));
   }
   {
+#if defined(STARBOARD)
+    auto post_interceptor = post_interceptor_;
+#else
     auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
         config_->test_url_loader_factory());
+#endif
     EXPECT_TRUE(post_interceptor->ExpectRequest(
         std::make_unique<PartialMatch>("updatecheck"),
         test_file("updatecheck_reply_1.json")));
@@ -832,8 +848,12 @@
   auto crx_component = component->crx_component();
 
   {
+#if defined(STARBOARD)
+    auto post_interceptor = post_interceptor_;
+#else
     auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
         config_->test_url_loader_factory());
+#endif
     EXPECT_TRUE(post_interceptor->ExpectRequest(
         std::make_unique<PartialMatch>("updatecheck"),
         test_file("updatecheck_reply_1.json")));
@@ -853,8 +873,12 @@
   {
     crx_component->disabled_reasons = {};
     component->set_crx_component(*crx_component);
+#if defined(STARBOARD)
+    auto post_interceptor = post_interceptor_;
+#else
     auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
         config_->test_url_loader_factory());
+#endif
     EXPECT_TRUE(post_interceptor->ExpectRequest(
         std::make_unique<PartialMatch>("updatecheck"),
         test_file("updatecheck_reply_1.json")));
@@ -874,8 +898,12 @@
   {
     crx_component->disabled_reasons = {0};
     component->set_crx_component(*crx_component);
+#if defined(STARBOARD)
+    auto post_interceptor = post_interceptor_;
+#else
     auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
         config_->test_url_loader_factory());
+#endif
     EXPECT_TRUE(post_interceptor->ExpectRequest(
         std::make_unique<PartialMatch>("updatecheck"),
         test_file("updatecheck_reply_1.json")));
@@ -896,8 +924,12 @@
   {
     crx_component->disabled_reasons = {1};
     component->set_crx_component(*crx_component);
+#if defined(STARBOARD)
+    auto post_interceptor = post_interceptor_;
+#else
     auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
         config_->test_url_loader_factory());
+#endif
     EXPECT_TRUE(post_interceptor->ExpectRequest(
         std::make_unique<PartialMatch>("updatecheck"),
         test_file("updatecheck_reply_1.json")));
@@ -919,8 +951,12 @@
   {
     crx_component->disabled_reasons = {4, 8, 16};
     component->set_crx_component(*crx_component);
+#if defined(STARBOARD)
+    auto post_interceptor = post_interceptor_;
+#else
     auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
         config_->test_url_loader_factory());
+#endif
     EXPECT_TRUE(post_interceptor->ExpectRequest(
         std::make_unique<PartialMatch>("updatecheck"),
         test_file("updatecheck_reply_1.json")));
@@ -944,8 +980,12 @@
   {
     crx_component->disabled_reasons = {0, 4, 8, 16};
     component->set_crx_component(*crx_component);
+#if defined(STARBOARD)
+    auto post_interceptor = post_interceptor_;
+#else
     auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
         config_->test_url_loader_factory());
+#endif
     EXPECT_TRUE(post_interceptor->ExpectRequest(
         std::make_unique<PartialMatch>("updatecheck"),
         test_file("updatecheck_reply_1.json")));
@@ -987,8 +1027,12 @@
     // Expects the group policy to be ignored and the update check to not
     // include the "updatedisabled" attribute.
     EXPECT_FALSE(crx_component->supports_group_policy_enable_component_updates);
+#if defined(STARBOARD)
+    auto post_interceptor = post_interceptor_;
+#else
     auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
         config_->test_url_loader_factory());
+#endif
     EXPECT_TRUE(post_interceptor->ExpectRequest(
         std::make_unique<PartialMatch>("updatecheck"),
         test_file("updatecheck_reply_1.json")));
@@ -1013,8 +1057,12 @@
     // Expects the update check to include the "updatedisabled" attribute.
     crx_component->supports_group_policy_enable_component_updates = true;
     component->set_crx_component(*crx_component);
+#if defined(STARBOARD)
+    auto post_interceptor = post_interceptor_;
+#else
     auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
         config_->test_url_loader_factory());
+#endif
     EXPECT_TRUE(post_interceptor->ExpectRequest(
         std::make_unique<PartialMatch>("updatecheck"),
         test_file("updatecheck_reply_1.json")));
@@ -1039,8 +1087,12 @@
     // Expects the update check to not include the "updatedisabled" attribute.
     crx_component->supports_group_policy_enable_component_updates = false;
     component->set_crx_component(*crx_component);
+#if defined(STARBOARD)
+    auto post_interceptor = post_interceptor_;
+#else
     auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
         config_->test_url_loader_factory());
+#endif
     EXPECT_TRUE(post_interceptor->ExpectRequest(
         std::make_unique<PartialMatch>("updatecheck"),
         test_file("updatecheck_reply_1.json")));
@@ -1065,8 +1117,12 @@
     // Expects the update check to not include the "updatedisabled" attribute.
     crx_component->supports_group_policy_enable_component_updates = true;
     component->set_crx_component(*crx_component);
+#if defined(STARBOARD)
+    auto post_interceptor = post_interceptor_;
+#else
     auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
         config_->test_url_loader_factory());
+#endif
     EXPECT_TRUE(post_interceptor->ExpectRequest(
         std::make_unique<PartialMatch>("updatecheck"),
         test_file("updatecheck_reply_1.json")));
@@ -1221,7 +1277,7 @@
 }
 
 // The update response contains a status |error-unknownApplication| for the
-// app. The response is succesfully parsed and a result is extracted to
+// app. The response is successfully parsed and a result is extracted to
 // indicate this status.
 TEST_P(UpdateCheckerTest, ParseErrorAppStatusErrorUnknownApplication) {
   EXPECT_TRUE(post_interceptor_->ExpectRequest(
diff --git a/src/components/update_client/update_client.cc b/src/components/update_client/update_client.cc
index 6ce65bf..0ba76cd 100644
--- a/src/components/update_client/update_client.cc
+++ b/src/components/update_client/update_client.cc
@@ -78,6 +78,10 @@
   DCHECK(task_queue_.empty());
   DCHECK(tasks_.empty());
 
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateClientImpl::~UpdateClientImpl: task_queue_.size=" << task_queue_.size() << " tasks.size=" << tasks_.size();
+#endif
+
   config_ = nullptr;
 }
 
@@ -135,6 +139,10 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(task);
 
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateClientImpl::OnTaskComplete";
+#endif
+
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), error));
 
diff --git a/src/components/update_client/update_client.gyp b/src/components/update_client/update_client.gyp
index 3b9edd7..6a15ed0 100644
--- a/src/components/update_client/update_client.gyp
+++ b/src/components/update_client/update_client.gyp
@@ -148,18 +148,19 @@
       'sources': [
         'component_unpacker_unittest.cc',
         # TODO: enable the tests commented out
-        # 'crx_downloader_unittest.cc',
+        'crx_downloader_unittest.cc',
         'persisted_data_unittest.cc',
         'ping_manager_unittest.cc',
         'protocol_parser_json_unittest.cc',
         # 'protocol_serializer_json_unittest.cc',
         'protocol_serializer_unittest.cc',
         'request_sender_unittest.cc',
-        # 'update_checker_unittest.cc',
+        'update_checker_unittest.cc',
         # 'update_client_unittest.cc',
         'update_query_params_unittest.cc',
         'updater_state_unittest.cc',
         'utils_unittest.cc',
+        '<(DEPTH)/cobalt/updater/utils.cc',
       ],
       'dependencies': [
         'update_client',
diff --git a/src/components/update_client/update_engine.cc b/src/components/update_client/update_engine.cc
index 29c260b..9e39a0f 100644
--- a/src/components/update_client/update_engine.cc
+++ b/src/components/update_client/update_engine.cc
@@ -51,7 +51,11 @@
   }
 }
 
-UpdateContext::~UpdateContext() {}
+UpdateContext::~UpdateContext() {
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateContext::~UpdateContext";
+#endif
+}
 
 UpdateEngine::UpdateEngine(
     scoped_refptr<Configurator> config,
@@ -66,10 +70,17 @@
       metadata_(
           std::make_unique<PersistedData>(config->GetPrefService(),
                                           config->GetActivityDataService())),
-      notify_observers_callback_(notify_observers_callback) {}
+      notify_observers_callback_(notify_observers_callback) {
+#if defined(STARBOARD)
+    LOG(INFO) << "UpdateEngine::UpdateEngine";
+#endif
+}
 
 UpdateEngine::~UpdateEngine() {
   DCHECK(thread_checker_.CalledOnValidThread());
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateEngine::~UpdateEngine";
+#endif
 }
 
 #if !defined(STARBOARD)
@@ -87,6 +98,10 @@
 #endif
   DCHECK(thread_checker_.CalledOnValidThread());
 
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateEngine::Update";
+#endif
+
   if (ids.empty()) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
@@ -169,6 +184,10 @@
   DCHECK_EQ(1u, update_context->components.count(id));
   DCHECK(update_context->components.at(id));
 
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateEngine::ComponentCheckingForUpdatesStart";
+#endif
+
   // Handle |kChecking| state.
   auto& component = *update_context->components.at(id);
   component.Handle(
@@ -190,6 +209,10 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(update_context);
 
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateEngine::DoUpdateCheck";
+#endif
+
   update_context->update_checker =
       update_checker_factory_(config_, metadata_.get());
 
@@ -211,6 +234,10 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(update_context);
 
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateEngine::UpdateCheckResultsAvailable";
+#endif
+
   update_context->retry_after_sec = retry_after_sec;
 
   // Only positive values for throttle_sec are effective. 0 means that no
@@ -283,6 +310,10 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(update_context);
 
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateEngine::ComponentCheckingForUpdatesComplete";
+#endif
+
   ++update_context->num_components_checked;
   if (update_context->num_components_checked <
       update_context->components_to_check_for_updates.size()) {
@@ -299,6 +330,10 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(update_context);
 
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateEngine::UpdateCheckComplete";
+#endif
+
   for (const auto& id : update_context->components_to_check_for_updates)
     update_context->component_queue.push(id);
 
@@ -312,6 +347,10 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(update_context);
 
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateEngine::HandleComponent";
+#endif
+
   auto& queue = update_context->component_queue;
 
   if (queue.empty()) {
@@ -354,6 +393,10 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(update_context);
 
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateEngine::HandleComponentComplete";
+#endif
+
   auto& queue = update_context->component_queue;
   DCHECK(!queue.empty());
 
@@ -380,6 +423,10 @@
 
 void UpdateEngine::UpdateComplete(scoped_refptr<UpdateContext> update_context,
                                   Error error) {
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateEngine::UpdateComplete";
+#endif
+
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(update_context);
 
@@ -393,6 +440,9 @@
 bool UpdateEngine::GetUpdateState(const std::string& id,
                                   CrxUpdateItem* update_item) {
   DCHECK(thread_checker_.CalledOnValidThread());
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateEngine::GetUpdateState";
+#endif
   for (const auto& context : update_contexts_) {
     const auto& components = context.second->components;
     const auto it = components.find(id);
@@ -406,6 +456,9 @@
 
 bool UpdateEngine::IsThrottled(bool is_foreground) const {
   DCHECK(thread_checker_.CalledOnValidThread());
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateEngine::IsThrottled";
+#endif
 
   if (is_foreground || throttle_updates_until_.is_null())
     return false;
@@ -421,7 +474,16 @@
 #if defined(STARBOARD)
 void UpdateEngine::Cancel(const std::string& update_context_session_id,
                           const std::vector<std::string>& crx_component_ids) {
+  LOG(INFO) << "UpdateEngine::Cancel";
+
+  if (ping_manager_.get()) {
+    ping_manager_->Cancel();
+  }
+
   const auto& context = update_contexts_.at(update_context_session_id);
+  if (context->update_checker.get()) {
+    context->update_checker->Cancel();
+  }
   for (const auto& crx_component_id : crx_component_ids) {
     auto& component = context->components.at(crx_component_id);
     component->Cancel();
@@ -435,6 +497,10 @@
                                      Callback callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
+#if defined(STARBOARD)
+  LOG(INFO) << "UpdateEngine::SendUninstallPing";
+#endif
+
   const auto update_context = base::MakeRefCounted<UpdateContext>(
       config_, false, std::vector<std::string>{id},
       UpdateClient::CrxDataCallback(), UpdateEngine::NotifyObserversCallback(),
diff --git a/src/components/update_client/update_engine.h b/src/components/update_client/update_engine.h
index 4384fa8..1cd0ac9 100644
--- a/src/components/update_client/update_engine.h
+++ b/src/components/update_client/update_engine.h
@@ -109,7 +109,8 @@
 #if defined(STARBOARD)
   // Cancels updates currently handled by the engine for each component
   // identified by one of |crx_component_ids| for the update context identified
-  // by the |update_context_session_id|.
+  // by the |update_context_session_id|. Also cancels the |UpdateChecker| for 
+  // the component and the |PingManager|.
   void Cancel(const std::string& update_context_session_id,
               const std::vector<std::string>& crx_component_ids);
 #endif
diff --git a/src/components/update_client/url_fetcher_downloader.cc b/src/components/update_client/url_fetcher_downloader.cc
index 078c275..b5c13cc 100644
--- a/src/components/update_client/url_fetcher_downloader.cc
+++ b/src/components/update_client/url_fetcher_downloader.cc
@@ -71,7 +71,9 @@
     scoped_refptr<Configurator> config)
     : CrxDownloader(std::move(successor)),
       config_(config),
-      network_fetcher_factory_(config->GetNetworkFetcherFactory()) {}
+      network_fetcher_factory_(config->GetNetworkFetcherFactory()) {
+  LOG(INFO) << "UrlFetcherDownloader::UrlFetcherDownloader";
+}
 #else
 UrlFetcherDownloader::UrlFetcherDownloader(
     std::unique_ptr<CrxDownloader> successor,
@@ -82,11 +84,14 @@
 
 UrlFetcherDownloader::~UrlFetcherDownloader() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+#if defined(STARBOARD)
+  LOG(INFO) << "UrlFetcherDownloader::UrlFetcherDownloader";
+#endif
 }
 
 #if defined(STARBOARD)
 void UrlFetcherDownloader::ConfirmSlot(const GURL& url) {
-  SB_LOG(INFO) << "UrlFetcherDownloader::ConfirmSlot: url=" << url;
+  LOG(INFO) << "UrlFetcherDownloader::ConfirmSlot: url=" << url;
   if (!cobalt_slot_management_.ConfirmSlot(download_dir_)) {
     ReportDownloadFailure(url, CrxDownloaderError::SLOT_UNAVAILABLE);
     return;
@@ -98,7 +103,7 @@
 }
 
 void UrlFetcherDownloader::SelectSlot(const GURL& url) {
-  SB_LOG(INFO) << "UrlFetcherDownloader::SelectSlot: url=" << url;
+  LOG(INFO) << "UrlFetcherDownloader::SelectSlot: url=" << url;
   if (!cobalt_slot_management_.SelectSlot(&download_dir_)) {
     ReportDownloadFailure(url, CrxDownloaderError::SLOT_UNAVAILABLE);
     return;
@@ -119,11 +124,13 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
 #if defined(STARBOARD)
+  LOG(INFO) << "UrlFetcherDownloader::DoStartDownload";
+
   const CobaltExtensionInstallationManagerApi* installation_api =
       static_cast<const CobaltExtensionInstallationManagerApi*>(
           SbSystemGetExtension(kCobaltExtensionInstallationManagerName));
   if (!installation_api) {
-    SB_LOG(ERROR) << "Failed to get installation manager";
+    LOG(ERROR) << "Failed to get installation manager";
     ReportDownloadFailure(url);
     return;
   }
@@ -146,8 +153,11 @@
 
 #if defined(STARBOARD)
 void UrlFetcherDownloader::DoCancelDownload() {
+  LOG(INFO) << "UrlFetcherDownloader::DoCancelDownload";
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  network_fetcher_->CancelDownloadToFile();
+  if (network_fetcher_.get()) {
+    network_fetcher_->Cancel();
+  }
 }
 #endif
 
@@ -158,6 +168,7 @@
 
 #if defined(STARBOARD)
 void UrlFetcherDownloader::ReportDownloadFailure(const GURL& url) {
+  LOG(INFO) << "UrlFetcherDownloader::ReportDownloadFailure";
   ReportDownloadFailure(url, CrxDownloaderError::GENERIC_ERROR);
 }
 
@@ -195,13 +206,13 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
 #if defined(STARBOARD)
-  SB_LOG(INFO) << "UrlFetcherDownloader::StartURLFetch: url" << url
+  LOG(INFO) << "UrlFetcherDownloader::StartURLFetch: url" << url
                << " download_dir=" << download_dir_;
 #endif
 
   if (download_dir_.empty()) {
 #if defined(STARBOARD)
-    SB_LOG(ERROR) << "UrlFetcherDownloader::StartURLFetch: failed with empty "
+    LOG(ERROR) << "UrlFetcherDownloader::StartURLFetch: failed with empty "
                      "download_dir";
 #endif
     ReportDownloadFailure(url);
@@ -227,6 +238,10 @@
                                                     int64_t content_size) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
+#if defined(STARBOARD)
+  LOG(INFO) << "UrlFetcherDownloader::OnNetworkFetcherComplete";
+#endif
+
   const base::TimeTicks download_end_time(base::TimeTicks::Now());
   const base::TimeDelta download_time =
       download_end_time >= download_start_time_
@@ -299,6 +314,10 @@
                                              int64_t content_length) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
+#if defined(STARBOARD)
+  LOG(INFO) << "UrlFetcherDownloader::OnResponseStarted";
+#endif
+
   VLOG(1) << "url fetcher response started for: " << final_url.spec();
 
   final_url_ = final_url;
diff --git a/src/docker-compose.yml b/src/docker-compose.yml
index 2401d96..b3ef1ce 100644
--- a/src/docker-compose.yml
+++ b/src/docker-compose.yml
@@ -165,7 +165,6 @@
       PLATFORM: linux-x64x11-clang-3-9
       CONFIG: ${CONFIG:-debug}
       USE_CCACHE: ${USE_CCACHE:-1}
-      NINJA_STATUS: ${NINJA_STATUS}
 
   # Define common build container for Android
   build-android:
diff --git a/src/docker/linux/base/build/Dockerfile b/src/docker/linux/base/build/Dockerfile
index 0de107d..85cc781 100644
--- a/src/docker/linux/base/build/Dockerfile
+++ b/src/docker/linux/base/build/Dockerfile
@@ -59,7 +59,7 @@
 
 # === Configure common env vars
 ENV OUTDIR=out \
-    NINJA_STATUS="[%f/%t %c/sec] " \
+    NINJA_STATUS="[%e sec | %f/%t %u remaining | %c/sec | j%r] " \
     NINJA_PARALLEL=4 \
     IS_CI=0 \
     CCACHE_DIR=/root/ccache \
diff --git a/src/docker/linux/raspi/gn/Dockerfile b/src/docker/linux/raspi/gn/Dockerfile
index 3339455..bb56179 100644
--- a/src/docker/linux/raspi/gn/Dockerfile
+++ b/src/docker/linux/raspi/gn/Dockerfile
@@ -14,5 +14,5 @@
 
 FROM cobalt-build-raspi
 
-CMD gn gen ${OUTDIR}/${PLATFORM}_${CONFIG} --args="target_platform=\"${PLATFORM}\" build_type=\"${CONFIG}\" target_cpu=\"arm\" is_clang=false can_build_evergreen_loader_apps=false " && \
+CMD gn gen ${OUTDIR}/${PLATFORM}_${CONFIG} --args="target_platform=\"${PLATFORM}\" build_type=\"${CONFIG}\" target_cpu=\"arm\" is_clang=false" && \
     ninja -j ${NINJA_PARALLEL} -C ${OUTDIR}/${PLATFORM}_${CONFIG}
diff --git a/src/nb/reuse_allocator_base.cc b/src/nb/reuse_allocator_base.cc
index 14f280d..b057413 100644
--- a/src/nb/reuse_allocator_base.cc
+++ b/src/nb/reuse_allocator_base.cc
@@ -1,18 +1,16 @@
-/*
- * Copyright 2014 Google Inc. All Rights Reserved.
- *
- * 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.
- */
+// Copyright 2014 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
 
 #include "nb/reuse_allocator_base.h"
 
@@ -49,7 +47,7 @@
 }
 
 bool ReuseAllocatorBase::MemoryBlock::CanFulfill(std::size_t request_size,
-                                                  std::size_t alignment) const {
+                                                 std::size_t alignment) const {
   const std::size_t extra_bytes_for_alignment =
       AlignUp(AsInteger(address_), alignment) - AsInteger(address_);
   const std::size_t aligned_size = request_size + extra_bytes_for_alignment;
@@ -233,62 +231,6 @@
   return true;
 }
 
-void* ReuseAllocatorBase::AllocateBestBlock(std::size_t alignment,
-                                            intptr_t context,
-                                            std::size_t* size_hint) {
-  const std::size_t kMinAlignment = 16;
-  std::size_t size =
-      AlignUp(std::max(*size_hint, kMinAlignment), kMinAlignment);
-  alignment = AlignUp(std::max<std::size_t>(alignment, 1), kMinAlignment);
-
-  bool allocate_from_front;
-  FreeBlockSet::iterator free_block_iter =
-      FindBestFreeBlock(size, alignment, context, free_blocks_.begin(),
-                        free_blocks_.end(), &allocate_from_front);
-
-  if (free_block_iter == free_blocks_.end()) {
-    if (CapacityExceeded()) {
-      return NULL;
-    }
-    free_block_iter = ExpandToFit(*size_hint, alignment);
-    if (free_block_iter == free_blocks_.end()) {
-      return NULL;
-    }
-  }
-
-  MemoryBlock block = *free_block_iter;
-  // The block is big enough.  We may waste some space due to alignment.
-  RemoveFreeBlock(free_block_iter);
-
-  MemoryBlock allocated_block;
-  void* user_address;
-
-  if (block.CanFulfill(size, alignment)) {
-    MemoryBlock free_block;
-    block.Allocate(size, alignment, allocate_from_front, &allocated_block,
-                   &free_block);
-    if (free_block.size() > 0) {
-      SB_DCHECK(free_block.address());
-      AddFreeBlock(free_block);
-    }
-    user_address = AlignUp(allocated_block.address(), alignment);
-  } else {
-    allocated_block = block;
-    user_address = AlignUp(allocated_block.address(), alignment);
-  }
-  SB_DCHECK(AsInteger(user_address) >= AsInteger(allocated_block.address()));
-  uintptr_t offset =
-      AsInteger(user_address) - AsInteger(allocated_block.address());
-  SB_DCHECK(allocated_block.size() >= offset);
-  if (allocated_block.size() - offset < *size_hint) {
-    *size_hint = allocated_block.size() - offset;
-  }
-
-  AddAllocatedBlock(user_address, allocated_block);
-
-  return user_address;
-}
-
 ReuseAllocatorBase::ReuseAllocatorBase(Allocator* fallback_allocator,
                                        std::size_t initial_capacity,
                                        std::size_t allocation_increment,
@@ -299,7 +241,7 @@
       capacity_(0),
       total_allocated_(0) {
   if (initial_capacity > 0) {
-    FreeBlockSet::iterator iter = ExpandToFit(initial_capacity, 1);
+    FreeBlockSet::iterator iter = ExpandToFit(initial_capacity, kMinAlignment);
     SB_DCHECK(iter != free_blocks_.end());
   }
 }
diff --git a/src/nb/reuse_allocator_base.h b/src/nb/reuse_allocator_base.h
index a437373..a285694 100644
--- a/src/nb/reuse_allocator_base.h
+++ b/src/nb/reuse_allocator_base.h
@@ -1,18 +1,16 @@
-/*
- * Copyright 2014 Google Inc. All Rights Reserved.
- *
- * 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.
- */
+// Copyright 2014 The Cobalt Authors. All Rights Reserved.
+//
+// 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 NB_REUSE_ALLOCATOR_BASE_H_
 #define NB_REUSE_ALLOCATOR_BASE_H_
@@ -52,19 +50,6 @@
 
   bool TryFree(void* memory);
 
-  // Try to allocate a memory block for the |*size_hint| passed in.  If there is
-  // no such block available, the function may return a block whose size is less
-  // than |*size_hint| and set |*size_hint| to that size.  |context| will be
-  // passed to FindBestFreeBlock() as is, which is useful when the user of a
-  // sub-class wants to pass extra information along with the allocation request
-  // to FindBestFreeBlock().  The function never sets |*size_hint| to a value
-  // greater than the value passed in.
-  // This allows the caller to allocate multiple smaller blocks to fulfill a
-  // large allocation request.
-  void* AllocateBestBlock(std::size_t alignment,
-                          intptr_t context,
-                          std::size_t* size_hint);
-
   std::size_t max_capacity() const { return max_capacity_; }
   void IncreaseMaxCapacityIfNecessary(std::size_t max_capacity) {
     max_capacity_ = std::max(max_capacity, max_capacity_);
@@ -132,20 +117,6 @@
                                                FreeBlockSet::iterator end,
                                                bool* allocate_from_front) = 0;
 
-  // The inherited class can implement this function to return a block whose
-  // size might be smaller than the |size| passed in.  AllocateBestBlock() uses
-  // this functional internally.  The default implementation simply calls
-  // FindFreeBlock() and fails if there isn't a block that is large enough for
-  // |size| bytes.
-  virtual FreeBlockSet::iterator FindBestFreeBlock(std::size_t size,
-                                                   std::size_t alignment,
-                                                   intptr_t context,
-                                                   FreeBlockSet::iterator begin,
-                                                   FreeBlockSet::iterator end,
-                                                   bool* allocate_from_front) {
-    return FindFreeBlock(size, alignment, begin, end, allocate_from_front);
-  }
-
  private:
   // Map from pointers we returned to the user, back to memory blocks.
   typedef std::map<void*, MemoryBlock> AllocatedBlockMap;
diff --git a/src/nb/starboard_memory_allocator.h b/src/nb/starboard_memory_allocator.h
index 0f6895c..44d9ed3 100644
--- a/src/nb/starboard_memory_allocator.h
+++ b/src/nb/starboard_memory_allocator.h
@@ -1,22 +1,22 @@
-/*
- * Copyright 2017 Google Inc. All Rights Reserved.
- *
- * 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.
- */
+// Copyright 2017 The Cobalt Authors. All Rights Reserved.
+//
+// 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 NB_STARBOARD_MEMORY_ALLOCATOR_H_
 #define NB_STARBOARD_MEMORY_ALLOCATOR_H_
 
+#include <algorithm>
+
 #include "nb/allocator.h"
 #include "starboard/configuration.h"
 #include "starboard/memory.h"
@@ -31,7 +31,7 @@
   void* Allocate(std::size_t size) override { return Allocate(size, 1); }
 
   void* Allocate(std::size_t size, std::size_t alignment) override {
-    return SbMemoryAllocateAligned(alignment, size);
+    return SbMemoryAllocateAligned(std::max(alignment, sizeof(void*)), size);
   }
 
   void* AllocateForAlignment(std::size_t* size,
diff --git a/src/net/net.gyp b/src/net/net.gyp
index 5f96584..e791500 100644
--- a/src/net/net.gyp
+++ b/src/net/net.gyp
@@ -2743,6 +2743,8 @@
         'test/url_request/url_request_slow_download_job.h',
         'url_request/test_url_fetcher_factory.cc',
         'url_request/test_url_fetcher_factory.h',
+        'url_request/test_url_request_interceptor.cc',
+        'url_request/test_url_request_interceptor.h',
         'url_request/url_request_test_job.cc',
         'url_request/url_request_test_job.h',
         'url_request/url_request_test_util.cc',
@@ -2786,7 +2788,7 @@
         'quic/mock_encrypter.h',
         'quic/test_task_runner.cc',
         'quic/test_task_runner.h',
-        # Requres quic_trace third-party library. Not yet supported.
+        # Requires quic_trace third-party library. Not yet supported.
         # 'third_party/quic/core/quic_trace_visitor.cc',
         # 'third_party/quic/core/quic_trace_visitor.h',
         'third_party/quic/platform/api/quic_expect_bug.h',
diff --git a/src/starboard/android/apk/BUILD.gn b/src/starboard/android/apk/BUILD.gn
index e0b3819..cd95c13 100644
--- a/src/starboard/android/apk/BUILD.gn
+++ b/src/starboard/android/apk/BUILD.gn
@@ -16,5 +16,5 @@
   script = "//starboard/build/touch.py"
   sources = [ "cobalt-gradle.sh" ]
   outputs = [ "$root_out_dir/gradle/apk_sources.stamp" ]
-  args = outputs
+  args = rebase_path(outputs, root_build_dir)
 }
diff --git a/src/starboard/android/apk/app/cobalt-ninja.sh b/src/starboard/android/apk/app/cobalt-ninja.sh
index f1edba2..7cd85ce 100755
--- a/src/starboard/android/apk/app/cobalt-ninja.sh
+++ b/src/starboard/android/apk/app/cobalt-ninja.sh
@@ -16,7 +16,7 @@
 # This wrapper allows us to run ninja without any assumptions about the
 # environment having been setup correctly to build Cobalt, since that won't
 # happen when started from Android Studio. See:
-# https://cobalt.googlesource.com/cobalt/+/master/src/#Building-and-Running-the-Code
+# https://cobalt.googlesource.com/cobalt/+/master/#Building-and-Running-the-Code
 
 # Allow for a developer-specific environment setup from .cobaltrc
 local_rc=$(dirname $0)/.cobaltrc
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/MediaPlaybackService.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/MediaPlaybackService.java
index 16518da..1b54143 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/MediaPlaybackService.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/MediaPlaybackService.java
@@ -25,11 +25,13 @@
 import android.os.Build;
 import android.os.Build.VERSION;
 import android.os.IBinder;
+import android.os.RemoteException;
 import androidx.annotation.RequiresApi;
 import androidx.core.app.NotificationCompat;
 import dev.cobalt.util.Log;
 import dev.cobalt.util.UsedByNative;
 
+/** Implementation of the MediaPlaybackService used for Background mode media playing. */
 public class MediaPlaybackService extends Service {
 
   private static final int NOTIFICATION_ID = 193266736; // CL number for uniqueness.
@@ -51,8 +53,8 @@
   public int onStartCommand(Intent intent, int flags, int startId) {
     Log.i(TAG, "Cold start - Starting the service.");
     startService();
-    // We don't want the system to recreate a service for us.
-    return START_NOT_STICKY;
+    // It is better for background media playback service.
+    return START_STICKY;
   }
 
   @Override
@@ -74,7 +76,11 @@
 
   public void startService() {
     createChannel();
-    startForeground(NOTIFICATION_ID, buildNotification());
+    try {
+      startForeground(NOTIFICATION_ID, buildNotification());
+    } catch (IllegalStateException e) {
+      Log.e(TAG, "Failed to start Foreground Service", e);
+    }
   }
 
   public void stopService() {
@@ -95,12 +101,16 @@
 
   private void createChannel() {
     if (Build.VERSION.SDK_INT >= 26) {
-      createChannelInternalV26();
+      try {
+        createChannelInternalV26();
+      } catch (RemoteException e) {
+        Log.e(TAG, "Failed to create Notification Channel.", e);
+      }
     }
   }
 
   @RequiresApi(26)
-  private void createChannelInternalV26() {
+  private void createChannelInternalV26() throws RemoteException {
     NotificationManager notificationManager =
         (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
     NotificationChannel channel =
@@ -109,11 +119,7 @@
             NOTIFICATION_CHANNEL_NAME,
             notificationManager.IMPORTANCE_DEFAULT);
     channel.setDescription("Channel for showing persistent notification");
-    try {
-      notificationManager.createNotificationChannel(channel);
-    } catch (IllegalArgumentException e) {
-      // intentional empty.
-    }
+    notificationManager.createNotificationChannel(channel);
   }
 
   public void deleteChannel() {
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
index 68d95ef..408ada0 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
@@ -31,6 +31,7 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.os.Build;
+import android.os.Build.VERSION;
 import android.util.Size;
 import android.util.SizeF;
 import android.view.Display;
@@ -171,6 +172,11 @@
   @SuppressWarnings("unused")
   @UsedByNative
   protected void startMediaPlaybackService() {
+    if (cobaltMediaSession == null || !cobaltMediaSession.isActive()) {
+      Log.w(TAG, "Do not start a MediaPlaybackService when the MediSsession is null or inactive.");
+      return;
+    }
+
     Service service = serviceHolder.get();
     if (service == null) {
       if (appContext == null) {
@@ -179,9 +185,18 @@
       }
       Log.i(TAG, "Cold start - Instantiating a MediaPlaybackService.");
       Intent intent = new Intent(appContext, MediaPlaybackService.class);
-      appContext.startService(intent);
+      try {
+        if (VERSION.SDK_INT >= 26) {
+          appContext.startForegroundService(intent);
+        } else {
+          appContext.startService(intent);
+        }
+      } catch (SecurityException e) {
+        Log.e(TAG, "Failed to start MediaPlaybackService with intent.", e);
+        return;
+      }
     } else {
-      Log.i(TAG, "Warm start - Restarting the service.");
+      Log.i(TAG, "Warm start - Restarting the MediaPlaybackService.");
       ((MediaPlaybackService) service).startService();
     }
   }
@@ -191,7 +206,7 @@
   protected void stopMediaPlaybackService() {
     Service service = serviceHolder.get();
     if (service != null) {
-      Log.i(TAG, "Stopping the Media playback service.");
+      Log.i(TAG, "Stopping the MediaPlaybackService.");
       ((MediaPlaybackService) service).stopService();
     }
   }
@@ -222,6 +237,9 @@
       for (CobaltService service : cobaltServices.values()) {
         service.beforeSuspend();
       }
+      // We need to stop MediaPlaybackService before suspending so that this foreground service
+      // would not prevent releasing activity's memory consumption.
+      stopMediaPlaybackService();
     } catch (Throwable e) {
       Log.i(TAG, "Caught exception in beforeSuspend: " + e.getMessage());
     }
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java
index ba0329d..4a95898 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java
@@ -123,6 +123,14 @@
     setMediaSession();
   }
 
+  public boolean isActive() {
+    if (this.mediaSession == null) {
+      return false;
+    } else {
+      return this.mediaSession.isActive();
+    }
+  }
+
   public void setLifecycleCallback(LifecycleCallback lifecycleCallback) {
     this.lifecycleCallback = lifecycleCallback;
     if (lifecycleCallback != null && this.mediaSession != null) {
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
index 2473e28..e47ce31 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
@@ -23,6 +23,7 @@
 import android.media.AudioFormat;
 import android.media.MediaCodec;
 import android.media.MediaCodec.CryptoInfo;
+import android.media.MediaCodec.CryptoInfo.Pattern;
 import android.media.MediaCodecInfo.CodecCapabilities;
 import android.media.MediaCodecInfo.VideoCapabilities;
 import android.media.MediaCrypto;
@@ -30,6 +31,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.view.Surface;
+import androidx.annotation.Nullable;
 import dev.cobalt.util.Log;
 import dev.cobalt.util.UsedByNative;
 import java.nio.ByteBuffer;
@@ -523,22 +525,22 @@
   public static MediaCodecBridge createAudioMediaCodecBridge(
       long nativeMediaCodecBridge,
       String mime,
-      boolean isSecure,
-      boolean requireSoftwareCodec,
       int sampleRate,
       int channelCount,
-      MediaCrypto crypto) {
+      MediaCrypto crypto,
+      @Nullable byte[] configurationData) {
     MediaCodec mediaCodec = null;
     try {
-      String decoderName = MediaCodecUtil.findAudioDecoder(mime, 0, false);
+      String decoderName =
+          MediaCodecUtil.findAudioDecoder(mime, 0, false /* mustSupportTunnelMode */);
       if (decoderName.equals("")) {
-        Log.e(TAG, String.format("Failed to find decoder: %s, isSecure: %s", mime, isSecure));
+        Log.e(TAG, String.format("Failed to find decoder: %s", mime));
         return null;
       }
       Log.i(TAG, String.format("Creating \"%s\" decoder.", decoderName));
       mediaCodec = MediaCodec.createByCodecName(decoderName);
     } catch (Exception e) {
-      Log.e(TAG, String.format("Failed to create MediaCodec: %s, isSecure: %s", mime, isSecure), e);
+      Log.e(TAG, String.format("Failed to create MediaCodec: %s, ", mime), e);
       return null;
     }
     if (mediaCodec == null) {
@@ -554,7 +556,17 @@
             -1);
 
     MediaFormat mediaFormat = createAudioFormat(mime, sampleRate, channelCount);
-    setFrameHasADTSHeader(mediaFormat);
+
+    if (mime.contains("opus")) {
+      if (!setOpusConfigurationData(mediaFormat, sampleRate, configurationData)) {
+        bridge.release();
+        return null;
+      }
+    } else {
+      // TODO: Determine if we should explicitly check the mime for AAC audio before calling
+      // setFrameHasADTSHeader(), as more codecs may be supported here in the future.
+      setFrameHasADTSHeader(mediaFormat);
+    }
     if (!bridge.configureAudio(mediaFormat, crypto, 0)) {
       Log.e(TAG, "Failed to configure audio codec.");
       bridge.release();
@@ -574,8 +586,8 @@
   public static void createVideoMediaCodecBridge(
       long nativeMediaCodecBridge,
       String mime,
-      boolean isSecure,
-      boolean requireSoftwareCodec,
+      boolean mustSupportSecure,
+      boolean mustSupportSoftwareCodec,
       int width,
       int height,
       int fps,
@@ -587,32 +599,52 @@
     MediaCodec mediaCodec = null;
     outCreateMediaCodecBridgeResult.mMediaCodecBridge = null;
 
-    boolean findHDRDecoder = android.os.Build.VERSION.SDK_INT >= 24 && colorInfo != null;
-    boolean findTunneledDecoder = tunnelModeAudioSessionId != -1;
+    boolean mustSupportHdr = android.os.Build.VERSION.SDK_INT >= 24 && colorInfo != null;
+    boolean mustSupportTunneled = tunnelModeAudioSessionId != -1;
     // On first pass, try to find a decoder with HDR if the color info is non-null.
     MediaCodecUtil.FindVideoDecoderResult findVideoDecoderResult =
         MediaCodecUtil.findVideoDecoder(
-            mime, isSecure, 0, 0, 0, 0, findHDRDecoder, requireSoftwareCodec, findTunneledDecoder);
-    if (findVideoDecoderResult.name.equals("") && findHDRDecoder) {
+            mime,
+            mustSupportSecure,
+            mustSupportHdr,
+            mustSupportSoftwareCodec,
+            mustSupportTunneled,
+            0,
+            0,
+            0,
+            0);
+    if (findVideoDecoderResult.name.equals("") && mustSupportHdr) {
       // On second pass, forget HDR.
       findVideoDecoderResult =
           MediaCodecUtil.findVideoDecoder(
-              mime, isSecure, 0, 0, 0, 0, false, requireSoftwareCodec, findTunneledDecoder);
+              mime,
+              mustSupportSecure,
+              false /* mustSupportHdr */,
+              mustSupportSoftwareCodec,
+              mustSupportTunneled,
+              0,
+              0,
+              0,
+              0);
     }
     try {
       String decoderName = findVideoDecoderResult.name;
       if (decoderName.equals("") || findVideoDecoderResult.videoCapabilities == null) {
-        Log.e(TAG, String.format("Failed to find decoder: %s, isSecure: %s", mime, isSecure));
-        outCreateMediaCodecBridgeResult.mErrorMessage =
-            String.format("Failed to find decoder: %s, isSecure: %s", mime, isSecure);
+        String message =
+            String.format(
+                "Failed to find decoder: %s, mustSupportSecure: %s", mime, mustSupportSecure);
+        Log.e(TAG, message);
+        outCreateMediaCodecBridgeResult.mErrorMessage = message;
         return;
       }
       Log.i(TAG, String.format("Creating \"%s\" decoder.", decoderName));
       mediaCodec = MediaCodec.createByCodecName(decoderName);
     } catch (Exception e) {
-      Log.e(TAG, String.format("Failed to create MediaCodec: %s, isSecure: %s", mime, isSecure), e);
-      outCreateMediaCodecBridgeResult.mErrorMessage =
-          String.format("Failed to create MediaCodec: %s, isSecure: %s", mime, isSecure);
+      String message =
+          String.format(
+              "Failed to create MediaCodec: %s, mustSupportSecure: %s", mime, mustSupportSecure);
+      Log.e(TAG, message, e);
+      outCreateMediaCodecBridgeResult.mErrorMessage = message;
       return;
     }
     if (mediaCodec == null) {
@@ -648,10 +680,7 @@
     if (tunnelModeAudioSessionId != -1) {
       mediaFormat.setFeatureEnabled(CodecCapabilities.FEATURE_TunneledPlayback, true);
       mediaFormat.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, tunnelModeAudioSessionId);
-      Log.d(
-          TAG,
-          String.format(
-              "Enabled tunnel mode playback on audio session %d", tunnelModeAudioSessionId));
+      Log.d(TAG, "Enabled tunnel mode playback on audio session " + tunnelModeAudioSessionId);
     }
 
     VideoCapabilities videoCapabilities = findVideoDecoderResult.videoCapabilities;
@@ -907,31 +936,22 @@
       int[] numBytesOfEncryptedData,
       int numSubSamples,
       int cipherMode,
-      int patternEncrypt,
-      int patternSkip,
+      int blocksToEncrypt,
+      int blocksToSkip,
       long presentationTimeUs) {
     resetLastPresentationTimeIfNeeded(presentationTimeUs);
     try {
-      boolean usesCbcs =
-          Build.VERSION.SDK_INT >= 24 && cipherMode == MediaCodec.CRYPTO_MODE_AES_CBC;
-
-      if (usesCbcs) {
-        Log.e(TAG, "Encryption scheme 'cbcs' not supported on this platform.");
-        return MEDIA_CODEC_ERROR;
-      }
       CryptoInfo cryptoInfo = new CryptoInfo();
       cryptoInfo.set(
           numSubSamples, numBytesOfClearData, numBytesOfEncryptedData, keyId, iv, cipherMode);
-      if (patternEncrypt != 0 && patternSkip != 0) {
-        if (usesCbcs) {
-          // Above platform check ensured that setting the pattern is indeed supported.
-          // MediaCodecUtil.setPatternIfSupported(cryptoInfo, patternEncrypt, patternSkip);
-          Log.e(TAG, "Only AES_CTR is supported.");
-        } else {
-          Log.e(TAG, "Pattern encryption only supported for 'cbcs' scheme (CBC mode).");
-          return MEDIA_CODEC_ERROR;
-        }
+
+      if (Build.VERSION.SDK_INT >= 24 && cipherMode == MediaCodec.CRYPTO_MODE_AES_CBC) {
+        cryptoInfo.setPattern(new Pattern(blocksToEncrypt, blocksToSkip));
+      } else if (blocksToEncrypt != 0 || blocksToSkip != 0) {
+        Log.e(TAG, "Pattern encryption only supported for 'cbcs' scheme (CBC mode).");
+        return MEDIA_CODEC_ERROR;
       }
+
       mMediaCodec.queueSecureInputBuffer(index, offset, cryptoInfo, presentationTimeUs, 0);
     } catch (MediaCodec.CryptoException e) {
       int errorCode = e.getErrorCode();
@@ -1065,7 +1085,7 @@
   }
 
   // Use some heuristics to set KEY_MAX_INPUT_SIZE (the size of the input buffers).
-  // Taken from exoplayer:
+  // Taken from ExoPlayer:
   // https://github.com/google/ExoPlayer/blob/8595c65678a181296cdf673eacb93d8135479340/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java
   private void maybeSetMaxInputSize(MediaFormat format) {
     if (format.containsKey(android.media.MediaFormat.KEY_MAX_INPUT_SIZE)) {
@@ -1148,6 +1168,46 @@
   }
 
   @SuppressWarnings("unused")
+  private static boolean setOpusConfigurationData(
+      MediaFormat format, int sampleRate, @Nullable byte[] configurationData) {
+    final int MIN_OPUS_INITIALIZATION_DATA_BUFFER_SIZE = 19;
+    final long NANOSECONDS_IN_ONE_SECOND = 1000000000L;
+    // 3840 is the default seek pre-roll samples used by ExoPlayer:
+    // https://github.com/google/ExoPlayer/blob/0ba317b1337eaa789f05dd6c5241246478a3d1e5/library/common/src/main/java/com/google/android/exoplayer2/audio/OpusUtil.java#L30.
+    final int DEFAULT_SEEK_PRE_ROLL_SAMPLES = 3840;
+    if (configurationData == null
+        || configurationData.length < MIN_OPUS_INITIALIZATION_DATA_BUFFER_SIZE) {
+      Log.e(
+          TAG,
+          "Failed to configure Opus audio codec. "
+              + (configurationData == null
+                  ? "|configurationData| is null."
+                  : String.format(
+                      "Configuration data size (%d) is less than the required size (%d).",
+                      configurationData.length, MIN_OPUS_INITIALIZATION_DATA_BUFFER_SIZE)));
+      return false;
+    }
+    // Both the number of samples to skip from the beginning of the stream and the amount of time
+    // to pre-roll when seeking must be specified when configuring the Opus decoder. Logic adapted
+    // from ExoPlayer:
+    // https://github.com/google/ExoPlayer/blob/0ba317b1337eaa789f05dd6c5241246478a3d1e5/library/common/src/main/java/com/google/android/exoplayer2/audio/OpusUtil.java#L52.
+    int preSkipSamples = ((configurationData[11] & 0xFF) << 8) | (configurationData[10] & 0xFF);
+    long preSkipNanos = (preSkipSamples * NANOSECONDS_IN_ONE_SECOND) / sampleRate;
+    long seekPreRollNanos =
+        (DEFAULT_SEEK_PRE_ROLL_SAMPLES * NANOSECONDS_IN_ONE_SECOND) / sampleRate;
+    setCodecSpecificData(format, 0, configurationData);
+    setCodecSpecificData(
+        format,
+        1,
+        ByteBuffer.allocate(8).order(ByteOrder.nativeOrder()).putLong(preSkipNanos).array());
+    setCodecSpecificData(
+        format,
+        2,
+        ByteBuffer.allocate(8).order(ByteOrder.nativeOrder()).putLong(seekPreRollNanos).array());
+    return true;
+  }
+
+  @SuppressWarnings("unused")
   @UsedByNative
   private static void setFrameHasADTSHeader(MediaFormat format) {
     format.setInteger(MediaFormat.KEY_IS_ADTS, 1);
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
index 68ea364..e2632dd 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
@@ -36,18 +36,18 @@
 
 /** Utility functions for dealing with MediaCodec related things. */
 public class MediaCodecUtil {
-  // A low priority black list of codec names that should never be used.
-  private static final Set<String> codecBlackList = new HashSet<>();
-  // A high priority white list of brands/model that should always attempt to
+  // A low priority deny list of video codec names that should never be used.
+  private static final Set<String> videoCodecDenyList = new HashSet<>();
+  // A high priority allow list of brands/model that should always attempt to
   // play vp9.
-  private static final Map<String, Set<String>> vp9WhiteList = new HashMap<>();
-  // A white list of software codec names that can be used.
-  private static final Set<String> softwareCodecWhiteList = new HashSet<>();
+  private static final Map<String, Set<String>> vp9AllowList = new HashMap<>();
+  // An allow list of software codec names that can be used.
+  private static final Set<String> softwareCodecAllowList = new HashSet<>();
 
   // Whether we should report vp9 codecs as supported or not.  Will be set
-  // based on whether vp9WhiteList contains our brand/model.  If this is set
-  // to true, then codecBlackList will be ignored.
-  private static boolean isVp9WhiteListed;
+  // based on whether vp9AllowList contains our brand/model.  If this is set
+  // to true, then videoCodecDenyList will be ignored.
+  private static boolean isVp9AllowListed;
   private static final String SECURE_DECODER_SUFFIX = ".secure";
   private static final String VP9_MIME_TYPE = "video/x-vnd.on2.vp9";
   private static final String AV1_MIME_TYPE = "video/av01";
@@ -71,315 +71,315 @@
 
   static {
     if (Build.VERSION.SDK_INT >= 24 && Build.BRAND.equals("google")) {
-      codecBlackList.add("OMX.Nvidia.vp9.decode");
+      videoCodecDenyList.add("OMX.Nvidia.vp9.decode");
     }
     if (Build.VERSION.SDK_INT >= 24 && Build.BRAND.equals("LGE")) {
-      codecBlackList.add("OMX.qcom.video.decoder.vp9");
+      videoCodecDenyList.add("OMX.qcom.video.decoder.vp9");
     }
     if (Build.VERSION.RELEASE.startsWith("6.0.1")) {
-      codecBlackList.add("OMX.Exynos.vp9.dec");
-      codecBlackList.add("OMX.Intel.VideoDecoder.VP9.hwr");
-      codecBlackList.add("OMX.MTK.VIDEO.DECODER.VP9");
-      codecBlackList.add("OMX.qcom.video.decoder.vp9");
+      videoCodecDenyList.add("OMX.Exynos.vp9.dec");
+      videoCodecDenyList.add("OMX.Intel.VideoDecoder.VP9.hwr");
+      videoCodecDenyList.add("OMX.MTK.VIDEO.DECODER.VP9");
+      videoCodecDenyList.add("OMX.qcom.video.decoder.vp9");
     }
     if (Build.VERSION.RELEASE.startsWith("6.0")) {
-      codecBlackList.add("OMX.MTK.VIDEO.DECODER.VP9");
-      codecBlackList.add("OMX.Nvidia.vp9.decode");
+      videoCodecDenyList.add("OMX.MTK.VIDEO.DECODER.VP9");
+      videoCodecDenyList.add("OMX.Nvidia.vp9.decode");
     }
     if (Build.VERSION.RELEASE.startsWith("5.1.1")) {
-      codecBlackList.add("OMX.allwinner.video.decoder.vp9");
-      codecBlackList.add("OMX.Exynos.vp9.dec");
-      codecBlackList.add("OMX.Intel.VideoDecoder.VP9.hwr");
-      codecBlackList.add("OMX.MTK.VIDEO.DECODER.VP9");
-      codecBlackList.add("OMX.qcom.video.decoder.vp9");
+      videoCodecDenyList.add("OMX.allwinner.video.decoder.vp9");
+      videoCodecDenyList.add("OMX.Exynos.vp9.dec");
+      videoCodecDenyList.add("OMX.Intel.VideoDecoder.VP9.hwr");
+      videoCodecDenyList.add("OMX.MTK.VIDEO.DECODER.VP9");
+      videoCodecDenyList.add("OMX.qcom.video.decoder.vp9");
     }
     if (Build.VERSION.RELEASE.startsWith("5.1")) {
-      codecBlackList.add("OMX.Exynos.VP9.Decoder");
-      codecBlackList.add("OMX.Intel.VideoDecoder.VP9.hwr");
-      codecBlackList.add("OMX.MTK.VIDEO.DECODER.VP9");
+      videoCodecDenyList.add("OMX.Exynos.VP9.Decoder");
+      videoCodecDenyList.add("OMX.Intel.VideoDecoder.VP9.hwr");
+      videoCodecDenyList.add("OMX.MTK.VIDEO.DECODER.VP9");
     }
     if (Build.VERSION.RELEASE.startsWith("5.0")) {
-      codecBlackList.add("OMX.allwinner.video.decoder.vp9");
-      codecBlackList.add("OMX.Exynos.vp9.dec");
-      codecBlackList.add("OMX.Intel.VideoDecoder.VP9.hwr");
-      codecBlackList.add("OMX.MTK.VIDEO.DECODER.VP9");
+      videoCodecDenyList.add("OMX.allwinner.video.decoder.vp9");
+      videoCodecDenyList.add("OMX.Exynos.vp9.dec");
+      videoCodecDenyList.add("OMX.Intel.VideoDecoder.VP9.hwr");
+      videoCodecDenyList.add("OMX.MTK.VIDEO.DECODER.VP9");
     }
 
     if (Build.BRAND.equals("google")) {
-      codecBlackList.add("OMX.Intel.VideoDecoder.VP9.hybrid");
+      videoCodecDenyList.add("OMX.Intel.VideoDecoder.VP9.hybrid");
     }
 
-    // Black list non hardware media codec names if we aren't running on an emulator.
+    // Denylist non hardware media codec names if we aren't running on an emulator.
     if (!IsEmulator.isEmulator()) {
-      codecBlackList.add("OMX.ffmpeg.vp9.decoder");
-      codecBlackList.add("OMX.Intel.sw_vd.vp9");
-      codecBlackList.add("OMX.MTK.VIDEO.DECODER.SW.VP9");
+      videoCodecDenyList.add("OMX.ffmpeg.vp9.decoder");
+      videoCodecDenyList.add("OMX.Intel.sw_vd.vp9");
+      videoCodecDenyList.add("OMX.MTK.VIDEO.DECODER.SW.VP9");
     }
 
-    // Black list the Google software vp9 decoder both on hardware and on the emulator.
+    // Denylist the Google software vp9 decoder both on hardware and on the emulator.
     // On the emulator it fails with the log: "storeMetaDataInBuffers failed w/ err -1010"
-    codecBlackList.add("OMX.google.vp9.decoder");
+    videoCodecDenyList.add("OMX.google.vp9.decoder");
 
-    vp9WhiteList.put("Amazon", new HashSet<String>());
-    vp9WhiteList.put("Amlogic", new HashSet<String>());
-    vp9WhiteList.put("Arcadyan", new HashSet<String>());
-    vp9WhiteList.put("arcelik", new HashSet<String>());
-    vp9WhiteList.put("BNO", new HashSet<String>());
-    vp9WhiteList.put("BROADCOM", new HashSet<String>());
-    vp9WhiteList.put("broadcom", new HashSet<String>());
-    vp9WhiteList.put("Foxconn", new HashSet<String>());
-    vp9WhiteList.put("Freebox", new HashSet<String>());
-    vp9WhiteList.put("Funai", new HashSet<String>());
-    vp9WhiteList.put("gfiber", new HashSet<String>());
-    vp9WhiteList.put("Google", new HashSet<String>());
-    vp9WhiteList.put("google", new HashSet<String>());
-    vp9WhiteList.put("Hisense", new HashSet<String>());
-    vp9WhiteList.put("HUAWEI", new HashSet<String>());
-    vp9WhiteList.put("KaonMedia", new HashSet<String>());
-    vp9WhiteList.put("LeTV", new HashSet<String>());
-    vp9WhiteList.put("LGE", new HashSet<String>());
-    vp9WhiteList.put("MediaTek", new HashSet<String>());
-    vp9WhiteList.put("MStar", new HashSet<String>());
-    vp9WhiteList.put("MTK", new HashSet<String>());
-    vp9WhiteList.put("NVIDIA", new HashSet<String>());
-    vp9WhiteList.put("PHILIPS", new HashSet<String>());
-    vp9WhiteList.put("Philips", new HashSet<String>());
-    vp9WhiteList.put("PIXELA CORPORATION", new HashSet<String>());
-    vp9WhiteList.put("RCA", new HashSet<String>());
-    vp9WhiteList.put("Sagemcom", new HashSet<String>());
-    vp9WhiteList.put("samsung", new HashSet<String>());
-    vp9WhiteList.put("SHARP", new HashSet<String>());
-    vp9WhiteList.put("Skyworth", new HashSet<String>());
-    vp9WhiteList.put("Sony", new HashSet<String>());
-    vp9WhiteList.put("STMicroelectronics", new HashSet<String>());
-    vp9WhiteList.put("SumitomoElectricIndustries", new HashSet<String>());
-    vp9WhiteList.put("TCL", new HashSet<String>());
-    vp9WhiteList.put("Technicolor", new HashSet<String>());
-    vp9WhiteList.put("Vestel", new HashSet<String>());
-    vp9WhiteList.put("wnc", new HashSet<String>());
-    vp9WhiteList.put("Xiaomi", new HashSet<String>());
-    vp9WhiteList.put("ZTE TV", new HashSet<String>());
+    vp9AllowList.put("Amazon", new HashSet<String>());
+    vp9AllowList.put("Amlogic", new HashSet<String>());
+    vp9AllowList.put("Arcadyan", new HashSet<String>());
+    vp9AllowList.put("arcelik", new HashSet<String>());
+    vp9AllowList.put("BNO", new HashSet<String>());
+    vp9AllowList.put("BROADCOM", new HashSet<String>());
+    vp9AllowList.put("broadcom", new HashSet<String>());
+    vp9AllowList.put("Foxconn", new HashSet<String>());
+    vp9AllowList.put("Freebox", new HashSet<String>());
+    vp9AllowList.put("Funai", new HashSet<String>());
+    vp9AllowList.put("gfiber", new HashSet<String>());
+    vp9AllowList.put("Google", new HashSet<String>());
+    vp9AllowList.put("google", new HashSet<String>());
+    vp9AllowList.put("Hisense", new HashSet<String>());
+    vp9AllowList.put("HUAWEI", new HashSet<String>());
+    vp9AllowList.put("KaonMedia", new HashSet<String>());
+    vp9AllowList.put("LeTV", new HashSet<String>());
+    vp9AllowList.put("LGE", new HashSet<String>());
+    vp9AllowList.put("MediaTek", new HashSet<String>());
+    vp9AllowList.put("MStar", new HashSet<String>());
+    vp9AllowList.put("MTK", new HashSet<String>());
+    vp9AllowList.put("NVIDIA", new HashSet<String>());
+    vp9AllowList.put("PHILIPS", new HashSet<String>());
+    vp9AllowList.put("Philips", new HashSet<String>());
+    vp9AllowList.put("PIXELA CORPORATION", new HashSet<String>());
+    vp9AllowList.put("RCA", new HashSet<String>());
+    vp9AllowList.put("Sagemcom", new HashSet<String>());
+    vp9AllowList.put("samsung", new HashSet<String>());
+    vp9AllowList.put("SHARP", new HashSet<String>());
+    vp9AllowList.put("Skyworth", new HashSet<String>());
+    vp9AllowList.put("Sony", new HashSet<String>());
+    vp9AllowList.put("STMicroelectronics", new HashSet<String>());
+    vp9AllowList.put("SumitomoElectricIndustries", new HashSet<String>());
+    vp9AllowList.put("TCL", new HashSet<String>());
+    vp9AllowList.put("Technicolor", new HashSet<String>());
+    vp9AllowList.put("Vestel", new HashSet<String>());
+    vp9AllowList.put("wnc", new HashSet<String>());
+    vp9AllowList.put("Xiaomi", new HashSet<String>());
+    vp9AllowList.put("ZTE TV", new HashSet<String>());
 
-    vp9WhiteList.get("Amazon").add("AFTS");
-    vp9WhiteList.get("Amlogic").add("p212");
-    vp9WhiteList.get("Arcadyan").add("Bouygtel4K");
-    vp9WhiteList.get("Arcadyan").add("HMB2213PW22TS");
-    vp9WhiteList.get("Arcadyan").add("IPSetTopBox");
-    vp9WhiteList.get("arcelik").add("arcelik_uhd_powermax_at");
-    vp9WhiteList.get("BNO").add("QM153E");
-    vp9WhiteList.get("broadcom").add("avko");
-    vp9WhiteList.get("broadcom").add("banff");
-    vp9WhiteList.get("BROADCOM").add("BCM7XXX_TEST_SETTOP");
-    vp9WhiteList.get("broadcom").add("cypress");
-    vp9WhiteList.get("broadcom").add("dawson");
-    vp9WhiteList.get("broadcom").add("elfin");
-    vp9WhiteList.get("Foxconn").add("ba101");
-    vp9WhiteList.get("Foxconn").add("bd201");
-    vp9WhiteList.get("Freebox").add("Freebox Player Mini v2");
-    vp9WhiteList.get("Funai").add("PHILIPS 4K TV");
-    vp9WhiteList.get("gfiber").add("GFHD254");
-    vp9WhiteList.get("google").add("avko");
-    vp9WhiteList.get("google").add("marlin");
-    vp9WhiteList.get("Google").add("Pixel XL");
-    vp9WhiteList.get("Google").add("Pixel");
-    vp9WhiteList.get("google").add("sailfish");
-    vp9WhiteList.get("google").add("sprint");
-    vp9WhiteList.get("Hisense").add("HAT4KDTV");
-    vp9WhiteList.get("HUAWEI").add("X21");
-    vp9WhiteList.get("KaonMedia").add("IC1110");
-    vp9WhiteList.get("KaonMedia").add("IC1130");
-    vp9WhiteList.get("KaonMedia").add("MCM4000");
-    vp9WhiteList.get("KaonMedia").add("PRDMK100T");
-    vp9WhiteList.get("KaonMedia").add("SFCSTB2LITE");
-    vp9WhiteList.get("LeTV").add("uMax85");
-    vp9WhiteList.get("LeTV").add("X4-43Pro");
-    vp9WhiteList.get("LeTV").add("X4-55");
-    vp9WhiteList.get("LeTV").add("X4-65");
-    vp9WhiteList.get("LGE").add("S60CLI");
-    vp9WhiteList.get("LGE").add("S60UPA");
-    vp9WhiteList.get("LGE").add("S60UPI");
-    vp9WhiteList.get("LGE").add("S70CDS");
-    vp9WhiteList.get("LGE").add("S70PCI");
-    vp9WhiteList.get("LGE").add("SH960C-DS");
-    vp9WhiteList.get("LGE").add("SH960C-LN");
-    vp9WhiteList.get("LGE").add("SH960S-AT");
-    vp9WhiteList.get("MediaTek").add("Archer");
-    vp9WhiteList.get("MediaTek").add("augie");
-    vp9WhiteList.get("MediaTek").add("kane");
-    vp9WhiteList.get("MStar").add("Denali");
-    vp9WhiteList.get("MStar").add("Rainier");
-    vp9WhiteList.get("MTK").add("Generic Android on sharp_2k15_us_android");
-    vp9WhiteList.get("NVIDIA").add("SHIELD Android TV");
-    vp9WhiteList.get("NVIDIA").add("SHIELD Console");
-    vp9WhiteList.get("NVIDIA").add("SHIELD Portable");
-    vp9WhiteList.get("PHILIPS").add("QM151E");
-    vp9WhiteList.get("PHILIPS").add("QM161E");
-    vp9WhiteList.get("PHILIPS").add("QM163E");
-    vp9WhiteList.get("Philips").add("TPM171E");
-    vp9WhiteList.get("PIXELA CORPORATION").add("POE-MP4000");
-    vp9WhiteList.get("RCA").add("XLDRCAV1");
-    vp9WhiteList.get("Sagemcom").add("DNA Android TV");
-    vp9WhiteList.get("Sagemcom").add("GigaTV");
-    vp9WhiteList.get("Sagemcom").add("M387_QL");
-    vp9WhiteList.get("Sagemcom").add("Sagemcom Android STB");
-    vp9WhiteList.get("Sagemcom").add("Sagemcom ATV Demo");
-    vp9WhiteList.get("Sagemcom").add("Telecable ATV");
-    vp9WhiteList.get("samsung").add("c71kw200");
-    vp9WhiteList.get("samsung").add("GX-CJ680CL");
-    vp9WhiteList.get("samsung").add("SAMSUNG-SM-G890A");
-    vp9WhiteList.get("samsung").add("SAMSUNG-SM-G920A");
-    vp9WhiteList.get("samsung").add("SAMSUNG-SM-G920AZ");
-    vp9WhiteList.get("samsung").add("SAMSUNG-SM-G925A");
-    vp9WhiteList.get("samsung").add("SAMSUNG-SM-G928A");
-    vp9WhiteList.get("samsung").add("SM-G9200");
-    vp9WhiteList.get("samsung").add("SM-G9208");
-    vp9WhiteList.get("samsung").add("SM-G9209");
-    vp9WhiteList.get("samsung").add("SM-G920A");
-    vp9WhiteList.get("samsung").add("SM-G920D");
-    vp9WhiteList.get("samsung").add("SM-G920F");
-    vp9WhiteList.get("samsung").add("SM-G920FD");
-    vp9WhiteList.get("samsung").add("SM-G920FQ");
-    vp9WhiteList.get("samsung").add("SM-G920I");
-    vp9WhiteList.get("samsung").add("SM-G920K");
-    vp9WhiteList.get("samsung").add("SM-G920L");
-    vp9WhiteList.get("samsung").add("SM-G920P");
-    vp9WhiteList.get("samsung").add("SM-G920R4");
-    vp9WhiteList.get("samsung").add("SM-G920R6");
-    vp9WhiteList.get("samsung").add("SM-G920R7");
-    vp9WhiteList.get("samsung").add("SM-G920S");
-    vp9WhiteList.get("samsung").add("SM-G920T");
-    vp9WhiteList.get("samsung").add("SM-G920T1");
-    vp9WhiteList.get("samsung").add("SM-G920V");
-    vp9WhiteList.get("samsung").add("SM-G920W8");
-    vp9WhiteList.get("samsung").add("SM-G9250");
-    vp9WhiteList.get("samsung").add("SM-G925A");
-    vp9WhiteList.get("samsung").add("SM-G925D");
-    vp9WhiteList.get("samsung").add("SM-G925F");
-    vp9WhiteList.get("samsung").add("SM-G925FQ");
-    vp9WhiteList.get("samsung").add("SM-G925I");
-    vp9WhiteList.get("samsung").add("SM-G925J");
-    vp9WhiteList.get("samsung").add("SM-G925K");
-    vp9WhiteList.get("samsung").add("SM-G925L");
-    vp9WhiteList.get("samsung").add("SM-G925P");
-    vp9WhiteList.get("samsung").add("SM-G925R4");
-    vp9WhiteList.get("samsung").add("SM-G925R6");
-    vp9WhiteList.get("samsung").add("SM-G925R7");
-    vp9WhiteList.get("samsung").add("SM-G925S");
-    vp9WhiteList.get("samsung").add("SM-G925T");
-    vp9WhiteList.get("samsung").add("SM-G925V");
-    vp9WhiteList.get("samsung").add("SM-G925W8");
-    vp9WhiteList.get("samsung").add("SM-G925Z");
-    vp9WhiteList.get("samsung").add("SM-G9280");
-    vp9WhiteList.get("samsung").add("SM-G9287");
-    vp9WhiteList.get("samsung").add("SM-G9287C");
-    vp9WhiteList.get("samsung").add("SM-G928A");
-    vp9WhiteList.get("samsung").add("SM-G928C");
-    vp9WhiteList.get("samsung").add("SM-G928F");
-    vp9WhiteList.get("samsung").add("SM-G928G");
-    vp9WhiteList.get("samsung").add("SM-G928I");
-    vp9WhiteList.get("samsung").add("SM-G928K");
-    vp9WhiteList.get("samsung").add("SM-G928L");
-    vp9WhiteList.get("samsung").add("SM-G928N0");
-    vp9WhiteList.get("samsung").add("SM-G928P");
-    vp9WhiteList.get("samsung").add("SM-G928S");
-    vp9WhiteList.get("samsung").add("SM-G928T");
-    vp9WhiteList.get("samsung").add("SM-G928V");
-    vp9WhiteList.get("samsung").add("SM-G928W8");
-    vp9WhiteList.get("samsung").add("SM-G928X");
-    vp9WhiteList.get("samsung").add("SM-G9300");
-    vp9WhiteList.get("samsung").add("SM-G9308");
-    vp9WhiteList.get("samsung").add("SM-G930A");
-    vp9WhiteList.get("samsung").add("SM-G930AZ");
-    vp9WhiteList.get("samsung").add("SM-G930F");
-    vp9WhiteList.get("samsung").add("SM-G930FD");
-    vp9WhiteList.get("samsung").add("SM-G930K");
-    vp9WhiteList.get("samsung").add("SM-G930L");
-    vp9WhiteList.get("samsung").add("SM-G930P");
-    vp9WhiteList.get("samsung").add("SM-G930R4");
-    vp9WhiteList.get("samsung").add("SM-G930R6");
-    vp9WhiteList.get("samsung").add("SM-G930R7");
-    vp9WhiteList.get("samsung").add("SM-G930S");
-    vp9WhiteList.get("samsung").add("SM-G930T");
-    vp9WhiteList.get("samsung").add("SM-G930T1");
-    vp9WhiteList.get("samsung").add("SM-G930U");
-    vp9WhiteList.get("samsung").add("SM-G930V");
-    vp9WhiteList.get("samsung").add("SM-G930VL");
-    vp9WhiteList.get("samsung").add("SM-G930W8");
-    vp9WhiteList.get("samsung").add("SM-G9350");
-    vp9WhiteList.get("samsung").add("SM-G935A");
-    vp9WhiteList.get("samsung").add("SM-G935D");
-    vp9WhiteList.get("samsung").add("SM-G935F");
-    vp9WhiteList.get("samsung").add("SM-G935FD");
-    vp9WhiteList.get("samsung").add("SM-G935J");
-    vp9WhiteList.get("samsung").add("SM-G935K");
-    vp9WhiteList.get("samsung").add("SM-G935L");
-    vp9WhiteList.get("samsung").add("SM-G935P");
-    vp9WhiteList.get("samsung").add("SM-G935R4");
-    vp9WhiteList.get("samsung").add("SM-G935S");
-    vp9WhiteList.get("samsung").add("SM-G935T");
-    vp9WhiteList.get("samsung").add("SM-G935U");
-    vp9WhiteList.get("samsung").add("SM-G935V");
-    vp9WhiteList.get("samsung").add("SM-G935W8");
-    vp9WhiteList.get("samsung").add("SM-N9200");
-    vp9WhiteList.get("samsung").add("SM-N9208");
-    vp9WhiteList.get("samsung").add("SM-N920A");
-    vp9WhiteList.get("samsung").add("SM-N920C");
-    vp9WhiteList.get("samsung").add("SM-N920F");
-    vp9WhiteList.get("samsung").add("SM-N920G");
-    vp9WhiteList.get("samsung").add("SM-N920I");
-    vp9WhiteList.get("samsung").add("SM-N920K");
-    vp9WhiteList.get("samsung").add("SM-N920L");
-    vp9WhiteList.get("samsung").add("SM-N920R4");
-    vp9WhiteList.get("samsung").add("SM-N920R6");
-    vp9WhiteList.get("samsung").add("SM-N920R7");
-    vp9WhiteList.get("samsung").add("SM-N920S");
-    vp9WhiteList.get("samsung").add("SM-N920T");
-    vp9WhiteList.get("samsung").add("SM-N920TP");
-    vp9WhiteList.get("samsung").add("SM-N920V");
-    vp9WhiteList.get("samsung").add("SM-N920W8");
-    vp9WhiteList.get("samsung").add("SM-N920X");
-    vp9WhiteList.get("SHARP").add("AN-NP40");
-    vp9WhiteList.get("SHARP").add("AQUOS-4KTVJ17");
-    vp9WhiteList.get("SHARP").add("AQUOS-4KTVT17");
-    vp9WhiteList.get("SHARP").add("AQUOS-4KTVX17");
-    vp9WhiteList.get("SHARP").add("LC-U35T");
-    vp9WhiteList.get("SHARP").add("LC-UE630X");
-    vp9WhiteList.get("SHARP").add("LC-Ux30US");
-    vp9WhiteList.get("SHARP").add("LC-XU35T");
-    vp9WhiteList.get("SHARP").add("LC-XU930X_830X");
-    vp9WhiteList.get("Skyworth").add("globe");
-    vp9WhiteList.get("Sony").add("Amai VP9");
-    vp9WhiteList.get("Sony").add("BRAVIA 4K 2015");
-    vp9WhiteList.get("Sony").add("BRAVIA 4K GB");
-    vp9WhiteList.get("STMicroelectronics").add("sti4k");
-    vp9WhiteList.get("SumitomoElectricIndustries").add("C02AS");
-    vp9WhiteList.get("SumitomoElectricIndustries").add("ST4173");
-    vp9WhiteList.get("SumitomoElectricIndustries").add("test_STW2000");
-    vp9WhiteList.get("TCL").add("Percee TV");
-    vp9WhiteList.get("Technicolor").add("AirTV Player");
-    vp9WhiteList.get("Technicolor").add("Bouygtel4K");
-    vp9WhiteList.get("Technicolor").add("CM-7600");
-    vp9WhiteList.get("Technicolor").add("cooper");
-    vp9WhiteList.get("Technicolor").add("Foxtel Now box");
-    vp9WhiteList.get("Technicolor").add("pearl");
-    vp9WhiteList.get("Technicolor").add("Sapphire");
-    vp9WhiteList.get("Technicolor").add("Shortcut");
-    vp9WhiteList.get("Technicolor").add("skipper");
-    vp9WhiteList.get("Technicolor").add("STING");
-    vp9WhiteList.get("Technicolor").add("TIM_BOX");
-    vp9WhiteList.get("Technicolor").add("uzx8020chm");
-    vp9WhiteList.get("Vestel").add("S7252");
-    vp9WhiteList.get("Vestel").add("SmartTV");
-    vp9WhiteList.get("wnc").add("c71kw400");
-    vp9WhiteList.get("Xiaomi").add("MIBOX3");
-    vp9WhiteList.get("ZTE TV").add("AV-ATB100");
-    vp9WhiteList.get("ZTE TV").add("B860H");
+    vp9AllowList.get("Amazon").add("AFTS");
+    vp9AllowList.get("Amlogic").add("p212");
+    vp9AllowList.get("Arcadyan").add("Bouygtel4K");
+    vp9AllowList.get("Arcadyan").add("HMB2213PW22TS");
+    vp9AllowList.get("Arcadyan").add("IPSetTopBox");
+    vp9AllowList.get("arcelik").add("arcelik_uhd_powermax_at");
+    vp9AllowList.get("BNO").add("QM153E");
+    vp9AllowList.get("broadcom").add("avko");
+    vp9AllowList.get("broadcom").add("banff");
+    vp9AllowList.get("BROADCOM").add("BCM7XXX_TEST_SETTOP");
+    vp9AllowList.get("broadcom").add("cypress");
+    vp9AllowList.get("broadcom").add("dawson");
+    vp9AllowList.get("broadcom").add("elfin");
+    vp9AllowList.get("Foxconn").add("ba101");
+    vp9AllowList.get("Foxconn").add("bd201");
+    vp9AllowList.get("Freebox").add("Freebox Player Mini v2");
+    vp9AllowList.get("Funai").add("PHILIPS 4K TV");
+    vp9AllowList.get("gfiber").add("GFHD254");
+    vp9AllowList.get("google").add("avko");
+    vp9AllowList.get("google").add("marlin");
+    vp9AllowList.get("Google").add("Pixel XL");
+    vp9AllowList.get("Google").add("Pixel");
+    vp9AllowList.get("google").add("sailfish");
+    vp9AllowList.get("google").add("sprint");
+    vp9AllowList.get("Hisense").add("HAT4KDTV");
+    vp9AllowList.get("HUAWEI").add("X21");
+    vp9AllowList.get("KaonMedia").add("IC1110");
+    vp9AllowList.get("KaonMedia").add("IC1130");
+    vp9AllowList.get("KaonMedia").add("MCM4000");
+    vp9AllowList.get("KaonMedia").add("PRDMK100T");
+    vp9AllowList.get("KaonMedia").add("SFCSTB2LITE");
+    vp9AllowList.get("LeTV").add("uMax85");
+    vp9AllowList.get("LeTV").add("X4-43Pro");
+    vp9AllowList.get("LeTV").add("X4-55");
+    vp9AllowList.get("LeTV").add("X4-65");
+    vp9AllowList.get("LGE").add("S60CLI");
+    vp9AllowList.get("LGE").add("S60UPA");
+    vp9AllowList.get("LGE").add("S60UPI");
+    vp9AllowList.get("LGE").add("S70CDS");
+    vp9AllowList.get("LGE").add("S70PCI");
+    vp9AllowList.get("LGE").add("SH960C-DS");
+    vp9AllowList.get("LGE").add("SH960C-LN");
+    vp9AllowList.get("LGE").add("SH960S-AT");
+    vp9AllowList.get("MediaTek").add("Archer");
+    vp9AllowList.get("MediaTek").add("augie");
+    vp9AllowList.get("MediaTek").add("kane");
+    vp9AllowList.get("MStar").add("Denali");
+    vp9AllowList.get("MStar").add("Rainier");
+    vp9AllowList.get("MTK").add("Generic Android on sharp_2k15_us_android");
+    vp9AllowList.get("NVIDIA").add("SHIELD Android TV");
+    vp9AllowList.get("NVIDIA").add("SHIELD Console");
+    vp9AllowList.get("NVIDIA").add("SHIELD Portable");
+    vp9AllowList.get("PHILIPS").add("QM151E");
+    vp9AllowList.get("PHILIPS").add("QM161E");
+    vp9AllowList.get("PHILIPS").add("QM163E");
+    vp9AllowList.get("Philips").add("TPM171E");
+    vp9AllowList.get("PIXELA CORPORATION").add("POE-MP4000");
+    vp9AllowList.get("RCA").add("XLDRCAV1");
+    vp9AllowList.get("Sagemcom").add("DNA Android TV");
+    vp9AllowList.get("Sagemcom").add("GigaTV");
+    vp9AllowList.get("Sagemcom").add("M387_QL");
+    vp9AllowList.get("Sagemcom").add("Sagemcom Android STB");
+    vp9AllowList.get("Sagemcom").add("Sagemcom ATV Demo");
+    vp9AllowList.get("Sagemcom").add("Telecable ATV");
+    vp9AllowList.get("samsung").add("c71kw200");
+    vp9AllowList.get("samsung").add("GX-CJ680CL");
+    vp9AllowList.get("samsung").add("SAMSUNG-SM-G890A");
+    vp9AllowList.get("samsung").add("SAMSUNG-SM-G920A");
+    vp9AllowList.get("samsung").add("SAMSUNG-SM-G920AZ");
+    vp9AllowList.get("samsung").add("SAMSUNG-SM-G925A");
+    vp9AllowList.get("samsung").add("SAMSUNG-SM-G928A");
+    vp9AllowList.get("samsung").add("SM-G9200");
+    vp9AllowList.get("samsung").add("SM-G9208");
+    vp9AllowList.get("samsung").add("SM-G9209");
+    vp9AllowList.get("samsung").add("SM-G920A");
+    vp9AllowList.get("samsung").add("SM-G920D");
+    vp9AllowList.get("samsung").add("SM-G920F");
+    vp9AllowList.get("samsung").add("SM-G920FD");
+    vp9AllowList.get("samsung").add("SM-G920FQ");
+    vp9AllowList.get("samsung").add("SM-G920I");
+    vp9AllowList.get("samsung").add("SM-G920K");
+    vp9AllowList.get("samsung").add("SM-G920L");
+    vp9AllowList.get("samsung").add("SM-G920P");
+    vp9AllowList.get("samsung").add("SM-G920R4");
+    vp9AllowList.get("samsung").add("SM-G920R6");
+    vp9AllowList.get("samsung").add("SM-G920R7");
+    vp9AllowList.get("samsung").add("SM-G920S");
+    vp9AllowList.get("samsung").add("SM-G920T");
+    vp9AllowList.get("samsung").add("SM-G920T1");
+    vp9AllowList.get("samsung").add("SM-G920V");
+    vp9AllowList.get("samsung").add("SM-G920W8");
+    vp9AllowList.get("samsung").add("SM-G9250");
+    vp9AllowList.get("samsung").add("SM-G925A");
+    vp9AllowList.get("samsung").add("SM-G925D");
+    vp9AllowList.get("samsung").add("SM-G925F");
+    vp9AllowList.get("samsung").add("SM-G925FQ");
+    vp9AllowList.get("samsung").add("SM-G925I");
+    vp9AllowList.get("samsung").add("SM-G925J");
+    vp9AllowList.get("samsung").add("SM-G925K");
+    vp9AllowList.get("samsung").add("SM-G925L");
+    vp9AllowList.get("samsung").add("SM-G925P");
+    vp9AllowList.get("samsung").add("SM-G925R4");
+    vp9AllowList.get("samsung").add("SM-G925R6");
+    vp9AllowList.get("samsung").add("SM-G925R7");
+    vp9AllowList.get("samsung").add("SM-G925S");
+    vp9AllowList.get("samsung").add("SM-G925T");
+    vp9AllowList.get("samsung").add("SM-G925V");
+    vp9AllowList.get("samsung").add("SM-G925W8");
+    vp9AllowList.get("samsung").add("SM-G925Z");
+    vp9AllowList.get("samsung").add("SM-G9280");
+    vp9AllowList.get("samsung").add("SM-G9287");
+    vp9AllowList.get("samsung").add("SM-G9287C");
+    vp9AllowList.get("samsung").add("SM-G928A");
+    vp9AllowList.get("samsung").add("SM-G928C");
+    vp9AllowList.get("samsung").add("SM-G928F");
+    vp9AllowList.get("samsung").add("SM-G928G");
+    vp9AllowList.get("samsung").add("SM-G928I");
+    vp9AllowList.get("samsung").add("SM-G928K");
+    vp9AllowList.get("samsung").add("SM-G928L");
+    vp9AllowList.get("samsung").add("SM-G928N0");
+    vp9AllowList.get("samsung").add("SM-G928P");
+    vp9AllowList.get("samsung").add("SM-G928S");
+    vp9AllowList.get("samsung").add("SM-G928T");
+    vp9AllowList.get("samsung").add("SM-G928V");
+    vp9AllowList.get("samsung").add("SM-G928W8");
+    vp9AllowList.get("samsung").add("SM-G928X");
+    vp9AllowList.get("samsung").add("SM-G9300");
+    vp9AllowList.get("samsung").add("SM-G9308");
+    vp9AllowList.get("samsung").add("SM-G930A");
+    vp9AllowList.get("samsung").add("SM-G930AZ");
+    vp9AllowList.get("samsung").add("SM-G930F");
+    vp9AllowList.get("samsung").add("SM-G930FD");
+    vp9AllowList.get("samsung").add("SM-G930K");
+    vp9AllowList.get("samsung").add("SM-G930L");
+    vp9AllowList.get("samsung").add("SM-G930P");
+    vp9AllowList.get("samsung").add("SM-G930R4");
+    vp9AllowList.get("samsung").add("SM-G930R6");
+    vp9AllowList.get("samsung").add("SM-G930R7");
+    vp9AllowList.get("samsung").add("SM-G930S");
+    vp9AllowList.get("samsung").add("SM-G930T");
+    vp9AllowList.get("samsung").add("SM-G930T1");
+    vp9AllowList.get("samsung").add("SM-G930U");
+    vp9AllowList.get("samsung").add("SM-G930V");
+    vp9AllowList.get("samsung").add("SM-G930VL");
+    vp9AllowList.get("samsung").add("SM-G930W8");
+    vp9AllowList.get("samsung").add("SM-G9350");
+    vp9AllowList.get("samsung").add("SM-G935A");
+    vp9AllowList.get("samsung").add("SM-G935D");
+    vp9AllowList.get("samsung").add("SM-G935F");
+    vp9AllowList.get("samsung").add("SM-G935FD");
+    vp9AllowList.get("samsung").add("SM-G935J");
+    vp9AllowList.get("samsung").add("SM-G935K");
+    vp9AllowList.get("samsung").add("SM-G935L");
+    vp9AllowList.get("samsung").add("SM-G935P");
+    vp9AllowList.get("samsung").add("SM-G935R4");
+    vp9AllowList.get("samsung").add("SM-G935S");
+    vp9AllowList.get("samsung").add("SM-G935T");
+    vp9AllowList.get("samsung").add("SM-G935U");
+    vp9AllowList.get("samsung").add("SM-G935V");
+    vp9AllowList.get("samsung").add("SM-G935W8");
+    vp9AllowList.get("samsung").add("SM-N9200");
+    vp9AllowList.get("samsung").add("SM-N9208");
+    vp9AllowList.get("samsung").add("SM-N920A");
+    vp9AllowList.get("samsung").add("SM-N920C");
+    vp9AllowList.get("samsung").add("SM-N920F");
+    vp9AllowList.get("samsung").add("SM-N920G");
+    vp9AllowList.get("samsung").add("SM-N920I");
+    vp9AllowList.get("samsung").add("SM-N920K");
+    vp9AllowList.get("samsung").add("SM-N920L");
+    vp9AllowList.get("samsung").add("SM-N920R4");
+    vp9AllowList.get("samsung").add("SM-N920R6");
+    vp9AllowList.get("samsung").add("SM-N920R7");
+    vp9AllowList.get("samsung").add("SM-N920S");
+    vp9AllowList.get("samsung").add("SM-N920T");
+    vp9AllowList.get("samsung").add("SM-N920TP");
+    vp9AllowList.get("samsung").add("SM-N920V");
+    vp9AllowList.get("samsung").add("SM-N920W8");
+    vp9AllowList.get("samsung").add("SM-N920X");
+    vp9AllowList.get("SHARP").add("AN-NP40");
+    vp9AllowList.get("SHARP").add("AQUOS-4KTVJ17");
+    vp9AllowList.get("SHARP").add("AQUOS-4KTVT17");
+    vp9AllowList.get("SHARP").add("AQUOS-4KTVX17");
+    vp9AllowList.get("SHARP").add("LC-U35T");
+    vp9AllowList.get("SHARP").add("LC-UE630X");
+    vp9AllowList.get("SHARP").add("LC-Ux30US");
+    vp9AllowList.get("SHARP").add("LC-XU35T");
+    vp9AllowList.get("SHARP").add("LC-XU930X_830X");
+    vp9AllowList.get("Skyworth").add("globe");
+    vp9AllowList.get("Sony").add("Amai VP9");
+    vp9AllowList.get("Sony").add("BRAVIA 4K 2015");
+    vp9AllowList.get("Sony").add("BRAVIA 4K GB");
+    vp9AllowList.get("STMicroelectronics").add("sti4k");
+    vp9AllowList.get("SumitomoElectricIndustries").add("C02AS");
+    vp9AllowList.get("SumitomoElectricIndustries").add("ST4173");
+    vp9AllowList.get("SumitomoElectricIndustries").add("test_STW2000");
+    vp9AllowList.get("TCL").add("Percee TV");
+    vp9AllowList.get("Technicolor").add("AirTV Player");
+    vp9AllowList.get("Technicolor").add("Bouygtel4K");
+    vp9AllowList.get("Technicolor").add("CM-7600");
+    vp9AllowList.get("Technicolor").add("cooper");
+    vp9AllowList.get("Technicolor").add("Foxtel Now box");
+    vp9AllowList.get("Technicolor").add("pearl");
+    vp9AllowList.get("Technicolor").add("Sapphire");
+    vp9AllowList.get("Technicolor").add("Shortcut");
+    vp9AllowList.get("Technicolor").add("skipper");
+    vp9AllowList.get("Technicolor").add("STING");
+    vp9AllowList.get("Technicolor").add("TIM_BOX");
+    vp9AllowList.get("Technicolor").add("uzx8020chm");
+    vp9AllowList.get("Vestel").add("S7252");
+    vp9AllowList.get("Vestel").add("SmartTV");
+    vp9AllowList.get("wnc").add("c71kw400");
+    vp9AllowList.get("Xiaomi").add("MIBOX3");
+    vp9AllowList.get("ZTE TV").add("AV-ATB100");
+    vp9AllowList.get("ZTE TV").add("B860H");
 
-    isVp9WhiteListed =
-        vp9WhiteList.containsKey(Build.BRAND)
-            && vp9WhiteList.get(Build.BRAND).contains(Build.MODEL);
+    isVp9AllowListed =
+        vp9AllowList.containsKey(Build.BRAND)
+            && vp9AllowList.get(Build.BRAND).contains(Build.MODEL);
 
-    softwareCodecWhiteList.add("OMX.google.h264.decoder");
+    softwareCodecAllowList.add("OMX.google.h264.decoder");
   }
 
   private MediaCodecUtil() {}
@@ -394,24 +394,24 @@
   @UsedByNative
   public static boolean hasVideoDecoderFor(
       String mimeType,
-      boolean secure,
+      boolean mustSupportSecure,
+      boolean mustSupportHdr,
+      boolean mustSupportTunnelMode,
       int frameWidth,
       int frameHeight,
       int bitrate,
-      int fps,
-      boolean mustSupportHdr,
-      boolean mustSupportTunnelMode) {
+      int fps) {
     FindVideoDecoderResult findVideoDecoderResult =
         findVideoDecoder(
             mimeType,
-            secure,
+            mustSupportSecure,
+            mustSupportHdr,
+            false /* mustSupportSoftwareCodec */,
+            mustSupportTunnelMode,
             frameWidth,
             frameHeight,
             bitrate,
-            fps,
-            mustSupportHdr,
-            false,
-            mustSupportTunnelMode);
+            fps);
     return !findVideoDecoderResult.name.equals("")
         && (!mustSupportHdr || isHdrCapableVideoDecoder(mimeType, findVideoDecoderResult));
   }
@@ -446,7 +446,7 @@
     }
 
     FindVideoDecoderResult findVideoDecoderResult =
-        findVideoDecoder(mimeType, false, 0, 0, 0, 0, true, false, false);
+        findVideoDecoder(mimeType, false, true, false, false, 0, 0, 0, 0);
     return isHdrCapableVideoDecoder(mimeType, findVideoDecoderResult);
   }
 
@@ -483,38 +483,38 @@
    */
   public static FindVideoDecoderResult findVideoDecoder(
       String mimeType,
-      boolean secure,
+      boolean mustSupportSecure,
+      boolean mustSupportHdr,
+      boolean mustSupportSoftwareCodec,
+      boolean mustSupportTunnelMode,
       int frameWidth,
       int frameHeight,
       int bitrate,
-      int fps,
-      boolean hdr, // TODO: Move all boolean feature parameters after |secure|
-      boolean requireSoftwareCodec,
-      boolean requireTunneledPlayback) {
+      int fps) {
     Log.v(
         TAG,
         String.format(
             "Searching for video decoder with parameters mimeType: %s, secure: %b, frameWidth: %d,"
-                + " frameHeight: %d, bitrate: %d, fps: %d, hdr: %b, requireSoftwareCodec: %b,"
-                + " requireTunneledPlayback: %b",
+                + " frameHeight: %d, bitrate: %d, fps: %d, mustSupportHdr: %b,"
+                + " mustSupportSoftwareCodec: %b, mustSupportTunnelMode: %b",
             mimeType,
-            secure,
+            mustSupportSecure,
             frameWidth,
             frameHeight,
             bitrate,
             fps,
-            hdr,
-            requireSoftwareCodec,
-            requireTunneledPlayback));
+            mustSupportHdr,
+            mustSupportSoftwareCodec,
+            mustSupportTunnelMode));
     Log.v(
         TAG,
         String.format(
-            "brand: %s, model: %s, version: %s, API level: %d, isVp9WhiteListed: %b",
+            "brand: %s, model: %s, version: %s, API level: %d, isVp9AllowListed: %b",
             Build.BRAND,
             Build.MODEL,
             Build.VERSION.RELEASE,
             Build.VERSION.SDK_INT,
-            isVp9WhiteListed));
+            isVp9AllowListed));
 
     // Note: MediaCodecList is sorted by the framework such that the best decoders come first.
     for (MediaCodecInfo info : new MediaCodecList(MediaCodecList.ALL_CODECS).getCodecInfos()) {
@@ -526,12 +526,12 @@
           continue;
         }
         String name = info.getName();
-        if (requireSoftwareCodec && !softwareCodecWhiteList.contains(name)) {
+        if (mustSupportSoftwareCodec && !softwareCodecAllowList.contains(name)) {
           Log.v(TAG, String.format("Rejecting %s, reason: require software codec", name));
           continue;
         }
-        if (!isVp9WhiteListed && codecBlackList.contains(name)) {
-          Log.v(TAG, String.format("Rejecting %s, reason: codec is black listed", name));
+        if (!isVp9AllowListed && videoCodecDenyList.contains(name)) {
+          Log.v(TAG, String.format("Rejecting %s, reason: codec is on deny list", name));
           continue;
         }
         // MediaCodecList is supposed to feed us names of decoders that do NOT end in ".secure".  We
@@ -541,60 +541,55 @@
         // decoders that end in ".secure".  Empirically, FEATURE_SecurePlayback has still been
         // correct when this happens.
         if (name.endsWith(SECURE_DECODER_SUFFIX)) {
-          // If we want a secure decoder, then make sure the version without ".secure" isn't
-          // blacklisted.
-          String nameWithoutSecureSuffix =
-              name.substring(0, name.length() - SECURE_DECODER_SUFFIX.length());
-          if (secure && !isVp9WhiteListed && codecBlackList.contains(nameWithoutSecureSuffix)) {
-            Log.v(
-                TAG,
-                String.format("Rejecting %s, reason: offpsec blacklisted secure decoder", name));
+          // If we don't want a secure decoder, then don't bother messing around with this thing.
+          if (!mustSupportSecure) {
+            String format = "Rejecting %s, reason: want !secure decoder and ends with .secure";
+            Log.v(TAG, String.format(format, name));
             continue;
           }
-          // If we don't want a secure decoder, then don't bother messing around with this thing.
-          if (!secure) {
-            Log.v(
-                TAG,
-                String.format(
-                    "Rejecting %s, reason: want !secure decoder and ends with .secure", name));
+          // If we want a secure decoder, then make sure the version without ".secure" isn't
+          // denylisted.
+          String nameWithoutSecureSuffix =
+              name.substring(0, name.length() - SECURE_DECODER_SUFFIX.length());
+          if (!isVp9AllowListed && videoCodecDenyList.contains(nameWithoutSecureSuffix)) {
+            String format = "Rejecting %s, reason: offpsec denylisted secure decoder";
+            Log.v(TAG, String.format(format, name));
             continue;
           }
         }
 
         CodecCapabilities codecCapabilities = info.getCapabilitiesForType(supportedType);
-        if (secure
-            && !codecCapabilities.isFeatureSupported(
-                MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback)) {
-          Log.v(
-              TAG,
+        boolean requiresSecurePlayback =
+            codecCapabilities.isFeatureRequired(
+                MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback);
+        boolean supportsSecurePlayback =
+            codecCapabilities.isFeatureSupported(
+                MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback);
+        if (mustSupportSecure && !supportsSecurePlayback
+            || !mustSupportSecure && requiresSecurePlayback) {
+          String message =
               String.format(
-                  "Rejecting %s, reason: want secure decoder and !FEATURE_SecurePlayback", name));
+                  "Rejecting %s, reason: secure decoder requested: %b, "
+                      + "codec FEATURE_SecurePlayback supported: %b, required: %b",
+                  name, mustSupportSecure, supportsSecurePlayback, requiresSecurePlayback);
+          Log.v(TAG, message);
           continue;
         }
-        if (!requireTunneledPlayback
-            && codecCapabilities.isFeatureRequired(
-                MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback)) {
-          Log.v(
-              TAG,
-              String.format("Rejecting %s, reason: codec requires FEATURE_TunneledPlayback", name));
-          continue;
-        }
-        if (!secure
-            && codecCapabilities.isFeatureRequired(
-                MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback)) {
-          Log.v(
-              TAG,
-              String.format("Rejecting %s, reason: code requires FEATURE_SecurePlayback", name));
-          continue;
-        }
-        if (requireTunneledPlayback
-            && !codecCapabilities.isFeatureSupported(
-                MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback)) {
-          Log.v(
-              TAG,
+
+        boolean requiresTunneledPlayback =
+            codecCapabilities.isFeatureRequired(
+                MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback);
+        boolean supportsTunneledPlayback =
+            codecCapabilities.isFeatureSupported(
+                MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback);
+        if (mustSupportTunnelMode && !supportsTunneledPlayback
+            || !mustSupportTunnelMode && requiresTunneledPlayback) {
+          String message =
               String.format(
-                  "Rejecting %s, reason: want tunneled playback and !FEATURE_TunneledPlayback",
-                  name));
+                  "Rejecting %s, reason: tunneled playback requested: %b, "
+                      + "codec FEATURE_TunneledPlayback supported: %b, required: %b",
+                  name, mustSupportTunnelMode, supportsTunneledPlayback, requiresTunneledPlayback);
+          Log.v(TAG, message);
           continue;
         }
 
@@ -608,6 +603,8 @@
         }
 
         VideoCapabilities videoCapabilities = codecCapabilities.getVideoCapabilities();
+        Range<Integer> supportedWidths = videoCapabilities.getSupportedWidths();
+        Range<Integer> supportedHeights = videoCapabilities.getSupportedHeights();
 
         // Enable the improved support check based on more specific APIs, like isSizeSupported() or
         // areSizeAndRateSupported(), for 8k content. These APIs are theoretically more accurate,
@@ -616,102 +613,81 @@
         if (enableImprovedCheck) {
           if (frameWidth != 0 && frameHeight != 0) {
             if (!videoCapabilities.isSizeSupported(frameWidth, frameHeight)) {
-              Log.v(
-                  TAG,
-                  String.format(
-                      "Rejecting %s, reason: width %s is not compatible with height %d",
-                      name, frameWidth, frameHeight));
+              String format = "Rejecting %s, reason: width %s is not compatible with height %d";
+              Log.v(TAG, String.format(format, name, frameWidth, frameHeight));
               continue;
             }
           } else if (frameWidth != 0) {
-            if (!videoCapabilities.getSupportedWidths().contains(frameWidth)) {
-              Log.v(
-                  TAG,
-                  String.format(
-                      "Rejecting %s, reason: supported widths %s does not contain %d",
-                      name, videoCapabilities.getSupportedWidths().toString(), frameWidth));
+            if (!supportedWidths.contains(frameWidth)) {
+              String format = "Rejecting %s, reason: supported widths %s does not contain %d";
+              Log.v(TAG, String.format(format, name, supportedWidths.toString(), frameWidth));
               continue;
             }
           } else if (frameHeight != 0) {
-            if (!videoCapabilities.getSupportedHeights().contains(frameHeight)) {
-              Log.v(
-                  TAG,
-                  String.format(
-                      "Rejecting %s, reason: supported heights %s does not contain %d",
-                      name, videoCapabilities.getSupportedHeights().toString(), frameHeight));
+            if (!supportedHeights.contains(frameHeight)) {
+              String format = "Rejecting %s, reason: supported heights %s does not contain %d";
+              Log.v(TAG, String.format(format, name, supportedHeights.toString(), frameHeight));
               continue;
             }
           }
         } else {
-          if (frameWidth != 0 && !videoCapabilities.getSupportedWidths().contains(frameWidth)) {
-            Log.v(
-                TAG,
-                String.format(
-                    "Rejecting %s, reason: supported widths %s does not contain %d",
-                    name, videoCapabilities.getSupportedWidths().toString(), frameWidth));
+          if (frameWidth != 0 && !supportedWidths.contains(frameWidth)) {
+            String format = "Rejecting %s, reason: supported widths %s does not contain %d";
+            Log.v(TAG, String.format(format, name, supportedWidths.toString(), frameWidth));
             continue;
           }
-          if (frameHeight != 0 && !videoCapabilities.getSupportedHeights().contains(frameHeight)) {
-            Log.v(
-                TAG,
-                String.format(
-                    "Rejecting %s, reason: supported heights %s does not contain %d",
-                    name, videoCapabilities.getSupportedHeights().toString(), frameHeight));
+          if (frameHeight != 0 && !supportedHeights.contains(frameHeight)) {
+            String format = "Rejecting %s, reason: supported heights %s does not contain %d";
+            Log.v(TAG, String.format(format, name, supportedHeights.toString(), frameHeight));
             continue;
           }
         }
-        if (bitrate != 0 && !videoCapabilities.getBitrateRange().contains(bitrate)) {
-          Log.v(
-              TAG,
-              String.format(
-                  "Rejecting %s, reason: bitrate range %s does not contain %d",
-                  name, videoCapabilities.getBitrateRange().toString(), bitrate));
+
+        Range<Integer> bitrates = videoCapabilities.getBitrateRange();
+        if (bitrate != 0 && !bitrates.contains(bitrate)) {
+          String format = "Rejecting %s, reason: bitrate range %s does not contain %d";
+          Log.v(TAG, String.format(format, name, bitrates.toString(), bitrate));
           continue;
         }
+
+        Range<Integer> supportedFrameRates = videoCapabilities.getSupportedFrameRates();
         if (enableImprovedCheck) {
           if (fps != 0) {
             if (frameHeight != 0 && frameWidth != 0) {
               if (!videoCapabilities.areSizeAndRateSupported(frameWidth, frameHeight, fps)) {
-                Log.v(
-                    TAG,
-                    String.format(
-                        "Rejecting %s, reason: supported frame rates %s does not contain %d",
-                        name,
-                        videoCapabilities
-                            .getSupportedFrameRatesFor(frameWidth, frameHeight)
-                            .toString(),
-                        fps));
+                String format =
+                    "Rejecting %s, reason: supported frame rates %s does not contain %d";
+                Log.v(TAG, String.format(format, name, supportedFrameRates.toString(), fps));
                 continue;
               }
             } else {
               // At least one of frameHeight or frameWidth is 0
-              if (!videoCapabilities.getSupportedFrameRates().contains(fps)) {
-                Log.v(
-                    TAG,
-                    String.format(
-                        "Rejecting %s, reason: supported frame rates %s does not contain %d",
-                        name, videoCapabilities.getSupportedFrameRates().toString(), fps));
+              if (!supportedFrameRates.contains(fps)) {
+                String format =
+                    "Rejecting %s, reason: supported frame rates %s does not contain %d";
+                Log.v(TAG, String.format(format, name, supportedFrameRates.toString(), fps));
                 continue;
               }
             }
           }
         } else {
-          if (fps != 0 && !videoCapabilities.getSupportedFrameRates().contains(fps)) {
+          if (fps != 0 && !supportedFrameRates.contains(fps)) {
             Log.v(
                 TAG,
                 String.format(
                     "Rejecting %s, reason: supported frame rates %s does not contain %d",
-                    name, videoCapabilities.getSupportedFrameRates().toString(), fps));
+                    name, supportedFrameRates.toString(), fps));
             continue;
           }
         }
+
         String resultName =
-            (secure && !name.endsWith(SECURE_DECODER_SUFFIX))
+            (mustSupportSecure && !name.endsWith(SECURE_DECODER_SUFFIX))
                 ? (name + SECURE_DECODER_SUFFIX)
                 : name;
         FindVideoDecoderResult findVideoDecoderResult =
             new FindVideoDecoderResult(resultName, videoCapabilities, codecCapabilities);
-        if (hdr && !isHdrCapableVideoDecoder(mimeType, findVideoDecoderResult)) {
+        if (mustSupportHdr && !isHdrCapableVideoDecoder(mimeType, findVideoDecoderResult)) {
           Log.v(TAG, String.format("Rejecting %s, reason: codec does not support HDR", name));
           continue;
         }
@@ -727,7 +703,7 @@
    * "" otherwise.
    */
   public static String findAudioDecoder(
-      String mimeType, int bitrate, boolean requireTunneledPlayback) {
+      String mimeType, int bitrate, boolean mustSupportTunnelMode) {
     // Note: MediaCodecList is sorted by the framework such that the best decoders come first.
     for (MediaCodecInfo info : new MediaCodecList(MediaCodecList.ALL_CODECS).getCodecInfos()) {
       if (info.isEncoder()) {
@@ -745,12 +721,12 @@
         if (!bitrateRange.contains(bitrate)) {
           continue;
         }
-        if (requireTunneledPlayback
+        if (mustSupportTunnelMode
             && !codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_TunneledPlayback)) {
           continue;
         }
         // TODO: Determine if we can safely check if an audio codec requires the tunneled playback
-        //  feature. i.e., reject when |requireTunneledPlayback| == false
+        //  feature. i.e., reject when |mustSupportTunnelMode| == false
         //  and codecCapabilities.isFeatureRequired(CodecCapabilities.FEATURE_TunneledPlayback) ==
         //  true.
         return name;
@@ -874,7 +850,7 @@
                 "name: %s (%s, %s): ",
                 name,
                 supportedType,
-                codecBlackList.contains(name) ? "blacklisted" : "not blacklisted");
+                videoCodecDenyList.contains(name) ? "denylisted" : "not denylisted");
         CodecCapabilities codecCapabilities = info.getCapabilitiesForType(supportedType);
         VideoCapabilities videoCapabilities = codecCapabilities.getVideoCapabilities();
         String resultName =
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java
index b3b8300..abb56c8 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java
@@ -162,7 +162,11 @@
    * @return true if the container and the crypto scheme is supported, or false otherwise.
    */
   @UsedByNative
-  static boolean isWidevineCryptoSchemeSupported() {
+  static boolean isWidevineCryptoSchemeSupported(boolean usesCbcs) {
+    if (Build.VERSION.SDK_INT < 24 && usesCbcs) {
+      Log.e(TAG, "Encryption scheme 'cbcs' is not supported on this platform.");
+      return false;
+    }
     return MediaDrm.isCryptoSchemeSupported(WIDEVINE_UUID);
   }
 
@@ -173,9 +177,13 @@
    * @return true if the container and the crypto scheme is supported, or false otherwise.
    */
   @UsedByNative
-  static boolean isWidevineCryptoSchemeSupported(String containerMimeType) {
+  static boolean isWidevineCryptoSchemeSupported(String containerMimeType, boolean usesCbcs) {
     if (containerMimeType.isEmpty()) {
-      return isWidevineCryptoSchemeSupported();
+      return isWidevineCryptoSchemeSupported(usesCbcs);
+    }
+    if (Build.VERSION.SDK_INT < 24 && usesCbcs) {
+      Log.e(TAG, "Encryption scheme 'cbcs' is not supported on this platform.");
+      return false;
     }
     return MediaDrm.isCryptoSchemeSupported(WIDEVINE_UUID, containerMimeType);
   }
diff --git a/src/starboard/android/shared/android_main.cc b/src/starboard/android/shared/android_main.cc
index a44497e..0c04c79 100644
--- a/src/starboard/android/shared/android_main.cc
+++ b/src/starboard/android/shared/android_main.cc
@@ -115,12 +115,20 @@
 
 void OnResume(ANativeActivity* activity) {
   if (g_app_running) {
+    // Stop the MediaPlaybackService if activity state transits from background
+    // to foreground. Note that the MediaPlaybackService may already have
+    // been stopped before Cobalt's lifecycle state transits from Concealed
+    // to Frozen.
+    ApplicationAndroid::Get()->StopMediaPlaybackService();
     ApplicationAndroid::Get()->SendAndroidCommand(AndroidCommand::kResume);
   }
 }
 
 void OnPause(ANativeActivity* activity) {
   if (g_app_running) {
+    // Start the MediaPlaybackService before activity state transits from
+    // foreground to background.
+    ApplicationAndroid::Get()->StartMediaPlaybackService();
     ApplicationAndroid::Get()->SendAndroidCommand(AndroidCommand::kPause);
   }
 }
@@ -133,9 +141,9 @@
 
 void OnWindowFocusChanged(ANativeActivity* activity, int focused) {
   if (g_app_running) {
-    ApplicationAndroid::Get()->SendAndroidCommand(focused
-        ? AndroidCommand::kWindowFocusGained
-        : AndroidCommand::kWindowFocusLost);
+    ApplicationAndroid::Get()->SendAndroidCommand(
+        focused ? AndroidCommand::kWindowFocusGained
+                : AndroidCommand::kWindowFocusLost);
   }
 }
 
@@ -168,15 +176,16 @@
 }
 
 extern "C" SB_EXPORT_PLATFORM void ANativeActivity_onCreate(
-    ANativeActivity *activity, void *savedState, size_t savedStateSize) {
-
+    ANativeActivity* activity,
+    void* savedState,
+    size_t savedStateSize) {
   // Start the Starboard thread the first time an Activity is created.
   if (!SbThreadIsValid(g_starboard_thread)) {
     Semaphore semaphore;
 
-    g_starboard_thread = SbThreadCreate(
-        0, kSbThreadPriorityNormal, kSbThreadNoAffinity, false,
-        "StarboardMain", &ThreadEntryPoint, &semaphore);
+    g_starboard_thread =
+        SbThreadCreate(0, kSbThreadPriorityNormal, kSbThreadNoAffinity, false,
+                       "StarboardMain", &ThreadEntryPoint, &semaphore);
 
     // Wait for the ApplicationAndroid to be created.
     semaphore.Take();
@@ -194,8 +203,8 @@
   activity->instance = ApplicationAndroid::Get();
 }
 
-extern "C" SB_EXPORT_PLATFORM
-jboolean Java_dev_cobalt_coat_StarboardBridge_nativeIsReleaseBuild() {
+extern "C" SB_EXPORT_PLATFORM jboolean
+Java_dev_cobalt_coat_StarboardBridge_nativeIsReleaseBuild() {
 #if defined(COBALT_BUILD_TYPE_GOLD)
   return true;
 #else
@@ -203,9 +212,10 @@
 #endif
 }
 
-extern "C" SB_EXPORT_PLATFORM
-void Java_dev_cobalt_coat_StarboardBridge_nativeInitialize(
-    JniEnvExt* env, jobject starboard_bridge) {
+extern "C" SB_EXPORT_PLATFORM void
+Java_dev_cobalt_coat_StarboardBridge_nativeInitialize(
+    JniEnvExt* env,
+    jobject starboard_bridge) {
   JniEnvExt::Initialize(env, starboard_bridge);
 }
 
diff --git a/src/starboard/android/shared/application_android.cc b/src/starboard/android/shared/application_android.cc
index 45a57af..f2986e7 100644
--- a/src/starboard/android/shared/application_android.cc
+++ b/src/starboard/android/shared/application_android.cc
@@ -250,10 +250,6 @@
       // Now that we have the window, signal that the Android UI thread can
       // continue, before we start or resume the Starboard app.
       android_command_condition_.Signal();
-      // Media playback service is tied to UI window being created/destroyed
-      // (rather than to the Activity lifecycle), the service should be
-      // stopped before native window being created.
-      StopMediaPlaybackService();
     }
       if (state() == kStateUnstarted) {
         // This is the initial launch, so we have to start Cobalt now that we
@@ -291,10 +287,6 @@
         // Now that we've suspended the Starboard app, and let go of the window,
         // signal that the Android UI thread can continue.
         android_command_condition_.Signal();
-        // Media playback service is tied to UI window being created/destroyed
-        // (rather than to the Activity lifecycle). The service should be
-        // started after window being destroyed.
-        StartMediaPlaybackService();
       }
       break;
 
diff --git a/src/starboard/android/shared/application_android.h b/src/starboard/android/shared/application_android.h
index 6e5a458..a1c92a6 100644
--- a/src/starboard/android/shared/application_android.h
+++ b/src/starboard/android/shared/application_android.h
@@ -96,6 +96,10 @@
 
   void SendDateTimeConfigurationChangedEvent();
 
+  // Methods to start/stop Media playback service.
+  void StartMediaPlaybackService();
+  void StopMediaPlaybackService();
+
  protected:
   // --- Application overrides ---
   void Initialize() override;
@@ -139,10 +143,6 @@
   void ProcessAndroidCommand();
   void ProcessAndroidInput();
   void ProcessKeyboardInject();
-
-  // Methods to start/stop Media playback service.
-  void StartMediaPlaybackService();
-  void StopMediaPlaybackService();
 };
 
 }  // namespace shared
diff --git a/src/starboard/android/shared/install_target.gni b/src/starboard/android/shared/install_target.gni
index 1442c9f..dbfe29c 100644
--- a/src/starboard/android/shared/install_target.gni
+++ b/src/starboard/android/shared/install_target.gni
@@ -28,6 +28,7 @@
     ]
 
     target_output = "$root_out_dir/lib${installable_target_name}.so"
+    gradle_content_dir = "$sb_install_output_dir/$content_dir"
     gradle_files_dir = "$root_out_dir/gradle/$installable_target_name"
     if (is_gold) {
       gradle_build_type = "release"
@@ -42,30 +43,12 @@
 
     outputs = [ "$root_out_dir/${installable_target_name}.apk" ]
 
-    cobalt_project_dir =
-        string_join("/",
-                    [
-                      root_build_dir,
-                      rebase_path("//starboard/android/apk", root_build_dir),
-                    ])
-    cobalt_deploy_apk = string_join("/",
-                                    [
-                                      root_build_dir,
-                                      rebase_path(outputs[0], root_build_dir),
-                                    ])
-    cobalt_content_dir =
-        string_join("/",
-                    [
-                      root_build_dir,
-                      rebase_path("$sb_install_output_dir/$content_dir",
-                                  root_build_dir),
-                    ])
-    cobalt_gradle_dir =
-        string_join("/",
-                    [
-                      root_build_dir,
-                      rebase_path(gradle_files_dir, root_build_dir),
-                    ])
+    cobalt_project_dir = rebase_path("//starboard/android/apk")
+    cobalt_deploy_apk = rebase_path(outputs[0])
+    cobalt_content_dir = rebase_path(gradle_content_dir)
+    cobalt_gradle_dir = rebase_path(gradle_files_dir)
+    cobalt_product_dir = rebase_path(root_out_dir)
+    cobalt_library_dir = rebase_path(root_out_dir)
 
     script = "//starboard/build/run_bash.py"
     bash_script =
@@ -75,7 +58,7 @@
       "--sdk",
       android_sdk_path,
       "--cache",
-      cobalt_gradle_dir,
+      rebase_path(root_build_dir),
       "--project-dir",
       cobalt_project_dir,
       "-P",
@@ -87,9 +70,9 @@
       "-P",
       "cobaltGradleDir=$cobalt_gradle_dir",
       "-P",
-      "cobaltProductDir=$root_out_dir",
+      "cobaltProductDir=$cobalt_product_dir",
       "-P",
-      "cobaltLibraryDir=$root_out_dir",
+      "cobaltLibraryDir=$cobalt_library_dir",
       "-P",
       "cobaltTarget=$installable_target_name",
       "-P",
diff --git a/src/starboard/android/shared/media_codec_bridge.cc b/src/starboard/android/shared/media_codec_bridge.cc
index 3b5427d..57acdad 100644
--- a/src/starboard/android/shared/media_codec_bridge.cc
+++ b/src/starboard/android/shared/media_codec_bridge.cc
@@ -166,16 +166,24 @@
     return scoped_ptr<MediaCodecBridge>(NULL);
   }
   JniEnvExt* env = JniEnvExt::Get();
+  ScopedLocalJavaRef<jbyteArray> configuration_data;
+  if (audio_codec == kSbMediaAudioCodecOpus &&
+      audio_sample_info.audio_specific_config_size != 0) {
+    configuration_data.Reset(env->NewByteArrayFromRaw(
+        static_cast<const jbyte*>(audio_sample_info.audio_specific_config),
+        audio_sample_info.audio_specific_config_size));
+  }
   ScopedLocalJavaRef<jstring> j_mime(env->NewStringStandardUTFOrAbort(mime));
   scoped_ptr<MediaCodecBridge> native_media_codec_bridge(
       new MediaCodecBridge(handler));
   jobject j_media_codec_bridge = env->CallStaticObjectMethodOrAbort(
       "dev/cobalt/media/MediaCodecBridge", "createAudioMediaCodecBridge",
-      "(JLjava/lang/String;ZZIILandroid/media/MediaCrypto;)Ldev/cobalt/media/"
+      "(JLjava/lang/String;IILandroid/media/MediaCrypto;[B)Ldev/cobalt/media/"
       "MediaCodecBridge;",
       reinterpret_cast<jlong>(native_media_codec_bridge.get()), j_mime.Get(),
-      !!j_media_crypto, false, audio_sample_info.samples_per_second,
-      audio_sample_info.number_of_channels, j_media_crypto);
+      audio_sample_info.samples_per_second,
+      audio_sample_info.number_of_channels, j_media_crypto,
+      configuration_data.Get());
 
   if (!j_media_codec_bridge) {
     return scoped_ptr<MediaCodecBridge>(NULL);
@@ -338,11 +346,20 @@
   ScopedLocalJavaRef<jintArray> j_encrypted_bytes(
       env->NewIntArrayFromRaw(encrypted_bytes.get(), subsample_count));
 
+  jint cipher_mode = CRYPTO_MODE_AES_CTR;
+  jint blocks_to_encrypt = 0;
+  jint blocks_to_skip = 0;
+  if (drm_sample_info.encryption_scheme == kSbDrmEncryptionSchemeAesCbc) {
+    cipher_mode = CRYPTO_MODE_AES_CBC;
+    blocks_to_encrypt = drm_sample_info.encryption_pattern.crypt_byte_block;
+    blocks_to_skip = drm_sample_info.encryption_pattern.skip_byte_block;
+  }
+
   return env->CallIntMethodOrAbort(
       j_media_codec_bridge_, "queueSecureInputBuffer", "(II[B[B[I[IIIIIJ)I",
       index, offset, j_iv.Get(), j_key_id.Get(), j_clear_bytes.Get(),
-      j_encrypted_bytes.Get(), subsample_count, CRYPTO_MODE_AES_CTR, 0, 0,
-      presentation_time_microseconds);
+      j_encrypted_bytes.Get(), subsample_count, cipher_mode, blocks_to_encrypt,
+      blocks_to_skip, presentation_time_microseconds);
 }
 
 jobject MediaCodecBridge::GetOutputBuffer(jint index) {
diff --git a/src/starboard/android/shared/media_common.h b/src/starboard/android/shared/media_common.h
index 5a8d57c..aa17a47 100644
--- a/src/starboard/android/shared/media_common.h
+++ b/src/starboard/android/shared/media_common.h
@@ -60,6 +60,9 @@
   if (audio_codec == kSbMediaAudioCodecAac) {
     return "audio/mp4a-latm";
   }
+  if (audio_codec == kSbMediaAudioCodecOpus) {
+    return "audio/opus";
+  }
   return nullptr;
 }
 
diff --git a/src/starboard/android/shared/media_decoder.cc b/src/starboard/android/shared/media_decoder.cc
index 69293d1..d6c7f71 100644
--- a/src/starboard/android/shared/media_decoder.cc
+++ b/src/starboard/android/shared/media_decoder.cc
@@ -90,7 +90,12 @@
     SB_LOG(ERROR) << "Failed to create audio media codec bridge.";
     return;
   }
-  if (audio_sample_info.audio_specific_config_size > 0) {
+  // When |audio_codec| == kSbMediaAudioCodecOpus, we instead send the audio
+  // specific configuration when we create the MediaCodec object.
+  // TODO: Determine if we should send the audio specific configuration here
+  // only when |audio_codec| == kSbMediaAudioCodecAac.
+  if (audio_codec != kSbMediaAudioCodecOpus &&
+      audio_sample_info.audio_specific_config_size > 0) {
     // |audio_sample_info.audio_specific_config| is guaranteed to be outlived
     // the decoder as it is stored in |FilterBasedPlayerWorkerHandler|.
     pending_tasks_.push_back(Event(
diff --git a/src/starboard/android/shared/media_get_initial_buffer_capacity.cc b/src/starboard/android/shared/media_get_initial_buffer_capacity.cc
index 1634373..6002db7 100644
--- a/src/starboard/android/shared/media_get_initial_buffer_capacity.cc
+++ b/src/starboard/android/shared/media_get_initial_buffer_capacity.cc
@@ -15,5 +15,5 @@
 #include "starboard/media.h"
 
 int SbMediaGetInitialBufferCapacity() {
-  return 80 * 1024 * 1024;
+  return 4 * 1024 * 1024;
 }
diff --git a/src/starboard/android/shared/media_is_audio_supported.cc b/src/starboard/android/shared/media_is_audio_supported.cc
index a9c63f6..419de2a 100644
--- a/src/starboard/android/shared/media_is_audio_supported.cc
+++ b/src/starboard/android/shared/media_is_audio_supported.cc
@@ -34,11 +34,6 @@
     return false;
   }
 
-  // Android now uses libopus based opus decoder.
-  if (audio_codec == kSbMediaAudioCodecOpus) {
-    return true;
-  }
-
   bool is_passthrough = false;
   const char* mime =
       SupportedAudioCodecToMimeType(audio_codec, &is_passthrough);
@@ -72,6 +67,12 @@
     return false;
   }
 
+  // Android uses a libopus based opus decoder for clear content, or a platform
+  // opus decoder for encrypted content, if available.
+  if (audio_codec == kSbMediaAudioCodecOpus) {
+    return true;
+  }
+
   JniEnvExt* env = JniEnvExt::Get();
   ScopedLocalJavaRef<jstring> j_mime(env->NewStringStandardUTFOrAbort(mime));
   auto media_codec_supported =
diff --git a/src/starboard/android/shared/media_is_supported.cc b/src/starboard/android/shared/media_is_supported.cc
index bd1f79e..f6cfe75 100644
--- a/src/starboard/android/shared/media_is_supported.cc
+++ b/src/starboard/android/shared/media_is_supported.cc
@@ -12,11 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <string>
+
 #include "starboard/shared/starboard/media/media_support_internal.h"
 
 #include "starboard/android/shared/jni_env_ext.h"
 #include "starboard/android/shared/media_common.h"
 #include "starboard/media.h"
+#include "starboard/shared/starboard/media/mime_type.h"
 #include "starboard/string.h"
 
 bool SbMediaIsSupported(SbMediaVideoCodec video_codec,
@@ -24,24 +27,35 @@
                         const char* key_system) {
   using starboard::android::shared::IsWidevineL1;
   using starboard::android::shared::JniEnvExt;
+  using starboard::shared::starboard::media::MimeType;
 
-  if (strchr(key_system, ';')) {
-    // TODO: Remove this check and enable key system with attributes support.
+  // It is possible that the |key_system| comes with extra attributes, like
+  // `com.widevine.alpha; encryptionscheme="cenc"`. We prepend "key_system/"
+  // to it, so it can be parsed by MimeType.
+  MimeType mime_type(std::string("key_system/") + key_system);
+  mime_type.RegisterStringParameter("encryptionscheme", "cenc|cbcs|cbcs-1-9");
+  if (!mime_type.is_valid()) {
     return false;
   }
 
-  // We support all codecs except Opus in L1.  Use allow list to avoid
-  // accidentally introducing the support of a codec brought in in future.
+  // Use allow list to avoid accidentally introducing the support of a codec
+  // brought in the future.
   if (audio_codec != kSbMediaAudioCodecNone &&
       audio_codec != kSbMediaAudioCodecAac &&
+      audio_codec != kSbMediaAudioCodecOpus &&
       audio_codec != kSbMediaAudioCodecAc3 &&
       audio_codec != kSbMediaAudioCodecEac3) {
     return false;
   }
-  if (!IsWidevineL1(key_system)) {
+  const char* key_system_type = mime_type.subtype().c_str();
+  if (!IsWidevineL1(key_system_type)) {
     return false;
   }
+  std::string encryption_scheme =
+      mime_type.GetParamStringValue("encryptionscheme", "");
+  bool uses_cbcs =
+      encryption_scheme == "cbcs" || encryption_scheme == "cbcs-1-9";
   return JniEnvExt::Get()->CallStaticBooleanMethodOrAbort(
              "dev/cobalt/media/MediaDrmBridge",
-             "isWidevineCryptoSchemeSupported", "()Z") == JNI_TRUE;
+             "isWidevineCryptoSchemeSupported", "(Z)Z", uses_cbcs) == JNI_TRUE;
 }
diff --git a/src/starboard/android/shared/media_is_video_supported.cc b/src/starboard/android/shared/media_is_video_supported.cc
index 09895ed..d4e8a48 100644
--- a/src/starboard/android/shared/media_is_video_supported.cc
+++ b/src/starboard/android/shared/media_is_video_supported.cc
@@ -130,8 +130,8 @@
   const bool require_secure_playback = must_support_tunnel_mode;
   return env->CallStaticBooleanMethodOrAbort(
              "dev/cobalt/media/MediaCodecUtil", "hasVideoDecoderFor",
-             "(Ljava/lang/String;ZIIIIZZ)Z", j_mime.Get(),
-             require_secure_playback, frame_width, frame_height,
-             static_cast<jint>(bitrate), fps, must_support_hdr,
-             must_support_tunnel_mode) == JNI_TRUE;
+             "(Ljava/lang/String;ZZZIIII)Z", j_mime.Get(),
+             require_secure_playback, must_support_hdr,
+             must_support_tunnel_mode, frame_width, frame_height,
+             static_cast<jint>(bitrate), fps) == JNI_TRUE;
 }
diff --git a/src/starboard/android/shared/platform_configuration/BUILD.gn b/src/starboard/android/shared/platform_configuration/BUILD.gn
index 75b13be..f2f9739 100644
--- a/src/starboard/android/shared/platform_configuration/BUILD.gn
+++ b/src/starboard/android/shared/platform_configuration/BUILD.gn
@@ -31,20 +31,13 @@
     cflags += [ "-O2" ]
   }
   if (is_debug) {
-    cflags += [
-      "-frtti",
-      "-O0",
-    ]
+    cflags += [ "-O0" ]
+    configs += [ "//build/config/compiler:rtti" ]
   } else if (is_devel) {
-    cflags += [
-      "-frtti",
-      "-O2",
-    ]
+    cflags += [ "-O2" ]
+    configs += [ "//build/config/compiler:rtti" ]
   } else {
-    cflags += [
-      "-fno-rtti",
-      "-gline-tables-only",
-    ]
+    cflags += [ "-gline-tables-only" ]
   }
 
   libs = [
@@ -96,7 +89,6 @@
       # Other flags
       "-fsigned-char",
       "-fno-limit-debug-info",
-      "-fno-exceptions",
       "-fcolor-diagnostics",
       "-fno-strict-aliasing",  # See http://crbug.com/32204
 
diff --git a/src/starboard/android/shared/player_components_factory.h b/src/starboard/android/shared/player_components_factory.h
index 037e757..a4041e2 100644
--- a/src/starboard/android/shared/player_components_factory.h
+++ b/src/starboard/android/shared/player_components_factory.h
@@ -54,6 +54,10 @@
 // mode on all playbacks.
 constexpr bool kForceTunnelMode = false;
 
+// By default, the platform Opus decoder is only enabled for encrypted playback.
+// Set the following variable to true to force it for clear playback.
+constexpr bool kForcePlatformOpusDecoder = false;
+
 // On some platforms tunnel mode is only supported in the secure pipeline.  Set
 // the following variable to true to force creating a secure pipeline in tunnel
 // mode, even for clear content.
@@ -350,15 +354,19 @@
 
       auto decoder_creator = [](const SbMediaAudioSampleInfo& audio_sample_info,
                                 SbDrmSystem drm_system) {
-        if (audio_sample_info.codec == kSbMediaAudioCodecAac) {
-          scoped_ptr<AudioDecoder> audio_decoder_impl(new AudioDecoder(
-              audio_sample_info.codec, audio_sample_info, drm_system));
+        bool use_libopus_decoder =
+            audio_sample_info.codec == kSbMediaAudioCodecOpus &&
+            !SbDrmSystemIsValid(drm_system) && !kForcePlatformOpusDecoder;
+        if (use_libopus_decoder) {
+          scoped_ptr<OpusAudioDecoder> audio_decoder_impl(
+              new OpusAudioDecoder(audio_sample_info));
           if (audio_decoder_impl->is_valid()) {
             return audio_decoder_impl.PassAs<AudioDecoderBase>();
           }
-        } else if (audio_sample_info.codec == kSbMediaAudioCodecOpus) {
-          scoped_ptr<OpusAudioDecoder> audio_decoder_impl(
-              new OpusAudioDecoder(audio_sample_info));
+        } else if (audio_sample_info.codec == kSbMediaAudioCodecAac ||
+                   audio_sample_info.codec == kSbMediaAudioCodecOpus) {
+          scoped_ptr<AudioDecoder> audio_decoder_impl(new AudioDecoder(
+              audio_sample_info.codec, audio_sample_info, drm_system));
           if (audio_decoder_impl->is_valid()) {
             return audio_decoder_impl.PassAs<AudioDecoderBase>();
           }
@@ -526,8 +534,8 @@
     bool is_encrypted = !!j_media_crypto;
     if (env->CallStaticBooleanMethodOrAbort(
             "dev/cobalt/media/MediaCodecUtil", "hasVideoDecoderFor",
-            "(Ljava/lang/String;ZIIIIZZ)Z", j_mime.Get(), is_encrypted, 0, 0, 0,
-            0, false, true) == JNI_TRUE) {
+            "(Ljava/lang/String;ZZZIIII)Z", j_mime.Get(), is_encrypted, false,
+            true, 0, 0, 0, 0) == JNI_TRUE) {
       return true;
     }
 
@@ -536,8 +544,8 @@
       auto support_tunnel_mode_under_secure_pipeline =
           env->CallStaticBooleanMethodOrAbort(
               "dev/cobalt/media/MediaCodecUtil", "hasVideoDecoderFor",
-              "(Ljava/lang/String;ZIIIIZZ)Z", j_mime.Get(), kIsEncrypted, 0, 0,
-              0, 0, false, true) == JNI_TRUE;
+              "(Ljava/lang/String;ZZZIIII)Z", j_mime.Get(), kIsEncrypted, false,
+              true, 0, 0, 0, 0) == JNI_TRUE;
       if (support_tunnel_mode_under_secure_pipeline) {
         *force_secure_pipeline_under_tunnel_mode = true;
         return true;
diff --git a/src/starboard/android/shared/player_create.cc b/src/starboard/android/shared/player_create.cc
index e9e1041..a79fa48 100644
--- a/src/starboard/android/shared/player_create.cc
+++ b/src/starboard/android/shared/player_create.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <string>
+
 #include "starboard/player.h"
 
 #include "starboard/android/shared/video_decoder.h"
@@ -92,21 +94,21 @@
   if (!sample_deallocate_func) {
     SB_LOG(ERROR) << "|sample_deallocate_func| cannot be null.";
     player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
-                      "|sample_deallocate_func| cannot be null.");
+                      "|sample_deallocate_func| cannot be null");
     return kSbPlayerInvalid;
   }
 
   if (!decoder_status_func) {
     SB_LOG(ERROR) << "|decoder_status_func| cannot be null.";
     player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
-                      "|decoder_status_func| cannot be null.");
+                      "|decoder_status_func| cannot be null");
     return kSbPlayerInvalid;
   }
 
   if (!player_status_func) {
     SB_LOG(ERROR) << "|player_status_func| cannot be null.";
     player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
-                      "|player_status_func| cannot be null.");
+                      "|player_status_func| cannot be null");
     return kSbPlayerInvalid;
   }
 
diff --git a/src/starboard/build/config/BUILDCONFIG.gn b/src/starboard/build/config/BUILDCONFIG.gn
index b3356ca..52910cb 100644
--- a/src/starboard/build/config/BUILDCONFIG.gn
+++ b/src/starboard/build/config/BUILDCONFIG.gn
@@ -26,9 +26,6 @@
   cobalt_fastbuild = getenv("IS_CI") == 1
 
   is_internal_build = false
-
-  # TODO: Remove this flag when the affected targets can be built.
-  can_build_evergreen_loader_apps = true
 }
 
 is_debug = build_type == "debug"
@@ -104,39 +101,89 @@
 default_compiler_configs = [
   "//build/config/compiler:default_include_dirs",
   "//build/config/compiler:no_exceptions",
-  "//$starboard_path/platform_configuration",
+  "//build/config/compiler:thin_archive",
   "//starboard/build/config:base",
   "//starboard/build/config:host",
   "//starboard/build/config:size",
   "//starboard/build/config:target",
-  "//starboard/build/config:no_pedantic_warnings",
 ]
 
 if (is_starboard) {
   default_compiler_configs += [ "//starboard/build/config:starboard" ]
 }
 
+if (is_qa || is_gold) {
+  default_compiler_configs += [ "//build/config/compiler:no_rtti" ]
+}
+
 set_defaults("static_library") {
   configs = default_compiler_configs + static_library_configs
+  has_pedantic_warnings = false
 }
 set_defaults("source_set") {
   configs = default_compiler_configs + source_set_configs
+  has_pedantic_warnings = false
 }
 set_defaults("loadable_module") {
   configs = default_compiler_configs + loadable_module_configs
+  has_pedantic_warnings = false
 }
 set_defaults("executable") {
   configs = default_compiler_configs + executable_configs
+  has_pedantic_warnings = false
 }
 set_defaults("shared_library") {
   configs = default_compiler_configs + shared_library_configs
+  has_pedantic_warnings = false
+}
+
+# We make sure to change the pedantic_warnings configs in a particular order to
+# ensure -Wno-foo compiler flags (usually set in no_pedantic_warnings and some
+# of the platform_configuration) come after -Wfoo flags (including -Wall and
+# -Wextra which are set in pedantic_warnings). It is only certain the the "foo"
+# error will be ignored if -Wfoo precedes -Wno-foo in the compilation line.
+template("target_with_platform_configs") {
+  target(invoker.target_type, target_name) {
+    forward_variables_from(invoker, "*", [ "target_type" ])
+
+    if (has_pedantic_warnings) {
+      configs += [ "//starboard/build/config:pedantic_warnings" ]
+    }
+    configs += [ "//$starboard_path/platform_configuration" ]
+    if (!has_pedantic_warnings) {
+      configs += [ "//starboard/build/config:no_pedantic_warnings" ]
+    }
+  }
+}
+
+# Ensure the platform_configuration config is the last one included.
+template("static_library") {
+  target_with_platform_configs(target_name) {
+    target_type = "static_library"
+    forward_variables_from(invoker, "*")
+  }
+}
+
+template("source_set") {
+  target_with_platform_configs(target_name) {
+    target_type = "source_set"
+    forward_variables_from(invoker, "*")
+  }
+}
+
+template("loadable_module") {
+  target_with_platform_configs(target_name) {
+    target_type = "loadable_module"
+    forward_variables_from(invoker, "*")
+  }
 }
 
 # Set up the method of generating the install targets as defined by the
 # platform.
 import("$install_target_path")
 template("executable") {
-  executable(target_name) {
+  target_with_platform_configs(target_name) {
+    target_type = "executable"
     forward_variables_from(invoker, "*", [ "install_target" ])
   }
 
@@ -155,7 +202,8 @@
 }
 
 template("shared_library") {
-  shared_library(target_name) {
+  target_with_platform_configs(target_name) {
+    target_type = "shared_library"
     forward_variables_from(invoker, "*", [ "install_target" ])
   }
 
diff --git a/src/starboard/build/config/components.gni b/src/starboard/build/config/components.gni
index d983e19..7aef3d1 100644
--- a/src/starboard/build/config/components.gni
+++ b/src/starboard/build/config/components.gni
@@ -50,6 +50,16 @@
   target(_component_mode, target_name) {
     forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
     forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)
+
+    if (is_starboard) {
+      if (_component_mode == "source_set") {
+        configs += source_set_configs
+      } else if (_component_mode == "static_library") {
+        configs += static_library_configs
+      } else if (_component_mode == "shared_library") {
+        configs += shared_library_configs
+      }
+    }
   }
 }
 
diff --git a/src/starboard/build/doc/gn_migrate_stub_to_platform.md b/src/starboard/build/doc/gn_migrate_stub_to_platform.md
index 2f16d36..a2468f2 100644
--- a/src/starboard/build/doc/gn_migrate_stub_to_platform.md
+++ b/src/starboard/build/doc/gn_migrate_stub_to_platform.md
@@ -13,14 +13,14 @@
 Here are the steps to do your migration:
 
 1.  [Copy stub files over to your platform and build them](#copy-stub-files-over-to-your-platform-and-build-them).
-2.  [Replace stub toolchain with your platform's toolchain](#replace-stub-toolchain).
-3.  [Replace stub configuration with your platform's configuration](#replace-stub-configuration).
-4.  [Replace stubbed starboard_platform target sources with your platform's
-    sources](#replace-stubbed-starboard).
+2.  [Replace stub toolchain with toolchain for your platform](#replace-stub-toolchain-with-toolchain-for-your-platform).
+3.  [Replace stub configuration with configuration for your platform](#replace-stub-configuration-with-configuration-for-your-platform).
+4.  [Replace stubbed starboard_platform sources with sources for your platform](#replace-stubbed-starboard_platform-sources-with-sources-for-your-platform).
 
-After each step, you should be able to build the starboard_platform target.
-For example, you would build raspi2 starboard_platform target with the following
+After each step, you should be able to build the starboard_platform target. For
+example, you would build raspi2 starboard_platform target with the following
 commands:
+
 ```
 $gn gen out/raspi-2gn_devel --args='target_platform="raspi-2" build_type="devel"'
 $ninja -C out/raspi-2gn_devel/ starboard
@@ -48,33 +48,35 @@
     should be able to build your platform target with the stubbed out files
     suggested in the above section.
 
-### Replace Stub Toolchain with Your Platform's Toolchain {#replace-stub-toolchain}
+### Replace Stub Toolchain with Toolchain for Your Platform
 
 Follow instructions [here](./migrating_gyp_to_gn.md#migrating-a-toolchain) for
 migrating the toolchain. Resolve errors and build the starboard_platform target
 with the stubbed files.
 
-### Replace Stub Configuration with Your Platform's Configuration {#replace-stub-configuration}
+### Replace Stub Configuration with Configuration for Your Platform
 
 This involves migrating the compiler flags and build variables as referenced
 [here](./migrating_gyp_to_gn.md#migrating-a-platform).
 
 > **Highly recommended** \
-> It’s good to turn off the `treat_warnings_as_errors flag` until you can compile
-> the starboard_platform target with the platform files.
-> If this flag is not disabled you might run into a lot of
-> warnings turned errors and it might take time to solve all those errors.
-> Meanwhile you won't be in a buildable state which might make it uncertain as to
-> how much progress you are actually making.
-> For disabling the flag you can pass that as an argument to gn.
-> Here's an example for disabling the flag for raspi2:
+> It’s good to turn off the `treat_warnings_as_errors flag` until you can
+> compile the starboard_platform target with the platform files. If this flag is
+> not disabled you might run into a lot of warnings turned errors and it might
+> take time to solve all those errors. Meanwhile you won't be in a buildable
+> state which might make it uncertain as to how much progress you are actually
+> making. For disabling the flag you can pass that as an argument to gn. Here's
+> an example for disabling the flag for raspi2:
+>
 > ```
-> $gn gen out/raspi-2gn_devel --args='target_platform="raspi-2" build_type="devel" treat_warnings_as_errors=false'
+> $gn gen out/raspi-2gn_devel
+> --args='target_platform="raspi-2" build_type="devel"
+> treat_warnings_as_errors=false'
 > ```
 
 Resolve errors and build the starboard_platform target with the stubbed files.
 
-### Replace Stubbed starboard_platform Sources with Your Platform's Sources {#replace-stubbed-starboard}
+### Replace Stubbed starboard_platform Sources with Sources for Your Platform
 
 This involves adding files for the starboard_platform target as suggested
 [here](./migrating_gyp_to_gn.md#migrating-a-platform).
@@ -123,6 +125,7 @@
         > Default variables such as `target_cpu`, `target_os` and others can be
         > overridden by passing arguments to gn while building. Here's an
         > example of passing the default argument `target_cpu` for raspi2:
+        >
         > ```
         > $gn gen out/raspi-2gn_devel --args='target_platform="raspi-2" build_type="devel" target_cpu="arm"'
         > ```
diff --git a/src/starboard/build/doc/migrating_gyp_to_gn.md b/src/starboard/build/doc/migrating_gyp_to_gn.md
index 5834300..276bc92 100644
--- a/src/starboard/build/doc/migrating_gyp_to_gn.md
+++ b/src/starboard/build/doc/migrating_gyp_to_gn.md
@@ -79,7 +79,7 @@
 ```
 
 You also may need to remove default configs. The default configs are listed in
-[BUILDCONFIG.gn](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/build/config/BUILDCONFIG.gn).
+[BUILDCONFIG.gn](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/starboard/build/config/BUILDCONFIG.gn).
 You remove a config like so:
 
 ```
@@ -145,7 +145,7 @@
 
 Instead of implicitly searching directories for certain files like GYP did, we
 explicitly enumerate our ports and their locations.
-[platforms.gni](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/build/platforms.gni)
+[platforms.gni](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/starboard/build/platforms.gni)
 contains all of this information, and you'll need to add your platform to that
 list following the same format.
 
@@ -173,10 +173,10 @@
 
 You may define a toolchain from scratch following the [reference][gn_toolchain],
 or you can use the
-[gcc/clang templates](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/build/toolchain/gcc_toolchain.gni)
+[gcc/clang templates](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/build/toolchain/gcc_toolchain.gni)
 provided. Almost all of the reference platforms use these templates, so look to
 those as examples for how to use it correctly. Here's the linux-x64x11
-[toolchain/BUILD.gn file](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/linux/x64x11/toolchain/BUILD.gn).
+[toolchain/BUILD.gn file](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/starboard/linux/x64x11/toolchain/BUILD.gn).
 
 ## Checking Your Migration
 
@@ -202,7 +202,7 @@
 
 If this was equivalent to a GYP target, you can compare the ninja compilation
 databases by using
-[format_ninja.py](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/tools/format_ninja.py)
+[format_ninja.py](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/tools/format_ninja.py)
 and a comparison tool, i.e. [meld](https://meldmerge.org/). This will allow you
 to see any changes in commands, i.e. with flags or otherwise.
 
diff --git a/src/starboard/build/doc/migration_changes.md b/src/starboard/build/doc/migration_changes.md
index 433d1a3..650d107 100644
--- a/src/starboard/build/doc/migration_changes.md
+++ b/src/starboard/build/doc/migration_changes.md
@@ -15,6 +15,7 @@
 `has_drm_system_extension`                | `is_internal_build` (true/false)                     | (global)
 `has_cdm`                                 | `is_internal_build` (true/false)                     | (global)
 `has_private_system_properties`           | `is_internal_build` (true/false)                     | (global)
+`sb_pedantic_warnings` (0/1)              | `has_pedantic_warnings` (true/false)                 | (global, see "Compiler Options" note)
 `sb_deploy_output_dir`                    | `sb_install_output_dir`                              | `//starboard/build/config/base_configuration.gni`
 `sb_evergreen` (0/1)                      | `sb_is_evergreen` (true/false)                       | `//starboard/build/config/base_configuration.gni`
 `sb_evergreen_compatible` (0/1)           | `sb_is_evergreen_compatible` (true/false)            | `//starboard/build/config/base_configuration.gni`
@@ -38,10 +39,8 @@
 `optimize_target_for_speed` (1) | `"//starboard/build/config:speed"`                    | Optimizations
 `compiler_flags_*_speed`        | `speed_config_path`                                   | Optimizations
 `compiler_flags_*_size`         | `size_config_path`                                    | Optimizations
-`sb_pedantic_warnings`          | `pedantic_warnings_config_path`                       | Compiler Options
-`sb_pedantic_warnings`          | `no_pedantic_warnings_config_path`                    | Compiler Options
 
-Notes:
+## Notes:
 
 *   *Starboard Implementation:* If your platform defined
     `STARBOARD_IMPLENTATION` in its implementation, you would now add the above
@@ -56,13 +55,12 @@
     correct ones for `speed_config_path` and `size_config_path` in your
     platform's `platform_configuration/configuration.gni` file.
 
-*   *Compiler Options:* Cobalt compiles some targets with stricter settings
-    than others, depending on the platform. Before these targets would opt into
-    the stricter settings by settings `sb_pedantic_warnings: 1` in their
-    `variables` section. Now they will add the appropriate config like so:
-    `configs += [ "//starboard/build/config:pedantic_warnings" ]` and remove
-    the default: `configs -= [ "//starboard/build/config:no_pedantic_warnings"
-    ]`. The additional config that is used to compile these targets is
-    specified with the `pedantic_warnings_config_path` and
-    `no_pedantic_warnings_config_path` variables in your platform's
-    `platform_configuration/configuration.gni` file.
+*   *Compiler Options:* Cobalt compiles some targets with stricter,
+    platform-dependent settings than others. Before these targets would opt into
+    the stricter settings by setting `sb_pedantic_warnings: 1` in their
+    `variables` section. Now targets will be compiled with pedantic warnings if
+    the target sets `has_pedantic_warnings=true`. The additional config that is
+    used to compile these targets is specified with the
+    `pedantic_warnings_config_path` and `no_pedantic_warnings_config_path`
+    variables in your platform's `platform_configuration/configuration.gni`
+    file.
diff --git a/src/starboard/doc/c99.md b/src/starboard/doc/c99.md
index 0030329..34a2131 100644
--- a/src/starboard/doc/c99.md
+++ b/src/starboard/doc/c99.md
@@ -31,13 +31,63 @@
 * tolower
 * toupper
 ### <math.h>
+* acos
+* acosf
+* asin
+* asinf
+* atan
+* atan2
+* atan2f
+* atanf
+* cbrt
+* cbrtf
+* ceil
+* ceilf
+* cos
+* cosf
+* div
+* erf
+* erff
+* exp
+* expf
+* exp2f
 * fabs
 * floor
+* floorf
+* fmod
+* fmodf
+* frexp
 * isfinite
 * isnan
+* labs
+* llround
+* llroundf
+* log
+* log10
+* log10f
+* log2
+* log2f
+* ldexp
+* lrint
+* lrintf
+* modf
+* nearbyint
+* nearbyintf
+* nextafter
+* nextafterf
 * pow
+* powf
+* round
+* roundf
+* scalbn
+* sin
+* sinf
 * sqrt
 * sqrtf
+* tan
+* tanf
+* trunc
+* truncf
 ### <stdlib.h>
 * abs
 * atoi
@@ -66,6 +116,9 @@
 * strrchr
 * strstr
 * strspn
+### <tgmath.h>
+* ceill
+* logl
 ### <wchar.h>
 * wcscat
 * wcschr
diff --git a/src/starboard/elf_loader/BUILD.gn b/src/starboard/elf_loader/BUILD.gn
index eff2b9e..9ce393b 100644
--- a/src/starboard/elf_loader/BUILD.gn
+++ b/src/starboard/elf_loader/BUILD.gn
@@ -97,33 +97,31 @@
   ]
 }
 
-if (can_build_evergreen_loader_apps) {
-  target(final_executable_type, "elf_loader_sys_sandbox") {
-    # To properly function the system loader requires the starboard
-    # symbols to be exported from the binary.
-    # To allow symbols to be exported remove the '-fvisibility=hidden' flag
-    # from your compiler_flags.gypi. For Linux this would be:
-    #   starboard/linux/shared/compiler_flags.gypi
-    # Example run:
-    # export LD_LIBRARY_PATH=.
-    # ./elf_loader_sys_sandbox --evergreen_library=app/cobalt/lib/libcobalt.so --evergreen_content=app/cobalt/content
-    sources = [ "sandbox.cc" ]
-    configs += [ ":elf_loader_config" ]
+target(final_executable_type, "elf_loader_sys_sandbox") {
+  # To properly function the system loader requires the starboard
+  # symbols to be exported from the binary.
+  # To allow symbols to be exported remove the '-fvisibility=hidden' flag
+  # from your compiler_flags.gypi. For Linux this would be:
+  #   starboard/linux/shared/compiler_flags.gypi
+  # Example run:
+  # export LD_LIBRARY_PATH=.
+  # ./elf_loader_sys_sandbox --evergreen_library=app/cobalt/lib/libcobalt.so --evergreen_content=app/cobalt/content
+  sources = [ "sandbox.cc" ]
+  configs += [ ":elf_loader_config" ]
 
-    starboard_syms_path =
-        rebase_path("//starboard/starboard.syms", root_build_dir)
-    ldflags = [
-      "-Wl,--dynamic-list=$starboard_syms_path",
-      "-ldl",
-    ]
+  starboard_syms_path =
+      rebase_path("//starboard/starboard.syms", root_build_dir)
+  ldflags = [
+    "-Wl,--dynamic-list=$starboard_syms_path",
+    "-ldl",
+  ]
 
-    deps = [
-      ":elf_loader_sys",
-      ":evergreen_info",
-      ":sabi_string",
-      "//starboard",
-    ]
-  }
+  deps = [
+    ":elf_loader_sys",
+    ":evergreen_info",
+    ":sabi_string",
+    "//starboard",
+  ]
 }
 
 target(gtest_target_type, "elf_loader_test") {
diff --git a/src/starboard/evergreen/testing/tests/suspend_resume_test.sh b/src/starboard/evergreen/testing/tests/suspend_resume_test.sh
new file mode 100755
index 0000000..35b549f
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/suspend_resume_test.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+#
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# 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.
+
+# Unset the previous test's name and runner function.
+unset TEST_NAME
+unset TEST_FILE
+unset -f run_test
+
+TEST_NAME="SuspendResume"
+TEST_FILE="test.html"
+
+function verify_log() {
+  declare -a CrashSignals=("SIGABRT" "SIGBUS" "SIGFPE" "SIGILL" "SIGSEGV" "SIGSYS")
+
+  for VAL in ${CrashSignals[@]}; do
+    if grep -Eq "Caught signal: ${VAL}" "${1}"; then
+      log "error" "Crash detected"
+      return 1
+    fi
+  done
+
+  return 0
+}
+
+function run_test() {
+  clear_storage
+
+  LOG="${TEST_NAME}.0.log"
+  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${LOG}" LOADER --update_check_delay=1
+
+  VAR=1
+
+  for i in {1..20}
+  do
+    log "info" "SuspendResume #${VAR}"
+    sleep ${VAR}
+
+    # suspend
+    kill -s USR1 "${LOADER}"
+    if [[ $? -ne 0 ]]; then
+      log "warning" "Failed to suspend"
+      break
+    fi
+
+    sleep 1
+
+    # resume
+    kill -s CONT "${LOADER}"
+    if [[ $? -ne 0 ]]; then
+      log "warning" "Failed to resume"
+      break
+    fi
+
+    let "VAR=${VAR}+1"
+  done
+
+  return $(verify_log "${LOG_PATH}/${LOG}")
+}
diff --git a/src/starboard/linux/shared/configuration_public.h b/src/starboard/linux/shared/configuration_public.h
index 81fb6b6..98a1ccc 100644
--- a/src/starboard/linux/shared/configuration_public.h
+++ b/src/starboard/linux/shared/configuration_public.h
@@ -271,12 +271,6 @@
 #define SB_CAN_MAP_EXECUTABLE_MEMORY 1
 
 #if SB_API_VERSION < 12
-// Whether this platform has and should use an growable heap (e.g. with sbrk())
-// to map physical memory to the virtual address space.
-#define SB_HAS_VIRTUAL_REGIONS 0
-#endif  // SB_API_VERSION < 12
-
-#if SB_API_VERSION < 12
 // Specifies the alignment for IO Buffers, in bytes. Some low-level network APIs
 // may require buffers to have a specific alignment, and this is the place to
 // specify that.
@@ -289,7 +283,7 @@
 #endif  // SB_API_VERSION < 12
 
 #if SB_API_VERSION < 12
-// Determines the threshhold of allocation size that should be done with mmap
+// Determines the threshold of allocation size that should be done with mmap
 // (if available), rather than allocated within the core heap.
 #define SB_DEFAULT_MMAP_THRESHOLD ((size_t)(256 * 1024U))
 #endif  // SB_API_VERSION < 12
diff --git a/src/starboard/linux/shared/gyp_configuration.py b/src/starboard/linux/shared/gyp_configuration.py
index 4e4a96c..d33e2e0 100644
--- a/src/starboard/linux/shared/gyp_configuration.py
+++ b/src/starboard/linux/shared/gyp_configuration.py
@@ -121,10 +121,30 @@
           'VideoDecoderTests/VideoDecoderTest.*Invalid*',
       ],
   }
+  __FILTERED_TESTS['nplb'] = []
   # Conditionally disables tests that require ipv6
-  if os.getenv('IPV6_AVAILABLE', 1) == '0':
-    __FILTERED_TESTS['nplb'] = [
+  if os.getenv('IPV6_AVAILABLE', '1') == '0':
+    __FILTERED_TESTS['nplb'].extend([
         'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDayDestination/1',
         'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDaySourceForDestination/1',
         'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDaySourceNotLoopback/1',
-    ]
+    ])
+    # TODO: Re-enable once tests or infra fixed.
+    __FILTERED_TESTS['nplb'].extend([
+        'SbSocketAddressTypes/SbSocketBindTest.RainyDayBadInterface/1',
+        'SbSocketAddressTypes/PairSbSocketGetLocalAddressTest.SunnyDayConnected/1',
+        'SbSocketAddressTypes/PairSbSocketIsConnectedAndIdleTest.SunnyDay/1',
+        'SbSocketAddressTypes/PairSbSocketIsConnectedTest.SunnyDay/1',
+        'SbSocketAddressTypes/PairSbSocketReceiveFromTest.SunnyDay/1',
+        'SbSocketAddressTypes/SbSocketResolveTest.Localhost/1',
+        'SbSocketAddressTypes/SbSocketResolveTest.SunnyDayFiltered/1',
+        'SbSocketAddressTypes/PairSbSocketSendToTest.RainyDaySendToClosedSocket/1',
+        'SbSocketAddressTypes/PairSbSocketSendToTest.RainyDaySendToSocketUntilBlocking/1',
+        'SbSocketAddressTypes/PairSbSocketSendToTest.RainyDaySendToSocketConnectionReset/1',
+        'SbSocketAddressTypes/PairSbSocketWaiterWaitTest.SunnyDay/1',
+        'SbSocketAddressTypes/PairSbSocketWaiterWaitTest.SunnyDayAlreadyReady/1',
+        'SbSocketAddressTypes/PairSbSocketWaiterWaitTimedTest.SunnyDay/1',
+        'SbSocketAddressTypes/PairSbSocketWrapperTest.SunnyDay/1',
+    ])
+
+  # pylint: enable=line-too-long
diff --git a/src/starboard/linux/shared/launcher.py b/src/starboard/linux/shared/launcher.py
index 8888d6d..4290703 100644
--- a/src/starboard/linux/shared/launcher.py
+++ b/src/starboard/linux/shared/launcher.py
@@ -50,7 +50,8 @@
     if self.device_id:
       self.device_ip = self.device_id
     else:
-      if socket.has_ipv6:  #  If the device supports IPv6:
+      if os.getenv("IPV6_AVAILABLE", "1") == "1" and socket.has_ipv6:
+        #  If the device supports IPv6:
         self.device_id = "[::1]"  #  Use IPv6 loopback address (rfc2732 format).
       else:
         self.device_id = socket.gethostbyname("localhost")  # pylint: disable=W6503
diff --git a/src/starboard/linux/shared/platform_configuration/BUILD.gn b/src/starboard/linux/shared/platform_configuration/BUILD.gn
index 9920863..7d23495 100644
--- a/src/starboard/linux/shared/platform_configuration/BUILD.gn
+++ b/src/starboard/linux/shared/platform_configuration/BUILD.gn
@@ -20,20 +20,11 @@
   ldflags = []
 
   if (is_debug) {
-    cflags += [
-      "-frtti",
-      "-O0",
-    ]
+    cflags += [ "-O0" ]
   } else if (is_devel) {
-    cflags += [
-      "-frtti",
-      "-O2",
-    ]
+    cflags += [ "-O2" ]
   } else {
-    cflags += [
-      "-fno-rtti",
-      "-gline-tables-only",
-    ]
+    cflags += [ "-gline-tables-only" ]
   }
 
   if (is_clang) {
@@ -149,11 +140,13 @@
       "_GLIBCXX_DEBUG",
       "_LIBCPP_DEBUG=1",
     ]
+    configs = [ "//build/config/compiler:rtti" ]
   } else if (is_devel) {
     defines += [
       "_GLIBCXX_DEBUG",
       "_LIBCPP_DEBUG=0",
     ]
+    configs = [ "//build/config/compiler:rtti" ]
   }
 }
 
diff --git a/src/starboard/loader_app/BUILD.gn b/src/starboard/loader_app/BUILD.gn
index cc35c66..481ccc8 100644
--- a/src/starboard/loader_app/BUILD.gn
+++ b/src/starboard/loader_app/BUILD.gn
@@ -42,24 +42,22 @@
   }
 }
 
-if (can_build_evergreen_loader_apps) {
-  target(final_executable_type, "loader_app_sys") {
-    if (target_cpu == "x86" || target_cpu == "x64" || target_cpu == "arm" ||
-        target_cpu == "arm64") {
-      sources = _common_loader_app_sources
+target(final_executable_type, "loader_app_sys") {
+  if (target_cpu == "x86" || target_cpu == "x64" || target_cpu == "arm" ||
+      target_cpu == "arm64") {
+    sources = _common_loader_app_sources
 
-      starboard_syms_path =
-          rebase_path("//starboard/starboard.syms", root_build_dir)
-      ldflags = [
-        "-Wl,--dynamic-list=$starboard_syms_path",
-        "-ldl",
-      ]
-      deps = [
-        ":common_loader_app_dependencies",
-        "//cobalt/content/fonts:copy_font_data",
-        "//starboard/elf_loader:elf_loader_sys",
-      ]
-    }
+    starboard_syms_path =
+        rebase_path("//starboard/starboard.syms", root_build_dir)
+    ldflags = [
+      "-Wl,--dynamic-list=$starboard_syms_path",
+      "-ldl",
+    ]
+    deps = [
+      ":common_loader_app_dependencies",
+      "//cobalt/content/fonts:copy_font_data",
+      "//starboard/elf_loader:elf_loader_sys",
+    ]
   }
 }
 
diff --git a/src/starboard/nplb/player_create_test.cc b/src/starboard/nplb/player_create_test.cc
index 535d4b9..29d8355 100644
--- a/src/starboard/nplb/player_create_test.cc
+++ b/src/starboard/nplb/player_create_test.cc
@@ -78,9 +78,9 @@
 TEST_P(SbPlayerTest, SunnyDay) {
   SbMediaAudioSampleInfo audio_sample_info =
       CreateAudioSampleInfo(kSbMediaAudioCodecAac);
-  SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
 
-  if (!IsOutputModeSupported(output_mode_, kVideoCodec)) {
+  if (!IsOutputModeSupported(output_mode_, kSbMediaAudioCodecAac,
+                             kSbMediaVideoCodecH264)) {
     return;
   }
 
@@ -101,9 +101,9 @@
 TEST_P(SbPlayerTest, NullCallbacks) {
   SbMediaAudioSampleInfo audio_sample_info =
       CreateAudioSampleInfo(kSbMediaAudioCodecAac);
-  SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
 
-  if (!IsOutputModeSupported(output_mode_, kVideoCodec)) {
+  if (!IsOutputModeSupported(output_mode_, kSbMediaAudioCodecAac,
+                             kSbMediaVideoCodecH264)) {
     return;
   }
 
@@ -160,14 +160,13 @@
 }
 
 TEST_P(SbPlayerTest, Audioless) {
-  SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
-
-  if (!IsOutputModeSupported(output_mode_, kVideoCodec)) {
+  if (!IsOutputModeSupported(output_mode_, kSbMediaAudioCodecNone,
+                             kSbMediaVideoCodecH264)) {
     return;
   }
 
   SbPlayer player = CallSbPlayerCreate(
-      fake_graphics_context_provider_.window(), kVideoCodec,
+      fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
       kSbMediaAudioCodecNone, kSbDrmSystemInvalid, NULL /* audio_sample_info */,
       "" /* max_video_capabilities */, DummyDeallocateSampleFunc,
       DummyDecoderStatusFunc, DummyStatusFunc, DummyErrorFunc,
@@ -183,16 +182,15 @@
 TEST_P(SbPlayerTest, AudioOnly) {
   SbMediaAudioSampleInfo audio_sample_info =
       CreateAudioSampleInfo(kSbMediaAudioCodecAac);
-  SbMediaAudioCodec kAudioCodec = kSbMediaAudioCodecAac;
-  SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
 
-  if (!IsOutputModeSupported(output_mode_, kVideoCodec)) {
+  if (!IsOutputModeSupported(output_mode_, kSbMediaAudioCodecAac,
+                             kSbMediaVideoCodecH264)) {
     return;
   }
 
   SbPlayer player = CallSbPlayerCreate(
       fake_graphics_context_provider_.window(), kSbMediaVideoCodecNone,
-      kAudioCodec, kSbDrmSystemInvalid, &audio_sample_info,
+      kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_sample_info,
       "" /* max_video_capabilities */, DummyDeallocateSampleFunc,
       DummyDecoderStatusFunc, DummyStatusFunc, DummyErrorFunc,
       NULL /* context */, output_mode_,
@@ -213,13 +211,10 @@
       kSbPlayerOutputModeDecodeToTexture, kSbPlayerOutputModePunchOut};
 
   constexpr SbMediaAudioCodec kAudioCodecs[] = {
-    kSbMediaAudioCodecNone,
+      kSbMediaAudioCodecNone,
 
-    kSbMediaAudioCodecAac,
-    kSbMediaAudioCodecAc3,
-    kSbMediaAudioCodecEac3,
-    kSbMediaAudioCodecOpus,
-    kSbMediaAudioCodecVorbis,
+      kSbMediaAudioCodecAac,  kSbMediaAudioCodecAc3,    kSbMediaAudioCodecEac3,
+      kSbMediaAudioCodecOpus, kSbMediaAudioCodecVorbis,
   };
 
   // TODO: turn this into a macro.
@@ -238,16 +233,11 @@
   }
 
   constexpr SbMediaVideoCodec kVideoCodecs[] = {
-    kSbMediaVideoCodecNone,
+      kSbMediaVideoCodecNone,
 
-    kSbMediaVideoCodecH264,
-    kSbMediaVideoCodecH265,
-    kSbMediaVideoCodecMpeg2,
-    kSbMediaVideoCodecTheora,
-    kSbMediaVideoCodecVc1,
-    kSbMediaVideoCodecAv1,
-    kSbMediaVideoCodecVp8,
-    kSbMediaVideoCodecVp9,
+      kSbMediaVideoCodecH264,   kSbMediaVideoCodecH265, kSbMediaVideoCodecMpeg2,
+      kSbMediaVideoCodecTheora, kSbMediaVideoCodecVc1,  kSbMediaVideoCodecAv1,
+      kSbMediaVideoCodecVp8,    kSbMediaVideoCodecVp9,
   };
 
   // TODO: turn this into a macro.
diff --git a/src/starboard/nplb/player_test_util.cc b/src/starboard/nplb/player_test_util.cc
index e4d6afd..ddcacf1 100644
--- a/src/starboard/nplb/player_test_util.cc
+++ b/src/starboard/nplb/player_test_util.cc
@@ -74,7 +74,8 @@
     if (SbMediaCanPlayMimeAndKeySystem(dmp_reader.audio_mime_type().c_str(),
                                        "")) {
       for (auto output_mode : kOutputModes) {
-        if (IsOutputModeSupported(output_mode, kSbMediaVideoCodecNone)) {
+        if (IsOutputModeSupported(output_mode, dmp_reader.audio_codec(),
+                                  kSbMediaVideoCodecNone)) {
           test_configs.push_back(
               std::make_tuple(audio_filename, kEmptyName, output_mode));
         }
@@ -90,7 +91,8 @@
       continue;
     }
     for (auto output_mode : kOutputModes) {
-      if (IsOutputModeSupported(output_mode, dmp_reader.video_codec())) {
+      if (IsOutputModeSupported(output_mode, kSbMediaAudioCodecNone,
+                                dmp_reader.video_codec())) {
         test_configs.push_back(
             std::make_tuple(kEmptyName, video_filename, output_mode));
       }
@@ -173,24 +175,26 @@
 
 #else  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 
-  return SbPlayerCreate(
-      window, video_codec, audio_codec, kSbDrmSystemInvalid, audio_sample_info,
-      max_video_capabilities,
-      sample_deallocate_func, decoder_status_func, player_status_func,
-      player_error_func, context, output_mode, context_provider);
+  return SbPlayerCreate(window, video_codec, audio_codec, kSbDrmSystemInvalid,
+                        audio_sample_info, max_video_capabilities,
+                        sample_deallocate_func, decoder_status_func,
+                        player_status_func, player_error_func, context,
+                        output_mode, context_provider);
 
 #endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 }
 
 bool IsOutputModeSupported(SbPlayerOutputMode output_mode,
-                           SbMediaVideoCodec codec) {
+                           SbMediaAudioCodec audio_codec,
+                           SbMediaVideoCodec video_codec) {
 #if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
   SbPlayerCreationParam creation_param =
-      CreatePlayerCreationParam(kSbMediaAudioCodecNone, codec);
+      CreatePlayerCreationParam(audio_codec, video_codec);
   creation_param.output_mode = output_mode;
   return SbPlayerGetPreferredOutputMode(&creation_param) == output_mode;
 #else   // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
-  return SbPlayerOutputModeSupported(output_mode, codec, kSbDrmSystemInvalid);
+  return SbPlayerOutputModeSupported(output_mode, video_codec,
+                                     kSbDrmSystemInvalid);
 #endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 }
 
diff --git a/src/starboard/nplb/player_test_util.h b/src/starboard/nplb/player_test_util.h
index dfb8f11..53503ee 100644
--- a/src/starboard/nplb/player_test_util.h
+++ b/src/starboard/nplb/player_test_util.h
@@ -70,7 +70,8 @@
     SbDecodeTargetGraphicsContextProvider* context_provider);
 
 bool IsOutputModeSupported(SbPlayerOutputMode output_mode,
-                           SbMediaVideoCodec codec);
+                           SbMediaAudioCodec audio_codec,
+                           SbMediaVideoCodec video_codec);
 
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/player_write_sample_test.cc b/src/starboard/nplb/player_write_sample_test.cc
index 13977ee..0a5d6fd 100644
--- a/src/starboard/nplb/player_write_sample_test.cc
+++ b/src/starboard/nplb/player_write_sample_test.cc
@@ -220,7 +220,9 @@
 }
 
 void SbPlayerWriteSampleTest::TearDown() {
-  SB_DCHECK(SbPlayerIsValid(player_));
+  if (!SbPlayerIsValid(player_)) {
+    return;
+  }
 
   ASSERT_FALSE(destroy_player_called_);
   destroy_player_called_ = true;
diff --git a/src/starboard/raspi/2/skia/toolchain/BUILD.gn b/src/starboard/raspi/2/skia/toolchain/BUILD.gn
index 653ad00..53ae193 100644
--- a/src/starboard/raspi/2/skia/toolchain/BUILD.gn
+++ b/src/starboard/raspi/2/skia/toolchain/BUILD.gn
@@ -25,7 +25,6 @@
   ld = cxx
 
   ar = "$gcc_toolchain_ar"
-  strip = "$gcc_toolchain_strip"
 
   toolchain_args = {
     is_clang = false
diff --git a/src/starboard/raspi/2/toolchain/BUILD.gn b/src/starboard/raspi/2/toolchain/BUILD.gn
index 7e872a6..ffd79f7 100644
--- a/src/starboard/raspi/2/toolchain/BUILD.gn
+++ b/src/starboard/raspi/2/toolchain/BUILD.gn
@@ -26,7 +26,6 @@
 
   # We use whatever 'ar' resolves to in gyp.
   ar = "$gcc_toolchain_ar"
-  strip = "$gcc_toolchain_strip"
 
   toolchain_args = {
     is_clang = false
diff --git a/src/starboard/raspi/shared/BUILD.gn b/src/starboard/raspi/shared/BUILD.gn
index 2cc3d1c..9813050 100644
--- a/src/starboard/raspi/shared/BUILD.gn
+++ b/src/starboard/raspi/shared/BUILD.gn
@@ -20,6 +20,8 @@
 static_library("starboard_platform_sources") {
   check_includes = false
 
+  has_pedantic_warnings = true
+
   sources = [
     "//starboard/linux/shared/atomic_public.h",
     "//starboard/linux/shared/configuration_constants.cc",
@@ -386,11 +388,7 @@
     ]
   }
 
-  configs += [
-    "//starboard/build/config:pedantic_warnings",
-    "//starboard/build/config:starboard_implementation",
-  ]
-  configs -= [ "//starboard/build/config:no_pedantic_warnings" ]
+  configs += [ "//starboard/build/config:starboard_implementation" ]
 
   public_deps = [
     ":starboard_base_symbolize",
diff --git a/src/starboard/raspi/shared/platform_configuration/BUILD.gn b/src/starboard/raspi/shared/platform_configuration/BUILD.gn
index fe181d1..988df3e 100644
--- a/src/starboard/raspi/shared/platform_configuration/BUILD.gn
+++ b/src/starboard/raspi/shared/platform_configuration/BUILD.gn
@@ -36,13 +36,10 @@
 
   if (is_debug) {
     cflags += [ "-O0" ]
-    cflags_cc += [ "-frtti" ]
   } else if (is_devel) {
     cflags += [ "-O2" ]
-    cflags_cc += [ "-frtti" ]
   } else {
     cflags += [ "-Wno-unused-but-set-variable" ]
-    cflags_cc += [ "-fno-rtti" ]
   }
 
   ldflags += [
@@ -63,9 +60,6 @@
   ]
 
   cflags += [
-    # Generated by Audio Renderer and Audio Sink implementations.
-    "-Wno-reorder",
-
     # Generated by code in the raspi/shared/open_max.
     "-Wno-sign-compare",
 
@@ -124,6 +118,9 @@
   cflags_cc += [
     "-std=gnu++14",
     "-Wno-literal-suffix",
+
+    # Generated by Audio Renderer and Audio Sink implementations.
+    "-Wno-reorder",
   ]
 }
 
@@ -149,6 +146,10 @@
   ]
 
   configs = [ "//starboard/raspi/shared/platform_configuration:compiler_flags" ]
+
+  if (is_debug || is_devel) {
+    configs += [ "//build/config/compiler:rtti" ]
+  }
 }
 
 config("speed") {
diff --git a/src/starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni b/src/starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni
index d773598..1b0b0e4 100644
--- a/src/starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni
+++ b/src/starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni
@@ -23,4 +23,3 @@
 gcc_toolchain_ar = "ar"
 gcc_toolchain_cc = "$_raspi_toolchain_path/arm-linux-gnueabihf-gcc"
 gcc_toolchain_cxx = "$_raspi_toolchain_path/arm-linux-gnueabihf-g++"
-gcc_toolchain_strip = "$_raspi_toolchain_path/arm-linux-gnueabihf-strip"
diff --git a/src/starboard/shared/dlmalloc/page_internal.h b/src/starboard/shared/dlmalloc/page_internal.h
index e72fd75..e26d509 100644
--- a/src/starboard/shared/dlmalloc/page_internal.h
+++ b/src/starboard/shared/dlmalloc/page_internal.h
@@ -27,83 +27,6 @@
 extern "C" {
 #endif
 
-// A virtual memory address.
-typedef void* SbPageVirtualMemory;
-
-// Internal Virtual memory API
-//
-// This was designed to provide common wrappers around OS functions relied upon
-// by dlmalloc. However the APIs can also be used for other custom allocators,
-// but, due to platform restrictions, this is not completely generic.
-//
-// When dlmalloc requires memory from the system, it uses two different
-// approaches to get it. It either extends a growing heap, or it uses mmap() to
-// request a bunch of pages from the system.
-//
-// Its default behavior is to place small allocations into a contiguous heap,
-// and allocate large (256K+) blocks with mmap. Separating large blocks from the
-// main heap has advantages for reducing fragmentation.
-//
-// In dlmalloc, extending the heap is called "MORECORE" and, by default on POSIX
-// systems, uses sbrk().
-//
-// Since almost none of our platforms support sbrk(), we implement MORECORE by
-// reserving a large virtual region and giving that to dlmalloc. This region
-// starts off unmapped, i.e. there are no physical pages backing it, so reading
-// or writing from that region is invalid.  As dlmalloc requests memory, we
-// allocate physical pages from the OS and map them to the top of the heap,
-// thereby growing the usable heap area. When the heap shrinks, we can unmap
-// those pages and free them back to the OS.
-//
-// mmap(), by contrast, allocates N pages from the OS, and the OS then maps them
-// into some arbitrary virtual address space. There is no guarantee that two
-// consecutive mmap() calls will return a contiguous block.
-//
-// SbMap() is our implementation of mmap(). On platforms such as Linux that
-// actually have mmap(), we call that directly. Otherwise we use
-// platform-specific system allocators.
-//
-// Platforms that support SbMap() must be at least starboard version 12 or
-// enable SB_HAS_MMAP in their configuration_public.h file. dlmalloc is very
-// flexible and if a platform can't implement virtual regions, it will use
-// Map() for all allocations, merging adjacent allocations when it can.
-//
-// If a platform can't use Map(), it will just use MORECORE for everything.
-// Currently we believe a mixture of both provides best behavior, but more
-// testing would be useful.
-//
-// See also dlmalloc_config.h which controls some dlmalloc behavior.
-
-#if SB_API_VERSION < 12 && SB_HAS(VIRTUAL_REGIONS)
-// Reserves a virtual address space |size_bytes| big, without mapping any
-// physical pages to that range, returning a pointer to the beginning of the
-// reserved virtual address range. To get memory that is actually usable and
-// backed by physical memory, a reserved virtual address needs to passed into
-// AllocateAndMap().
-// [Presumably size_bytes should be a multiple of a physical page size? -DG]
-SbPageVirtualMemory SbPageReserveVirtualRegion(size_t size_bytes);
-
-// Releases a virtual address space reserved with ReserveVirtualRegion().
-// [What happens if that address space is wholly or partially mapped? -DG]
-void SbPageReleaseVirtualRegion(SbPageVirtualMemory range_start);
-
-// Allocate |size_bytes| of physical memory and map it to a virtual address
-// range starting at |virtual_address|. |virtual_address| should be a pointer
-// into the range returned by ReserveVirtualRegion().
-int SbPageAllocatePhysicalAndMap(SbPageVirtualMemory virtual_address,
-                                 size_t size_bytes);
-
-// Frees |size_bytes| of physical memory that had been mapped to
-// |virtual_address| and return them to the system. After this,
-// [virtual_address, virtual_address + size_bytes) will not be read/writable.
-int SbPageUnmapAndFreePhysical(SbPageVirtualMemory virtual_address,
-                               size_t size_bytes);
-
-// How big of a virtual region dlmalloc should allocate.
-size_t SbPageGetVirtualRegionSize();
-#endif  // SB_API_VERSION < 12 &&
-        // SB_HAS(VIRTUAL_REGIONS)
-
 #if SB_API_VERSION >= 12 || SB_HAS(MMAP)
 // Allocates |size_bytes| worth of physical memory pages and maps them into an
 // available virtual region. On some platforms, |name| appears in the debugger
@@ -157,26 +80,6 @@
 // always be a multiple of kSbMemoryPageSize.
 size_t SbPageGetMappedBytes();
 
-// Declaration of the allocator API.
-
-#if defined(ADDRESS_SANITIZER)
-#define SB_ALLOCATOR_PREFIX
-#include <stdlib.h>
-#else
-#define SB_ALLOCATOR_PREFIX dl
-#endif
-
-#define SB_ALLOCATOR_MANGLER_2(prefix, fn) prefix##fn
-#define SB_ALLOCATOR_MANGLER(prefix, fn) SB_ALLOCATOR_MANGLER_2(prefix, fn)
-#define SB_ALLOCATOR(fn) SB_ALLOCATOR_MANGLER(SB_ALLOCATOR_PREFIX, fn)
-
-void SB_ALLOCATOR(_malloc_init)();
-void SB_ALLOCATOR(_malloc_finalize)();
-void* SB_ALLOCATOR(malloc)(size_t size);
-void* SB_ALLOCATOR(memalign)(size_t align, size_t size);
-void* SB_ALLOCATOR(realloc)(void* ptr, size_t size);
-void SB_ALLOCATOR(free)(void* ptr);
-
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/src/starboard/shared/dlmalloc/system_get_used_cpu_memory.cc b/src/starboard/shared/dlmalloc/system_get_used_cpu_memory.cc
deleted file mode 100644
index 2e9a4d3..0000000
--- a/src/starboard/shared/dlmalloc/system_get_used_cpu_memory.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "starboard/memory.h"
-
-#include "starboard/shared/dlmalloc/page_internal.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Returns the number of bytes obtained from the system.  The total
-// number of bytes allocated by malloc, realloc etc., is less than this
-// value. Unlike mallinfo, this function returns only a precomputed
-// result, so can be called frequently to monitor memory consumption.
-// Even if locks are otherwise defined, this function does not use them,
-// so results might not be up to date.
-//
-// See http://gee.cs.oswego.edu/pub/misc/malloc.h for more details.
-size_t SB_ALLOCATOR(malloc_footprint)();
-
-#ifdef __cplusplus
-}  // extern "C"
-#endif
-
-int64_t SbSystemGetUsedCPUMemory() {
-  return static_cast<int64_t>(SB_ALLOCATOR(malloc_footprint)());
-}
diff --git a/src/starboard/shared/libdav1d/dav1d_video_decoder.cc b/src/starboard/shared/libdav1d/dav1d_video_decoder.cc
index 80ce0d5..1f793ac 100644
--- a/src/starboard/shared/libdav1d/dav1d_video_decoder.cc
+++ b/src/starboard/shared/libdav1d/dav1d_video_decoder.cc
@@ -37,21 +37,8 @@
 using starboard::player::JobThread;
 using starboard::player::filter::CpuVideoFrame;
 
-int AllocatePicture(Dav1dPicture* picture, void* context) {
-  SB_DCHECK(picture);
-  SB_DCHECK(context);
-
-  VideoDecoder* decoder = static_cast<VideoDecoder*>(context);
-  return decoder->AllocatePicture(picture);
-}
-
-void ReleasePicture(Dav1dPicture* picture, void* context) {
-  SB_DCHECK(picture);
-  SB_DCHECK(context);
-
-  VideoDecoder* decoder = static_cast<VideoDecoder*>(context);
-  decoder->ReleasePicture(picture);
-}
+constexpr int kMaxDecodedFrameWidth = 3840;
+constexpr int kMaxDecodedFrameHeight = 2160;
 
 void ReleaseInputBuffer(const uint8_t* buf, void* context) {
   SB_DCHECK(context);
@@ -154,52 +141,6 @@
   frames_ = std::queue<scoped_refptr<CpuVideoFrame>>();
 }
 
-int VideoDecoder::AllocatePicture(Dav1dPicture* picture) const {
-  SB_DCHECK(decoder_thread_);
-  SB_DCHECK(decoder_thread_->job_queue()->BelongsToCurrentThread());
-  SB_DCHECK(picture->data[0] == NULL);
-  SB_DCHECK(picture->data[1] == NULL);
-  SB_DCHECK(picture->data[2] == NULL);
-
-  // dav1d requires that the allocated width and height is a multiple of 128.
-  // NOTE: UV resolution is half that of Y resolution.
-  int uv_width = (((picture->p.w + 127) / 128) * 128) / 2;
-  int uv_height = (((picture->p.h + 127) / 128) * 128) / 2;
-  // dav1d requires DAV1D_PICTURE_ALIGNMENT padded to allocated memory areas.
-  int uv_size = uv_width * uv_height + DAV1D_PICTURE_ALIGNMENT;
-
-  // This guarantees that the Y dimension is double the UV dimension.
-  int y_width = uv_width * 2;
-  int y_height = uv_height * 2;
-  int y_size = y_width * y_height + DAV1D_PICTURE_ALIGNMENT;
-
-  picture->stride[0] = y_width;
-  picture->stride[1] = uv_width;
-
-  void* ptr =
-      SbMemoryAllocateAligned(DAV1D_PICTURE_ALIGNMENT, y_size + uv_size * 2);
-  if (ptr == NULL) {
-    return DAV1D_ERR(ENOMEM);
-  }
-  picture->data[0] = ptr;
-  picture->data[1] = static_cast<uint8_t*>(ptr) + y_size;
-  picture->data[2] = static_cast<uint8_t*>(ptr) + y_size + uv_size;
-  return 0;
-}
-
-void VideoDecoder::ReleasePicture(Dav1dPicture* picture) const {
-  SB_DCHECK(picture->data[0]);
-  SB_DCHECK(picture->data[1]);
-  SB_DCHECK(picture->data[2]);
-  SB_DCHECK(picture->data[0] < picture->data[1]);
-  SB_DCHECK(picture->data[1] < picture->data[2]);
-
-  SbMemoryDeallocateAligned(picture->data[0]);
-  for (int i = 0; i < 3; ++i) {
-    picture->data[i] = NULL;
-  }
-}
-
 void VideoDecoder::UpdateDecodeTarget_Locked(
     const scoped_refptr<CpuVideoFrame>& frame) {
   SbDecodeTarget decode_target = DecodeTargetCreate(
@@ -229,13 +170,8 @@
   // TODO: Verify this setting is optimal.
   dav1d_settings.n_threads = 8;
 
-  Dav1dPicAllocator allocator;
-  allocator.cookie = this;  // dav1d refers to context pointers as "cookie".
-  allocator.alloc_picture_callback =
-      &::starboard::shared::libdav1d::AllocatePicture;
-  allocator.release_picture_callback =
-      &::starboard::shared::libdav1d::ReleasePicture;
-  dav1d_settings.allocator = allocator;
+  dav1d_settings.frame_size_limit =
+      kMaxDecodedFrameHeight * kMaxDecodedFrameWidth;
 
   int result = dav1d_open(&dav1d_context_, &dav1d_settings);
   if (result != kDav1dSuccess) {
diff --git a/src/starboard/shared/libdav1d/dav1d_video_decoder.h b/src/starboard/shared/libdav1d/dav1d_video_decoder.h
index 0cc2c3f..49d590a 100644
--- a/src/starboard/shared/libdav1d/dav1d_video_decoder.h
+++ b/src/starboard/shared/libdav1d/dav1d_video_decoder.h
@@ -54,9 +54,6 @@
   void WriteEndOfStream() override;
   void Reset() override;
 
-  int AllocatePicture(Dav1dPicture* picture) const;
-  void ReleasePicture(Dav1dPicture* picture) const;
-
  private:
   typedef ::starboard::shared::starboard::player::filter::CpuVideoFrame
       CpuVideoFrame;
diff --git a/src/starboard/shared/starboard/player/player_create.cc b/src/starboard/shared/starboard/player/player_create.cc
index 41552de..dd59c65 100644
--- a/src/starboard/shared/starboard/player/player_create.cc
+++ b/src/starboard/shared/starboard/player/player_create.cc
@@ -12,9 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <string>
+
 #include "starboard/player.h"
 
 #include "starboard/common/log.h"
+#include "starboard/common/media.h"
+#include "starboard/common/string.h"
 #include "starboard/configuration.h"
 #include "starboard/decode_target.h"
 #include "starboard/shared/media_session/playback_state.h"
@@ -43,8 +47,15 @@
                         SbPlayerErrorFunc player_error_func,
                         void* context,
                         SbDecodeTargetGraphicsContextProvider* provider) {
+  if (!player_error_func) {
+    SB_LOG(ERROR) << "|player_error_func| cannot be null.";
+    return kSbPlayerInvalid;
+  }
+
   if (!creation_param) {
     SB_LOG(ERROR) << "CreationParam cannot be null.";
+    player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
+                      "CreationParam cannot be null");
     return kSbPlayerInvalid;
   }
 
@@ -62,15 +73,22 @@
 
   if (!audio_mime) {
     SB_LOG(ERROR) << "creation_param->audio_sample_info.mime cannot be null.";
+    player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
+                      "creation_param->video_sample_info.mime cannot be null");
     return kSbPlayerInvalid;
   }
   if (!video_mime) {
     SB_LOG(ERROR) << "creation_param->video_sample_info.mime cannot be null.";
+    player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
+                      "creation_param->video_sample_info.mime cannot be null");
     return kSbPlayerInvalid;
   }
   if (!max_video_capabilities) {
     SB_LOG(ERROR) << "creation_param->video_sample_info.max_video_capabilities"
                   << " cannot be null.";
+    player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
+                      "creation_param->video_sample_info.max_video_"
+                      "capabilities cannot be null");
     return kSbPlayerInvalid;
   }
 
@@ -105,12 +123,33 @@
                         SbDecodeTargetGraphicsContextProvider* provider) {
 #endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 
+  if (!player_error_func) {
+    SB_LOG(ERROR) << "|player_error_func| cannot be null.";
+    return kSbPlayerInvalid;
+  }
+
   if (audio_sample_info) {
     SB_DCHECK(audio_sample_info->codec == audio_codec);
   }
 
-  if (!sample_deallocate_func || !decoder_status_func || !player_status_func ||
-      !player_error_func) {
+  if (!sample_deallocate_func) {
+    SB_LOG(ERROR) << "|sample_deallocate_func| cannot be null.";
+    player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
+                      "|sample_deallocate_func| cannot be null");
+    return kSbPlayerInvalid;
+  }
+
+  if (!decoder_status_func) {
+    SB_LOG(ERROR) << "|decoder_status_func| cannot be null.";
+    player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
+                      "|decoder_status_func| cannot be null");
+    return kSbPlayerInvalid;
+  }
+
+  if (!player_status_func) {
+    SB_LOG(ERROR) << "|player_status_func| cannot be null.";
+    player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
+                      "|player_status_func| cannot be null");
     return kSbPlayerInvalid;
   }
 
@@ -121,7 +160,13 @@
                                audio_mime,
 #endif  // SB_API_VERSION >= 12
                                kDefaultBitRate)) {
-    SB_LOG(ERROR) << "Unsupported audio codec " << audio_codec;
+    SB_LOG(ERROR) << "Unsupported audio codec "
+                  << starboard::GetMediaAudioCodecName(audio_codec) << ".";
+    player_error_func(
+        kSbPlayerInvalid, context, kSbPlayerErrorDecode,
+        starboard::FormatString("Unsupported audio codec: %s",
+                                starboard::GetMediaAudioCodecName(audio_codec))
+            .c_str());
     return kSbPlayerInvalid;
   }
 
@@ -145,14 +190,24 @@
           kDefaultFrameWidth, kDefaultFrameHeight, kDefaultBitRate,
           kDefaultFrameRate,
           output_mode == kSbPlayerOutputModeDecodeToTexture)) {
-    SB_LOG(ERROR) << "Unsupported video codec " << video_codec;
+    SB_LOG(ERROR) << "Unsupported video codec "
+                  << starboard::GetMediaVideoCodecName(video_codec) << ".";
+    player_error_func(
+        kSbPlayerInvalid, context, kSbPlayerErrorDecode,
+        starboard::FormatString("Unsupported video codec: %s",
+                                starboard::GetMediaVideoCodecName(video_codec))
+            .c_str());
     return kSbPlayerInvalid;
   }
 
   if (audio_codec != kSbMediaAudioCodecNone && !audio_sample_info) {
     SB_LOG(ERROR)
         << "SbPlayerCreate() requires a non-NULL SbMediaAudioSampleInfo "
-        << "when |audio_codec| is not kSbMediaAudioCodecNone";
+        << "when |audio_codec| is not kSbMediaAudioCodecNone.";
+    player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
+                      "SbPlayerCreate() requires a non-NULL "
+                      "SbMediaAudioSampleInfo when |audio_codec| is not "
+                      "kSbMediaAudioCodecNone");
     return kSbPlayerInvalid;
   }
 
@@ -160,27 +215,41 @@
       video_codec == kSbMediaVideoCodecNone) {
     SB_LOG(ERROR) << "SbPlayerCreate() requires at least one audio track or"
                   << " one video track.";
+    player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
+                      "SbPlayerCreate() requires at least one audio track or "
+                      "one video track");
     return kSbPlayerInvalid;
   }
 
+  std::string error_message;
   if (audio_sample_info &&
       audio_sample_info->number_of_channels > SbAudioSinkGetMaxChannels()) {
-    SB_LOG(ERROR) << "audio_sample_info->number_of_channels ("
-                  << audio_sample_info->number_of_channels
-                  << ") exceeds the maximum"
-                  << " number of audio channels supported by this platform ("
-                  << SbAudioSinkGetMaxChannels() << ").";
+    error_message = starboard::FormatString(
+        "Number of audio channels (%d) exceeds the maximum number of audio "
+        "channels supported by this platform (%d)",
+        audio_sample_info->number_of_channels, SbAudioSinkGetMaxChannels());
+    SB_LOG(ERROR) << error_message << ".";
+    player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
+                      error_message.c_str());
     return kSbPlayerInvalid;
   }
 
 #if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
   if (SbPlayerGetPreferredOutputMode(creation_param) != output_mode) {
-    SB_LOG(ERROR) << "Unsupported player output mode " << output_mode;
+    error_message = starboard::FormatString("Unsupported player output mode %d",
+                                            output_mode);
+    SB_LOG(ERROR) << error_message << ".";
+    player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
+                      error_message.c_str());
     return kSbPlayerInvalid;
   }
 #else   // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
   if (!SbPlayerOutputModeSupported(output_mode, video_codec, drm_system)) {
-    SB_LOG(ERROR) << "Unsupported player output mode " << output_mode;
+    error_message = starboard::FormatString("Unsupported player output mode %d",
+                                            output_mode);
+    SB_LOG(ERROR) << error_message << ".";
+    player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
+                      error_message.c_str());
     return kSbPlayerInvalid;
   }
 #endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
diff --git a/src/starboard/stub/platform_configuration/BUILD.gn b/src/starboard/stub/platform_configuration/BUILD.gn
index a5bdb8f..e75f459 100644
--- a/src/starboard/stub/platform_configuration/BUILD.gn
+++ b/src/starboard/stub/platform_configuration/BUILD.gn
@@ -25,18 +25,13 @@
   }
 
   if (is_debug) {
-    cflags += [
-      "-frtti",
-      "-O0",
-    ]
+    cflags += [ "-O0" ]
+    configs += [ "//build/config/compiler:rtti" ]
   } else if (is_devel) {
-    cflags += [
-      "-frtti",
-      "-O2",
-    ]
+    cflags += [ "-O2" ]
+    configs += [ "//build/config/compiler:rtti" ]
   } else {
     cflags += [
-      "-fno-rtti",
       "-O2",
       "-gline-tables-only",
     ]
diff --git a/src/third_party/angle/src/libANGLE/HandleAllocator.cpp b/src/third_party/angle/src/libANGLE/HandleAllocator.cpp
index 013f1df..f9efef0 100644
--- a/src/third_party/angle/src/libANGLE/HandleAllocator.cpp
+++ b/src/third_party/angle/src/libANGLE/HandleAllocator.cpp
@@ -11,6 +11,7 @@
 
 #include <algorithm>
 #include <functional>
+#include <limits>
 
 #include "common/debug.h"
 
diff --git a/src/third_party/crashpad/client/crashpad_client.h b/src/third_party/crashpad/client/crashpad_client.h
index 6d0c094..b9bfeeb 100644
--- a/src/third_party/crashpad/client/crashpad_client.h
+++ b/src/third_party/crashpad/client/crashpad_client.h
@@ -40,6 +40,7 @@
 
 #if defined(STARBOARD)
 #include "starboard/elf_loader/evergreen_info.h"
+#include "third_party/crashpad/snapshot/sanitized/sanitization_information.h"  // nogncheck
 #include "third_party/crashpad/wrapper/annotations.h"
 #endif
 
@@ -398,6 +399,16 @@
   //!
   //! \return `true` on success, `false` on failure with a message logged.
   static bool SendAnnotationsToHandler(CrashpadAnnotations annotations);
+
+  //! \brief Sends mapping info to the handler
+  //!
+  //! A handler must have already been installed before calling this method.
+  //! \param[in] sanitization_information A SanitizationInformation struct, whose information
+  //!     was created on Evergreen startup.
+  //!
+  //! \return `true` on success, `false` on failure with a message logged.
+  static bool SendSanitizationInformationToHandler(SanitizationInformation sanitization_information);
+
 #endif
 
   //! \brief Requests that the handler capture a dump even though there hasn't
diff --git a/src/third_party/crashpad/client/crashpad_client_linux.cc b/src/third_party/crashpad/client/crashpad_client_linux.cc
index b1bba00..570be2e 100644
--- a/src/third_party/crashpad/client/crashpad_client_linux.cc
+++ b/src/third_party/crashpad/client/crashpad_client_linux.cc
@@ -137,6 +137,10 @@
   }
 
 #if defined(STARBOARD)
+  bool SendSanitizationInformation(SanitizationInformation sanitization_information) {
+    sanitization_information_ = sanitization_information;
+    return true;
+  }
   bool SendEvergreenInfo(EvergreenInfo evergreen_info) {
     evergreen_info_ = evergreen_info;
     return SendEvergreenInfoImpl();
@@ -187,6 +191,9 @@
 #if defined(STARBOARD)
   const EvergreenInfo& GetEvergreenInfo() { return evergreen_info_; }
   const CrashpadAnnotations& GetAnnotations() { return annotations_; }
+  const SanitizationInformation& GetSanitizationInformation() {
+    return sanitization_information_;
+  }
 #endif
 
   const ExceptionInformation& GetExceptionInfo() {
@@ -219,6 +226,7 @@
 #if defined(STARBOARD)
   EvergreenInfo evergreen_info_;
   CrashpadAnnotations annotations_;
+  SanitizationInformation sanitization_information_;
 #endif
 
   static SignalHandler* handler_;
@@ -376,6 +384,11 @@
     ExceptionHandlerProtocol::ClientInformation info = {};
     info.exception_information_address =
         FromPointerCast<VMAddress>(&GetExceptionInfo());
+#if defined(STARBOARD)
+    info.sanitization_information_address =
+        FromPointerCast<VMAddress>(&GetSanitizationInformation());
+#endif
+
 #if defined(OS_CHROMEOS)
     info.crash_loop_before_time = crash_loop_before_time_;
 #endif
@@ -603,6 +616,16 @@
 
   return SignalHandler::Get()->SendAnnotations(annotations);
 }
+
+bool CrashpadClient::SendSanitizationInformationToHandler(
+    SanitizationInformation sanitization_information) {
+  if (!SignalHandler::Get()) {
+    DLOG(ERROR) << "Crashpad isn't enabled";
+    return false;
+  }
+
+  return SignalHandler::Get()->SendSanitizationInformation(sanitization_information);
+}
 #endif
 
 // static
diff --git a/src/third_party/crashpad/wrapper/wrapper.cc b/src/third_party/crashpad/wrapper/wrapper.cc
index f482f2f..aa40f75 100644
--- a/src/third_party/crashpad/wrapper/wrapper.cc
+++ b/src/third_party/crashpad/wrapper/wrapper.cc
@@ -27,6 +27,7 @@
 #include "starboard/directory.h"
 #include "starboard/file.h"
 #include "starboard/system.h"
+#include "third_party/crashpad/snapshot/sanitized/sanitization_information.h"  // nogncheck
 
 namespace third_party {
 namespace crashpad {
@@ -202,6 +203,9 @@
                        default_arguments,
                        false,
                        false);
+
+  ::crashpad::SanitizationInformation sanitization_info = {0, 0, 0, 1};
+  client->SendSanitizationInformationToHandler(sanitization_info);
 }
 
 bool AddEvergreenInfoToCrashpad(EvergreenInfo evergreen_info) {
diff --git a/src/third_party/google_benchmark/src/benchmark_register.h b/src/third_party/google_benchmark/src/benchmark_register.h
index 61377d7..204bf1d 100644
--- a/src/third_party/google_benchmark/src/benchmark_register.h
+++ b/src/third_party/google_benchmark/src/benchmark_register.h
@@ -1,6 +1,7 @@
 #ifndef BENCHMARK_REGISTER_H
 #define BENCHMARK_REGISTER_H
 
+#include <limits>
 #include <vector>
 
 #include "check.h"