Import Cobalt 21.master.0.260628
diff --git a/src/starboard/CHANGELOG.md b/src/starboard/CHANGELOG.md
index b4e9fe3..7217425 100644
--- a/src/starboard/CHANGELOG.md
+++ b/src/starboard/CHANGELOG.md
@@ -6,7 +6,7 @@
 this file describes the changes made to the Starboard interface since the
 version previous to it.
 
-**NOTE: Starboard versions 3 and below are no longer supported.**
+**NOTE: Starboard versions 9 and below are no longer supported.**
 
 ## Experimental Version
 
@@ -353,289 +353,3 @@
 
 Please see [contrib/README.md](contrib/README.md) for description of
 expectations for contents in this directory.
-
-## Version 9
-
-### Add string label to `SbMicrophoneInfo`.
-
-This should indicate the friendly name of the microphone type.
-
-### Introduce additional SbSocketError enum values.
-
-Instead of the single generic kSbSocketErrorFailed to indicate socket errors,
-the enum kSbSocketErrorConnectionReset has been introduced corresponding to
-various dropped TCP connection errors.  This is particularly useful in
-identifying socket errors that can be retried.
-
-### Add new keycode kSbKeyInstantReplay
-
-Identical to OCAP's `VK_INSTANT_REPLAY`
-
-## Version 8
-
-### Add `SbPlayerCreateWithUrl()`, `SbPlayerSetDrmSystem()`, `SbPlayerOutputModeSupportedWithUrl()`
-
-For platform media players that rely on using a URL (like an m3u playlist URL)
-for playback, add `SbPlayerCreateWithUrl()` which takes in a URL, no video or
-audio configs, and no DRM system. Allow the DRM system to be set on a running
-SbPlayer exactly once for SbPlayers created with a URL. Also, since URL players
-will not expose codec information, use a custom
-`SbPlayerOutputModeSupportedWithUrl()` to query player output mode support.
-
-### Add `kSbEventTypeWindowSizeChanged`
-
-An event indicating that an `SbWindow`'s size has changed. The event data is
-`SbEventWindowSizeChangedData`, containing a `SbWindow` and `SbWindowSize`.
-
-### Add `SbWindowShowOnScreenKeyboard()`, `SbWindowHideOnScreenKeyboard()`, `SbWindowFocusOnScreenKeyboard()`, `SbWindowBlurOnScreenKeyboard()`, `SbWindowIsOnScreenKeyboardShown()`, `SbWindowSetOnScreenKeyboardKeepFocus()`
-
-These methods show, hide, focus, and blur a native on screen keyboard,
-determine if the on screen keyboard is shown, and set whether focus is kept to
-the on screen keyboard. The on screen keyboard also handles
-`kSbInputEventTypeInput`, which use a new field `input_text` of `SbInputData`.
-
-## Version 7
-
-### `SbDecodeTargetInfoPlane` can specify color plane information
-
-Previously: Planes of type `kSbDecodeTargetFormat2PlaneYUVNV12`
-were assumed to have the luma mapped to the alpha channel (`GL_ALPHA`)
-and the chroma mapped to blue and alpha (`GL_LUMINANCE_ALPHA`). However,
-some graphics systems require that luma is on `GL_RED_EXT` and the chroma
-is on `GL_RG_EXT`.
-
-## Version 6
-
-### Named `SbStorageRecord`s
-
-This extends the `SbStorage` interface with the ability to open named
-`SbStorageRecord`s. Calling `SbStorageOpenRecord` and `SbStorageDeleteRecord`
-with a `NULL` `name` parameter provides access to the old "default" record.
-
-### Introduce pointer (mouse) input support
-
-This extends the `SbInput` interface with some enum values and data members to
-allow mouse, wheel, and more generic pointer input.
-
-### Flexible audio specific config
-
-`SbMediaAudioHeader::audio_specific_config` will be a pointer instead of an
-array.
-
-### Time Zone API Cleanup
-
-Removes `SbTimeZoneGetDstName()` -- The Daylight Savings Time version of the
-time zone.
-
-Changes `SbTimeZoneGetName()` to be more flexible in what it is allowed to
-return.
-
-### `SbDecodeTargetNumberOfPlanesForFormat`
-
-Adds the convenience inline function, SbDecodeTargetNumberOfPlanesForFormat() to
-`starboard/decode_target.h`.
-
-### Preload Support
-
-Adds the `kSbEventTypePreload` event, and modifies the application state machine
-to utilize it.
-
-### Platform Error Cleanup
-
-Removes `SbSystemPlatformErrorType` values specific to user status.
-
-### `SbDecodeTarget` support for the UYVY (i.e. YUV 422) format
-
-Add support for UYVY decode targets (e.g. YUV 422) via the
-`kSbDecodeTargetFormat1PlaneUYVY` enum.
-
-### Add More Remote Keys
-
-This adds SbKey codes for:
-
-  * Color keys
-  * Closed Caption key
-  * Application launch key
-  * Channel Up/Down keys
-  * Info key
-  * Guide key
-  * Last/Previous Channel key
-  * Media audio track select key
-
-### `kSbEventTypeLowMemory`
-
-Adds a new event type -- `kSbEventTypeLowMemory` -- to allow a platform to
-signal that the application may soon be terminated due to low memory
-availability.
-
-### Interface change to `SbPlayerWriteSample()`
-`const` is added to `sample_buffers` and `sample_buffer_sizes` parameters.
-
-### Support key status change
-Add `key_statuses_changed_callback` parameter to `SbDrmCreateSystem()` to
-support MediaKeySession::keyStatuses and MediaKeySession::onkeystatuseschange.
-
-### Changes thumbstick direction
-
-Change the meaning of negative values for thumbstick position from bottom
-right to upper left.
-
-## Version 5
-
-### Add Speech Recognizer API
-Introduce `starboard/speech_recognizer.h`.
-This newly-introduced `starboard/speech_recognizer.h` adds the on-device speech
-recognizer feature.
-
-### Added new system property to allow platform-specific user agent suffixes
-Adds `kSbSystemPropertyUserAgentAuxField` to the `SbSystemPropertyId` enum to
-allow platform-specific User-Agent suffix.
-
-### Remove unused enums from `starboard/input.h`
-The following unused enum values are removed from `starboard/input.h`:
-  * `kSbInputDeviceTypeMicrophone`
-  * `kSbInputDeviceTypeSpeechCommand`
-  * `kSbInputEventTypeAudio`
-  * `kSbInputEventTypeCommand`
-  * `kSbInputEventTypeGrab`
-  * `kSbInputEventTypeUngrab`
-
-## Version 4
-
-### Decode-to-Texture Player Output Mode
-Feature introducing support for decode-to-texture player output mode, and
-runtime player output mode selection and detection.
-In `starboard/configuration.h`,
-  * `SB_IS_PLAYER_PUNCHED_OUT`, `SB_IS_PLAYER_PRODUCING_TEXTURE`, and
-    `SB_IS_PLAYER_COMPOSITED` now no longer need to be defined (and should not
-    be defined) by platforms.  Instead, these capabilities are detected at
-    runtime via `SbPlayerOutputModeSupported()`.
-
-In `starboard/player.h`,
-  * The enum `SbPlayerOutputMode` is introduced.
-  * `SbPlayerOutputModeSupported()` is introduced to let applications query
-    for player output mode support.
-  * `SbPlayerCreate()` now takes an additional parameter that specifies the
-    desired output mode.
-  * The punch out specific function `SbPlayerSetBounds()` must now be
-    defined on all platforms, even if they don't support punch out (in which
-    case they can implement a stub).
-  * The function `SbPlayerGetCompositionHandle()` is removed.
-  * The function `SbPlayerGetTextureId()` is replaced by the new
-    `SbPlayerGetCurrentFrame()`, which returns a `SbDecodeTarget`.
-
-In `starboard/decode_target.h`,
-  * All get methods (`SbDecodeTargetGetPlane()` and `SbDecodeTargetGetFormat()`,
-    `SbDecodeTargetIsOpaque()`) are now replaced with `SbDecodeTargetGetInfo()`.
-  * The `SbDecodeTargetInfo` structure is introduced and is the return value
-    type of `SbDecodeTargetGetInfo()`.
-  * `SbDecdodeTargetCreate()` is now responsible for creating all its internal
-    planes, and so its `planes` parameter is replaced by `width` and
-    `height` parameters.
-  * The GLES2 version of `SbDecdodeTargetCreate()` has its EGL types
-    (`EGLDisplay`, `EGLContext`) replaced by `void*` types, so that
-    `decode_target.h` can avoid #including EGL/GLES2 headers.
-  * `SbDecodeTargetDestroy()` is renamed to `SbDecodeTargetRelease()`.
-
-In `starboard/player.h`, `starboard/image.h` and `starboard/decode_target.h`,
-  * Replace `SbDecodeTargetProvider` with
-    `SbDecodeTargetGraphicsContextProvider`.
-  * Instead of restricting Starboard implementations to only be able to run
-    `SbDecodeTarget` creation and destruction code on the application's
-    renderer's thread with the application's renderer's `EGLContext` current,
-    Starboard implementations can now run arbitrary code on the application's
-    renderer's thread with its `EGLContext` current.
-  * Remove `SbDecodeTargetCreate()`, `SbDecodeTarget` creation is now an
-    implementation detail to be dealt with in other Starboard API functions
-    that create `SbDecodeTargets`, like `SbImageDecode()` or `SbPlayerCreate()`.
-
-### Playback Rate
-Support for setting the playback rate on an SbPlayer.  This allows for control
-of the playback speed of video at runtime.
-
-### Floating Point Input Vector
-Change `input.h`'s `SbInputVector` structure to contain float members instead of
-ints.
-
-### Delete SbUserApplicationTokenResults
-Deleted the vestigal struct `SbUserApplicationTokenResults` from `user.h`.
-
-### Storage Options for Encoded Audio/Video Data
-Enables the SbPlayer implementation to provide instructions to its user on
-how to store audio/video data.  Encoded audio/video data is cached once being
-demuxed and may occupy a significant amount of memory.  Enabling this feature
-allows the SbPlayer implementation to have better control on where encoded
-audio/video data is stored.
-
-### Unified implementation of `SbMediaCanPlayMimeAndKeySystem()`
-Use a unified implementation of `SbMediaCanPlayMimeAndKeySystem()` based on
-`SbMediaIsSupported()`, `SbMediaIsAudioSupported()`, and
-`SbMediaIsVideoSupported()`.
-
-### Introduce `ticket` parameter to `SbDrmGenerateSessionUpdateRequest()`
-Introduce `ticket` parameter to `SbDrmGenerateSessionUpdateRequest()`
-and `SbDrmSessionUpdateRequestFunc` to allow distinguishing between callbacks
-from multiple concurrent calls.
-
-### Introduce `SbSocketGetInterfaceAddress()`
-`SbSocketGetInterfaceAddress()` is introduced to let applications find out
-which source IP address and the associated netmask will be used to connect to
-the destination. This is very important for multi-homed devices, and for
-certain conditions in IPv6.
-
-### Introduce `starboard/cryptography.h`
-In newly-introduced `starboard/cryptography.h`,
-  * Optional support for accelerated cryptography, which can, in
-    particular, be used for accelerating SSL.
-
-### Introduce z-index parameter to `SbPlayerSetBounds()`
-Allow `SbPlayerSetBounds` to use an extra parameter to indicate the z-index of
-the video so multiple overlapping videos can be rendered.
-
-### Media source buffer settings removed from `configuration.h`
-Media source buffer settings in Starboard.
-
-### Introduce `starboard/accessibility.h`
-In particular, the functions `SbAccessibilityGetDisplaySettings()` and
-`SbAccessibilityGetTextToSpeechSettings()` have now been introduced.
-
-Additionally, a new Starboard event, `kSbEventTypeAccessiblitySettingsChanged`,
-has been defined in `starboard/event.h`.
-
-### HDR decode support
-In `starboard/media.h`, `SbMediaColorMetadata` is now defined and it contains
-HDR metadata. The field `SbMediaColorMetadata color_metadata` is now added to
-`SbMediaVideoSampleInfo`.
-
-### Add `kSbSystemDeviceTypeAndroidTV` to `starboard/system.h`
-A new device type, `kSbSystemDeviceTypeAndroidTV`, is added to
-starboard/system.h.
-
-### Deprecate `SbSpeechSynthesisSetLanguage()`
-SbSpeechSynthesisSetLanguage() has been deprecated.
-
-### Request State Change Support
-Added `SbSystemRequestPause()`, `SbSystemRequestUnpause()`,
-`SbSystemRequestSuspend()`.
-
-`SbSystemRequestSuspend()` in particular can be hooked into a platform's "hide"
-or "minimize" window functionality.
-
-### Font Directory Path Support
-Added `kSbSystemPathFontDirectory` and `kSbSystemPathFontConfigurationDirectory`
-which can be optionally specified for platforms that want to provide system
-fonts to Starboard applications. The font and font configuration formats
-supported are application-specific.
-
-### Add `SB_NORETURN` to `starboard/configuration.h`.
-Added attribute macro `SB_NORETURN` to allow functions to be marked as noreturn.
-
-### Mark `SbSystemBreakIntoDebugger` `SB_NORETURN`.
-Add `SB_NORETURN` to declaration of `SbSystemBreakIntoDebugger`, to allow it to
-be used in a manner similar to `abort`.
-
-### Introduce `SbAudioSinkGetMinBufferSizeInFrames()`
-
-Introduce `SbAudioSinkGetMinBufferSizeInFrames()` to `starboard/audio_sink.h`
-which communicates to the platform how many audio frames are required to ensure
-that audio sink can keep playing without underflow.
diff --git a/src/starboard/android/apk/app/CMakeLists.txt b/src/starboard/android/apk/app/CMakeLists.txt
index 3e0fd3c..539ef3d 100644
--- a/src/starboard/android/apk/app/CMakeLists.txt
+++ b/src/starboard/android/apk/app/CMakeLists.txt
@@ -17,7 +17,7 @@
 # be started:
 # Run -> Edit Configurations -> "app" -> Debugger -> Debug Type = Dual
 
-cmake_minimum_required(VERSION 3.4.1)
+cmake_minimum_required(VERSION 3.10.2)
 
 # Map Android ABI to Cobalt architecture
 if(ANDROID_ABI STREQUAL x86)
@@ -92,3 +92,25 @@
 # Add a dependency to run the external cobalt build as a side effect of
 # building the phony native library.
 add_dependencies(native external_cobalt_build)
+
+if (ENABLE_VULKAN)
+  # Add angle shared libraries.
+  list(APPEND angle_libs EGL_angle GLESv1_CM_angle
+      GLESv2_angle feature_support_angle c++_shared)
+
+  # Copy each library into APK.
+  foreach(angle_lib IN LISTS angle_libs)
+    add_dependencies(native ${angle_lib}_lib)
+    add_library(${angle_lib} SHARED IMPORTED)
+    set_target_properties(${angle_lib} PROPERTIES
+        IMPORTED_LOCATION
+            ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/lib${angle_lib}.so
+    )
+    add_custom_target(${angle_lib}_lib
+        DEPENDS external_cobalt_build
+        COMMAND ${CMAKE_COMMAND} -E copy
+        ${COBALT_PRODUCT_DIR}/lib/lib${angle_lib}.so
+        ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/lib${angle_lib}.so
+    )
+  endforeach()
+endif()
\ No newline at end of file
diff --git a/src/starboard/android/apk/app/build.gradle b/src/starboard/android/apk/app/build.gradle
index 231fbb9..07ab31a 100644
--- a/src/starboard/android/apk/app/build.gradle
+++ b/src/starboard/android/apk/app/build.gradle
@@ -17,6 +17,11 @@
 final DEFAULT_COBALT_TARGET = 'cobalt'
 final String[] SUPPORTED_ABIS = [ 'x86', 'armeabi-v7a', 'arm64-v8a' ]
 
+// Parse the NDK_VERSION and CMAKE_VERSION defined in sdk_utils.py
+final SDK_UTILS = project.rootProject.file('../shared/sdk_utils.py')
+final NDK_VERSION = SDK_UTILS.find { it.startsWith('_NDK_VERSION =') }.split("'")[1]
+final CMAKE_VERSION = SDK_UTILS.find { it.startsWith('_CMAKE_VERSION =') }.split("'")[1]
+
 ext {
     // Provide defaults if these properties aren't specified on the command line.
     buildAbi = project.hasProperty('cobaltBuildAbi') ? cobaltBuildAbi : SUPPORTED_ABIS
@@ -25,6 +30,8 @@
             project.hasProperty('cobaltProductDir') ? new File(cobaltProductDir).canonicalPath : ''
     cobaltContentDir =
             project.hasProperty('cobaltContentDir') ? new File(cobaltContentDir).canonicalPath : ''
+    enableVulkan =
+            project.hasProperty('enableVulkan') ? enableVulkan : 0
 
     buildIdFile = rootProject.file('build.id')
     buildId = buildIdFile.exists() ? buildIdFile.text.trim() : '0'
@@ -35,6 +42,7 @@
 android {
     compileSdkVersion 29
     buildToolsVersion "29.0.2"
+    ndkVersion NDK_VERSION
 
     signingConfigs {
         // A signing config that matches what is implicitly used for the "debug" build type.
@@ -58,6 +66,7 @@
                 arguments "-DCOBALT_PRODUCT_DIR=${cobaltProductDir}"
                 arguments "-DCOBALT_CONTENT_DIR=${cobaltContentDir}"
                 arguments "-DCOBALT_PLATFORM_DEPLOY=${project.hasProperty('cobaltDeployApk')}"
+                arguments "-DENABLE_VULKAN=${enableVulkan}"
             }
         }
     }
@@ -129,6 +138,7 @@
     }
     externalNativeBuild {
         cmake {
+            version CMAKE_VERSION
             path 'CMakeLists.txt'
             // Move the staging directory to be a sibling of the build directory, which we moved
             // in the top-level build.gradle to be in a common top-level 'build' directory. Get
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 ed92953..f487c65 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
@@ -467,10 +467,12 @@
     boolean findHDRDecoder = android.os.Build.VERSION.SDK_INT >= 24 && colorInfo != null;
     // 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);
+        MediaCodecUtil.findVideoDecoder(
+            mime, isSecure, 0, 0, 0, 0, findHDRDecoder, requireSoftwareCodec);
     if (findVideoDecoderResult.name.equals("") && findHDRDecoder) {
       // On second pass, forget HDR.
-      findVideoDecoderResult = MediaCodecUtil.findVideoDecoder(mime, isSecure, 0, 0, 0, 0, false);
+      findVideoDecoderResult =
+          MediaCodecUtil.findVideoDecoder(mime, isSecure, 0, 0, 0, 0, false, requireSoftwareCodec);
     }
     try {
       String decoderName = findVideoDecoderResult.name;
@@ -496,7 +498,7 @@
     boolean shouldConfigureHdr =
         android.os.Build.VERSION.SDK_INT >= 24
             && colorInfo != null
-            && MediaCodecUtil.isHdrCapableVp9Decoder(findVideoDecoderResult);
+            && MediaCodecUtil.isHdrCapableVideoDecoder(mime, findVideoDecoderResult);
     if (shouldConfigureHdr) {
       Log.d(TAG, "Setting HDR info.");
       mediaFormat.setInteger(MediaFormat.KEY_COLOR_TRANSFER, colorInfo.colorTransfer);
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 b7fdad8..de35fb4 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
@@ -38,12 +38,16 @@
   // A high priority white 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<>();
+
   // 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;
   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";
 
   /**
    * A simple "struct" to bundle up the results from findVideoDecoder, as its clients may require
@@ -369,6 +373,8 @@
     isVp9WhiteListed =
         vp9WhiteList.containsKey(Build.BRAND)
             && vp9WhiteList.get(Build.BRAND).contains(Build.MODEL);
+
+    softwareCodecWhiteList.add("OMX.google.h264.decoder");
   }
 
   private MediaCodecUtil() {}
@@ -390,9 +396,10 @@
       int fps,
       boolean mustSupportHdr) {
     FindVideoDecoderResult findVideoDecoderResult =
-        findVideoDecoder(mimeType, secure, frameWidth, frameHeight, bitrate, fps, mustSupportHdr);
+        findVideoDecoder(
+            mimeType, secure, frameWidth, frameHeight, bitrate, fps, mustSupportHdr, false);
     return !findVideoDecoderResult.name.equals("")
-        && (!mustSupportHdr || isHdrCapableVp9Decoder(findVideoDecoderResult));
+        && (!mustSupportHdr || isHdrCapableVideoDecoder(mimeType, findVideoDecoderResult));
   }
 
   /**
@@ -405,23 +412,32 @@
     return !findAudioDecoder(mimeType, bitrate).equals("");
   }
 
-  /** Determine whether the system has a decoder capable of playing HDR VP9. */
+  /**
+   * Determine whether the system has a decoder capable of playing HDR. Currently VP9 and AV1 are
+   * HDR supported codecs
+   */
   @SuppressWarnings("unused")
   @UsedByNative
-  public static boolean hasHdrCapableVp9Decoder() {
+  public static boolean hasHdrCapableVideoDecoder(String mimeType) {
     // VP9Profile* values were not added until API level 24.  See
     // https://developer.android.com/reference/android/media/MediaCodecInfo.CodecProfileLevel.html.
     if (Build.VERSION.SDK_INT < 24) {
       return false;
     }
+    // AV1ProfileMain10HDR10 value was not added until API level 29.  See
+    // https://developer.android.com/reference/android/media/MediaCodecInfo.CodecProfileLevel.html.
+    if (mimeType.equals(AV1_MIME_TYPE) && Build.VERSION.SDK_INT < 29) {
+      return false;
+    }
 
     FindVideoDecoderResult findVideoDecoderResult =
-        findVideoDecoder(VP9_MIME_TYPE, false, 0, 0, 0, 0, true);
-    return isHdrCapableVp9Decoder(findVideoDecoderResult);
+        findVideoDecoder(mimeType, false, 0, 0, 0, 0, true, false);
+    return isHdrCapableVideoDecoder(mimeType, findVideoDecoderResult);
   }
 
-  /** Determine whether findVideoDecoderResult is capable of playing HDR VP9 */
-  public static boolean isHdrCapableVp9Decoder(FindVideoDecoderResult findVideoDecoderResult) {
+  /** Determine whether findVideoDecoderResult is capable of playing HDR */
+  public static boolean isHdrCapableVideoDecoder(
+      String mimeType, FindVideoDecoderResult findVideoDecoderResult) {
     CodecCapabilities codecCapabilities = findVideoDecoderResult.codecCapabilities;
     if (codecCapabilities == null) {
       return false;
@@ -431,9 +447,15 @@
       return false;
     }
     for (CodecProfileLevel codecProfileLevel : codecProfileLevels) {
-      if (codecProfileLevel.profile == CodecProfileLevel.VP9Profile2HDR
-          || codecProfileLevel.profile == CodecProfileLevel.VP9Profile3HDR) {
-        return true;
+      if (mimeType.equals(VP9_MIME_TYPE)) {
+        if (codecProfileLevel.profile == CodecProfileLevel.VP9Profile2HDR
+            || codecProfileLevel.profile == CodecProfileLevel.VP9Profile3HDR) {
+          return true;
+        }
+      } else if (mimeType.equals(AV1_MIME_TYPE)) {
+        if (codecProfileLevel.profile == CodecProfileLevel.AV1ProfileMain10HDR10) {
+          return true;
+        }
       }
     }
 
@@ -451,13 +473,15 @@
       int frameHeight,
       int bitrate,
       int fps,
-      boolean hdr) {
+      boolean hdr,
+      boolean requireSoftwareCodec) {
     Log.v(
         TAG,
         String.format(
             "Searching for video decoder with parameters "
-                + "mimeType: %s, secure: %b, frameWidth: %d, frameHeight: %d, bitrate: %d, fps: %d",
-            mimeType, secure, frameWidth, frameHeight, bitrate, fps));
+                + "mimeType: %s, secure: %b, frameWidth: %d, frameHeight: %d,"
+                + " bitrate: %d, fps: %d, hdr: %b, requireSoftwareCodec: %b",
+            mimeType, secure, frameWidth, frameHeight, bitrate, fps, hdr, requireSoftwareCodec));
     Log.v(
         TAG,
         String.format(
@@ -477,8 +501,11 @@
         if (!supportedType.equalsIgnoreCase(mimeType)) {
           continue;
         }
-
         String name = info.getName();
+        if (requireSoftwareCodec && !softwareCodecWhiteList.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));
           continue;
@@ -569,7 +596,8 @@
                 : name;
         FindVideoDecoderResult findVideoDecoderResult =
             new FindVideoDecoderResult(resultName, videoCapabilities, codecCapabilities);
-        if (hdr && !isHdrCapableVp9Decoder(findVideoDecoderResult)) {
+        if (hdr && !isHdrCapableVideoDecoder(mimeType, findVideoDecoderResult)) {
+          Log.v(TAG, String.format("Rejecting %s, reason: codec does not support HDR", name));
           continue;
         }
         Log.v(TAG, String.format("Found suitable decoder, %s", name));
diff --git a/src/starboard/android/apk/build.gradle b/src/starboard/android/apk/build.gradle
index 525c56b..c21ed86 100644
--- a/src/starboard/android/apk/build.gradle
+++ b/src/starboard/android/apk/build.gradle
@@ -20,7 +20,7 @@
         jcenter()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.5.3'
+        classpath 'com.android.tools.build:gradle:3.6.1'
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
diff --git a/src/starboard/android/apk/cobalt-gradle.sh b/src/starboard/android/apk/cobalt-gradle.sh
index d135b4b..156c8f2 100755
--- a/src/starboard/android/apk/cobalt-gradle.sh
+++ b/src/starboard/android/apk/cobalt-gradle.sh
@@ -22,7 +22,6 @@
 while [ "$1" ]; do
   case "$1" in
     --sdk) shift; ANDROID_HOME="$1" ;;
-    --ndk) shift; ANDROID_NDK_HOME="$1" ;;
     --cache) shift; mkdir -p "$1";
              GRADLE_ARGS+=("--project-cache-dir" $(cd "$1"; pwd)) ;;
     --reset) RESET_GRADLE=1 ;;
@@ -34,7 +33,7 @@
 
 # Cleanup Gradle from previous builds. Used as part of the GYP step.
 if [[ "${RESET_GRADLE}" ]]; then
-  echo "Cleaning Gradle deamons and locks."
+  echo "Cleaning Gradle daemons and locks."
   # If there are any lock files, kill any hung processes still waiting on them.
   if compgen -G '/var/lock/cobalt-gradle.lock.*'; then
     lsof -t /var/lock/cobalt-gradle.lock.* | xargs -rt kill
@@ -50,15 +49,22 @@
 fi
 
 export ANDROID_HOME
-export ANDROID_NDK_HOME
 echo "ANDROID_HOME=${ANDROID_HOME}"
-echo "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}"
-echo "TASK: ${GRADLE_ARGS[-1]}"
 
 # Allow parallel gradle builds, as defined by a COBALT_GRADLE_BUILD_COUNT envvar
 # or default to 1 if that's not set (so buildbot only runs 1 gradle at a time).
 BUCKETS=${COBALT_GRADLE_BUILD_COUNT:-1}
+if [ "$BUCKETS"==1 ]; then
+  echo "Gradle daemon and parallel gradle disabled for Cobalt build"
+  GRADLE_ARGS+=(
+    "-Dorg.gradle.parallel=false"
+    "-Dorg.gradle.workers.max=1"
+    "-Dorg.gradle.daemon=false"
+  )
+fi
+
 MD5=$(echo "${GRADLE_ARGS[@]}" | md5sum)
 LOCKNUM=$(( ${BUCKETS} * 0x${MD5:0:6} / 0x1000000 ))
 
+echo "TASK: ${GRADLE_ARGS[-1]}"
 flock /var/lock/cobalt-gradle.lock.${LOCKNUM} $(dirname "$0")/gradlew "${GRADLE_ARGS[@]}"
diff --git a/src/starboard/android/apk/gradle/wrapper/gradle-wrapper.jar b/src/starboard/android/apk/gradle/wrapper/gradle-wrapper.jar
index 13372ae..5c2d1cf 100644
--- a/src/starboard/android/apk/gradle/wrapper/gradle-wrapper.jar
+++ b/src/starboard/android/apk/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/src/starboard/android/apk/gradle/wrapper/gradle-wrapper.properties b/src/starboard/android/apk/gradle/wrapper/gradle-wrapper.properties
index cb820ac..0ebb310 100644
--- a/src/starboard/android/apk/gradle/wrapper/gradle-wrapper.properties
+++ b/src/starboard/android/apk/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Thu Jan 02 11:15:25 PST 2020
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
diff --git a/src/starboard/android/apk/gradlew b/src/starboard/android/apk/gradlew
index 9d82f78..b0d6d0a 100755
--- a/src/starboard/android/apk/gradlew
+++ b/src/starboard/android/apk/gradlew
@@ -1,4 +1,20 @@
-#!/usr/bin/env bash
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# 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.
+#
 
 ##############################################################################
 ##
@@ -6,42 +22,6 @@
 ##
 ##############################################################################
 
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn ( ) {
-    echo "$*"
-}
-
-die ( ) {
-    echo
-    echo "$*"
-    echo
-    exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-case "`uname`" in
-  CYGWIN* )
-    cygwin=true
-    ;;
-  Darwin* )
-    darwin=true
-    ;;
-  MINGW* )
-    msys=true
-    ;;
-esac
-
 # Attempt to set APP_HOME
 # Resolve links: $0 may be a link
 PRG="$0"
@@ -60,6 +40,46 @@
 APP_HOME="`pwd -P`"
 cd "$SAVED" >/dev/null
 
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
 CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
 
 # Determine the Java command to use to start the JVM.
@@ -85,7 +105,7 @@
 fi
 
 # Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
     MAX_FD_LIMIT=`ulimit -H -n`
     if [ $? -eq 0 ] ; then
         if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -150,11 +170,19 @@
     esac
 fi
 
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
-    JVM_OPTS=("$@")
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
 }
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+APP_ARGS=$(save "$@")
 
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/src/starboard/android/apk/gradlew.bat b/src/starboard/android/apk/gradlew.bat
index aec9973..15e1ee3 100644
--- a/src/starboard/android/apk/gradlew.bat
+++ b/src/starboard/android/apk/gradlew.bat
@@ -1,3 +1,19 @@
+@rem

+@rem Copyright 2015 the original author or authors.

+@rem

+@rem Licensed under the Apache License, Version 2.0 (the "License");

+@rem you may not use this file except in compliance with the License.

+@rem You may obtain a copy of the License at

+@rem

+@rem      http://www.apache.org/licenses/LICENSE-2.0

+@rem

+@rem Unless required by applicable law or agreed to in writing, software

+@rem distributed under the License is distributed on an "AS IS" BASIS,

+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+@rem See the License for the specific language governing permissions and

+@rem limitations under the License.

+@rem

+

 @if "%DEBUG%" == "" @echo off

 @rem ##########################################################################

 @rem

@@ -8,14 +24,14 @@
 @rem Set local scope for the variables with windows NT shell

 if "%OS%"=="Windows_NT" setlocal

 

-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

-set DEFAULT_JVM_OPTS=

-

 set DIRNAME=%~dp0

 if "%DIRNAME%" == "" set DIRNAME=.

 set APP_BASE_NAME=%~n0

 set APP_HOME=%DIRNAME%

 

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

+

 @rem Find java.exe

 if defined JAVA_HOME goto findJavaFromJavaHome

 

@@ -46,10 +62,9 @@
 goto fail

 

 :init

-@rem Get command-line arguments, handling Windowz variants

+@rem Get command-line arguments, handling Windows variants

 

 if not "%OS%" == "Windows_NT" goto win9xME_args

-if "%@eval[2+2]" == "4" goto 4NT_args

 

 :win9xME_args

 @rem Slurp the command line arguments.

@@ -60,11 +75,6 @@
 if "x%~1" == "x" goto execute

 

 set CMD_LINE_ARGS=%*

-goto execute

-

-:4NT_args

-@rem Get arguments from the 4NT Shell from JP Software

-set CMD_LINE_ARGS=%$

 

 :execute

 @rem Setup the command line

diff --git a/src/starboard/android/arm/gyp_configuration.gypi b/src/starboard/android/arm/gyp_configuration.gypi
index d3302b1..4a8ef2d 100644
--- a/src/starboard/android/arm/gyp_configuration.gypi
+++ b/src/starboard/android/arm/gyp_configuration.gypi
@@ -14,7 +14,6 @@
 
 {
   'variables': {
-    'target_arch': 'arm',
     'arm_version': 7,
     'armv7': 1,
     'arm_thumb': 0,
diff --git a/src/starboard/android/arm64/gyp_configuration.gypi b/src/starboard/android/arm64/gyp_configuration.gypi
index d865017..1d30080 100644
--- a/src/starboard/android/arm64/gyp_configuration.gypi
+++ b/src/starboard/android/arm64/gyp_configuration.gypi
@@ -14,7 +14,6 @@
 
 {
   'variables': {
-    'target_arch': 'arm64',
     'arm_version': 8,
     'armv7': 0,
     'arm_thumb': 0,
diff --git a/src/starboard/android/arm64/vulkan/gyp_configuration.gypi b/src/starboard/android/arm64/vulkan/gyp_configuration.gypi
index f37376b..54e206a 100644
--- a/src/starboard/android/arm64/vulkan/gyp_configuration.gypi
+++ b/src/starboard/android/arm64/vulkan/gyp_configuration.gypi
@@ -14,12 +14,31 @@
 
 {
   'variables': {
-    'target_arch': 'arm64',
     'arm_version': 8,
     'armv7': 0,
     'arm_thumb': 0,
     'arm_neon': 0,
     'arm_fpu': 'vfpv3-d16',
+
+    'enable_vulkan': 1,
+
+    'platform_libraries': [
+      '-lc++_shared',
+      '-lEGL_angle',
+      '-lGLESv2_angle',
+      '-lGLESv1_CM_angle',
+      '-lfeature_support_angle',
+      '-lOpenSLES',
+      '-landroid',
+      '-llog',
+      '-lmediandk',
+    ],
+
+    'linker_flags': [
+      '-Wl',
+      '--verbose',
+      '-Llib',
+    ],
   },
 
   'target_defaults': {
diff --git a/src/starboard/android/shared/cobalt/configuration.py b/src/starboard/android/shared/cobalt/configuration.py
index 3633045..8fac1a4 100644
--- a/src/starboard/android/shared/cobalt/configuration.py
+++ b/src/starboard/android/shared/cobalt/configuration.py
@@ -60,6 +60,7 @@
         # Disabled because of: Fail (Tests do not account for player with url?).
         ('csp/WebPlatformTest.Run/'
          'content_security_policy_media_src_media_src_allowed_html'),
+        ('websockets/WebPlatformTest.Run/websockets_*'),
     ]
     return filters
 
diff --git a/src/starboard/android/shared/configuration.cc b/src/starboard/android/shared/configuration.cc
new file mode 100644
index 0000000..374d0ed
--- /dev/null
+++ b/src/starboard/android/shared/configuration.cc
@@ -0,0 +1,73 @@
+// Copyright 2020 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/android/shared/configuration.h"
+
+#include "cobalt/extension/configuration.h"
+#include "starboard/common/configuration_defaults.h"
+
+namespace starboard {
+namespace android {
+namespace shared {
+
+namespace {
+
+const char* CobaltUserOnExitStrategy() {
+  return "suspend";
+}
+
+int CobaltEglSwapInterval() {
+  return 0;
+}
+
+bool CobaltEnableJit() {
+  return true;
+}
+
+const CobaltExtensionConfigurationApi kConfigurationApi = {
+    kCobaltExtensionConfigurationName,
+    1,
+    &CobaltUserOnExitStrategy,
+    &common::CobaltRenderDirtyRegionOnlyDefault,
+    &CobaltEglSwapInterval,
+    &common::CobaltFallbackSplashScreenUrlDefault,
+    &common::CobaltEnableQuicDefault,
+    &common::CobaltSkiaCacheSizeInBytesDefault,
+    &common::CobaltOffscreenTargetCacheSizeInBytesDefault,
+    &common::CobaltEncodedImageCacheSizeInBytesDefault,
+    &common::CobaltImageCacheSizeInBytesDefault,
+    &common::CobaltLocalTypefaceCacheSizeInBytesDefault,
+    &common::CobaltRemoteTypefaceCacheSizeInBytesDefault,
+    &common::CobaltMeshCacheSizeInBytesDefault,
+    &common::CobaltSoftwareSurfaceCacheSizeInBytesDefault,
+    &common::CobaltImageCacheCapacityMultiplierWhenPlayingVideoDefault,
+    &common::CobaltSkiaGlyphAtlasWidthDefault,
+    &common::CobaltSkiaGlyphAtlasHeightDefault,
+    &common::CobaltJsGarbageCollectionThresholdInBytesDefault,
+    &common::CobaltReduceCpuMemoryByDefault,
+    &common::CobaltReduceGpuMemoryByDefault,
+    &common::CobaltGcZealDefault,
+    &common::CobaltRasterizerTypeDefault,
+    &CobaltEnableJit,
+};
+
+}  // namespace
+
+const void* GetConfigurationApi() {
+  return &kConfigurationApi;
+}
+
+}  // namespace shared
+}  // namespace android
+}  // namespace starboard
diff --git a/src/starboard/android/shared/configuration.h b/src/starboard/android/shared/configuration.h
new file mode 100644
index 0000000..b6196e4
--- /dev/null
+++ b/src/starboard/android/shared/configuration.h
@@ -0,0 +1,28 @@
+// Copyright 2020 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 STARBOARD_ANDROID_SHARED_CONFIGURATION_H_
+#define STARBOARD_ANDROID_SHARED_CONFIGURATION_H_
+
+namespace starboard {
+namespace android {
+namespace shared {
+
+const void* GetConfigurationApi();
+
+}  // namespace shared
+}  // namespace android
+}  // namespace starboard
+
+#endif  // STARBOARD_ANDROID_SHARED_CONFIGURATION_H_
diff --git a/src/starboard/android/shared/configuration_constants.cc b/src/starboard/android/shared/configuration_constants.cc
index 62e403d..a70e4db 100644
--- a/src/starboard/android/shared/configuration_constants.cc
+++ b/src/starboard/android/shared/configuration_constants.cc
@@ -55,9 +55,6 @@
 // Specifies whether this platform updates audio frames asynchronously.
 const bool kSbHasAsyncAudioFramesReporting = true;
 
-// Allow playing audioless video.
-const bool kSbHasAudiolessVideo = true;
-
 // Specifies whether this platform has webm/vp9 support.  This should be set to
 // non-zero on platforms with webm/vp9 support.
 const bool kSbHasMediaWebmVp9Support = true;
@@ -141,5 +138,10 @@
 // The string form of SB_PATH_SEP_CHAR.
 const char* kSbPathSepString = ":";
 
+// Specifies the preferred byte order of color channels in a pixel. Refer to
+// starboard/configuration.h for the possible values. EGL/GLES platforms should
+// generally prefer a byte order of RGBA, regardless of endianness.
+const int kSbPreferredRgbaByteOrder = SB_PREFERRED_RGBA_BYTE_ORDER_RGBA;
+
 // The maximum number of users that can be signed in at the same time.
 const uint32_t kSbUserMaxSignedIn = 1;
diff --git a/src/starboard/android/shared/configuration_public.h b/src/starboard/android/shared/configuration_public.h
index 27a6b7f..4c1c8e9 100644
--- a/src/starboard/android/shared/configuration_public.h
+++ b/src/starboard/android/shared/configuration_public.h
@@ -159,11 +159,6 @@
 // API. The basic requirement is a scaled, clipped, alpha-blended blit.
 #define SB_HAS_BLITTER 0
 
-// Specifies the preferred byte order of color channels in a pixel. Refer to
-// starboard/configuration.h for the possible values. EGL/GLES platforms should
-// generally prefer a byte order of RGBA, regardless of endianness.
-#define SB_PREFERRED_RGBA_BYTE_ORDER SB_PREFERRED_RGBA_BYTE_ORDER_RGBA
-
 // Indicates whether or not the given platform supports bilinear filtering.
 // This can be checked to enable/disable renderer tests that verify that this is
 // working properly.
diff --git a/src/starboard/android/shared/gyp_configuration.gypi b/src/starboard/android/shared/gyp_configuration.gypi
index d5f23e0..4267214 100644
--- a/src/starboard/android/shared/gyp_configuration.gypi
+++ b/src/starboard/android/shared/gyp_configuration.gypi
@@ -26,6 +26,8 @@
     'gl_type': 'system_gles2',
     'enable_remote_debugging': 0,
 
+    'enable_vulkan%': 0,
+
     'linker_flags': [
       # The NDK default "ld" is actually the gold linker for all architectures
       # except arm64 (aarch64) where it's the bfd linker. Don't use either of
diff --git a/src/starboard/android/shared/media_codec_bridge.cc b/src/starboard/android/shared/media_codec_bridge.cc
index 398f3b5..c3c599d 100644
--- a/src/starboard/android/shared/media_codec_bridge.cc
+++ b/src/starboard/android/shared/media_codec_bridge.cc
@@ -177,7 +177,8 @@
     Handler* handler,
     jobject j_surface,
     jobject j_media_crypto,
-    const SbMediaColorMetadata* color_metadata) {
+    const SbMediaColorMetadata* color_metadata,
+    bool require_software_codec) {
   const char* mime = SupportedVideoCodecToMimeType(video_codec);
   if (!mime) {
     return scoped_ptr<MediaCodecBridge>(NULL);
@@ -221,8 +222,8 @@
       "Ldev/cobalt/media/MediaCodecBridge$ColorInfo;)"
       "Ldev/cobalt/media/MediaCodecBridge;",
       reinterpret_cast<jlong>(native_media_codec_bridge.get()), j_mime.Get(),
-      !!j_media_crypto, false, width, height, j_surface, j_media_crypto,
-      j_color_info.Get());
+      !!j_media_crypto, require_software_codec, width, height, j_surface,
+      j_media_crypto, j_color_info.Get());
 
   if (!j_media_codec_bridge) {
     return scoped_ptr<MediaCodecBridge>(NULL);
@@ -394,7 +395,6 @@
   JniEnvExt* env = JniEnvExt::Get();
   SB_DCHECK(env->GetObjectRefType(j_media_codec_bridge_) == JNIGlobalRefType);
 
-
   j_reused_get_output_format_result_ = env->NewObjectOrAbort(
       "dev/cobalt/media/MediaCodecBridge$GetOutputFormatResult", "()V");
   SB_DCHECK(j_reused_get_output_format_result_);
diff --git a/src/starboard/android/shared/media_codec_bridge.h b/src/starboard/android/shared/media_codec_bridge.h
index 0329f06..0937860 100644
--- a/src/starboard/android/shared/media_codec_bridge.h
+++ b/src/starboard/android/shared/media_codec_bridge.h
@@ -106,7 +106,8 @@
       Handler* handler,
       jobject j_surface,
       jobject j_media_crypto,
-      const SbMediaColorMetadata* color_metadata);
+      const SbMediaColorMetadata* color_metadata,
+      bool require_software_codec);
 
   ~MediaCodecBridge();
 
diff --git a/src/starboard/android/shared/media_decoder.cc b/src/starboard/android/shared/media_decoder.cc
index 24ed8a4..b9ade2d 100644
--- a/src/starboard/android/shared/media_decoder.cc
+++ b/src/starboard/android/shared/media_decoder.cc
@@ -106,7 +106,8 @@
                            int height,
                            jobject j_output_surface,
                            SbDrmSystem drm_system,
-                           const SbMediaColorMetadata* color_metadata)
+                           const SbMediaColorMetadata* color_metadata,
+                           bool require_software_codec)
     : media_type_(kSbMediaTypeVideo),
       host_(host),
       drm_system_(static_cast<DrmSystem*>(drm_system)),
@@ -115,7 +116,7 @@
   SB_DCHECK(!drm_system_ || j_media_crypto);
   media_codec_bridge_ = MediaCodecBridge::CreateVideoMediaCodecBridge(
       video_codec, width, height, this, j_output_surface, j_media_crypto,
-      color_metadata);
+      color_metadata, require_software_codec);
   if (!media_codec_bridge_) {
     SB_LOG(ERROR) << "Failed to create video media codec bridge.";
   }
diff --git a/src/starboard/android/shared/media_decoder.h b/src/starboard/android/shared/media_decoder.h
index ca05791..a474746 100644
--- a/src/starboard/android/shared/media_decoder.h
+++ b/src/starboard/android/shared/media_decoder.h
@@ -77,7 +77,8 @@
                int height,
                jobject j_output_surface,
                SbDrmSystem drm_system,
-               const SbMediaColorMetadata* color_metadata);
+               const SbMediaColorMetadata* color_metadata,
+               bool require_software_codec);
   ~MediaDecoder();
 
   void Initialize(const ErrorCB& error_cb);
diff --git a/src/starboard/android/shared/media_is_audio_supported.cc b/src/starboard/android/shared/media_is_audio_supported.cc
index 4677b00..9952bf2 100644
--- a/src/starboard/android/shared/media_is_audio_supported.cc
+++ b/src/starboard/android/shared/media_is_audio_supported.cc
@@ -24,7 +24,11 @@
 using starboard::android::shared::ScopedLocalJavaRef;
 using starboard::android::shared::SupportedAudioCodecToMimeType;
 
-bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec, int64_t bitrate) {
+bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
+                             const char* content_type,
+                             int64_t bitrate) {
+  SB_UNREFERENCED_PARAMETER(content_type);
+
   // Android now uses libopus based opus decoder.
   if (audio_codec == kSbMediaAudioCodecOpus &&
       bitrate < kSbMediaMaxAudioBitrateInBitsPerSecond) {
diff --git a/src/starboard/android/shared/media_is_video_supported.cc b/src/starboard/android/shared/media_is_video_supported.cc
index 15ff35b..fb9900a 100644
--- a/src/starboard/android/shared/media_is_video_supported.cc
+++ b/src/starboard/android/shared/media_is_video_supported.cc
@@ -33,15 +33,21 @@
 const jint HDR_TYPE_HDR10 = 2;
 const jint HDR_TYPE_HLG = 3;
 
-bool IsHDRTransferCharacteristicsSupported(SbMediaTransferId transfer_id) {
-  SB_DCHECK(transfer_id != kSbMediaTransferIdBt709 &&
-            transfer_id != kSbMediaTransferIdUnspecified);
-  // An HDR capable VP9 decoder is needed to handle HDR at all.
-  bool has_hdr_capable_vp9_decoder =
+bool IsHDRTransferCharacteristicsSupported(SbMediaVideoCodec video_codec,
+                                           SbMediaTransferId transfer_id) {
+  const char* mime = SupportedVideoCodecToMimeType(video_codec);
+  if (!mime) {
+    return false;
+  }
+  JniEnvExt* env = JniEnvExt::Get();
+
+  // An HDR capable VP9 or AV1 decoder is needed to handle HDR at all.
+  bool has_hdr_capable_decoder =
       JniEnvExt::Get()->CallStaticBooleanMethodOrAbort(
-          "dev/cobalt/media/MediaCodecUtil", "hasHdrCapableVp9Decoder",
-          "()Z") == JNI_TRUE;
-  if (!has_hdr_capable_vp9_decoder) {
+          "dev/cobalt/media/MediaCodecUtil", "hasHdrCapableVideoDecoder",
+          "(Ljava/lang/String;)Z",
+          env->NewStringStandardUTFOrAbort(mime)) == JNI_TRUE;
+  if (!has_hdr_capable_decoder) {
     return false;
   }
 
@@ -63,6 +69,7 @@
 }  // namespace
 
 bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
+                             const char* content_type,
                              int profile,
                              int level,
                              int bit_depth,
@@ -75,14 +82,15 @@
                              int fps,
                              bool decode_to_texture_required) {
   if (!IsSDRVideo(bit_depth, primary_id, transfer_id, matrix_id)) {
-    if (!IsHDRTransferCharacteristicsSupported(transfer_id)) {
+    if (!IsHDRTransferCharacteristicsSupported(video_codec, transfer_id)) {
       return false;
     }
   }
-  // While not necessarily true, for now we assume that all Android devices
-  // can play decode-to-texture video just as well as normal video.
+  SB_UNREFERENCED_PARAMETER(content_type);
   SB_UNREFERENCED_PARAMETER(profile);
   SB_UNREFERENCED_PARAMETER(level);
+  // While not necessarily true, for now we assume that all Android devices
+  // can play decode-to-texture video just as well as normal video.
   SB_UNREFERENCED_PARAMETER(decode_to_texture_required);
 
   const char* mime = SupportedVideoCodecToMimeType(video_codec);
diff --git a/src/starboard/android/shared/platform_deploy.gypi b/src/starboard/android/shared/platform_deploy.gypi
index 3d6e60f..d00db17 100644
--- a/src/starboard/android/shared/platform_deploy.gypi
+++ b/src/starboard/android/shared/platform_deploy.gypi
@@ -40,15 +40,15 @@
       'action': [
         '<(DEPTH)/starboard/android/apk/cobalt-gradle.sh',
         '--sdk', '<(ANDROID_HOME)',
-        '--ndk', '<(NDK_HOME)',
-        '--cache', '<(GRADLE_FILES_DIR)/cache',
-        '--project-dir', '<(DEPTH)/starboard/android/apk/',
+        '--cache', '$${PWD}/<(GRADLE_FILES_DIR)/cache',
+        '--project-dir', '$${PWD}/<(DEPTH)/starboard/android/apk/',
         '-P', 'cobaltBuildAbi=<(ANDROID_ABI)',
-        '-P', 'cobaltDeployApk=<(apk)',
-        '-P', 'cobaltContentDir=<(content_deploy_dir)',
-        '-P', 'cobaltGradleDir=<(GRADLE_FILES_DIR)',
-        '-P', 'cobaltProductDir=<(PRODUCT_DIR)',
+        '-P', 'cobaltDeployApk=$${PWD}/<(apk)',
+        '-P', 'cobaltContentDir=$${PWD}/<(content_deploy_dir)',
+        '-P', 'cobaltGradleDir=$${PWD}/<(GRADLE_FILES_DIR)',
+        '-P', 'cobaltProductDir=$${PWD}/<(PRODUCT_DIR)',
         '-P', 'cobaltTarget=<(executable_name)',
+        '-P', 'enableVulkan=<(enable_vulkan)',
         'assembleCobalt_<(GRADLE_BUILD_TYPE)',
       ],
       'message': 'Build APK: <(apk)',
diff --git a/src/starboard/android/shared/player_components_factory.cc b/src/starboard/android/shared/player_components_factory.cc
index 414f317..ebddae0 100644
--- a/src/starboard/android/shared/player_components_factory.cc
+++ b/src/starboard/android/shared/player_components_factory.cc
@@ -55,18 +55,16 @@
 
       auto decoder_creator = [](const SbMediaAudioSampleInfo& audio_sample_info,
                                 SbDrmSystem drm_system) {
-        using AacAudioDecoder = ::starboard::android::shared::AudioDecoder;
-        using OpusAudioDecoder = ::starboard::shared::opus::OpusAudioDecoder;
-
         if (audio_sample_info.codec == kSbMediaAudioCodecAac) {
-          scoped_ptr<AacAudioDecoder> audio_decoder_impl(new AacAudioDecoder(
-              audio_sample_info.codec, audio_sample_info, drm_system));
+          scoped_ptr<android::shared::AudioDecoder> audio_decoder_impl(
+              new android::shared::AudioDecoder(audio_sample_info.codec,
+                                                audio_sample_info, drm_system));
           if (audio_decoder_impl->is_valid()) {
             return audio_decoder_impl.PassAs<AudioDecoder>();
           }
         } else if (audio_sample_info.codec == kSbMediaAudioCodecOpus) {
-          scoped_ptr<OpusAudioDecoder> audio_decoder_impl(
-              new OpusAudioDecoder(audio_sample_info));
+          scoped_ptr<opus::OpusAudioDecoder> audio_decoder_impl(
+              new opus::OpusAudioDecoder(audio_sample_info));
           if (audio_decoder_impl->is_valid()) {
             return audio_decoder_impl.PassAs<AudioDecoder>();
           }
@@ -83,19 +81,18 @@
     }
 
     if (creation_parameters.video_codec() != kSbMediaVideoCodecNone) {
-      using VideoDecoderImpl = ::starboard::android::shared::VideoDecoder;
-      using VideoRenderAlgorithmImpl =
-          ::starboard::android::shared::VideoRenderAlgorithm;
-
       SB_DCHECK(video_decoder);
       SB_DCHECK(video_render_algorithm);
       SB_DCHECK(video_renderer_sink);
       SB_DCHECK(error_message);
 
-      scoped_ptr<VideoDecoderImpl> video_decoder_impl(new VideoDecoderImpl(
-          creation_parameters.video_codec(), creation_parameters.drm_system(),
-          creation_parameters.output_mode(),
-          creation_parameters.decode_target_graphics_context_provider()));
+      scoped_ptr<android::shared::VideoDecoder> video_decoder_impl(
+          new android::shared::VideoDecoder(
+              creation_parameters.video_codec(),
+              creation_parameters.drm_system(),
+              creation_parameters.output_mode(),
+              creation_parameters.decode_target_graphics_context_provider(),
+              creation_parameters.max_video_capabilities()));
       if (video_decoder_impl->is_valid()) {
         *video_renderer_sink = video_decoder_impl->GetSink();
         video_decoder->reset(video_decoder_impl.release());
@@ -106,7 +103,7 @@
         return false;
       }
 
-      video_render_algorithm->reset(new VideoRenderAlgorithmImpl);
+      video_render_algorithm->reset(new android::shared::VideoRenderAlgorithm);
     }
 
     return true;
diff --git a/src/starboard/android/shared/player_create.cc b/src/starboard/android/shared/player_create.cc
index d832483..2b3e80c 100644
--- a/src/starboard/android/shared/player_create.cc
+++ b/src/starboard/android/shared/player_create.cc
@@ -15,12 +15,15 @@
 #include "starboard/player.h"
 
 #include "starboard/android/shared/cobalt/android_media_session_client.h"
+#include "starboard/android/shared/video_decoder.h"
+#include "starboard/android/shared/video_window.h"
 #include "starboard/common/log.h"
 #include "starboard/configuration.h"
 #include "starboard/decode_target.h"
 #include "starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 #include "starboard/shared/starboard/player/player_worker.h"
+#include "starboard/string.h"
 
 using starboard::shared::starboard::player::filter::
     FilterBasedPlayerWorkerHandler;
@@ -28,6 +31,7 @@
 using starboard::android::shared::cobalt::kPlaying;
 using starboard::android::shared::cobalt::
     UpdateActiveSessionPlatformPlaybackState;
+using starboard::android::shared::VideoDecoder;
 
 SbPlayer SbPlayerCreate(SbWindow window,
                         const SbPlayerCreationParam* creation_param,
@@ -76,9 +80,7 @@
                << "\".";
 
   if (!sample_deallocate_func || !decoder_status_func || !player_status_func
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
       || !player_error_func
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
       ) {
     return kSbPlayerInvalid;
   }
@@ -115,13 +117,31 @@
     return kSbPlayerInvalid;
   }
 
-  // TODO: increase this once we support multiple video windows.
-  const int kMaxNumberOfPlayers = 1;
-  if (SbPlayerPrivate::number_of_players() >= kMaxNumberOfPlayers) {
-    return kSbPlayerInvalid;
+  if (SbStringGetLength(max_video_capabilities) == 0) {
+    // Check the availability of hardware video decoder. Main player must use a
+    // hardware codec, but Android doesn't support multiple concurrent hardware
+    // codecs. Since it's not safe to have multiple hardware codecs, we only
+    // support one main player on Android, which can be either in punch out mode
+    // or decode to target mode.
+    const int kMaxNumberOfHardwareDecoders = 1;
+    if (VideoDecoder::number_of_hardware_decoders() >=
+        kMaxNumberOfHardwareDecoders) {
+      return kSbPlayerInvalid;
+    }
+    // Only update session state for main player.
+    UpdateActiveSessionPlatformPlaybackState(kPlaying);
   }
 
-  UpdateActiveSessionPlatformPlaybackState(kPlaying);
+  if (creation_param->output_mode != kSbPlayerOutputModeDecodeToTexture) {
+    // Check the availability of the video window. As we only support one main
+    // player, and sub players are in decode to texture mode on Android, a
+    // single video window should be enough.
+    if (!starboard::android::shared::VideoSurfaceHolder::
+            IsVideoSurfaceAvailable()) {
+      SB_LOG(ERROR) << "Video surface is not available now.";
+      return kSbPlayerInvalid;
+    }
+  }
 
   starboard::scoped_ptr<PlayerWorker::Handler> handler(
       new FilterBasedPlayerWorkerHandler(creation_param, provider));
@@ -130,10 +150,12 @@
       sample_deallocate_func, decoder_status_func, player_status_func,
       player_error_func, context, handler.Pass());
 
-  // TODO: accomplish this through more direct means.
-  // Set the bounds to initialize the VideoSurfaceView. The initial values don't
-  // matter.
-  SbPlayerSetBounds(player, 0, 0, 0, 0, 0);
+  if (creation_param->output_mode != kSbPlayerOutputModeDecodeToTexture) {
+    // TODO: accomplish this through more direct means.
+    // Set the bounds to initialize the VideoSurfaceView. The initial values
+    // don't matter.
+    SbPlayerSetBounds(player, 0, 0, 0, 0, 0);
+  }
 
   return player;
 }
diff --git a/src/starboard/android/shared/player_get_preferred_output_mode.cc b/src/starboard/android/shared/player_get_preferred_output_mode.cc
new file mode 100644
index 0000000..70c29cf
--- /dev/null
+++ b/src/starboard/android/shared/player_get_preferred_output_mode.cc
@@ -0,0 +1,92 @@
+// Copyright 2020 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/player.h"
+
+#include <algorithm>
+
+#include "starboard/configuration.h"
+#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
+#include "starboard/string.h"
+
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
+SbPlayerOutputMode SbPlayerGetPreferredOutputMode(
+    const SbPlayerCreationParam* creation_param) {
+  using starboard::shared::starboard::player::filter::VideoDecoder;
+
+  if (!creation_param) {
+    SB_LOG(ERROR) << "creation_param cannot be NULL";
+    return kSbPlayerOutputModeInvalid;
+  }
+
+  if (creation_param->audio_sample_info.codec != kSbMediaAudioCodecNone &&
+      !creation_param->audio_sample_info.mime) {
+    SB_LOG(ERROR) << "creation_param->audio_sample_info.mime cannot be NULL";
+    return kSbPlayerOutputModeInvalid;
+  }
+
+  if (creation_param->video_sample_info.codec != kSbMediaVideoCodecNone &&
+      !creation_param->video_sample_info.mime) {
+    SB_LOG(ERROR) << "creation_param->video_sample_info.mime cannot be NULL";
+    return kSbPlayerOutputModeInvalid;
+  }
+
+  if (creation_param->video_sample_info.codec != kSbMediaVideoCodecNone &&
+      !creation_param->video_sample_info.max_video_capabilities) {
+    SB_LOG(ERROR) << "creation_param->video_sample_info.max_video_capabilities"
+                  << " cannot be NULL";
+    return kSbPlayerOutputModeInvalid;
+  }
+
+  auto codec = creation_param->video_sample_info.codec;
+  auto drm_system = creation_param->drm_system;
+  auto max_video_capabilities =
+      creation_param->video_sample_info.max_video_capabilities;
+
+  // Sub players must use decode-to-texture on Android.
+  if (max_video_capabilities && SbStringGetLength(max_video_capabilities) > 0) {
+    if (VideoDecoder::OutputModeSupported(kSbPlayerOutputModeDecodeToTexture,
+                                          codec, drm_system)) {
+      return kSbPlayerOutputModeDecodeToTexture;
+    }
+    SB_NOTREACHED();
+    return kSbPlayerOutputModeInvalid;
+  }
+
+  // The main player may use any output mode.
+  SbPlayerOutputMode output_modes_to_check[] = {
+      kSbPlayerOutputModePunchOut, kSbPlayerOutputModeDecodeToTexture,
+  };
+
+  // Check |kSbPlayerOutputModeDecodeToTexture| first if the caller prefers it.
+  if (creation_param->output_mode == kSbPlayerOutputModeDecodeToTexture) {
+    std::swap(output_modes_to_check[0], output_modes_to_check[1]);
+  }
+
+  if (VideoDecoder::OutputModeSupported(output_modes_to_check[0], codec,
+                                        drm_system)) {
+    return output_modes_to_check[0];
+  }
+
+  if (VideoDecoder::OutputModeSupported(output_modes_to_check[1], codec,
+                                        drm_system)) {
+    return output_modes_to_check[1];
+  }
+
+  SB_NOTREACHED();
+  return kSbPlayerOutputModeInvalid;
+}
+
+#endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
diff --git a/src/starboard/android/shared/sdk_utils.py b/src/starboard/android/shared/sdk_utils.py
index 3457efb..dee8241d 100644
--- a/src/starboard/android/shared/sdk_utils.py
+++ b/src/starboard/android/shared/sdk_utils.py
@@ -13,14 +13,11 @@
 # limitations under the License.
 """Utilities to use the toolchain from the Android NDK."""
 
-import ConfigParser
 import fcntl
-import hashlib
 import logging
 import os
 import re
 import shutil
-import StringIO
 import subprocess
 import sys
 import time
@@ -29,20 +26,26 @@
 
 from starboard.tools import build
 
+# Which version of the Android NDK and CMake to install and build with.
+# Note that build.gradle parses these out of this file too.
+_NDK_VERSION = '21.0.6113669'
+_CMAKE_VERSION = '3.10.2.4988404'
+
 # Packages to install in the Android SDK.
 # We download ndk-bundle separately, so it's not in this list.
 # Get available packages from "sdkmanager --list --verbose"
 _ANDROID_SDK_PACKAGES = [
     'build-tools;29.0.2',
-    'cmake;3.10.2.4988404',
+    'cmake;' + _CMAKE_VERSION,
+    'cmdline-tools;1.0',
     'emulator',
     'extras;android;m2repository',
     'extras;google;m2repository',
     'lldb;3.1',
+    'ndk;' + _NDK_VERSION,
     'patcher;v4',
     'platforms;android-29',
     'platform-tools',
-    'tools',
 ]
 
 # Seconds to sleep before writing "y" for android sdk update license prompt.
@@ -50,7 +53,7 @@
 
 # Location from which to download the SDK command-line tools
 # see https://developer.android.com/studio/index.html#command-tools
-_SDK_URL = 'https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip'
+_SDK_URL = 'https://dl.google.com/android/repository/commandlinetools-linux-6200805_latest.zip'
 
 # Location from which to download the Android NDK.
 # see https://developer.android.com/ndk/downloads (perhaps in "NDK archives")
@@ -70,44 +73,10 @@
 else:
   _SDK_PATH = _STARBOARD_TOOLCHAINS_SDK_DIR
 
-_ANDROID_NDK_HOME = os.environ.get('ANDROID_NDK_HOME')
-if _ANDROID_NDK_HOME:
-  _NDK_PATH = _ANDROID_NDK_HOME
-else:
-  _NDK_PATH = os.path.join(_SDK_PATH, 'ndk-bundle')
+_NDK_PATH = os.path.join(_SDK_PATH, 'ndk', _NDK_VERSION)
 
-_SDKMANAGER_TOOL = os.path.join(_SDK_PATH, 'tools', 'bin', 'sdkmanager')
-
-# Maps the Android ABI to the architecture name of the toolchain.
-_TOOLS_ABI_ARCH_MAP = {
-    'x86': 'x86',
-    'armeabi': 'arm',
-    'armeabi-v7a': 'arm',
-    'arm64-v8a': 'arm64',
-}
-
-_SCRIPT_HASH_PROPERTY = 'SdkUtils.Hash'
-
-with open(__file__, 'rb') as script:
-  _SCRIPT_HASH = hashlib.md5(script.read()).hexdigest()
-
-
-def _CheckStamp(dir_path):
-  """Checks that the specified directory is up-to-date with the NDK."""
-  stamp_path = os.path.join(dir_path, 'ndk.stamp')
-  return (os.path.exists(stamp_path) and
-          _ReadNdkRevision(stamp_path) == _GetInstalledNdkRevision() and
-          _ReadProperty(stamp_path, _SCRIPT_HASH_PROPERTY) == _SCRIPT_HASH)
-
-
-def _UpdateStamp(dir_path):
-  """Updates the stamp file in the specified directory to the NDK revision."""
-  path = GetNdkPath()
-  properties_path = os.path.join(path, 'source.properties')
-  stamp_path = os.path.join(dir_path, 'ndk.stamp')
-  shutil.copyfile(properties_path, stamp_path)
-  with open(stamp_path, 'a') as stamp:
-    stamp.write('{} = {}\n'.format(_SCRIPT_HASH_PROPERTY, _SCRIPT_HASH))
+_SDKMANAGER_TOOL = os.path.join(_SDK_PATH, 'cmdline-tools', '1.0', 'bin',
+                                'sdkmanager')
 
 
 def GetNdkPath():
@@ -118,32 +87,6 @@
   return _SDK_PATH
 
 
-def _ReadNdkRevision(properties_path):
-  return _ReadProperty(properties_path, 'pkg.revision')
-
-
-def _ReadProperty(properties_path, property_key):
-  with open(properties_path, 'r') as f:
-    ini_str = '[properties]\n' + f.read()
-  config = ConfigParser.RawConfigParser()
-  config.readfp(StringIO.StringIO(ini_str))
-  try:
-    return config.get('properties', property_key)
-  except ConfigParser.NoOptionError:
-    return None
-
-
-def _GetInstalledNdkRevision():
-  """Returns the installed NDK's revision."""
-  path = GetNdkPath()
-  properties_path = os.path.join(path, 'source.properties')
-  try:
-    return _ReadNdkRevision(properties_path)
-  except IOError:
-    logging.error("Error: Can't read NDK properties in %s", properties_path)
-    sys.exit(1)
-
-
 def _DownloadAndUnzipFile(url, destination_path):
   dl_file, dummy_headers = urllib.urlretrieve(url)
   _UnzipFile(dl_file, destination_path)
@@ -165,49 +108,34 @@
     toolchains_dir_fd = os.open(_STARBOARD_TOOLCHAINS_DIR, os.O_RDONLY)
     fcntl.flock(toolchains_dir_fd, fcntl.LOCK_EX)
 
+    if os.environ.get('ANDROID_NDK_HOME'):
+      logging.warning('Warning: ANDROID_NDK_HOME is deprecated and ignored.')
+
     if _ANDROID_HOME:
       if not os.access(_SDKMANAGER_TOOL, os.X_OK):
-        logging.error('Error: ANDROID_HOME is set but SDK is not present!')
+        logging.error('Error: ANDROID_HOME is set but SDK is not present.')
         sys.exit(1)
       logging.warning('Warning: Using Android SDK in ANDROID_HOME,'
                       ' which is not automatically updated.\n'
                       '         The following package versions are installed:')
       installed_packages = _GetInstalledSdkPackages()
       for package in _ANDROID_SDK_PACKAGES:
-        version = installed_packages.get(package, '< MISSING! >')
+        version = installed_packages.get(package, '< NOT INSTALLED >')
         msg = '  {:30} : {}'.format(package, version)
         logging.warning(msg)
-    else:
-      logging.warning('Checking Android SDK.')
-      _DownloadInstallOrUpdateSdk()
 
-    ndk_path = GetNdkPath()
-    if _ANDROID_NDK_HOME:
-      logging.warning('Warning: ANDROID_NDK_HOME references NDK %s in %s,'
-                      ' which is not automatically updated.',
-                      _GetInstalledNdkRevision(), ndk_path)
+      if not os.path.exists(GetNdkPath()):
+        logging.error('Error: ANDROID_HOME is is missing NDK %s.',
+                      _NDK_VERSION)
+        sys.exit(1)
 
-    if _ANDROID_HOME or _ANDROID_NDK_HOME:
       reply = raw_input(
           'Do you want to continue using your custom Android tools? [y/N]')
       if reply.upper() != 'Y':
         sys.exit(1)
-    elif not _CheckStamp(ndk_path):
-      logging.warning('Downloading NDK from %s to %s', _NDK_URL, ndk_path)
-      if os.path.exists(ndk_path):
-        shutil.rmtree(ndk_path)
-      # Download the NDK into _STARBOARD_TOOLCHAINS_DIR and move the top
-      # _NDK_ZIP_REVISION directory that is in the zip to 'ndk-bundle'.
-      ndk_unzip_path = os.path.join(_STARBOARD_TOOLCHAINS_DIR,
-                                    _NDK_ZIP_REVISION)
-      if os.path.exists(ndk_unzip_path):
-        shutil.rmtree(ndk_unzip_path)
-      _DownloadAndUnzipFile(_NDK_URL, _STARBOARD_TOOLCHAINS_DIR)
-      # Move NDK into its proper final place.
-      os.rename(ndk_unzip_path, ndk_path)
-      _UpdateStamp(ndk_path)
-
-    logging.warning('Using Android NDK version %s', _GetInstalledNdkRevision())
+    else:
+      logging.warning('Checking Android SDK.')
+      _DownloadInstallOrUpdateSdk()
   finally:
     fcntl.flock(toolchains_dir_fd, fcntl.LOCK_UN)
     os.close(toolchains_dir_fd)
@@ -242,7 +170,8 @@
   new_style = False
   old_style = False
   for line in iter(p.stdout.readline, ''):
-
+    # Throw away loading progress indicators up to the last CR.
+    line = line.split('\r')[-1]
     if section_re.match(line):
       if new_style or old_style:
         # We left the new/old style installed packages section
@@ -292,6 +221,14 @@
     if os.path.exists(_STARBOARD_TOOLCHAINS_SDK_DIR):
       shutil.rmtree(_STARBOARD_TOOLCHAINS_SDK_DIR)
     _DownloadAndUnzipFile(_SDK_URL, _STARBOARD_TOOLCHAINS_SDK_DIR)
+    # TODO: Remove this workaround for sdkmanager incorrectly picking up the
+    # "tools" directory from the ZIP as the name of its component.
+    if not os.access(_SDKMANAGER_TOOL, os.X_OK):
+      old_tools_dir = os.path.join(_STARBOARD_TOOLCHAINS_SDK_DIR, 'tools')
+      new_tools_dir = os.path.join(_STARBOARD_TOOLCHAINS_SDK_DIR,
+                                   'cmdline-tools', '1.0')
+      os.mkdir(os.path.dirname(new_tools_dir))
+      os.rename(old_tools_dir, new_tools_dir)
     if not os.access(_SDKMANAGER_TOOL, os.X_OK):
       logging.error('SDK download failed.')
       sys.exit(1)
diff --git a/src/starboard/android/shared/starboard_platform.gypi b/src/starboard/android/shared/starboard_platform.gypi
index 23150de..aa9fe4e 100644
--- a/src/starboard/android/shared/starboard_platform.gypi
+++ b/src/starboard/android/shared/starboard_platform.gypi
@@ -77,6 +77,8 @@
         'bionic/net_if.cpp',
         'bionic/private/bionic_macros.h',
         'bionic/private/ErrnoRestorer.h',
+        'configuration.cc',
+        'configuration.h',
         'configuration_public.h',
         'configuration_constants.cc',
         'decode_target_create.cc',
@@ -136,6 +138,7 @@
         'microphone_impl.cc',
         'player_create.cc',
         'player_destroy.cc',
+        'player_get_preferred_output_mode.cc',
         'player_set_bounds.cc',
         'player_set_playback_rate.cc',
         'sanitizer_options.cc',
@@ -404,19 +407,15 @@
         '<(DEPTH)/starboard/shared/starboard/player/job_thread.cc',
         '<(DEPTH)/starboard/shared/starboard/player/job_thread.h',
         '<(DEPTH)/starboard/shared/starboard/player/player_get_current_frame.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_get_info2.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_get_maximum_number_of_samples_per_write.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_seek2.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_worker.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_worker.h',
         '<(DEPTH)/starboard/shared/starboard/player/player_write_end_of_stream.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_write_sample.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_write_sample2.cc',
         '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
         '<(DEPTH)/starboard/shared/starboard/queue_application.h',
diff --git a/src/starboard/android/shared/system_get_extensions.cc b/src/starboard/android/shared/system_get_extensions.cc
index 262d04c..869446b 100644
--- a/src/starboard/android/shared/system_get_extensions.cc
+++ b/src/starboard/android/shared/system_get_extensions.cc
@@ -14,7 +14,9 @@
 
 #include "starboard/system.h"
 
+#include "cobalt/extension/configuration.h"
 #include "cobalt/extension/platform_service.h"
+#include "starboard/android/shared/configuration.h"
 #include "starboard/android/shared/platform_service.h"
 #include "starboard/common/log.h"
 #include "starboard/common/string.h"
@@ -23,5 +25,8 @@
   if (SbStringCompareAll(name, kCobaltExtensionPlatformServiceName) == 0) {
     return starboard::android::shared::GetPlatformServiceApi();
   }
+  if (SbStringCompareAll(name, kCobaltExtensionConfigurationName) == 0) {
+    return starboard::android::shared::GetConfigurationApi();
+  }
   return NULL;
 }
diff --git a/src/starboard/android/shared/video_decoder.cc b/src/starboard/android/shared/video_decoder.cc
index b3d542b..1a32e22 100644
--- a/src/starboard/android/shared/video_decoder.cc
+++ b/src/starboard/android/shared/video_decoder.cc
@@ -32,6 +32,7 @@
 #include "starboard/drm.h"
 #include "starboard/memory.h"
 #include "starboard/shared/starboard/player/filter/video_frame_internal.h"
+#include "starboard/string.h"
 #include "starboard/thread.h"
 
 namespace starboard {
@@ -113,6 +114,8 @@
 
 }  // namespace
 
+int VideoDecoder::number_of_hardware_decoders_ = 0;
+
 class VideoDecoder::Sink : public VideoDecoder::VideoRendererSink {
  public:
   bool Render() {
@@ -157,14 +160,24 @@
                            SbDrmSystem drm_system,
                            SbPlayerOutputMode output_mode,
                            SbDecodeTargetGraphicsContextProvider*
-                               decode_target_graphics_context_provider)
+                               decode_target_graphics_context_provider,
+                           const char* max_video_capabilities)
     : video_codec_(video_codec),
       drm_system_(static_cast<DrmSystem*>(drm_system)),
       output_mode_(output_mode),
       decode_target_graphics_context_provider_(
           decode_target_graphics_context_provider),
       has_new_texture_available_(false),
-      surface_condition_variable_(surface_destroy_mutex_) {
+      surface_condition_variable_(surface_destroy_mutex_),
+      require_software_codec_(max_video_capabilities &&
+                              SbStringGetLength(max_video_capabilities) > 0) {
+  if (require_software_codec_) {
+    SB_DCHECK(output_mode_ == kSbPlayerOutputModeDecodeToTexture);
+  }
+  if (!require_software_codec_) {
+    number_of_hardware_decoders_++;
+  }
+
   if (!InitializeCodec()) {
     SB_LOG(ERROR) << "Failed to initialize video decoder.";
     TeardownCodec();
@@ -174,6 +187,10 @@
 VideoDecoder::~VideoDecoder() {
   TeardownCodec();
   ClearVideoWindow();
+
+  if (!require_software_codec_) {
+    number_of_hardware_decoders_--;
+  }
 }
 
 scoped_refptr<VideoDecoder::VideoRendererSink> VideoDecoder::GetSink() {
@@ -344,7 +361,7 @@
   SB_DCHECK(!drm_system_ || j_media_crypto);
   media_decoder_.reset(new MediaDecoder(
       this, video_codec_, width, height, j_output_surface, drm_system_,
-      color_metadata_ ? &*color_metadata_ : nullptr));
+      color_metadata_ ? &*color_metadata_ : nullptr, require_software_codec_));
   if (media_decoder_->is_valid()) {
     if (error_cb_) {
       media_decoder_->Initialize(error_cb_);
diff --git a/src/starboard/android/shared/video_decoder.h b/src/starboard/android/shared/video_decoder.h
index 18f3730..46b260b 100644
--- a/src/starboard/android/shared/video_decoder.h
+++ b/src/starboard/android/shared/video_decoder.h
@@ -15,7 +15,9 @@
 #ifndef STARBOARD_ANDROID_SHARED_VIDEO_DECODER_H_
 #define STARBOARD_ANDROID_SHARED_VIDEO_DECODER_H_
 
+#include <atomic>
 #include <deque>
+#include <string>
 
 #include "starboard/android/shared/drm_system.h"
 #include "starboard/android/shared/media_codec_bridge.h"
@@ -49,11 +51,16 @@
 
   class Sink;
 
+  static int number_of_hardware_decoders() {
+    return number_of_hardware_decoders_;
+  }
+
   VideoDecoder(SbMediaVideoCodec video_codec,
                SbDrmSystem drm_system,
                SbPlayerOutputMode output_mode,
                SbDecodeTargetGraphicsContextProvider*
-                   decode_target_graphics_context_provider);
+                   decode_target_graphics_context_provider,
+               const char* max_video_capabilities);
   ~VideoDecoder() override;
 
   scoped_refptr<VideoRendererSink> GetSink();
@@ -93,17 +100,22 @@
 
   void OnSurfaceDestroyed() override;
 
+  static int number_of_hardware_decoders_;
+
   // These variables will be initialized inside ctor or Initialize() and will
   // not be changed during the life time of this class.
   const SbMediaVideoCodec video_codec_;
   DecoderStatusCB decoder_status_cb_;
   ErrorCB error_cb_;
   DrmSystem* drm_system_;
-
-  SbPlayerOutputMode output_mode_;
-
+  const SbPlayerOutputMode output_mode_;
   SbDecodeTargetGraphicsContextProvider*
       decode_target_graphics_context_provider_;
+  // Android doesn't offically support multi concurrent codecs. But the device
+  // usually has at least one hardware decoder and Google's software decoders.
+  // Google's software decoders can work concurrently. So, we use HW decoder for
+  // the main player and SW decoder for sub players.
+  const bool require_software_codec_;
 
   // If decode-to-texture is enabled, then we store the decode target texture
   // inside of this |decode_target_| member.
diff --git a/src/starboard/android/shared/video_window.cc b/src/starboard/android/shared/video_window.cc
index 2fd56da..2216d08 100644
--- a/src/starboard/android/shared/video_window.cc
+++ b/src/starboard/android/shared/video_window.cc
@@ -68,6 +68,15 @@
   }
 }
 
+// static
+bool VideoSurfaceHolder::IsVideoSurfaceAvailable() {
+  // We only consider video surface is available when there is a video
+  // surface and it is not held by any decoder, i.e.
+  // g_video_surface_holder is NULL.
+  ScopedLock lock(*GetViewSurfaceMutex());
+  return !g_video_surface_holder && g_j_video_surface;
+}
+
 jobject VideoSurfaceHolder::AcquireVideoSurface() {
   ScopedLock lock(*GetViewSurfaceMutex());
   SB_DCHECK(g_video_surface_holder == NULL);
diff --git a/src/starboard/android/shared/video_window.h b/src/starboard/android/shared/video_window.h
index 908b567..17db407 100644
--- a/src/starboard/android/shared/video_window.h
+++ b/src/starboard/android/shared/video_window.h
@@ -24,6 +24,9 @@
 
 class VideoSurfaceHolder {
  public:
+  // Return true only if the video surface is available.
+  static bool IsVideoSurfaceAvailable();
+
   // OnSurfaceDestroyed() will be invoked when surface is destroyed. When this
   // function is called, the decoder no longer owns the surface. Calling
   // AcquireVideoSurface(), ReleaseVideoSurface(), GetVideoWindowSize() or
diff --git a/src/starboard/android/x86/gyp_configuration.gypi b/src/starboard/android/x86/gyp_configuration.gypi
index 3d239cf..a339ae6 100644
--- a/src/starboard/android/x86/gyp_configuration.gypi
+++ b/src/starboard/android/x86/gyp_configuration.gypi
@@ -14,7 +14,6 @@
 
 {
   'variables': {
-    'target_arch': 'x86',
     # Android x86 does not support the instruction set required by SSE4, while
     # Opus still tries to build all SSE code in for run time selection, causing
     # build errors. Exclude all SSE-related code.
diff --git a/src/starboard/atomic.h b/src/starboard/atomic.h
index b09a68c..81b897a 100644
--- a/src/starboard/atomic.h
+++ b/src/starboard/atomic.h
@@ -30,9 +30,7 @@
 extern "C" {
 #endif
 
-#if SB_API_VERSION >= 10
 typedef int8_t SbAtomic8;
-#endif
 typedef int32_t SbAtomic32;
 
 // Atomically execute:
@@ -88,14 +86,12 @@
 static SbAtomic32 SbAtomicAcquire_Load(volatile const SbAtomic32* ptr);
 static SbAtomic32 SbAtomicRelease_Load(volatile const SbAtomic32* ptr);
 
-#if SB_API_VERSION >= 10
 // Overloaded functions for Atomic8.
 static SbAtomic8 SbAtomicRelease_CompareAndSwap8(volatile SbAtomic8* ptr,
                                                  SbAtomic8 old_value,
                                                  SbAtomic8 new_value);
 static void SbAtomicNoBarrier_Store8(volatile SbAtomic8* ptr, SbAtomic8 value);
 static SbAtomic8 SbAtomicNoBarrier_Load8(volatile const SbAtomic8* ptr);
-#endif
 
 // 64-bit atomic operations (only available on 64-bit processors).
 #if SB_HAS(64_BIT_ATOMICS)
@@ -262,7 +258,6 @@
 namespace starboard {
 namespace atomic {
 
-#if SB_API_VERSION >= 10
 inline SbAtomic8 Release_CompareAndSwap(volatile SbAtomic8* ptr,
                                         SbAtomic8 old_value,
                                         SbAtomic8 new_value) {
@@ -276,7 +271,6 @@
 inline SbAtomic8 NoBarrier_Load(volatile const SbAtomic8* ptr) {
   return SbAtomicNoBarrier_Load8(ptr);
 }
-#endif
 
 inline SbAtomic32 NoBarrier_CompareAndSwap(volatile SbAtomic32* ptr,
                                            SbAtomic32 old_value,
diff --git a/src/starboard/build/base_configuration.gypi b/src/starboard/build/base_configuration.gypi
index 068c3aa..0da4c16 100644
--- a/src/starboard/build/base_configuration.gypi
+++ b/src/starboard/build/base_configuration.gypi
@@ -105,7 +105,8 @@
     # The source of EGL and GLES headers and libraries.
     # Valid values (case and everything sensitive!):
     #   'none'   - No EGL + GLES implementation is available on this platform.
-    #   'system_gles3' - Use the system implementation of EGL + GLES3. The
+    #   'system_gles3' - Deprecated. Use system_gles2 instead.
+    #                    Use the system implementation of EGL + GLES3. The
     #                    headers and libraries must be on the system include and
     #                    link paths.
     #   'system_gles2' - Use the system implementation of EGL + GLES2. The
@@ -208,7 +209,7 @@
       ['host_os=="linux"', {
         'conditions': [
           ['target_arch=="arm" or target_arch=="x86" or target_arch=="mips" or \
-	    target_arch=="mipsel" or target_arch=="ppc"', {
+            target_arch=="mipsel" or target_arch=="ppc"', {
             # All the 32 bit CPU architectures v8 supports.
             'compiler_flags_cc_host%': [
               '-m32',
diff --git a/src/starboard/build/config/base.gni b/src/starboard/build/config/base.gni
index 41e0b08..77bfda2 100644
--- a/src/starboard/build/config/base.gni
+++ b/src/starboard/build/config/base.gni
@@ -36,7 +36,8 @@
 # The source of EGL and GLES headers and libraries.
 # Valid values (case and everything sensitive!):
 #   "none"   - No EGL + GLES implementation is available on this platform.
-#   "system_gles3" - Use the system implementation of EGL + GLES3. The
+#   "system_gles3" - Deprecated. Use system_gles2 instead.
+#                    Use the system implementation of EGL + GLES3. The
 #                    headers and libraries must be on the system include and
 #                    link paths.
 #   "system_gles2" - Use the system implementation of EGL + GLES2. The
diff --git a/src/starboard/build/platform_configuration.py b/src/starboard/build/platform_configuration.py
index 80f7305..2d53f61 100644
--- a/src/starboard/build/platform_configuration.py
+++ b/src/starboard/build/platform_configuration.py
@@ -256,7 +256,6 @@
         # V8 are supported.  Note that V8 can only be used on platforms that
         # support JIT.
         'javascript_engine': 'v8',
-
         'sabi_json_path': self.GetPathToSabiJsonFile(),
 
         # TODO: Remove these compatibility variables.
@@ -352,8 +351,11 @@
       A list of strings of test target names.
     """
     tests = [
+        'elf_loader_test',
+        'installation_manager_test',
         'nplb',
         'nplb_blitter_pixel_tests',
+        'nplb_evergreen_compat_tests',
         'player_filter_tests',
         'starboard_platform_tests',
     ]
diff --git a/src/starboard/common/README.md b/src/starboard/common/README.md
new file mode 100644
index 0000000..61772a4
--- /dev/null
+++ b/src/starboard/common/README.md
@@ -0,0 +1,9 @@
+# Starboard Common Library
+Shared library code of C++ helper classes on top of the C Starboard APIs.
+A good example is the `Socket` class built on top of the `SbSocket` API. The
+class provides RAII-style mechanism to manage resources.
+
+## Intended Use
+The library is intended to be used by both Starboard and Cobalt layers.
+
+
diff --git a/src/starboard/common/common.gyp b/src/starboard/common/common.gyp
index 3195314..3b3c0b8 100644
--- a/src/starboard/common/common.gyp
+++ b/src/starboard/common/common.gyp
@@ -28,6 +28,8 @@
         'common.cc',
         'condition_variable.cc',
         'condition_variable.h',
+        'configuration_defaults.cc',
+        'configuration_defaults.h',
         'flat_map.h',
         'locked_ptr.h',
         'log.cc',
diff --git a/src/starboard/common/configuration_defaults.cc b/src/starboard/common/configuration_defaults.cc
new file mode 100644
index 0000000..6dbeb7b
--- /dev/null
+++ b/src/starboard/common/configuration_defaults.cc
@@ -0,0 +1,109 @@
+// Copyright 2020 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/common/configuration_defaults.h"
+
+namespace starboard {
+namespace common {
+
+const char* CobaltUserOnExitStrategyDefault() {
+  return "stop";
+}
+
+bool CobaltRenderDirtyRegionOnlyDefault() {
+  return false;
+}
+
+int CobaltEglSwapIntervalDefault() {
+  return 1;
+}
+
+const char* CobaltFallbackSplashScreenUrlDefault() {
+  return "none";
+}
+
+bool CobaltEnableQuicDefault() {
+  return true;
+}
+
+int CobaltSkiaCacheSizeInBytesDefault() {
+  return 4 * 1024 * 1024;
+}
+
+int CobaltOffscreenTargetCacheSizeInBytesDefault() {
+  return -1;
+}
+
+int CobaltEncodedImageCacheSizeInBytesDefault() {
+  return 1024 * 1024;
+}
+
+int CobaltImageCacheSizeInBytesDefault() {
+  return -1;
+}
+
+int CobaltLocalTypefaceCacheSizeInBytesDefault() {
+  return 16 * 1024 * 1024;
+}
+
+int CobaltRemoteTypefaceCacheSizeInBytesDefault() {
+  return 4 * 1024 * 1024;
+}
+
+int CobaltMeshCacheSizeInBytesDefault() {
+  return 1024 * 1024;
+}
+
+int CobaltSoftwareSurfaceCacheSizeInBytesDefault() {
+  return 8 * 1024 * 1024;
+}
+
+float CobaltImageCacheCapacityMultiplierWhenPlayingVideoDefault() {
+  return 1.0f;
+}
+
+int CobaltSkiaGlyphAtlasWidthDefault() {
+  return -1;
+}
+
+int CobaltSkiaGlyphAtlasHeightDefault() {
+  return -1;
+}
+
+int CobaltJsGarbageCollectionThresholdInBytesDefault() {
+  return 8 * 1024 * 1024;
+}
+
+int CobaltReduceCpuMemoryByDefault() {
+  return -1;
+}
+
+int CobaltReduceGpuMemoryByDefault() {
+  return -1;
+}
+
+bool CobaltGcZealDefault() {
+  return false;
+}
+
+const char* CobaltRasterizerTypeDefault() {
+  return "direct-gles";
+}
+
+bool CobaltEnableJitDefault() {
+  return true;
+}
+
+}  // namespace common
+}  // namespace starboard
diff --git a/src/starboard/common/configuration_defaults.h b/src/starboard/common/configuration_defaults.h
new file mode 100644
index 0000000..c79c210
--- /dev/null
+++ b/src/starboard/common/configuration_defaults.h
@@ -0,0 +1,68 @@
+// Copyright 2020 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 STARBOARD_COMMON_CONFIGURATION_DEFAULTS_H_
+#define STARBOARD_COMMON_CONFIGURATION_DEFAULTS_H_
+
+namespace starboard {
+namespace common {
+
+const char* CobaltUserOnExitStrategyDefault();
+
+int CobaltEglSwapIntervalDefault();
+
+bool CobaltRenderDirtyRegionOnlyDefault();
+
+const char* CobaltFallbackSplashScreenUrlDefault();
+
+bool CobaltEnableQuicDefault();
+
+int CobaltSkiaCacheSizeInBytesDefault();
+
+int CobaltOffscreenTargetCacheSizeInBytesDefault();
+
+int CobaltEncodedImageCacheSizeInBytesDefault();
+
+int CobaltImageCacheSizeInBytesDefault();
+
+int CobaltLocalTypefaceCacheSizeInBytesDefault();
+
+int CobaltRemoteTypefaceCacheSizeInBytesDefault();
+
+int CobaltMeshCacheSizeInBytesDefault();
+
+int CobaltSoftwareSurfaceCacheSizeInBytesDefault();
+
+float CobaltImageCacheCapacityMultiplierWhenPlayingVideoDefault();
+
+int CobaltSkiaGlyphAtlasWidthDefault();
+
+int CobaltSkiaGlyphAtlasHeightDefault();
+
+int CobaltJsGarbageCollectionThresholdInBytesDefault();
+
+int CobaltReduceCpuMemoryByDefault();
+
+int CobaltReduceGpuMemoryByDefault();
+
+bool CobaltGcZealDefault();
+
+const char* CobaltRasterizerTypeDefault();
+
+bool CobaltEnableJitDefault();
+
+}  // namespace common
+}  // namespace starboard
+
+#endif  // STARBOARD_COMMON_CONFIGURATION_DEFAULTS_H_
diff --git a/src/starboard/condition_variable.h b/src/starboard/condition_variable.h
index da9e622..09325e0 100644
--- a/src/starboard/condition_variable.h
+++ b/src/starboard/condition_variable.h
@@ -32,7 +32,7 @@
 #if SB_API_VERSION >= SB_PORTABLE_THREAD_TYPES_VERSION
 
 // Max size of the SbConditionVariable type.
-#define SB_CONDITION_VARIABLE_MAX_SIZE 64
+#define SB_CONDITION_VARIABLE_MAX_SIZE 80
 
 // An opaque handle to a condition variable type with
 // reserved memory buffer of size SB_CONDITION_VARIABLE_MAX_SIZE and
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index aa42d5c..d130c45 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -38,7 +38,7 @@
 
 // The minimum API version allowed by this version of the Starboard headers,
 // inclusive.
-#define SB_MINIMUM_API_VERSION 6
+#define SB_MINIMUM_API_VERSION 10
 
 // The maximum API version allowed by this version of the Starboard headers,
 // inclusive.
@@ -97,6 +97,10 @@
 // interface instead.
 #define SB_BLITTER_DEPRECATED_VERSION SB_EXPERIMENTAL_API_VERSION
 
+// Crypto API is no longer supported on any platform. BoringSSL CPU
+// optimizations are used instead.
+#define SB_CRYPTOAPI_DEPRECATED_VERSION SB_EXPERIMENTAL_API_VERSION
+
 // Require the captions API.
 // The system must implement the captions functions in
 // `starboard/accessibility.h` or use the provided stub implementations.
@@ -262,6 +266,28 @@
 
 // Add link register to SbThreadContext.
 #define SB_THREAD_CONTEXT_LINK_REGISTER_VERSION SB_EXPERIMENTAL_API_VERSION
+
+// Make GYP configuration variables cobalt extensions instead.
+// This change moves all of the GYP configuration variables to be members of
+// the struct declared in "cobalt/extension/configuration.h". All members are
+// function pointers that can be set for each platform, otherwise defaults
+// will be used. These can be referenced through functions declared in
+// "cobalt/configuration/configuration.h", which will use the extension API if
+// available, but will otherwise fall back onto default values.
+#define SB_FEATURE_GYP_CONFIGURATION_VERSION SB_EXPERIMENTAL_API_VERSION
+
+// Add the PCLMULQDQ instruction feature to the Starboard CPU features interface
+// for x86 architectures.
+#define SB_CPU_FEATURE_PCLMULQDQ SB_EXPERIMENTAL_API_VERSION
+
+// |content_type| is added to SbMediaIsVideoSupported() and
+// SbMediaIsAudioSupported().
+#define SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION \
+  SB_EXPERIMENTAL_API_VERSION
+
+// Deprecate support for gles3 features.
+#define SB_GLES3_DEPRECATED_VERSION SB_EXPERIMENTAL_API_VERSION
+
 // --- Release Candidate Feature Defines -------------------------------------
 
 // --- Common Detected Features ----------------------------------------------
@@ -337,7 +363,7 @@
   TypeName(const TypeName&) = delete;         \
   void operator=(const TypeName&) = delete
 
-// An enumeration of values for the SB_PREFERRED_RGBA_BYTE_ORDER configuration
+// An enumeration of values for the kSbPreferredByteOrder configuration
 // variable.  Setting this up properly means avoiding slow color swizzles when
 // passing pixel data from one library to another.  Note that these definitions
 // are in byte-order and so are endianness-independent.
@@ -545,8 +571,8 @@
 #error "Your platform's SB_API_VERSION < SB_MINIMUM_API_VERSION."
 #endif
 
-#if !SB_IS(ARCH_ARM) && !SB_IS(ARCH_ARM64) && !SB_IS(ARCH_MIPS) && \
-    !SB_IS(ARCH_PPC) && !SB_IS(ARCH_X86) && !SB_IS(ARCH_X64)
+#if !SB_IS(ARCH_ARM) && !SB_IS(ARCH_ARM64) && !SB_IS(ARCH_X86) && \
+    !SB_IS(ARCH_X64)
 #error "Your platform doesn't define a known architecture."
 #endif
 
@@ -740,13 +766,6 @@
 "starboard/<PLATFORM_PATH>/configuration_constants.cc."
 #endif
 
-#if defined(SB_HAS_AUDIOLESS_VIDEO)
-#error \
-    "SB_HAS_AUDIOLESS_VIDEO should not be defined in Starboard " \
-"versions 12 and later. Instead, define kSbHasAudiolessVideo in " \
-"starboard/<PLATFORM_PATH>/configuration_constants.cc."
-#endif
-
 #if defined(SB_HAS_MEDIA_WEBM_VP9_SUPPORT)
 #error \
     "SB_HAS_MEDIA_WEBM_VP9_SUPPORT should not be defined in Starboard " \
@@ -862,6 +881,13 @@
 "starboard/<PLATFORM_PATH>/configuration_constants.cc."
 #endif
 
+#if defined(SB_PREFERRED_RGBA_BYTE_ORDER)
+#error \
+    "SB_PREFERRED_RGBA_BYTE_ORDER should not be defined in Starboard " \
+"versions 12 and later. Instead, define kSbPreferredRgbaByteOrder in " \
+"starboard/<PLATFORM_PATH>/configuration_constants.cc."
+#endif
+
 #if defined(SB_USER_MAX_SIGNED_IN)
 #error \
     "SB_USER_MAX_SIGNED_IN should not be defined in Starboard " \
@@ -889,17 +915,11 @@
 #endif  // defined(SB_HAS_AC3_AUDIO)
 #endif  // SB_API_VERSION >= 11
 
-#if SB_API_VERSION >= 10
 #if !defined(SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING)
 #error \
     "Your platform must define SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING in API "\
     "version 10 or later."
 #endif  // !defined(SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING)
-#endif  // SB_API_VERSION >= 10
-
-#if SB_API_VERSION >= 10
-#define SB_HAS_AUDIOLESS_VIDEO 1
-#endif
 
 #if !defined(SB_HAS_THREAD_PRIORITY_SUPPORT)
 #error "Your platform must define SB_HAS_THREAD_PRIORITY_SUPPORT."
@@ -941,6 +961,20 @@
 #error "Your platform must define SB_PATH_SEP_STRING."
 #endif
 
+#if !defined(SB_PREFERRED_RGBA_BYTE_ORDER)
+// Legal values for SB_PREFERRED_RGBA_BYTE_ORDER are defined in this file above
+// as SB_PREFERRED_RGBA_BYTE_ORDER_*.
+// If your platform uses GLES, you should set this to
+// SB_PREFERRED_RGBA_BYTE_ORDER_RGBA.
+#error "Your platform must define SB_PREFERRED_RGBA_BYTE_ORDER."
+#endif
+
+#if SB_PREFERRED_RGBA_BYTE_ORDER != SB_PREFERRED_RGBA_BYTE_ORDER_RGBA && \
+    SB_PREFERRED_RGBA_BYTE_ORDER != SB_PREFERRED_RGBA_BYTE_ORDER_BGRA && \
+    SB_PREFERRED_RGBA_BYTE_ORDER != SB_PREFERRED_RGBA_BYTE_ORDER_ARGB
+#error "SB_PREFERRED_RGBA_BYTE_ORDER has been assigned an invalid value."
+#endif
+
 #endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
 
 #if (SB_API_VERSION < 12 && !defined(SB_HAS_MICROPHONE))
@@ -959,20 +993,6 @@
 #error "New versions of Starboard specify player output mode at runtime."
 #endif
 
-#if !defined(SB_PREFERRED_RGBA_BYTE_ORDER)
-// Legal values for SB_PREFERRED_RGBA_BYTE_ORDER are defined in this file above
-// as SB_PREFERRED_RGBA_BYTE_ORDER_*.
-// If your platform uses GLES, you should set this to
-// SB_PREFERRED_RGBA_BYTE_ORDER_RGBA.
-#error "Your platform must define SB_PREFERRED_RGBA_BYTE_ORDER."
-#endif
-
-#if SB_PREFERRED_RGBA_BYTE_ORDER != SB_PREFERRED_RGBA_BYTE_ORDER_RGBA && \
-    SB_PREFERRED_RGBA_BYTE_ORDER != SB_PREFERRED_RGBA_BYTE_ORDER_BGRA && \
-    SB_PREFERRED_RGBA_BYTE_ORDER != SB_PREFERRED_RGBA_BYTE_ORDER_ARGB
-#error "SB_PREFERRED_RGBA_BYTE_ORDER has been assigned an invalid value."
-#endif
-
 #if !defined(SB_HAS_BILINEAR_FILTERING_SUPPORT)
 #error "Your platform must define SB_HAS_BILINEAR_FILTERING_SUPPORT."
 #endif
@@ -993,15 +1013,97 @@
 #endif
 #endif
 
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+#if defined(COBALT_MAX_CPU_USAGE_IN_BYTES)
+#error "|max_cobalt_cpu_usage| is deprecated "
+#error "SbSystemGetTotalCPUMemory() instead."
+#endif
+#if defined(COBALT_MAX_GPU_USAGE_IN_BYTES)
+#error "|max_cobalt_gpu_usage| is deprecated. "
+#error "Implement SbSystemGetTotalGPUMemory() instead."
+#endif
+#endif  // SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+
+#if defined(COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET)
+#error "COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET is deprecated."
+#error "Implement |SbMediaGetAudioBufferBudget| instead."
+#endif  // defined(COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET)
+
+#if defined(COBALT_MEDIA_BUFFER_ALIGNMENT)
+#error "COBALT_MEDIA_BUFFER_ALIGNMENT is deprecated."
+#error "Implement |SbMediaGetBufferAlignment| instead."
+#endif  // defined(COBALT_MEDIA_BUFFER_ALIGNMENT
+
+#if defined(COBALT_MEDIA_BUFFER_ALLOCATION_UNIT)
+#error "COBALT_MEDIA_BUFFER_ALLOCATION_UNIT is deprecated."
+#error "Implement |SbMediaGetBufferAllocationUnit| instead."
+#endif  // defined(COBALT_MEDIA_BUFFER_ALLOCATION_UNIT
+
+#if defined( \
+    COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS)
+#error "COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS"
+#error "is deprecated. Implement"
+#error "|SbMediaGetBufferGarbageCollectionDurationThreshold| instead."
+#endif  // defined(COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS)
+
+#if defined(COBALT_MEDIA_BUFFER_PADDING)
+#error "COBALT_MEDIA_BUFFER_PADDING is deprecated."
+#error "Implement |SbMediaGetBufferPadding| instead."
+#endif  // defined(COBALT_MEDIA_BUFFER_PADDING)
+
+#if defined(COBALT_MEDIA_BUFFER_STORAGE_TYPE_FILE)
+#error "COBALT_MEDIA_BUFFER_STORAGE_TYPE_FILE is deprecated."
+#error "Implement |SbMediaGetBufferStorageType| instead."
+#endif  // defined(COBALT_MEDIA_BUFFER_STORAGE_TYPE_FILE)
+
+#if defined(COBALT_MEDIA_BUFFER_STORAGE_TYPE_MEMORY)
+#error "COBALT_MEDIA_BUFFER_STORAGE_TYPE_MEMORY is deprecated."
+#error "Implement |SbMediaGetBufferStorageType| instead."
+#endif  // defined(COBALT_MEDIA_BUFFER_STORAGE_TYPE_MEMORY)
+
+#if defined(COBALT_MEDIA_BUFFER_INITIAL_CAPACITY)
+#error "COBALT_MEDIA_BUFFER_INITIAL_CAPACITY is deprecated."
+#error "implement |SbMediaGetInitialBufferCapacity| instead."
+#endif  // defined(COBALT_MEDIA_BUFFER_INITIAL_CAPACITY)
+
+#if defined(COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P)
+#error "COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P is deprecated."
+#error "Implement |SbMediaGetMaxBufferCapacity| instead."
+#endif  // defined(COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P)
+
+#if defined(COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K)
+#error "COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K is deprecated."
+#error "Implement |SbMediaGetMaxBufferCapacity| instead."
+#endif  // defined(COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K)
+
+#if defined(COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET)
+#error "COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET is deprecated."
+#error "Implement |SbMediaGetProgressiveBufferBudget| instead."
+#endif  // defined(COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET)
+
+#if defined(COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P)
+#error "COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P is deprecated."
+#error "Implement |SbMediaGetVideoBufferBudget| instead."
+#endif  // defined(COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P)
+
+#if defined(COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K)
+#error "COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K is deprecated."
+#error "Implement |SbMediaGetVideoBufferBudget| instead."
+#endif  // defined(COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K)
+
+#if defined(COBALT_MEDIA_BUFFER_POOL_ALLOCATE_ON_DEMAND)
+#error "COBALT_MEDIA_BUFFER_POOL_ALLOCATE_ON_DEMAND is deprecated."
+#error "Implement |SbMediaIsBufferPoolAllocateOnDemand| instead."
+#endif  // defined(COBALT_MEDIA_BUFFER_POOL_ALLOCATE_ON_DEMAND)
+
 #if defined(SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT)
 #error "SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT is deprecated."
-#error "Use gyp variable |cobalt_media_buffer_non_video_budget| instead."
+#error "Implement function |SbMediaGetAudioBufferBudget| instead."
 #endif  // defined(SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT)
 
 #if defined(SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT)
 #error "SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT is deprecated."
-#error "Use gyp variable |cobalt_media_buffer_video_budget_1080p| instead."
-#error "Use gyp variable |cobalt_media_buffer_video_budget_4k| instead."
+#error "Implement function |SbMediaGetVideoBufferBudget| instead."
 #endif  // defined(SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT)
 
 #if defined(SB_MEDIA_MAIN_BUFFER_BUDGET)
@@ -1012,6 +1114,10 @@
 #error "SB_MEDIA_GPU_BUFFER_BUDGET is deprecated."
 #endif  // defined(SB_MEDIA_GPU_BUFFER_BUDGET)
 
+#if defined(SB_HAS_AUDIOLESS_VIDEO)
+#error "SB_HAS_AUDIOLESS_VIDEO is deprecated."
+#endif  // defined(SB_HAS_AUDIOLESS_VIDEO)
+
 #if SB_API_VERSION >= 11
 #if defined(SB_HAS_MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
 #if !SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
@@ -1024,15 +1130,9 @@
 #endif  // defined(SB_HAS_MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
 #endif  // SB_API_VERSION >= 11
 
-#if SB_API_VERSION >= 10
 #if defined(SB_HAS_DRM_SESSION_CLOSED)
-#if !SB_HAS(DRM_SESSION_CLOSED)
-#error "SB_HAS_DRM_SESSION_CLOSED is required in this API version."
-#endif  // !SB_HAS(DRM_SESSION_CLOSED)
-#else   // defined(SB_HAS_DRM_SESSION_CLOSED)
-#define SB_HAS_DRM_SESSION_CLOSED 1
+#error "SB_HAS_DRM_SESSION_CLOSED should not be defined for API version >= 10."
 #endif  // defined(SB_HAS_DRM_SESSION_CLOSED)
-#endif  // SB_API_VERSION >= 10
 
 #if SB_API_VERSION < SB_SPEECH_RECOGNIZER_IS_REQUIRED && SB_API_VERSION >= 5
 #if !defined(SB_HAS_SPEECH_RECOGNIZER)
@@ -1053,23 +1153,13 @@
 #error "SB_HAS_ON_SCREEN_KEYBOARD not supported in this API version."
 #endif
 
-#if SB_HAS(CAPTIONS) && (SB_API_VERSION < 10)
-#error "SB_HAS_CAPTIONS not supported in this API version."
-#endif
+#if defined(SB_HAS_PLAYER_FILTER_TESTS)
+#error "SB_HAS_PLAYER_FILTER_TESTS should not be defined in API versions >= 10."
+#endif  // defined(SB_HAS_PLAYER_FILTER_TESTS)
 
-#if SB_API_VERSION >= 10
-#define SB_HAS_PLAYER_FILTER_TESTS 1
-#endif
-
-#if SB_API_VERSION >= 10
-#define SB_HAS_PLAYER_ERROR_MESSAGE 1
-#endif
-
-#if SB_API_VERSION < 10
-#if !SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
-#define SB_HAS_QUIRK_SUPPORT_INT16_AUDIO_SAMPLES 1
-#endif  // !SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
-#endif  // SB_API_VERSION < 10
+#if defined(SB_HAS_PLAYER_ERROR_MESSAGE)
+#error "SB_HAS_PLAYER_ERROR_MESSAGE should not be defined in API versions >= 10."
+#endif  // defined(SB_HAS_PLAYER_ERROR_MESSAGE)
 
 #if SB_API_VERSION >= \
     SB_PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT_VERSION
diff --git a/src/starboard/configuration_constants.h b/src/starboard/configuration_constants.h
index c88e127..8e6c27d 100644
--- a/src/starboard/configuration_constants.h
+++ b/src/starboard/configuration_constants.h
@@ -64,9 +64,6 @@
 // Specifies whether this platform updates audio frames asynchronously.
 extern const bool kSbHasAsyncAudioFramesReporting;
 
-// Allow playing audioless video.
-extern const bool kSbHasAudiolessVideo;
-
 // Specifies whether this platform has webm/vp9 support.  This should be set to
 // non-zero on platforms with webm/vp9 support.
 extern const bool kSbHasMediaWebmVp9Support;
@@ -142,6 +139,11 @@
 // like mutexes, so we want to keep this manageable.
 extern const uint32_t kSbMaxThreads;
 
+// Specifies the preferred byte order of color channels in a pixel. Refer to
+// starboard/configuration.h for the possible values. EGL/GLES platforms should
+// generally prefer a byte order of RGBA, regardless of endianness.
+extern const int kSbPreferredRgbaByteOrder;
+
 // The current platform's search path component separator character. When
 // specifying an ordered list of absolute paths of directories to search for a
 // given reason, this is the character that appears between entries. For
diff --git a/src/starboard/contrib/creator/ci20x11/system_get_property.cc b/src/starboard/contrib/creator/ci20x11/system_get_property.cc
index 61fc987..43dabb3 100644
--- a/src/starboard/contrib/creator/ci20x11/system_get_property.cc
+++ b/src/starboard/contrib/creator/ci20x11/system_get_property.cc
@@ -36,52 +36,6 @@
   return true;
 }
 
-#if SB_API_VERSION < 10
-
-bool GetPlatformUuid(char* out_value, int value_length) {
-  struct ifreq interface;
-  struct ifconf config;
-  char buf[1024];
-
-  int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
-  if (fd == -1) {
-    return false;
-  }
-  config.ifc_len = sizeof(buf);
-  config.ifc_buf = buf;
-  int result = ioctl(fd, SIOCGIFCONF, &config);
-  if (result == -1) {
-    return false;
-  }
-
-  struct ifreq* cur_interface = config.ifc_req;
-  const struct ifreq* const end =
-      cur_interface + (config.ifc_len / sizeof(struct ifreq));
-
-  for (; cur_interface != end; ++cur_interface) {
-    SbStringCopy(interface.ifr_name, cur_interface->ifr_name,
-                 sizeof(cur_interface->ifr_name));
-    if (ioctl(fd, SIOCGIFFLAGS, &interface) == -1) {
-      continue;
-    }
-    if (interface.ifr_flags & IFF_LOOPBACK) {
-      continue;
-    }
-    if (ioctl(fd, SIOCGIFHWADDR, &interface) == -1) {
-      continue;
-    }
-    SbStringFormatF(
-        out_value, value_length, "%x:%x:%x:%x:%x:%x",
-        interface.ifr_addr.sa_data[0], interface.ifr_addr.sa_data[1],
-        interface.ifr_addr.sa_data[2], interface.ifr_addr.sa_data[3],
-        interface.ifr_addr.sa_data[4], interface.ifr_addr.sa_data[5]);
-    return true;
-  }
-  return false;
-}
-
-#endif  // SB_API_VERSION < 10
-
 }  // namespace
 
 bool SbSystemGetProperty(SbSystemPropertyId property_id,
@@ -111,11 +65,6 @@
     case kSbSystemPropertyPlatformName:
       return CopyStringAndTestIfSuccess(out_value, value_length, kPlatformName);
 
-#if SB_API_VERSION < 10
-    case kSbSystemPropertyPlatformUuid:
-      return GetPlatformUuid(out_value, value_length);
-#endif  // SB_API_VERSION < 10
-
     default:
       SB_DLOG(WARNING) << __FUNCTION__
                        << ": Unrecognized property: " << property_id;
diff --git a/src/starboard/contrib/creator/shared/starboard_platform.gypi b/src/starboard/contrib/creator/shared/starboard_platform.gypi
index a7f092d..b63ce99 100644
--- a/src/starboard/contrib/creator/shared/starboard_platform.gypi
+++ b/src/starboard/contrib/creator/shared/starboard_platform.gypi
@@ -264,13 +264,11 @@
       '<(DEPTH)/starboard/shared/starboard/player/player_create.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_get_current_frame.cc',
-      '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_get_info2.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_get_maximum_number_of_samples_per_write.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_output_mode_supported.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_internal.h',
-      '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_seek2.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_set_playback_rate.cc',
@@ -278,7 +276,6 @@
       '<(DEPTH)/starboard/shared/starboard/player/player_worker.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_worker.h',
       '<(DEPTH)/starboard/shared/starboard/player/player_write_end_of_stream.cc',
-      '<(DEPTH)/starboard/shared/starboard/player/player_write_sample.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_write_sample2.cc',
       '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
       '<(DEPTH)/starboard/shared/starboard/string_concat.cc',
diff --git a/src/starboard/contrib/linux/x64wl/system_get_property.cc b/src/starboard/contrib/linux/x64wl/system_get_property.cc
index 9c4a529..552dc5a 100644
--- a/src/starboard/contrib/linux/x64wl/system_get_property.cc
+++ b/src/starboard/contrib/linux/x64wl/system_get_property.cc
@@ -36,52 +36,6 @@
   return true;
 }
 
-#if SB_API_VERSION < 10
-
-bool GetPlatformUuid(char* out_value, int value_length) {
-  struct ifreq interface;
-  struct ifconf config;
-  char buf[1024];
-
-  int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
-  if (fd == -1) {
-    return false;
-  }
-  config.ifc_len = sizeof(buf);
-  config.ifc_buf = buf;
-  int result = ioctl(fd, SIOCGIFCONF, &config);
-  if (result == -1) {
-    return false;
-  }
-
-  struct ifreq* cur_interface = config.ifc_req;
-  const struct ifreq* const end =
-      cur_interface + (config.ifc_len / sizeof(struct ifreq));
-
-  for (; cur_interface != end; ++cur_interface) {
-    SbStringCopy(interface.ifr_name, cur_interface->ifr_name,
-                 sizeof(cur_interface->ifr_name));
-    if (ioctl(fd, SIOCGIFFLAGS, &interface) == -1) {
-      continue;
-    }
-    if (interface.ifr_flags & IFF_LOOPBACK) {
-      continue;
-    }
-    if (ioctl(fd, SIOCGIFHWADDR, &interface) == -1) {
-      continue;
-    }
-    SbStringFormatF(
-        out_value, value_length, "%x:%x:%x:%x:%x:%x",
-        interface.ifr_addr.sa_data[0], interface.ifr_addr.sa_data[1],
-        interface.ifr_addr.sa_data[2], interface.ifr_addr.sa_data[3],
-        interface.ifr_addr.sa_data[4], interface.ifr_addr.sa_data[5]);
-    return true;
-  }
-  return false;
-}
-
-#endif  // SB_API_VERSION < 10
-
 }  // namespace
 
 bool SbSystemGetProperty(SbSystemPropertyId property_id,
@@ -111,11 +65,6 @@
     case kSbSystemPropertyPlatformName:
       return CopyStringAndTestIfSuccess(out_value, value_length, kPlatformName);
 
-#if SB_API_VERSION < 10
-    case kSbSystemPropertyPlatformUuid:
-      return GetPlatformUuid(out_value, value_length);
-#endif  // SB_API_VERSION < 10
-
     default:
       SB_DLOG(WARNING) << __FUNCTION__
                        << ": Unrecognized property: " << property_id;
diff --git a/src/starboard/contrib/tizen/shared/starboard_platform.gypi b/src/starboard/contrib/tizen/shared/starboard_platform.gypi
index 650d7f7..2697fe2 100644
--- a/src/starboard/contrib/tizen/shared/starboard_platform.gypi
+++ b/src/starboard/contrib/tizen/shared/starboard_platform.gypi
@@ -306,13 +306,11 @@
       '<(DEPTH)/starboard/shared/starboard/player/player_create.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_get_current_frame.cc',
-      '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_get_info2.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_get_maximum_number_of_samples_per_write.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_internal.h',
       '<(DEPTH)/starboard/shared/starboard/player/player_output_mode_supported.cc',
-      '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_seek2.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_set_playback_rate.cc',
@@ -320,7 +318,6 @@
       '<(DEPTH)/starboard/shared/starboard/player/player_worker.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_worker.h',
       '<(DEPTH)/starboard/shared/starboard/player/player_write_end_of_stream.cc',
-      '<(DEPTH)/starboard/shared/starboard/player/player_write_sample.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_write_sample2.cc',
       '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
       '<(DEPTH)/starboard/shared/starboard/string_concat.cc',
diff --git a/src/starboard/contrib/tizen/shared/system_has_capability.cc b/src/starboard/contrib/tizen/shared/system_has_capability.cc
index 9958b10..176bf84 100644
--- a/src/starboard/contrib/tizen/shared/system_has_capability.cc
+++ b/src/starboard/contrib/tizen/shared/system_has_capability.cc
@@ -22,10 +22,8 @@
       return false;
     case kSbSystemCapabilityCanQueryGPUMemoryStats:
       return false;
-#if SB_API_VERSION >= 10
     case kSbSystemCapabilitySetsInputTimestamp:
       return true;
-#endif
   }
 
   SB_DLOG(WARNING) << "Unrecognized capability: " << capability_id;
diff --git a/src/starboard/cpu_features.h b/src/starboard/cpu_features.h
index 166bd74..c204ac4 100644
--- a/src/starboard/cpu_features.h
+++ b/src/starboard/cpu_features.h
@@ -192,6 +192,10 @@
 
   // SSE3 extensions.
   bool has_sse3;
+#if defined(SB_CPU_FEATURE_PCLMULQDQ)
+  // PCLMULQDQ instruction.
+  bool has_pclmulqdq;
+#endif  // defined(SB_CPU_FEATURE_PCLMULQDQ)
   // Supplemental SSE3 extensions.
   bool has_ssse3;
   // SSE-4.1 extensions.
diff --git a/src/starboard/cryptography.h b/src/starboard/cryptography.h
index 2ae2fea..3edc4ba 100644
--- a/src/starboard/cryptography.h
+++ b/src/starboard/cryptography.h
@@ -54,6 +54,10 @@
 #include "starboard/export.h"
 #include "starboard/types.h"
 
+#if SB_API_VERSION >= SB_CRYPTOAPI_DEPRECATED_VERSION
+#error "Starboard Crypto API is deprecated"
+#else
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -149,6 +153,8 @@
 // |initialization_vector_size|: The size, in bytes, of the IV.
 // |key|: The key to use for this transformation.
 // |key_size|: The size, in bytes, of the key.
+//
+// presubmit: allow sb_export mismatch
 SB_EXPORT SbCryptographyTransformer
 SbCryptographyCreateTransformer(const char* algorithm,
                                 int block_size_bits,
@@ -160,6 +166,8 @@
                                 int key_size);
 
 // Destroys the given |transformer| instance.
+//
+// presubmit: allow sb_export mismatch
 SB_EXPORT void SbCryptographyDestroyTransformer(
     SbCryptographyTransformer transformer);
 
@@ -177,6 +185,8 @@
 // |out_data|: A buffer where the transformed data should be placed. Must have
 // at least capacity for |in_data_size| bytes. May point to the same memory as
 // |in_data|.
+//
+// presubmit: allow sb_export mismatch
 SB_EXPORT int SbCryptographyTransform(
     SbCryptographyTransformer transformer,
     const void* in_data,
@@ -187,6 +197,8 @@
 // internally-set IV. The block cipher mode algorithm will update the IV
 // appropriately after every block, so this is not necessary unless the stream
 // is discontiguous in some way. This happens with AES-GCM in TLS.
+//
+// presubmit: allow sb_export mismatch
 SB_EXPORT void SbCryptographySetInitializationVector(
     SbCryptographyTransformer transformer,
     const void* initialization_vector,
@@ -196,6 +208,8 @@
 // modes that support it (GCM). Returns whether the data was successfully
 // set. This can fail if the chaining mode doesn't support AAD, if the
 // parameters are invalid, or if the internal state is invalid for setting AAD.
+//
+// presubmit: allow sb_export mismatch
 SB_EXPORT bool SbCryptographySetAuthenticatedData(
     SbCryptographyTransformer transformer,
     const void* data,
@@ -205,6 +219,8 @@
 // |out_tag_size| bytes of it in |out_tag|. Returns whether it was able to get
 // the tag, which mainly has to do with whether it is compatible with the
 // current block cipher mode.
+//
+// presubmit: allow sb_export mismatch
 SB_EXPORT bool SbCryptographyGetTag(
     SbCryptographyTransformer transformer,
     void* out_tag,
@@ -214,4 +230,6 @@
 }  // extern "C"
 #endif
 
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #endif  // STARBOARD_CRYPTOGRAPHY_H_
diff --git a/src/starboard/decode_target.h b/src/starboard/decode_target.h
index 3daddc5..cb7576e 100644
--- a/src/starboard/decode_target.h
+++ b/src/starboard/decode_target.h
@@ -135,11 +135,9 @@
   // A decoder target format consisting of Y, U, and V planes, in that order.
   kSbDecodeTargetFormat3PlaneYUVI420,
 
-#if SB_API_VERSION >= 10
   // A decoder target format consisting of 10bit Y, U, and V planes, in that
   // order.
   kSbDecodeTargetFormat3Plane10BitYUVI420,
-#endif
 
   // A decoder target format consisting of a single plane with pixels layed out
   // in the format UYVY.  Since there are two Y values per sample, but only one
@@ -353,9 +351,7 @@
       return 1;
     case kSbDecodeTargetFormat2PlaneYUVNV12:
       return 2;
-#if SB_API_VERSION >= 10
     case kSbDecodeTargetFormat3Plane10BitYUVI420:
-#endif
     case kSbDecodeTargetFormat3PlaneYUVI420:
       return 3;
     default:
diff --git a/src/starboard/drm.h b/src/starboard/drm.h
index 43c4332..98e9b15 100644
--- a/src/starboard/drm.h
+++ b/src/starboard/drm.h
@@ -135,7 +135,6 @@
 // SbDrmGenerateSessionUpdateRequest() was called on. |context| will be the same
 // context that was passed into the call to SbDrmCreateSystem().
 //
-#if SB_API_VERSION >= 10
 // |status| is the status of the session request.
 //
 // |type| is the status of the session request.
@@ -144,13 +143,14 @@
 // |kSbDrmStatusSuccess| to provide more details about the error.  It may be
 // NULL if |status| is |kSbDrmStatusSuccess| or if no error message can be
 // provided.
-#endif  // SB_API_VERSION >= 10
 // |ticket| will be the same ticket that was passed to
 // SbDrmGenerateSessionUpdateRequest() or |kSbDrmTicketInvalid| if the update
 // request was generated by the DRM system.
 //
-// |session_id| can be NULL if there was an error generating the request.
-#if SB_API_VERSION >= 10
+// |session_id| cannot be NULL when |ticket| is |kSbDrmTicketInvalid|, even when
+// there was an error generating the request.  This allows Cobalt to find and
+// reject the correct Promise corresponding to the associated
+// SbDrmGenerateSessionUpdateRequest().
 typedef void (*SbDrmSessionUpdateRequestFunc)(SbDrmSystem drm_system,
                                               void* context,
                                               int ticket,
@@ -162,16 +162,6 @@
                                               const void* content,
                                               int content_size,
                                               const char* url);
-#else   // SB_API_VERSION >= 10
-typedef void (*SbDrmSessionUpdateRequestFunc)(SbDrmSystem drm_system,
-                                              void* context,
-                                              int ticket,
-                                              const void* session_id,
-                                              int session_id_size,
-                                              const void* content,
-                                              int content_size,
-                                              const char* url);
-#endif  // SB_API_VERSION >= 10
 
 // A callback for notifications that a session has been added, and subsequent
 // encrypted samples are actively ready to be decoded. |drm_system| will be the
@@ -180,16 +170,13 @@
 //
 // |ticket| will be the same ticket that was passed to SbDrmUpdateSession().
 //
-#if SB_API_VERSION >= 10
 // |status| is the status of the session request.
 //
 // |error_message| may contain an optional error message when |status| isn't
 // |kSbDrmStatusSuccess| to provide more details about the error.  It may be
 // NULL if |status| is |kSbDrmStatusSuccess| or if no error message can be
 // provided.
-#endif  // SB_API_VERSION >= 10
 // |succeeded| is whether the session was successfully updated or not.
-#if SB_API_VERSION >= 10
 typedef void (*SbDrmSessionUpdatedFunc)(SbDrmSystem drm_system,
                                         void* context,
                                         int ticket,
@@ -197,14 +184,6 @@
                                         const char* error_message,
                                         const void* session_id,
                                         int session_id_size);
-#else   // SB_API_VERSION >= 10
-typedef void (*SbDrmSessionUpdatedFunc)(SbDrmSystem drm_system,
-                                        void* context,
-                                        int ticket,
-                                        const void* session_id,
-                                        int session_id_size,
-                                        bool succeeded);
-#endif  // SB_API_VERSION >= 10
 
 // A callback for notifications that the status of one or more keys in a session
 // has been changed.  All keys of the session and their new status will be
@@ -219,15 +198,12 @@
     const SbDrmKeyStatus* key_statuses);
 
 // A callback for signalling that a session has been closed by the SbDrmSystem
-#if SB_HAS(DRM_SESSION_CLOSED)
 typedef void (*SbDrmSessionClosedFunc)(
     SbDrmSystem drm_system,
     void* context,
     const void* session_id,
     int session_id_size);
-#endif  // SB_HAS(DRM_SESSION_CLOSED))
 
-#if SB_API_VERSION >= 10
 // A callback to notify the caller of SbDrmUpdateServerCertificate() whether the
 // update has been successfully updated or not.
 typedef void (*SbDrmServerCertificateUpdatedFunc)(SbDrmSystem drm_system,
@@ -235,7 +211,6 @@
                                                   int ticket,
                                                   SbDrmStatus status,
                                                   const char* error_message);
-#endif  // SB_API_VERSION >= 10
 
 // --- Constants -------------------------------------------------------------
 
@@ -284,14 +259,10 @@
 // function returns.
 // |session_closed_callback|: A function that can be called to indicate that a
 // session has closed.
-#if SB_API_VERSION >= 10
 // If |NULL| is passed for any of the callbacks (|update_request_callback|,
 // |session_updated_callback|, |key_statuses_changed_callback|,
 // |server_certificate_updated_callback|, or |session_closed_callback|), then
 // |kSbDrmSystemInvalid| must be returned.
-#endif  // SB_API_VERSION >= 10
-
-#if SB_API_VERSION >= 10
 
 SB_EXPORT SbDrmSystem SbDrmCreateSystem(
     const char* key_system,
@@ -302,27 +273,6 @@
     SbDrmServerCertificateUpdatedFunc server_certificate_updated_callback,
     SbDrmSessionClosedFunc session_closed_callback);
 
-#elif SB_HAS(DRM_SESSION_CLOSED)
-
-SB_EXPORT SbDrmSystem SbDrmCreateSystem(
-    const char* key_system,
-    void* context,
-    SbDrmSessionUpdateRequestFunc update_request_callback,
-    SbDrmSessionUpdatedFunc session_updated_callback,
-    SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback,
-    SbDrmSessionClosedFunc session_closed_callback);
-
-#else  // SB_HAS(DRM_SESSION_CLOSED)
-
-SB_EXPORT SbDrmSystem SbDrmCreateSystem(
-    const char* key_system,
-    void* context,
-    SbDrmSessionUpdateRequestFunc update_request_callback,
-    SbDrmSessionUpdatedFunc session_updated_callback,
-    SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback);
-
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
-
 // Asynchronously generates a session update request payload for
 // |initialization_data|, of |initialization_data_size|, in case sensitive
 // |type|, extracted from the media stream, in |drm_system|'s key system.
@@ -389,8 +339,6 @@
                                  const void* session_id,
                                  int session_id_size);
 
-#if SB_API_VERSION >= 10
-
 // Returns true if server certificate of |drm_system| can be updated via
 // SbDrmUpdateServerCertificate().  The return value should remain the same
 // during the life time of |drm_system|.
@@ -418,8 +366,6 @@
                                             const void* certificate,
                                             int certificate_size);
 
-#endif  // SB_API_VERSION >= 10
-
 // Destroys |drm_system|, which implicitly removes all keys installed in it and
 // invalidates all outstanding session update requests. A DRM system cannot be
 // destroyed unless any associated SbPlayer or SbDecoder has first been
diff --git a/src/starboard/egl_and_gles/egl_and_gles_angle.gyp b/src/starboard/egl_and_gles/egl_and_gles_angle.gyp
index 7ad57ae..8fed38d 100644
--- a/src/starboard/egl_and_gles/egl_and_gles_angle.gyp
+++ b/src/starboard/egl_and_gles/egl_and_gles_angle.gyp
@@ -36,7 +36,6 @@
         ['target_os=="win" and enable_d3d11_feature_level_11==1', {
           'direct_dependent_settings': {
             'defines': [
-              'GLES3_SUPPORTED',
               'GL_GLEXT_PROTOTYPES',
             ],
           },
diff --git a/src/starboard/egl_and_gles/egl_and_gles_glimp.gyp b/src/starboard/egl_and_gles/egl_and_gles_glimp.gyp
index 7c98fee..55dd8cd 100644
--- a/src/starboard/egl_and_gles/egl_and_gles_glimp.gyp
+++ b/src/starboard/egl_and_gles/egl_and_gles_glimp.gyp
@@ -26,7 +26,6 @@
       ],
       'direct_dependent_settings': {
         'defines': [
-          'GLES3_SUPPORTED',
           'GL_GLEXT_PROTOTYPES',
         ],
       },
diff --git a/src/starboard/egl_and_gles/egl_and_gles_system_gles3.gyp b/src/starboard/egl_and_gles/egl_and_gles_system_gles3.gyp
index 491b829..01ddbca 100644
--- a/src/starboard/egl_and_gles/egl_and_gles_system_gles3.gyp
+++ b/src/starboard/egl_and_gles/egl_and_gles_system_gles3.gyp
@@ -15,16 +15,10 @@
 {
   'targets': [
     {
+      # Though we support as a valid gl_type option, it will use neither GLES3
+      # features nor GLES3 egl context currently.
       'target_name': 'egl_and_gles_implementation',
       'type': 'none',
-
-      # Use the system-provided implementation of GLES3.
-
-      'direct_dependent_settings': {
-        'defines': [
-          'GLES3_SUPPORTED',
-        ],
-      },
     },
   ],
 }
diff --git a/src/starboard/elf_loader/dynamic_section.cc b/src/starboard/elf_loader/dynamic_section.cc
new file mode 100644
index 0000000..d582124
--- /dev/null
+++ b/src/starboard/elf_loader/dynamic_section.cc
@@ -0,0 +1,193 @@
+// 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 "starboard/elf_loader/dynamic_section.h"
+
+#include "starboard/common/log.h"
+#include "starboard/elf_loader/log.h"
+
+namespace starboard {
+namespace elf_loader {
+
+DynamicSection::DynamicSection(Addr base_memory_address,
+                               Dyn* dynamic,
+                               size_t dynamic_count,
+                               Word dynamic_flags)
+    : base_memory_address_(base_memory_address),
+      soname_(NULL),
+      dynamic_(dynamic),
+      dynamic_count_(dynamic_count),
+      dynamic_flags_(dynamic_flags),
+      has_DT_SYMBOLIC_(false),
+      symbol_table_(NULL),
+      string_table_(NULL),
+      preinit_array_(NULL),
+      preinit_array_count_(0),
+      init_array_(NULL),
+      init_array_count_(0),
+      fini_array_(NULL),
+      fini_array_count_(0),
+      init_func_(NULL),
+      fini_func_(NULL) {}
+
+bool DynamicSection::InitDynamicSection() {
+  SB_DLOG(INFO) << "Dynamic section count=" << dynamic_count_;
+  for (int i = 0; i < dynamic_count_; i++) {
+    Addr dyn_value = dynamic_[i].d_un.d_val;
+    uintptr_t dyn_addr = base_memory_address_ + dynamic_[i].d_un.d_ptr;
+    SB_DLOG(INFO) << "Dynamic tag=" << dynamic_[i].d_tag;
+    switch (dynamic_[i].d_tag) {
+      case DT_DEBUG:
+        // TODO: implement.
+        break;
+      case DT_INIT:
+        SB_DLOG(INFO) << "  DT_INIT addr=0x" << std::hex << dyn_addr;
+        init_func_ = reinterpret_cast<linker_function_t>(dyn_addr);
+        break;
+      case DT_FINI:
+        SB_DLOG(INFO) << "  DT_FINI addr=0x" << std::hex << dyn_addr;
+        fini_func_ = reinterpret_cast<linker_function_t>(dyn_addr);
+        break;
+      case DT_INIT_ARRAY:
+        SB_DLOG(INFO) << "  DT_INIT_ARRAY addr=0x" << std::hex << dyn_addr;
+        init_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
+        break;
+      case DT_INIT_ARRAYSZ:
+        init_array_count_ = dyn_value / sizeof(Addr);
+        SB_DLOG(INFO) << "  DT_INIT_ARRAYSZ value=0x" << std::hex << dyn_value
+                      << " count=" << std::dec << init_array_count_;
+        break;
+      case DT_FINI_ARRAY:
+        SB_DLOG(INFO) << "  DT_FINI_ARRAY addr=0x" << std::hex << dyn_addr;
+        fini_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
+        break;
+      case DT_FINI_ARRAYSZ:
+        fini_array_count_ = dyn_value / sizeof(Addr);
+        SB_DLOG(INFO) << "  DT_FINI_ARRAYSZ value=0x" << std::hex << dyn_value
+                      << " count=" << fini_array_count_;
+        break;
+      case DT_PREINIT_ARRAY:
+        SB_DLOG(INFO) << "  DT_PREINIT_ARRAY addr=0x" << std::hex << dyn_addr;
+        preinit_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
+        break;
+      case DT_PREINIT_ARRAYSZ:
+        preinit_array_count_ = dyn_value / sizeof(Addr);
+        SB_DLOG(INFO) << "  DT_PREINIT_ARRAYSZ addr=" << dyn_addr
+                      << " count=" << preinit_array_count_;
+        break;
+      case DT_SYMBOLIC:
+        SB_DLOG(INFO) << "  DT_SYMBOLIC";
+        has_DT_SYMBOLIC_ = true;
+        break;
+      case DT_FLAGS:
+        if (dyn_value & DF_SYMBOLIC)
+          has_DT_SYMBOLIC_ = true;
+        break;
+      case DT_SONAME:
+        soname_ = string_table_ + dyn_value;
+        break;
+      case DT_HASH:
+        SB_DLOG(INFO) << "  DT_HASH addr=0x" << std::hex << dyn_addr;
+        elf_hash_.Init(dyn_addr);
+        break;
+      case DT_GNU_HASH:
+        SB_DLOG(INFO) << "  DT_GNU_HASH addr=0x" << std::hex << dyn_addr;
+        gnu_hash_.Init(dyn_addr);
+        break;
+      case DT_STRTAB:
+        SB_DLOG(INFO) << "  DT_STRTAB addr=0x" << std::hex << dyn_addr;
+        string_table_ = reinterpret_cast<const char*>(dyn_addr);
+        break;
+      case DT_SYMTAB:
+        SB_DLOG(INFO) << "  DT_SYMTAB addr=0x" << std::hex << dyn_addr;
+        symbol_table_ = reinterpret_cast<Sym*>(dyn_addr);
+        break;
+      default:
+        break;
+    }
+  }
+  return true;
+}
+
+const Dyn* DynamicSection::GetDynamicTable() {
+  return dynamic_;
+}
+
+size_t DynamicSection::GetDynamicTableSize() {
+  return dynamic_count_;
+}
+
+void DynamicSection::CallConstructors() {
+  CallFunction(init_func_, "DT_INIT");
+  for (size_t n = 0; n < init_array_count_; ++n) {
+    CallFunction(init_array_[n], "DT_INIT_ARRAY");
+  }
+}
+
+void DynamicSection::CallDestructors() {
+  for (size_t n = fini_array_count_; n > 0; --n) {
+    CallFunction(fini_array_[n - 1], "DT_FINI_ARRAY");
+  }
+  CallFunction(fini_func_, "DT_FINI");
+}
+
+void DynamicSection::CallFunction(linker_function_t func,
+                                  const char* func_type) {
+  uintptr_t func_address = reinterpret_cast<uintptr_t>(func);
+
+  // On some platforms  the entries in the array can be 0 or -1,
+  // and should  be ignored e.g. Android:
+  // https://android.googlesource.com/platform/bionic/+/android-4.2_r1/linker/README.TXT
+  if (func_address != 0 && func_address != uintptr_t(-1)) {
+    func();
+  }
+}
+
+const Sym* DynamicSection::LookupById(size_t symbol_id) const {
+  // TODO: Calculated the symbol_table size and validation check.
+  return &symbol_table_[symbol_id];
+}
+
+bool DynamicSection::IsWeakById(size_t symbol_id) const {
+  // TODO: Calculated the symbol_table size and validation check.
+  return ELF_ST_BIND(symbol_table_[symbol_id].st_info) == STB_WEAK;
+}
+
+const char* DynamicSection::LookupNameById(size_t symbol_id) const {
+  const Sym* sym = LookupById(symbol_id);
+  // TODO: Confirm that LookupById actually can return NULL.
+  if (!sym)
+    return NULL;
+  return string_table_ + sym->st_name;
+}
+
+const Sym* DynamicSection::LookupByName(const char* symbol_name) const {
+  const Sym* sym =
+      gnu_hash_.IsValid()
+          ? gnu_hash_.LookupByName(symbol_name, symbol_table_, string_table_)
+          : elf_hash_.LookupByName(symbol_name, symbol_table_, string_table_);
+
+  // Ignore undefined symbols or those that are not global or weak definitions.
+  if (!sym || sym->st_shndx == SHN_UNDEF)
+    return NULL;
+
+  uint8_t info = ELF_ST_BIND(sym->st_info);
+  if (info != STB_GLOBAL && info != STB_WEAK)
+    return NULL;
+
+  return sym;
+}
+
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/dynamic_section.h b/src/starboard/elf_loader/dynamic_section.h
new file mode 100644
index 0000000..8d22bf1
--- /dev/null
+++ b/src/starboard/elf_loader/dynamic_section.h
@@ -0,0 +1,99 @@
+// 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.
+
+#ifndef STARBOARD_ELF_LOADER_DYNAMIC_SECTION_H_
+#define STARBOARD_ELF_LOADER_DYNAMIC_SECTION_H_
+
+#include "starboard/elf_loader/elf.h"
+#include "starboard/elf_loader/elf_hash_table.h"
+#include "starboard/elf_loader/exported_symbols.h"
+#include "starboard/elf_loader/gnu_hash_table.h"
+#include "starboard/elf_loader/program_table.h"
+
+namespace starboard {
+namespace elf_loader {
+
+typedef void (*linker_function_t)();
+
+// class representing the ELF dynamic
+// section with dynamic symbols and a
+// string tables.
+
+class DynamicSection {
+ public:
+  DynamicSection(Addr base_memory_address,
+                 Dyn* dynamic,
+                 size_t dynamic_count,
+                 Word dynamic_flags);
+
+  // Initialize the dynamic section.
+  bool InitDynamicSection();
+
+  // Get pointer to the dynamic table.
+  const Dyn* GetDynamicTable();
+
+  // Get the size of the dynamic table
+  size_t GetDynamicTableSize();
+
+  // Call all the global constructors.
+  void CallConstructors();
+
+  // Call all the global destructors.
+  void CallDestructors();
+
+  // Lookup a symbol using its name.
+  const Sym* LookupByName(const char* symbol_name) const;
+
+  // Lookup a symbol using its id.
+  const Sym* LookupById(size_t symbol_id) const;
+
+  // Checks if a symbols is weak.
+  bool IsWeakById(size_t symbol_id) const;
+
+  // Lookup the name of a symbol by using its id.
+  const char* LookupNameById(size_t symbol_id) const;
+
+ private:
+  // Call a function.
+  void CallFunction(linker_function_t func, const char* func_type);
+
+  Addr base_memory_address_;
+  const char* soname_;
+
+  Dyn* dynamic_;
+  size_t dynamic_count_;
+  Word dynamic_flags_;
+  bool has_DT_SYMBOLIC_;
+
+  Sym* symbol_table_;
+  const char* string_table_;
+  ElfHashTable elf_hash_;
+  GnuHashTable gnu_hash_;
+
+  linker_function_t* preinit_array_;
+  size_t preinit_array_count_;
+  linker_function_t* init_array_;
+  size_t init_array_count_;
+  linker_function_t* fini_array_;
+  size_t fini_array_count_;
+  linker_function_t init_func_;
+  linker_function_t fini_func_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(DynamicSection);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_DYNAMIC_SECTION_H_
diff --git a/src/starboard/elf_loader/dynamic_section_test.cc b/src/starboard/elf_loader/dynamic_section_test.cc
new file mode 100644
index 0000000..6812046
--- /dev/null
+++ b/src/starboard/elf_loader/dynamic_section_test.cc
@@ -0,0 +1,207 @@
+// 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 "starboard/elf_loader/dynamic_section.h"
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_API_VERSION >= 12 &&                                         \
+    (SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP)) && \
+    SB_CAN(MAP_EXECUTABLE_MEMORY)
+
+namespace starboard {
+namespace elf_loader {
+
+namespace {
+
+class DynamicSectionTest : public ::testing::Test {
+ protected:
+  DynamicSectionTest() {}
+  ~DynamicSectionTest() {}
+};
+
+typedef struct DynamicSectionMemory {
+  Dyn dynamic_table_[3];
+  linker_function_t init_array_[2];
+  linker_function_t fini_array_[2];
+  Sym symbol_table_[2];
+  char string_table_[128];
+} DynamicSectionMemory;
+
+bool gInitCalled = false;
+bool gCtor1Called = false;
+bool gCtor2Called = false;
+bool gDtor1Called = false;
+bool gDtor2Called = false;
+
+void InitFunction() {
+  gInitCalled = true;
+}
+
+void TestConstructor1() {
+  gCtor1Called = true;
+}
+
+void TestConstructor2() {
+  gCtor2Called = true;
+}
+
+void TestDestructor1() {
+  gDtor1Called = true;
+}
+
+void TestDestructor2() {
+  gDtor2Called = true;
+}
+
+TEST_F(DynamicSectionTest, Initialize) {
+  Addr addr = 0x1234;
+  Dyn dynamic_table[3];
+  Word flags = 0;
+
+  DynamicSection d(addr, dynamic_table, 3, flags);
+  ASSERT_TRUE(d.InitDynamicSection());
+
+  ASSERT_EQ(dynamic_table, d.GetDynamicTable());
+  ASSERT_EQ(3, d.GetDynamicTableSize());
+}
+
+TEST_F(DynamicSectionTest, CallInit) {
+  Word flags = 0;
+  DynamicSectionMemory dynamic_section_memory;
+  Addr base_addr = reinterpret_cast<Addr>(&dynamic_section_memory);
+  dynamic_section_memory.dynamic_table_[0].d_tag = DT_INIT;
+  dynamic_section_memory.dynamic_table_[0].d_un.d_ptr =
+      reinterpret_cast<Addr>(InitFunction) - base_addr;
+
+  DynamicSection d(base_addr, dynamic_section_memory.dynamic_table_, 1, flags);
+  ASSERT_TRUE(d.InitDynamicSection());
+
+  ASSERT_EQ(dynamic_section_memory.dynamic_table_, d.GetDynamicTable());
+  ASSERT_EQ(1, d.GetDynamicTableSize());
+  d.CallConstructors();
+  ASSERT_TRUE(gInitCalled);
+}
+
+TEST_F(DynamicSectionTest, CallConstructors) {
+  Word flags = 0;
+  DynamicSectionMemory dynamic_section_memory;
+  Addr base_addr = reinterpret_cast<Addr>(&dynamic_section_memory);
+
+  dynamic_section_memory.init_array_[0] =
+      reinterpret_cast<linker_function_t>(&TestConstructor1);
+  dynamic_section_memory.init_array_[1] =
+      reinterpret_cast<linker_function_t>(&TestConstructor2);
+
+  dynamic_section_memory.dynamic_table_[0].d_tag = DT_INIT_ARRAY;
+  dynamic_section_memory.dynamic_table_[0].d_un.d_ptr =
+      reinterpret_cast<Addr>(dynamic_section_memory.init_array_) - base_addr;
+
+  dynamic_section_memory.dynamic_table_[1].d_tag = DT_INIT_ARRAYSZ;
+  dynamic_section_memory.dynamic_table_[1].d_un.d_val =
+      sizeof(dynamic_section_memory.init_array_);
+
+  DynamicSection d(base_addr, dynamic_section_memory.dynamic_table_, 2, flags);
+  ASSERT_TRUE(d.InitDynamicSection());
+
+  ASSERT_EQ(dynamic_section_memory.dynamic_table_, d.GetDynamicTable());
+  ASSERT_EQ(2, d.GetDynamicTableSize());
+  d.CallConstructors();
+  ASSERT_TRUE(gCtor1Called);
+  ASSERT_TRUE(gCtor2Called);
+}
+
+TEST_F(DynamicSectionTest, CallDestructors) {
+  Word flags = 0;
+  DynamicSectionMemory dynamic_section_memory;
+  Addr base_addr = reinterpret_cast<Addr>(&dynamic_section_memory);
+
+  dynamic_section_memory.fini_array_[0] =
+      reinterpret_cast<linker_function_t>(&TestDestructor1);
+  dynamic_section_memory.fini_array_[1] =
+      reinterpret_cast<linker_function_t>(&TestDestructor2);
+
+  dynamic_section_memory.dynamic_table_[0].d_tag = DT_FINI_ARRAY;
+  dynamic_section_memory.dynamic_table_[0].d_un.d_ptr =
+      reinterpret_cast<Addr>(dynamic_section_memory.fini_array_) - base_addr;
+
+  dynamic_section_memory.dynamic_table_[1].d_tag = DT_FINI_ARRAYSZ;
+  dynamic_section_memory.dynamic_table_[1].d_un.d_val =
+      sizeof(dynamic_section_memory.fini_array_);
+
+  DynamicSection d(base_addr, dynamic_section_memory.dynamic_table_, 2, flags);
+  ASSERT_TRUE(d.InitDynamicSection());
+
+  ASSERT_EQ(dynamic_section_memory.dynamic_table_, d.GetDynamicTable());
+  ASSERT_EQ(2, d.GetDynamicTableSize());
+  d.CallDestructors();
+  ASSERT_TRUE(gDtor1Called);
+  ASSERT_TRUE(gDtor2Called);
+}
+
+TEST_F(DynamicSectionTest, LookupById) {
+  Word flags = 0;
+  DynamicSectionMemory dynamic_section_memory;
+  Addr base_addr = reinterpret_cast<Addr>(&dynamic_section_memory);
+
+  dynamic_section_memory.dynamic_table_[0].d_tag = DT_SYMTAB;
+  dynamic_section_memory.dynamic_table_[0].d_un.d_ptr =
+      reinterpret_cast<Addr>(dynamic_section_memory.symbol_table_) - base_addr;
+
+  DynamicSection d(base_addr, dynamic_section_memory.dynamic_table_, 1, flags);
+
+  ASSERT_TRUE(d.InitDynamicSection());
+
+  ASSERT_EQ(dynamic_section_memory.dynamic_table_, d.GetDynamicTable());
+  ASSERT_EQ(1, d.GetDynamicTableSize());
+  ASSERT_EQ(dynamic_section_memory.symbol_table_, d.LookupById(0));
+  ASSERT_EQ(dynamic_section_memory.symbol_table_ + 1, d.LookupById(1));
+}
+
+TEST_F(DynamicSectionTest, LookupNameById) {
+  Word flags = 0;
+  DynamicSectionMemory dynamic_section_memory;
+  Addr base_addr = reinterpret_cast<Addr>(&dynamic_section_memory);
+
+  dynamic_section_memory.dynamic_table_[0].d_tag = DT_SYMTAB;
+  dynamic_section_memory.dynamic_table_[0].d_un.d_ptr =
+      reinterpret_cast<Addr>(dynamic_section_memory.symbol_table_) - base_addr;
+
+  dynamic_section_memory.dynamic_table_[1].d_tag = DT_STRTAB;
+  dynamic_section_memory.dynamic_table_[1].d_un.d_ptr =
+      reinterpret_cast<Addr>(dynamic_section_memory.string_table_) - base_addr;
+
+  SbMemoryCopy(dynamic_section_memory.string_table_, "test1\x00test2\x00", 12);
+
+  dynamic_section_memory.symbol_table_[0].st_name = 0;  // the offset of test1
+  dynamic_section_memory.symbol_table_[1].st_name = 6;  // the offset of test2
+
+  DynamicSection d(base_addr, dynamic_section_memory.dynamic_table_, 2, flags);
+
+  ASSERT_TRUE(d.InitDynamicSection());
+
+  ASSERT_EQ(dynamic_section_memory.dynamic_table_, d.GetDynamicTable());
+  ASSERT_EQ(2, d.GetDynamicTableSize());
+
+  ASSERT_EQ(0, SbStringCompareAll("test1", d.LookupNameById(0)));
+  ASSERT_EQ(0, SbStringCompareAll("test2", d.LookupNameById(1)));
+}
+
+}  // namespace
+}  // namespace elf_loader
+}  // namespace starboard
+#endif  // SB_API_VERSION >= 12 && (SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION
+        // || SB_HAS(MMAP)) && SB_CAN(MAP_EXECUTABLE_MEMORY)
diff --git a/src/starboard/elf_loader/elf.h b/src/starboard/elf_loader/elf.h
new file mode 100644
index 0000000..64d4365
--- /dev/null
+++ b/src/starboard/elf_loader/elf.h
@@ -0,0 +1,661 @@
+// 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.
+
+#ifndef STARBOARD_ELF_LOADER_ELF_H_
+#define STARBOARD_ELF_LOADER_ELF_H_
+
+// Subset of the ELF specification for loading Dynamic Shared Libraries.
+// System V Application Binary Interface - DRAFT - 10 June 2013
+// http://www.sco.com/developers/gabi/latest/contents.html
+
+#ifndef __cplusplus
+#error "Only C++ files can include this header."
+#endif
+
+#include "starboard/types.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// 32 bit data types
+
+// Unsigned program address - 4 bytes.
+typedef uint32_t Elf32_Addr;
+
+// Unsigned medium integer - 2 bytes.
+typedef uint16_t Elf32_Half;
+
+// Unsigned file offset - 4 bytes.
+typedef uint32_t Elf32_Off;
+
+// Signed large integer - 4 bytes.
+typedef int32_t Elf32_Sword;
+
+// Unsigned large integer - 4 bytes.
+typedef uint32_t Elf32_Word;
+
+// 64 bit data types
+
+// Unsigned program address - 8 bytes.
+typedef uint64_t Elf64_Addr;
+
+// Unsigned file offset - 8 bytes.
+typedef uint64_t Elf64_Off;
+
+// Unsigned medium intege 2 - bytes.
+typedef uint16_t Elf64_Half;
+
+// Unsigned integer - 4 bytes.
+typedef uint32_t Elf64_Word;
+
+// Signed integer - 4 bytes.
+typedef int32_t Elf64_Sword;
+
+// Unsigned long integer - 8 bytes.
+typedef uint64_t Elf64_Xword;
+
+// Signed long integer - 8 bytes.
+typedef int64_t Elf64_Sxword;
+
+#define EI_NIDENT (16)
+
+// Pack all the structs at 1 byte alignment.
+#pragma pack(push)
+#pragma pack(1)
+
+// 32 bit ELF file header.
+typedef struct {
+  // The initial bytes mark the file as an object file and provide
+  // machine-independent data.
+  unsigned char e_ident[EI_NIDENT];
+
+  // The object file type. We support only ET_DYN.
+  Elf32_Half e_type;
+
+  // Architecture of the file.
+  Elf32_Half e_machine;
+
+  // Object file version. The value should be 1.
+  Elf32_Word e_version;
+
+  // Virtual address to which the system first transfers
+  // control, thus starting the process.
+  Elf32_Addr e_entry;
+
+  // Program header table's file offset in bytes.
+  Elf32_Off e_phoff;
+
+  // Section header table's file offset in bytes.
+  Elf32_Off e_shoff;
+
+  // Processor-specific flags associated with the file.
+  Elf32_Word e_flags;
+
+  // ELF header's size in bytes.
+  Elf32_Half e_ehsize;
+
+  // Size in bytes of one entry in the file's program  header table
+  Elf32_Half e_phentsize;
+
+  // The number of entries in the program header table.
+  Elf32_Half e_phnum;
+
+  // Section header's size in bytes.
+  Elf32_Half e_shentsize;
+
+  // The number of entries in the section header table.
+  Elf32_Half e_shnum;
+
+  // The section header table index of the entry associated
+  // with the section name string table.
+  Elf32_Half e_shstrndx;
+} Elf32_Ehdr;
+
+// 64 bit ELF file header.
+typedef struct {
+  // The initial bytes mark the file as an object file and provide
+  // machine-independent data.
+  unsigned char e_ident[EI_NIDENT];
+
+  // The object file type. We support only ET_DYN.
+  Elf64_Half e_type;
+
+  // Architecture of the file.
+  Elf64_Half e_machine;
+
+  // Object file version. The value should be 1.
+  Elf64_Word e_version;
+
+  // Virtual address to which the system first transfers
+  // control, thus starting the process.
+  Elf64_Addr e_entry;
+
+  // Program header table's file offset in bytes.
+  Elf64_Off e_phoff;
+
+  // Section header table's file offset in bytes.
+  Elf64_Off e_shoff;
+
+  // Processor-specific flags associated with the file.
+  Elf64_Word e_flags;
+
+  // This member holds the ELF header's size in bytes.
+  Elf64_Half e_ehsize;
+
+  // Size in bytes of one entry in the file's program  header table
+  Elf64_Half e_phentsize;
+
+  // The number of entries in the section header table.
+  Elf64_Half e_phnum;
+
+  // Section header's size in bytes.
+  Elf64_Half e_shentsize;
+
+  // The number of entries in the section header table.
+  Elf64_Half e_shnum;
+
+  // The section header table index of the entry associated
+  // with the section name string table.
+  Elf64_Half e_shstrndx;
+} Elf64_Ehdr;
+
+// 32 bit Program header.
+typedef struct {
+  // The kind of segment this array element describes.
+  Elf32_Word p_type;
+
+  // The offset from the beginning of the file at which the
+  // first byte of the segment resides.
+  Elf32_Off p_offset;
+
+  // The virtual address at which the first byte of the
+  // segment resides in memory.
+  Elf32_Addr p_vaddr;
+
+  // Segment's physical address. Unused for shared libraries.
+  Elf32_Addr p_paddr;
+
+  // The number of bytes in the file image of the segment.  May be zero.
+  Elf32_Word p_filesz;
+
+  // The number of bytes in the memory image of the segment.  May be zero.
+  Elf32_Word p_memsz;
+
+  // Segment flags
+  Elf32_Word p_flags;
+
+  // Segment alignment constraint.
+  Elf32_Word p_align;
+} Elf32_Phdr;
+
+// 64 bit Program header.
+typedef struct {
+  // The kind of segment this array element describes.
+  Elf64_Word p_type;
+
+  // Segment flags
+  Elf64_Word p_flags;
+
+  // The offset from the beginning of the file at which the
+  // first byte of the segment resides.
+  Elf64_Off p_offset;
+
+  // The virtual address at which the first byte of the
+  // segment resides in memory.
+  Elf64_Addr p_vaddr;
+
+  // Segment's physical address. Unused for shared libraries.
+  Elf64_Addr p_paddr;
+
+  // The number of bytes in the file image of the segment. May be zero.
+  Elf64_Xword p_filesz;
+
+  // The number of bytes in the memory image of the segment.  May be zero.
+  Elf64_Xword p_memsz;
+
+  // Segment alignment constraint
+  Elf64_Xword p_align;
+} Elf64_Phdr;
+
+// 32 bit Dynamic Section Entry
+typedef struct {
+  // Controls the interpretation of d_un.
+  Elf32_Sword d_tag;
+  union {
+    // These objects represent integer values with various interpretations.
+    Elf32_Word d_val;
+    // These objects represent program virtual addresses.
+    Elf32_Addr d_ptr;
+  } d_un;
+} Elf32_Dyn;
+
+// 64 bit Dynamic Section Entry
+typedef struct {
+  // Controls the interpretation of d_un.
+  Elf64_Sxword d_tag;
+  union {
+    // These objects represent integer values with various interpretations.
+    Elf64_Xword d_val;
+    // These objects represent program virtual addresses.
+    Elf64_Addr d_ptr;
+  } d_un;
+} Elf64_Dyn;
+
+// 32 bit Symbol Table Entry
+typedef struct {
+  // An index into the object file's symbol string table,
+  // which holds the character representations of the symbol names. If the value
+  // is non-zero, it represents a string table index that gives the symbol name.
+  // Otherwise, the symbol table entry has no name.
+  Elf32_Word st_name;
+
+  // The value of the associated symbol. Depending on the
+  // context, this may be an absolute value, an address, and so on;
+  Elf32_Addr st_value;
+
+  // Many symbols have associated sizes. For example, a data object's size is
+  // the number of bytes contained in the object.
+  Elf32_Word st_size;
+
+  // The symbol's type and binding attributes.
+  unsigned char st_info;
+
+  // Symbol's visibility.
+  unsigned char st_other;
+
+  // Every symbol table entry is defined in relation to some section. This
+  // member holds the relevant section header table index.
+  Elf32_Half st_shndx;
+} Elf32_Sym;
+
+// 64 bit Symbol Table Entry
+typedef struct {
+  // An index into the object file's symbol string table,
+  // which holds the character representations of the symbol names. If the value
+  // is non-zero, it represents a string table index that gives the symbol name.
+  // Otherwise, the symbol table entry has no name.
+  Elf64_Word st_name;
+
+  // The symbol's type and binding attributes.
+  unsigned char st_info;
+
+  // Symbol's visibility.
+  unsigned char st_other;
+
+  // Every symbol table entry is defined in relation to some section. This
+  // member holds the relevant section header table index.
+  Elf64_Half st_shndx;
+
+  // The value of the associated symbol. Depending on the
+  // context, this may be an absolute value, an address, and so on;
+  Elf64_Addr st_value;
+
+  // Many symbols have associated sizes. For example, a data object's size is
+  // the number of bytes contained in the object.
+  Elf64_Xword st_size;
+} Elf64_Sym;
+
+// 32 bit Relocation Entry
+typedef struct {
+  // The location at which to apply the relocation action. For  a relocatable
+  // file, the value is the byte offset from the beginning of the  section to
+  // the storage unit affected by the relocation. For an executable file or a
+  // shared object, the value is the virtual address of the storage unit
+  // affected by the relocation.
+  Elf32_Addr r_offset;
+  // The symbol table index with respect to which the
+  // relocation must be made, and the type of relocation to apply.
+  Elf32_Word r_info;
+} Elf32_Rel;
+
+// 64 bit Relocation Entry
+typedef struct {
+  // The location at which to apply the relocation action. For
+  // a relocatable file, the value is the byte offset from the beginning of the
+  // section to the storage unit affected by the relocation. For an executable
+  // file or a shared object, the value is the virtual address of the storage
+  // unit affected by the relocation.
+  Elf64_Addr r_offset;
+
+  // The symbol table index with respect to which the
+  // relocation must be made, and the type of relocation to apply.
+  Elf64_Xword r_info;
+} Elf64_Rel;
+
+// 32 bit Relocation Entry with Addend.
+typedef struct {
+  // The location at which to apply the relocation action. For
+  // a relocatable file, the value is the byte offset from the beginning of the
+  // section to the storage unit affected by the relocation. For an executable
+  // file or a shared object, the value is the virtual address of the storage
+  // unit affected by the relocation.
+  Elf32_Addr r_offset;
+
+  // The symbol table index with respect to which the
+  // relocation must be made, and the type of relocation to apply.
+  Elf32_Word r_info;
+
+  // A constant addend used to compute the value to be stored into the
+  // relocatable field.
+  Elf32_Sword r_addend;
+} Elf32_Rela;
+
+// 64 bit Relocation Entry with Addend.
+typedef struct {
+  // The location at which to apply the relocation action. For  a relocatable
+  // file, the value is the byte offset from the beginning of the section to the
+  // storage unit affected by the relocation. For an executable file or a shared
+  // object, the value is the virtual address of the storage unit affected by
+  // the relocation.
+  Elf64_Addr r_offset;
+
+  // The symbol table index with respect to which the
+  // relocation must be made, and the type of relocation to apply.
+  Elf64_Xword r_info;
+
+  // A constant addend used to compute the value to be stored into the
+  // relocatable field.
+  Elf64_Sxword r_addend;
+} Elf64_Rela;
+
+#pragma pack(pop)
+
+#define EI_CLASS 4
+#define ELFCLASS32 1
+#define ELFCLASS64 2
+#define EI_DATA 5
+#define ELFDATA2LSB 1
+#define ET_DYN 3
+#define EV_CURRENT 1
+
+#if SB_SIZE_OF(POINTER) == 4
+typedef Elf32_Ehdr Ehdr;
+typedef Elf32_Phdr Phdr;
+typedef Elf32_Addr Addr;
+typedef Elf32_Dyn Dyn;
+typedef Elf32_Word Word;
+typedef Elf32_Sym Sym;
+typedef Elf32_Rel Rel;
+typedef Elf32_Rela Rela;
+typedef Elf32_Word Relr;
+typedef Elf32_Sword Sword;
+#define ELF_BITS 32
+#define ELF_R_TYPE ELF32_R_TYPE
+#define ELF_R_SYM ELF32_R_SYM
+#define ELF_CLASS_VALUE ELFCLASS32
+#elif SB_SIZE_OF(POINTER) == 8
+typedef Elf64_Ehdr Ehdr;
+typedef Elf64_Phdr Phdr;
+typedef Elf64_Addr Addr;
+typedef Elf64_Dyn Dyn;
+typedef Elf64_Word Word;
+typedef Elf64_Sym Sym;
+typedef Elf64_Rel Rel;
+typedef Elf64_Rela Rela;
+typedef Elf64_Word Relr;
+typedef Elf64_Sword Sword;
+#define ELF_BITS 64
+#define ELF_R_TYPE ELF64_R_TYPE
+#define ELF_R_SYM ELF64_R_SYM
+#define ELF_CLASS_VALUE ELFCLASS64
+#else
+#error "Unsupported pointer size"
+#endif
+
+#define ELF32_R_SYM(val) ((val) >> 8)
+#define ELF32_R_TYPE(val) ((val)&0xff)
+#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type)&0xff))
+
+#define ELF64_R_SYM(i) ((i) >> 32)
+#define ELF64_R_TYPE(i) ((i)&0xffffffff)
+#define ELF64_R_INFO(sym, type) ((((Elf64_Xword)(sym)) << 32) + (type))
+
+#define ELFMAG "\177ELF"
+#define SELFMAG 4
+
+// TODO: Refactor the code to detect it at runtime
+// using DT_PLTREL.
+#if SB_IS(ARCH_ARM64) || SB_IS(ARCH_X64)
+#define USE_RELA
+#endif
+
+#if defined(USE_RELA)
+typedef Rela rel_t;
+#else
+typedef Rel rel_t;
+#endif
+
+#if SB_IS(ARCH_ARM)
+#define ELF_MACHINE 40
+#elif SB_IS(ARCH_X86)
+#define ELF_MACHINE 3
+#elif SB_IS(ARCH_X64)
+#define ELF_MACHINE 62
+#elif SB_IS(ARCH_ARM64)
+#define ELF_MACHINE 183
+#else
+#error "Unsupported target CPU architecture"
+#endif
+
+// Segment types.
+typedef enum SegmentTypes {
+  // Unused segment.
+  PT_NULL = 0,
+
+  // Loadable segment.
+  PT_LOAD = 1,
+
+  // Dynamic linking information.
+  PT_DYNAMIC = 2,
+
+  // Interpreter pathname.
+  PT_INTERP = 3,
+
+  // Auxiliary information.
+  PT_NOTE = 4,
+
+  // Reserved.
+  PT_SHLIB = 5,
+
+  // The program header table itself.
+  PT_PHDR = 6,
+
+  // The thread-local storage template.
+  PT_TLS = 7
+} SegmentTypes;
+
+// Symbol bindings.
+typedef enum SymbolBindings {
+  // Local symbol, not visible outside obj file containing def
+  STB_LOCAL = 0,
+
+  // Global symbol, visible to all object files being combined
+  STB_GLOBAL = 1,
+
+  // Weak symbol, like global but lower-precedence
+  STB_WEAK = 2,
+
+  STB_GNU_UNIQUE = 10,
+
+  // Lowest operating system-specific binding type
+  STB_LOOS = 10,
+
+  // Highest operating system-specific binding type
+  STB_HIOS = 12,
+
+  // Lowest processor-specific binding type
+  STB_LOPROC = 13,
+
+  // Highest processor-specific binding type
+  STB_HIPROC = 15
+} SymbolBindings;
+
+#define ELF_ST_BIND(x) ((x) >> 4)
+#define ELF32_ST_BIND(x) ELF_ST_BIND(x)
+#define ELF64_ST_BIND(x) ELF_ST_BIND(x)
+
+#define PF_X (1 << 0)
+#define PF_W (1 << 1)
+#define PF_R (1 << 2)
+#define PF_MASKOS 0x0ff00000
+#define PF_MASKPROC 0xf0000000
+
+// Dynamic table tags.
+typedef enum DynamicTags {
+  DT_NULL = 0,
+  DT_NEEDED = 1,
+  DT_PLTRELSZ = 2,
+  DT_PLTGOT = 3,
+  DT_HASH = 4,
+  DT_STRTAB = 5,
+  DT_SYMTAB = 6,
+  DT_RELA = 7,
+  DT_RELASZ = 8,
+  DT_RELAENT = 9,
+  DT_STRSZ = 10,
+  DT_SYMENT = 11,
+  DT_INIT = 12,
+  DT_FINI = 13,
+  DT_SONAME = 14,
+  DT_RPATH = 15,
+  DT_SYMBOLIC = 16,
+  DT_REL = 17,
+  DT_RELSZ = 18,
+  DT_RELENT = 19,
+  DT_PLTREL = 20,
+  DT_DEBUG = 21,
+  DT_TEXTREL = 22,
+  DT_JMPREL = 23,
+  DT_BIND_NOW = 24,
+  DT_INIT_ARRAY = 25,
+  DT_FINI_ARRAY = 26,
+  DT_INIT_ARRAYSZ = 27,
+  DT_FINI_ARRAYSZ = 28,
+  DT_RUNPATH = 29,
+  DT_FLAGS = 30,
+  DT_ENCODING = 32,
+  DT_PREINIT_ARRAY = 32,
+  DT_PREINIT_ARRAYSZ = 33,
+  DT_SYMTAB_SHNDX = 34,
+  DT_RELRSZ = 35,
+  DT_RELR = 36,
+  DT_RELRENT = 37,
+  DT_LOOS = 0x6000000D,
+  DT_ANDROID_REL = 0x6000000F,
+  DT_ANDROID_RELSZ = 0x60000010,
+  DT_ANDROID_RELA = 0x60000011,
+  DT_ANDROID_RELASZ = 0x60000012,
+  DT_HIOS = 0x6ffff000,
+  DT_ANDROID_RELR = 0x6fffe000,
+  DT_ANDROID_RELRSZ = 0x6fffe001,
+  DT_ANDROID_RELRENT = 0x6fffe003,
+  DT_GNU_HASH = 0x6ffffef5,
+  DT_LOPROC = 0x70000000,
+  DT_HIPROC = 0x7fffffff,
+} DynamicTags;
+
+typedef enum DynamicFlags {
+  // This flag signifies that the object being loaded may make reference to the
+  DF_ORIGIN = 0x00000001,
+
+  // If this flag is set in a shared object library, the dynamic linker's symbol
+  // resolution algorithm for references within the library is changed. Instead
+  // of starting a symbol search with the executable file, the dynamic linker
+  // starts from the shared object itself. If the shared object fails to supply
+  // the referenced symbol, the dynamic linker then searches the executable file
+  // and other shared objects as usual.
+  DF_SYMBOLIC = 0x00000002,
+
+  //  This flag is not set, no relocation entry should cause a modification to a
+  //  non-writable segment, as specified by the segment permissions in the
+  //  program header table.
+  DF_TEXTREL = 0x00000004,
+
+  // If set in a shared object or executable, this flag instructs the dynamic
+  // linker to process all relocations for the object containing this entry
+  // before transferring control to the program.
+  DF_BIND_NOW = 0x00000008,
+
+  // If set in a shared object or executable, this flag instructs the dynamic
+  // linker to reject attempts to load this file dynamically. It indicates that
+  // the shared object or executable contains code using a static thread-local
+  // storage scheme. Implementations need not support any form of thread-local
+  // storage.
+  DF_STATIC_TLS = 0x00000010,
+} DynamicFalgs;
+
+// Relocation types per CPU architecture
+#if SB_IS(ARCH_ARM)
+typedef enum RelocationTypes {
+  R_ARM_ABS32 = 2,
+  R_ARM_REL32 = 3,
+  R_ARM_GLOB_DAT = 21,
+  R_ARM_JUMP_SLOT = 22,
+  R_ARM_COPY = 20,
+  R_ARM_RELATIVE = 23,
+} RelocationTypes;
+#elif SB_IS(ARCH_ARM64)
+typedef enum RelocationTypes {
+  R_AARCH64_ABS64 = 257,
+  R_AARCH64_COPY = 1024,
+  R_AARCH64_GLOB_DAT = 1025,
+  R_AARCH64_JUMP_SLOT = 1026,
+  R_AARCH64_RELATIVE = 1027,
+} RelocationTypes;
+#elif SB_IS(ARCH_X86)
+typedef enum RelocationTypes {
+  R_386_32 = 1,
+  R_386_PC32 = 2,
+  R_386_GLOB_DAT = 6,
+  R_386_JMP_SLOT = 7,
+  R_386_RELATIVE = 8,
+} RelocationTypes;
+#elif SB_IS(ARCH_X64)
+typedef enum RelocationTypes {
+  R_X86_64_64 = 1,
+  R_X86_64_PC32 = 2,
+  R_X86_64_GLOB_DAT = 6,
+  R_X86_64_JMP_SLOT = 7,
+  R_X86_64_RELATIVE = 8,
+} RelocationTypes;
+#else
+#error "Unsupported architecture for relocations."
+#endif
+
+// Helper macros for memory page computations.
+#ifndef PAGE_SIZE
+#define PAGE_SHIFT 12
+
+#if SB_SIZE_OF(POINTER) == 4
+#define PAGE_SIZE (1UL << PAGE_SHIFT)
+#elif SB_SIZE_OF(POINTER) == 8
+#define PAGE_SIZE (1ULL << PAGE_SHIFT)
+#else
+#error "Unsupported pointer size"
+#endif
+#endif
+
+#ifndef PAGE_MASK
+#define PAGE_MASK (~(PAGE_SIZE - 1))
+#endif
+
+#define PAGE_START(x) ((x)&PAGE_MASK)
+#define PAGE_OFFSET(x) ((x) & ~PAGE_MASK)
+#define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE - 1))
+
+#define SHN_UNDEF 0
+
+}  // namespace elf_loader
+}  // namespace starboard
+#endif  // STARBOARD_ELF_LOADER_ELF_H_
diff --git a/src/starboard/elf_loader/elf_hash_table.cc b/src/starboard/elf_loader/elf_hash_table.cc
new file mode 100644
index 0000000..b6b6c70
--- /dev/null
+++ b/src/starboard/elf_loader/elf_hash_table.cc
@@ -0,0 +1,69 @@
+// 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 "starboard/elf_loader/elf_hash_table.h"
+#include "starboard/string.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// Compute the ELF hash of a given symbol.
+// Defined in
+// https://refspecs.linuxfoundation.org/elf/gabi4+/ch5.dynamic.html#hash
+static unsigned ElfHash(const char* name) {
+  const uint8_t* ptr = reinterpret_cast<const uint8_t*>(name);
+  unsigned h = 0;
+  while (*ptr) {
+    h = (h << 4) + *ptr++;
+    unsigned g = h & 0xf0000000;
+    h ^= g;
+    h ^= g >> 24;
+  }
+  return h;
+}
+
+ElfHashTable::ElfHashTable()
+    : hash_bucket_(NULL),
+      hash_bucket_size_(0),
+      hash_chain_(NULL),
+      hash_chain_size_(0) {}
+void ElfHashTable::Init(uintptr_t dt_elf_hash) {
+  const Word* data = reinterpret_cast<const Word*>(dt_elf_hash);
+  hash_bucket_size_ = data[0];
+  hash_bucket_ = data + 2;
+  hash_chain_size_ = data[1];
+  hash_chain_ = hash_bucket_ + hash_bucket_size_;
+}
+
+bool ElfHashTable::IsValid() const {
+  return hash_bucket_size_ > 0;
+}
+
+const Sym* ElfHashTable::LookupByName(const char* symbol_name,
+                                      const Sym* symbol_table,
+                                      const char* string_table) const {
+  unsigned hash = ElfHash(symbol_name);
+
+  for (unsigned n = hash_bucket_[hash % hash_bucket_size_]; n != 0;
+       n = hash_chain_[n]) {
+    const Sym* symbol = &symbol_table[n];
+    // Check that the symbol has the appropriate name.
+    if (!SbStringCompareAll(string_table + symbol->st_name, symbol_name))
+      return symbol;
+  }
+  return NULL;
+}
+
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/elf_hash_table.h b/src/starboard/elf_loader/elf_hash_table.h
new file mode 100644
index 0000000..14bb9ca
--- /dev/null
+++ b/src/starboard/elf_loader/elf_hash_table.h
@@ -0,0 +1,62 @@
+// 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.
+
+#ifndef STARBOARD_ELF_LOADER_ELF_HASH_TABLE_H_
+#define STARBOARD_ELF_LOADER_ELF_HASH_TABLE_H_
+
+#include <stddef.h>
+#include "starboard/elf_loader/elf.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// Models the hash table used to map symbol names to symbol entries using
+// the standard ELF format.
+class ElfHashTable {
+ public:
+  ElfHashTable();
+  // Initialize instance. |dt_elf_hash| should be the address that the
+  // DT_HASH entry points to in the input ELF dynamic section. Call IsValid()
+  // to determine whether the table was well-formed.
+  void Init(uintptr_t dt_elf_hash);
+
+  // Returns true iff the content of the table is valid.
+  bool IsValid() const;
+
+  // Index of the first dynamic symbol within the ELF symbol table.
+  size_t dyn_symbols_offset() const { return 1; }
+
+  // Number of dynamic symbols in the ELF symbol table.
+  size_t dyn_symbols_count() const { return hash_chain_size_ - 1; }
+
+  // Lookup |symbol_name| in the table. |symbol_table| should point to the
+  // ELF symbol table, and |string_table| to the start of its string table.
+  // Returns NULL on failure.
+  const Sym* LookupByName(const char* symbol_name,
+                          const Sym* symbol_table,
+                          const char* string_table) const;
+
+ private:
+  const Word* hash_bucket_;
+  size_t hash_bucket_size_;
+  const Word* hash_chain_;
+  size_t hash_chain_size_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(ElfHashTable);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_ELF_HASH_TABLE_H_
diff --git a/src/starboard/elf_loader/elf_header.cc b/src/starboard/elf_loader/elf_header.cc
new file mode 100644
index 0000000..74695ca
--- /dev/null
+++ b/src/starboard/elf_loader/elf_header.cc
@@ -0,0 +1,76 @@
+// 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 "starboard/elf_loader/elf_header.h"
+
+#include "starboard/common/log.h"
+#include "starboard/elf_loader/log.h"
+#include "starboard/memory.h"
+
+namespace starboard {
+namespace elf_loader {
+
+ElfHeader::ElfHeader() {
+  elf_header_.reset(new Ehdr());
+}
+
+bool ElfHeader::LoadElfHeader(File* file) {
+  SB_DLOG(INFO) << "LoadElfHeader";
+  if (!file->ReadFromOffset(0, reinterpret_cast<char*>(elf_header_.get()),
+                            sizeof(Ehdr))) {
+    SB_LOG(ERROR) << "Failed to read file";
+    return false;
+  }
+
+  if (SbMemoryCompare(elf_header_->e_ident, ELFMAG, SELFMAG) != 0) {
+    SB_LOG(ERROR) << "Bad ELF magic: " << elf_header_->e_ident;
+    return false;
+  }
+
+  if (elf_header_->e_ident[EI_CLASS] != ELF_CLASS_VALUE) {
+    SB_LOG(ERROR) << "Not a " << ELF_BITS
+                  << "-bit class: " << elf_header_->e_ident[EI_CLASS];
+    return false;
+  }
+  if (elf_header_->e_ident[EI_DATA] != ELFDATA2LSB) {
+    SB_LOG(ERROR) << "Not little-endian class" << elf_header_->e_ident[EI_DATA];
+    return false;
+  }
+
+  if (elf_header_->e_type != ET_DYN) {
+    SB_LOG(ERROR) << "Not a shared library type:" << std::hex
+                  << elf_header_->e_type;
+    return false;
+  }
+
+  if (elf_header_->e_version != EV_CURRENT) {
+    SB_LOG(ERROR) << "Unexpected ELF version: " << elf_header_->e_version;
+    return false;
+  }
+
+  if (elf_header_->e_machine != ELF_MACHINE) {
+    SB_LOG(ERROR) << "Unexpected ELF machine type: " << std::hex
+                  << elf_header_->e_machine;
+    return false;
+  }
+
+  return true;
+}
+
+const Ehdr* ElfHeader::GetHeader() {
+  return elf_header_.get();
+}
+
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/elf_header.h b/src/starboard/elf_loader/elf_header.h
new file mode 100644
index 0000000..a8b666c
--- /dev/null
+++ b/src/starboard/elf_loader/elf_header.h
@@ -0,0 +1,45 @@
+// 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.
+
+#ifndef STARBOARD_ELF_LOADER_ELF_HEADER_H_
+#define STARBOARD_ELF_LOADER_ELF_HEADER_H_
+
+#include "starboard/elf_loader/elf.h"
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/elf_loader/file.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// Class for loading, parsing and validating the ELF header section.
+class ElfHeader {
+ public:
+  ElfHeader();
+
+  // Load, parse and validate the ELF header.
+  bool LoadElfHeader(File* file);
+
+  // Get the actual header data.
+  const Ehdr* GetHeader();
+
+ private:
+  scoped_ptr<Ehdr> elf_header_;
+  SB_DISALLOW_COPY_AND_ASSIGN(ElfHeader);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_ELF_HEADER_H_
diff --git a/src/starboard/elf_loader/elf_header_test.cc b/src/starboard/elf_loader/elf_header_test.cc
new file mode 100644
index 0000000..b2b94d2
--- /dev/null
+++ b/src/starboard/elf_loader/elf_header_test.cc
@@ -0,0 +1,130 @@
+// 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 "starboard/elf_loader/elf_header.h"
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/elf_loader/file.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_API_VERSION >= 12 &&                                         \
+    (SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP)) && \
+    SB_CAN(MAP_EXECUTABLE_MEMORY)
+namespace starboard {
+namespace elf_loader {
+
+namespace {
+
+class DummyFile : public File {
+ public:
+  DummyFile(const char* buffer, int size) : buffer_(buffer), size_(size) {}
+
+  bool Open(const char* name) { return true; }
+  bool ReadFromOffset(int64_t offset, char* buffer, int size) {
+    SB_LOG(INFO) << "ReadFromOffset";
+    if (offset != 0) {
+      SB_LOG(ERROR) << "ReadFromOffset: Invalid offset " << offset;
+      return false;
+    }
+    if (size < size_) {
+      SB_LOG(ERROR) << "ReadFromOffset: Invalid size " << size;
+      return false;
+    }
+    SbMemoryCopy(buffer, buffer_, size);
+    return true;
+  }
+  void Close() {}
+
+ private:
+  const char* buffer_;
+  int size_;
+};
+
+class ElfHeaderTest : public ::testing::Test {
+ protected:
+  ElfHeaderTest() {
+    elf_header_.reset(new ElfHeader());
+    SbMemorySet(reinterpret_cast<char*>(&ehdr_data_), 0, sizeof(ehdr_data_));
+    ehdr_data_.e_machine = ELF_MACHINE;
+    ehdr_data_.e_ident[0] = 0x7F;
+    ehdr_data_.e_ident[1] = 'E';
+    ehdr_data_.e_ident[2] = 'L';
+    ehdr_data_.e_ident[3] = 'F';
+    ehdr_data_.e_ident[EI_CLASS] = ELF_CLASS_VALUE;
+    ehdr_data_.e_ident[EI_DATA] = ELFDATA2LSB;
+    ehdr_data_.e_type = ET_DYN;
+    ehdr_data_.e_version = EV_CURRENT;
+    ehdr_data_.e_machine = ELF_MACHINE;
+
+    dummy_file_.reset(new DummyFile(reinterpret_cast<const char*>(&ehdr_data_),
+                                    sizeof(ehdr_data_)));
+  }
+  ~ElfHeaderTest() {}
+
+  scoped_ptr<ElfHeader> elf_header_;
+  Ehdr ehdr_data_;
+  scoped_ptr<DummyFile> dummy_file_;
+};
+
+TEST_F(ElfHeaderTest, Initialize) {
+  EXPECT_TRUE(elf_header_->LoadElfHeader(dummy_file_.get()));
+}
+
+TEST_F(ElfHeaderTest, NegativeBadImage) {
+  ehdr_data_.e_ident[1] = 'F';
+  dummy_file_.reset(new DummyFile(reinterpret_cast<const char*>(&ehdr_data_),
+                                  sizeof(ehdr_data_)));
+  EXPECT_FALSE(elf_header_->LoadElfHeader(dummy_file_.get()));
+}
+
+TEST_F(ElfHeaderTest, NegativeBadClass) {
+  ehdr_data_.e_ident[EI_CLASS] = 0;
+  dummy_file_.reset(new DummyFile(reinterpret_cast<const char*>(&ehdr_data_),
+                                  sizeof(ehdr_data_)));
+  EXPECT_FALSE(elf_header_->LoadElfHeader(dummy_file_.get()));
+}
+
+TEST_F(ElfHeaderTest, NegativeWrongGulliverLilliput) {
+  ehdr_data_.e_type = 2;
+  dummy_file_.reset(new DummyFile(reinterpret_cast<const char*>(&ehdr_data_),
+                                  sizeof(ehdr_data_)));
+  EXPECT_FALSE(elf_header_->LoadElfHeader(dummy_file_.get()));
+}
+
+TEST_F(ElfHeaderTest, NegativeBadType) {
+  ehdr_data_.e_type = 2;
+  dummy_file_.reset(new DummyFile(reinterpret_cast<const char*>(&ehdr_data_),
+                                  sizeof(ehdr_data_)));
+  EXPECT_FALSE(elf_header_->LoadElfHeader(dummy_file_.get()));
+}
+
+TEST_F(ElfHeaderTest, NegativeBadVersion) {
+  ehdr_data_.e_version = 2;
+  dummy_file_.reset(new DummyFile(reinterpret_cast<const char*>(&ehdr_data_),
+                                  sizeof(ehdr_data_)));
+  EXPECT_FALSE(elf_header_->LoadElfHeader(dummy_file_.get()));
+}
+
+TEST_F(ElfHeaderTest, NegativeBadMachine) {
+  ehdr_data_.e_machine = 0;
+  dummy_file_.reset(new DummyFile(reinterpret_cast<const char*>(&ehdr_data_),
+                                  sizeof(ehdr_data_)));
+  EXPECT_FALSE(elf_header_->LoadElfHeader(dummy_file_.get()));
+}
+}  // namespace
+}  // namespace elf_loader
+}  // namespace starboard
+#endif  // SB_API_VERSION >= 12 && (SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION
+        // || SB_HAS(MMAP)) && SB_CAN(MAP_EXECUTABLE_MEMORY)
diff --git a/src/starboard/elf_loader/elf_loader.cc b/src/starboard/elf_loader/elf_loader.cc
new file mode 100644
index 0000000..e86fb77
--- /dev/null
+++ b/src/starboard/elf_loader/elf_loader.cc
@@ -0,0 +1,105 @@
+// 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 "starboard/elf_loader/elf_loader.h"
+
+#include <vector>
+
+#include "starboard/atomic.h"
+#include "starboard/common/log.h"
+#include "starboard/configuration_constants.h"
+#include "starboard/elf_loader/elf_loader_impl.h"
+#include "starboard/elf_loader/evergreen_config.h"
+#include "starboard/elf_loader/file_impl.h"
+
+namespace starboard {
+namespace elf_loader {
+
+ElfLoader* ElfLoader::g_instance = NULL;
+
+ElfLoader::ElfLoader() {
+  ElfLoader* old_instance =
+      reinterpret_cast<ElfLoader*>(SbAtomicAcquire_CompareAndSwapPtr(
+          reinterpret_cast<SbAtomicPtr*>(&g_instance),
+          reinterpret_cast<SbAtomicPtr>(reinterpret_cast<void*>(NULL)),
+          reinterpret_cast<SbAtomicPtr>(this)));
+  SB_DCHECK(!old_instance);
+  SB_UNREFERENCED_PARAMETER(old_instance);
+
+  impl_.reset(new ElfLoaderImpl());
+}
+
+ElfLoader::~ElfLoader() {
+  ElfLoader* old_instance =
+      reinterpret_cast<ElfLoader*>(SbAtomicAcquire_CompareAndSwapPtr(
+          reinterpret_cast<SbAtomicPtr*>(&g_instance),
+          reinterpret_cast<SbAtomicPtr>(this),
+          reinterpret_cast<SbAtomicPtr>(reinterpret_cast<void*>(NULL))));
+  SB_DCHECK(old_instance);
+  SB_DCHECK(old_instance == this);
+}
+
+ElfLoader* ElfLoader::Get() {
+  ElfLoader* elf_loader = reinterpret_cast<ElfLoader*>(
+      SbAtomicAcquire_LoadPtr(reinterpret_cast<SbAtomicPtr*>(&g_instance)));
+  SB_DCHECK(elf_loader);
+  return elf_loader;
+}
+
+bool ElfLoader::Load(const std::string& library_path,
+                     const std::string& content_path,
+                     bool is_relative_path,
+                     const void* (*custom_get_extension)(const char* name)) {
+  if (is_relative_path) {
+    library_path_ = MakeRelativeToContentPath(library_path);
+    content_path_ = MakeRelativeToContentPath(content_path);
+  } else {
+    library_path_ = library_path;
+    content_path_ = content_path;
+  }
+
+  if (library_path_.empty() || content_path_.empty()) {
+    SB_LOG(ERROR) << "|library_path_| and |content_path_| cannot be empty.";
+    return false;
+  }
+  EvergreenConfig::Create(library_path_.c_str(), content_path_.c_str(),
+                          custom_get_extension);
+  SB_LOG(INFO) << "evergreen_config: content_path=" << content_path_;
+  return impl_->Load(library_path_.c_str(), custom_get_extension);
+}
+
+void* ElfLoader::LookupSymbol(const char* symbol) {
+  return impl_->LookupSymbol(symbol);
+}
+
+std::string ElfLoader::MakeRelativeToContentPath(const std::string& path) {
+  std::vector<char> content_path(kSbFileMaxPath);
+
+  if (!SbSystemGetPath(kSbSystemPathContentDirectory, content_path.data(),
+                       kSbFileMaxPath)) {
+    SB_LOG(ERROR) << "Failed to make '" << path.data()
+                  << "' relative to the ELF Loader content directory.";
+    return "";
+  }
+
+  std::string relative_path(content_path.data());
+
+  relative_path.push_back(kSbFileSepChar);
+  relative_path.append(path);
+
+  return relative_path;
+}
+
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/elf_loader.gyp b/src/starboard/elf_loader/elf_loader.gyp
new file mode 100644
index 0000000..afb5bd6
--- /dev/null
+++ b/src/starboard/elf_loader/elf_loader.gyp
@@ -0,0 +1,179 @@
+# 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.
+
+{
+  'variables': {
+    'common_elf_loader_sources': [
+        'elf_header.h',
+        'elf_header.cc',
+        'elf_hash_table.h',
+        'elf_hash_table.cc',
+        'elf_loader.h',
+        'elf_loader.cc',
+        'elf_loader_switches.h',
+        'elf_loader_switches.cc',
+        'exported_symbols.cc',
+        'file.h',
+        'file_impl.h',
+        'file_impl.cc',
+        'gnu_hash_table.h',
+        'gnu_hash_table.cc',
+        'dynamic_section.h',
+        'dynamic_section.cc',
+        'program_table.h',
+        'program_table.cc',
+        'relocations.h',
+        'relocations.cc',
+    ],
+    'elf_loader_impl_sources': [
+        'elf_loader_impl.h',
+        'elf_loader_impl.cc',
+    ],
+    'elf_loader_sys_sources': [
+        'elf_loader_sys_impl.h',
+        'elf_loader_sys_impl.cc',
+    ]
+  },
+  'targets': [
+    {
+      'target_name': 'elf_loader',
+      'type': 'static_library',
+      'include_dirs': [
+        'src/include',
+        'src/src/',
+      ],
+      'dependencies': [
+        '<(DEPTH)/starboard/elf_loader/evergreen_config.gyp:evergreen_config',
+        '<(DEPTH)/starboard/elf_loader/evergreen_info.gyp:evergreen_info',
+        '<(DEPTH)/starboard/starboard.gyp:starboard',
+      ],
+      'sources': [
+        '<@(common_elf_loader_sources)',
+        '<@(elf_loader_impl_sources)',
+      ],
+    },
+    {
+      # System loader based on dlopen/dlsym.
+      # Should be used only for debugging/troubleshooting.
+      'target_name': 'elf_loader_sys',
+      'type': 'static_library',
+      'include_dirs': [
+        'src/include',
+        'src/src/',
+      ],
+      'dependencies': [
+        '<(DEPTH)/starboard/elf_loader/evergreen_config.gyp:evergreen_config',
+        '<(DEPTH)/starboard/starboard.gyp:starboard',
+      ],
+      'sources': [
+        '<@(common_elf_loader_sources)',
+        '<@(elf_loader_sys_sources)',
+      ],
+    },
+    {
+      'target_name': 'elf_loader_sandbox',
+      'type': '<(final_executable_type)',
+      'include_dirs': [
+        'src/include',
+        'src/src/',
+      ],
+      'dependencies': [
+        'elf_loader',
+        '<(DEPTH)/starboard/starboard.gyp:starboard_full',
+        # TODO: Remove this dependency once MediaSession is migrated to use CobaltExtensions.
+        '<@(cobalt_platform_dependencies)',
+      ],
+      'sources': [
+        'sandbox.cc',
+      ],
+    },
+    {
+      'target_name': 'elf_loader_sandbox_deploy',
+      'type': 'none',
+      'dependencies': [
+        'elf_loader_sandbox',
+      ],
+      'variables': {
+        'executable_name': 'elf_loader_sandbox',
+      },
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+    },
+    {
+      # 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
+      'target_name': 'elf_loader_sys_sandbox',
+      'type': '<(final_executable_type)',
+      'include_dirs': [
+        'src/include',
+        'src/src/',
+      ],
+      'dependencies': [
+        'elf_loader_sys',
+        '<(DEPTH)/starboard/starboard.gyp:starboard_full',
+      ],
+      'sources': [
+        'sandbox.cc',
+      ],
+      'ldflags': [
+        '-Wl,--dynamic-list=<(DEPTH)/starboard/starboard.syms',
+        '-ldl' ,
+      ],
+    },
+    {
+      'target_name': 'elf_loader_test',
+      'type': '<(gtest_target_type)',
+      'sources': [
+        '<(DEPTH)/starboard/common/test_main.cc',
+      ],
+      'dependencies': [
+        '<(DEPTH)/starboard/starboard.gyp:starboard_full',
+        # TODO: Remove this dependency once MediaSession is migrated to use CobaltExtensions.
+        '<@(cobalt_platform_dependencies)',
+        '<(DEPTH)/testing/gmock.gyp:gmock',
+        '<(DEPTH)/testing/gtest.gyp:gtest',
+      ],
+      'conditions': [
+        ['target_arch in ["x86", "x64", "arm", "arm64"] ', {
+          'sources': [
+            'elf_loader_test.cc',
+            'elf_header_test.cc',
+            'dynamic_section_test.cc',
+            'program_table_test.cc',
+            'relocations_test.cc',
+          ],
+          'dependencies': [
+            'elf_loader',
+          ],
+        }],
+      ],
+    },
+    {
+      'target_name': 'elf_loader_test_deploy',
+      'type': 'none',
+      'dependencies': [
+        'elf_loader_test',
+      ],
+      'variables': {
+        'executable_name': 'elf_loader_test',
+      },
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+    },
+  ]
+}
diff --git a/src/starboard/elf_loader/elf_loader.h b/src/starboard/elf_loader/elf_loader.h
new file mode 100644
index 0000000..7119651
--- /dev/null
+++ b/src/starboard/elf_loader/elf_loader.h
@@ -0,0 +1,75 @@
+// 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.
+
+#ifndef STARBOARD_ELF_LOADER_ELF_LOADER_H_
+#define STARBOARD_ELF_LOADER_ELF_LOADER_H_
+
+#include <string>
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/configuration.h"
+
+namespace starboard {
+namespace elf_loader {
+
+class ElfLoaderImpl;
+
+// A loader for ELF dynamic shared library.
+class ElfLoader {
+ public:
+  ElfLoader();
+  ~ElfLoader();
+
+  // Gets the current instance of the ELF Loader. SB_DCHECKS if called before
+  // the ELF Loader has been constructed.
+  static ElfLoader* Get();
+
+  // Loads the shared library. Returns false if |library_path| or |content_path|
+  // is empty, or if the library could not be loaded.
+  // An optional |custom_get_extension| function pointer can be passed in order
+  // to override the |SbSystemGetExtension| function.
+  bool Load(const std::string& library_path,
+            const std::string& content_path,
+            bool is_relative_path,
+            const void* (*custom_get_extension)(const char* name) = NULL);
+
+  // Looks up the symbol address in the
+  // shared library.
+  void* LookupSymbol(const char* symbol);
+
+  const std::string& GetLibraryPath() const { return library_path_; }
+  const std::string& GetContentPath() const { return content_path_; }
+
+ private:
+  // Adjusts |path| to be relative to the content directory of the ELF Loader.
+  // Returns an empty string on error.
+  std::string MakeRelativeToContentPath(const std::string& path);
+
+  // The paths to the loaded shared library and it's content.
+  std::string library_path_;
+  std::string content_path_;
+
+  // The ELF Loader implementation.
+  scoped_ptr<ElfLoaderImpl> impl_;
+
+  // The single ELF Loader instance.
+  static ElfLoader* g_instance;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(ElfLoader);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_ELF_LOADER_H_
diff --git a/src/starboard/elf_loader/elf_loader_impl.cc b/src/starboard/elf_loader/elf_loader_impl.cc
new file mode 100644
index 0000000..091b154
--- /dev/null
+++ b/src/starboard/elf_loader/elf_loader_impl.cc
@@ -0,0 +1,146 @@
+// 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 "starboard/elf_loader/elf_loader_impl.h"
+#include "starboard/common/log.h"
+#include "starboard/elf_loader/elf.h"
+#include "starboard/elf_loader/file_impl.h"
+#include "starboard/elf_loader/log.h"
+#include "starboard/memory.h"
+#include "starboard/string.h"
+
+namespace starboard {
+namespace elf_loader {
+
+ElfLoaderImpl::ElfLoaderImpl() {
+#if SB_API_VERSION < 12 ||                                           \
+    !(SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP)) || \
+    !SB_CAN(MAP_EXECUTABLE_MEMORY)
+  SB_CHECK(false) << "The elf_loader requires SB_API_VERSION >= 12 with "
+                     "executable memory map support!";
+#endif
+}
+
+bool ElfLoaderImpl::Load(
+    const char* name,
+    const void* (*custom_get_extension)(const char* name)) {
+  SB_LOG(INFO) << "Loading: " << name;
+  elf_file_.reset(new FileImpl());
+  elf_file_->Open(name);
+
+  elf_header_loader_.reset(new ElfHeader());
+  if (!elf_header_loader_->LoadElfHeader(elf_file_.get())) {
+    SB_LOG(ERROR) << "Failed to loaded ELF header";
+    return false;
+  }
+
+  SB_DLOG(INFO) << "Loaded ELF header";
+
+  program_table_.reset(new ProgramTable());
+  program_table_->LoadProgramHeader(elf_header_loader_->GetHeader(),
+                                    elf_file_.get());
+
+  SB_DLOG(INFO) << "Loaded Program header";
+
+  if (!program_table_->ReserveLoadMemory()) {
+    SB_LOG(ERROR) << "Failed to reserve memory space";
+    return false;
+  }
+
+  SB_DLOG(INFO) << "Reserved address space";
+
+  if (!program_table_->LoadSegments(elf_file_.get())) {
+    SB_LOG(ERROR) << "Failed to load segments";
+    return false;
+  }
+  SB_DLOG(INFO) << "Loaded segments";
+
+  Dyn* dynamic = NULL;
+  size_t dynamic_count = 0;
+  Word dynamic_flags = 0;
+  program_table_->GetDynamicSection(&dynamic, &dynamic_count, &dynamic_flags);
+  if (!dynamic) {
+    SB_LOG(ERROR) << "No PT_DYNAMIC section!";
+    return false;
+  }
+  dynamic_section_.reset(
+      new DynamicSection(program_table_->GetBaseMemoryAddress(), dynamic,
+                         dynamic_count, dynamic_flags));
+  if (!dynamic_section_->InitDynamicSection()) {
+    SB_LOG(ERROR) << "Failed to initialize dynamic section";
+    return false;
+  }
+  SB_DLOG(INFO) << "Initialized dynamic section";
+
+  exported_symbols_.reset(new ExportedSymbols(custom_get_extension));
+  relocations_.reset(new Relocations(program_table_->GetBaseMemoryAddress(),
+                                     dynamic_section_.get(),
+                                     exported_symbols_.get()));
+  if (!relocations_->InitRelocations()) {
+    SB_LOG(ERROR) << "Failed to initialize relocations";
+    return false;
+  }
+  if (relocations_->HasTextRelocations()) {
+    SB_DLOG(INFO) << "HasTextRelocations";
+    // Adjust the memory protection to its to allow modifications.
+    if (program_table_->AdjustMemoryProtectionOfReadOnlySegments(
+            kSbMemoryMapProtectWrite) < 0) {
+      SB_LOG(ERROR) << "Unable to make segments writable";
+      return false;
+    }
+  }
+  SB_DLOG(INFO) << "Loaded relocations";
+  if (!relocations_->ApplyAllRelocations()) {
+    SB_LOG(ERROR) << "Failed to apply relocations";
+    return false;
+  }
+
+  if (relocations_->HasTextRelocations()) {
+    // Restores the memory protection to its original state.
+#if SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP)
+    if (program_table_->AdjustMemoryProtectionOfReadOnlySegments(
+            kSbMemoryMapProtectReserved) < 0) {
+      SB_LOG(ERROR) << "Unable to restore segment protection";
+      return false;
+    }
+#endif
+  }
+
+  SB_DLOG(INFO) << "Applied relocations";
+
+  program_table_->PublishEvergreenInfo(name);
+  SB_DLOG(INFO) << "Published Evergreen Info";
+
+  SB_DLOG(INFO) << "Call constructors";
+  dynamic_section_->CallConstructors();
+
+  SB_DLOG(INFO) << "Finished loading";
+
+  return true;
+}
+void* ElfLoaderImpl::LookupSymbol(const char* symbol) {
+  const Sym* sym = dynamic_section_->LookupByName(symbol);
+  void* address = NULL;
+  if (sym) {
+    address = reinterpret_cast<void*>(program_table_->GetBaseMemoryAddress() +
+                                      sym->st_value);
+  }
+  return address;
+}
+
+ElfLoaderImpl::~ElfLoaderImpl() {
+  dynamic_section_->CallDestructors();
+}
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/elf_loader_impl.h b/src/starboard/elf_loader/elf_loader_impl.h
new file mode 100644
index 0000000..da9c5f3
--- /dev/null
+++ b/src/starboard/elf_loader/elf_loader_impl.h
@@ -0,0 +1,54 @@
+// 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.
+
+#ifndef STARBOARD_ELF_LOADER_ELF_LOADER_IMPL_H_
+#define STARBOARD_ELF_LOADER_ELF_LOADER_IMPL_H_
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/elf_loader/dynamic_section.h"
+#include "starboard/elf_loader/elf.h"
+#include "starboard/elf_loader/elf_hash_table.h"
+#include "starboard/elf_loader/elf_header.h"
+#include "starboard/elf_loader/exported_symbols.h"
+#include "starboard/elf_loader/file.h"
+#include "starboard/elf_loader/gnu_hash_table.h"
+#include "starboard/elf_loader/program_table.h"
+#include "starboard/elf_loader/relocations.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// Implementation of the elf loader.
+class ElfLoaderImpl {
+ public:
+  ElfLoaderImpl();
+  bool Load(const char* file_name,
+            const void* (*custom_get_extension)(const char* name));
+  void* LookupSymbol(const char* symbol);
+  ~ElfLoaderImpl();
+
+ private:
+  scoped_ptr<File> elf_file_;
+  scoped_ptr<ElfHeader> elf_header_loader_;
+  scoped_ptr<ProgramTable> program_table_;
+  scoped_ptr<DynamicSection> dynamic_section_;
+  scoped_ptr<ExportedSymbols> exported_symbols_;
+  scoped_ptr<Relocations> relocations_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(ElfLoaderImpl);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+#endif  // STARBOARD_ELF_LOADER_ELF_LOADER_IMPL_H_
diff --git a/src/starboard/linux/x64x11/mozjs/thread_types_public.h b/src/starboard/elf_loader/elf_loader_switches.cc
similarity index 63%
copy from src/starboard/linux/x64x11/mozjs/thread_types_public.h
copy to src/starboard/elf_loader/elf_loader_switches.cc
index 32fbba2..d859d86 100644
--- a/src/starboard/linux/x64x11/mozjs/thread_types_public.h
+++ b/src/starboard/elf_loader/elf_loader_switches.cc
@@ -1,4 +1,4 @@
-// Copyright 2018 The Cobalt Authors. All Rights Reserved.
+// 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.
@@ -12,9 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_LINUX_X64X11_MOZJS_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_MOZJS_THREAD_TYPES_PUBLIC_H_
+#include "starboard/elf_loader/elf_loader_switches.h"
 
-#include "starboard/linux/shared/thread_types_public.h"
+namespace starboard {
+namespace elf_loader {
 
-#endif  // STARBOARD_LINUX_X64X11_MOZJS_THREAD_TYPES_PUBLIC_H_
+const char kEvergreenLibrary[] = "evergreen_library";
+const char kEvergreenContent[] = "evergreen_content";
+
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/elf_loader_switches.h b/src/starboard/elf_loader/elf_loader_switches.h
new file mode 100644
index 0000000..be37d59
--- /dev/null
+++ b/src/starboard/elf_loader/elf_loader_switches.h
@@ -0,0 +1,29 @@
+// 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.
+
+#ifndef STARBOARD_ELF_LOADER_ELF_LOADER_SWITCHES_H_
+#define STARBOARD_ELF_LOADER_ELF_LOADER_SWITCHES_H_
+
+#include "starboard/configuration.h"
+
+namespace starboard {
+namespace elf_loader {
+
+extern const char kEvergreenLibrary[];
+extern const char kEvergreenContent[];
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_ELF_LOADER_SWITCHES_H_
diff --git a/src/starboard/elf_loader/elf_loader_sys_impl.cc b/src/starboard/elf_loader/elf_loader_sys_impl.cc
new file mode 100644
index 0000000..00c26c6
--- /dev/null
+++ b/src/starboard/elf_loader/elf_loader_sys_impl.cc
@@ -0,0 +1,61 @@
+// 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 "starboard/elf_loader/elf_loader_sys_impl.h"
+
+#include <dlfcn.h>
+
+#include "starboard/common/log.h"
+#include "starboard/elf_loader/exported_symbols.h"
+
+namespace starboard {
+namespace elf_loader {
+
+ElfLoaderImpl::ElfLoaderImpl() {}
+
+bool ElfLoaderImpl::Load(
+    const char* name,
+    const void* (*custom_get_extension)(const char* name)) {
+  SB_UNREFERENCED_PARAMETER(custom_get_extension);
+  SB_LOG(INFO) << "Loading: " << name;
+
+  // Creating the instance forces the binary to keep all the symbols.
+  ExportedSymbols symbols;
+
+  handle_ = dlopen(name, RTLD_NOW);
+  if (!handle_) {
+    SB_LOG(ERROR) << "dlopen failure: " << dlerror();
+    return false;
+  }
+  return true;
+}
+
+void* ElfLoaderImpl::LookupSymbol(const char* symbol) {
+  if (handle_) {
+    void* p = dlsym(handle_, symbol);
+    if (!p) {
+      SB_LOG(ERROR) << "dlsym failure: " << dlerror();
+    }
+    return p;
+  }
+  return NULL;
+}
+
+ElfLoaderImpl::~ElfLoaderImpl() {
+  if (handle_) {
+    dlclose(handle_);
+  }
+}
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/elf_loader_sys_impl.h b/src/starboard/elf_loader/elf_loader_sys_impl.h
new file mode 100644
index 0000000..bcc7af0
--- /dev/null
+++ b/src/starboard/elf_loader/elf_loader_sys_impl.h
@@ -0,0 +1,40 @@
+// 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.
+
+#ifndef STARBOARD_ELF_LOADER_ELF_LOADER_SYS_IMPL_H_
+#define STARBOARD_ELF_LOADER_ELF_LOADER_SYS_IMPL_H_
+
+#include "starboard/common/scoped_ptr.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// Implementation of the elf loader.
+class ElfLoaderImpl {
+ public:
+  ElfLoaderImpl();
+  bool Load(const char* file_name,
+            const void* (*custom_get_extension)(const char* name) = NULL);
+  void* LookupSymbol(const char* symbol);
+  ~ElfLoaderImpl();
+
+ private:
+  void* handle_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(ElfLoaderImpl);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+#endif  // STARBOARD_ELF_LOADER_ELF_LOADER_SYS_IMPL_H_
diff --git a/src/starboard/elf_loader/elf_loader_test.cc b/src/starboard/elf_loader/elf_loader_test.cc
new file mode 100644
index 0000000..11c31f5
--- /dev/null
+++ b/src/starboard/elf_loader/elf_loader_test.cc
@@ -0,0 +1,43 @@
+// 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 "starboard/elf_loader/elf_loader_impl.h"
+
+#include "starboard/common/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_API_VERSION >= 12 &&                                         \
+    (SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP)) && \
+    SB_CAN(MAP_EXECUTABLE_MEMORY)
+namespace starboard {
+namespace elf_loader {
+
+namespace {
+
+// TODO: implement using real shared library fro the file system.
+class ElfLoaderTest : public ::testing::Test {
+ protected:
+  ElfLoaderTest() {}
+  ~ElfLoaderTest() {}
+};
+
+TEST_F(ElfLoaderTest, Initialize) {
+  EXPECT_TRUE(true);
+}
+
+}  // namespace
+}  // namespace elf_loader
+}  // namespace starboard
+#endif  // SB_API_VERSION >= 12 && (SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION
+        // || SB_HAS(MMAP)) && SB_CAN(MAP_EXECUTABLE_MEMORY)
diff --git a/src/starboard/elf_loader/evergreen_config.cc b/src/starboard/elf_loader/evergreen_config.cc
new file mode 100644
index 0000000..b0823e5
--- /dev/null
+++ b/src/starboard/elf_loader/evergreen_config.cc
@@ -0,0 +1,51 @@
+// Copyright 2020 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/elf_loader/evergreen_config.h"
+
+#include "starboard/common/log.h"
+#include "starboard/common/mutex.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/memory.h"
+#include "starboard/string.h"
+
+namespace starboard {
+namespace elf_loader {
+
+static starboard::Mutex g_evergreen_config_mutex;
+static scoped_ptr<EvergreenConfig> g_evergreen_config;
+
+void EvergreenConfig::Create(
+    const char* library_path,
+    const char* content_path,
+    const void* (*custom_get_extension)(const char* name)) {
+  starboard::ScopedLock lock(g_evergreen_config_mutex);
+  g_evergreen_config.reset(
+      new EvergreenConfig(library_path, content_path, custom_get_extension));
+}
+
+const EvergreenConfig* EvergreenConfig::GetInstance() {
+  starboard::ScopedLock lock(g_evergreen_config_mutex);
+  return g_evergreen_config.get();
+}
+
+EvergreenConfig::EvergreenConfig(
+    const char* library_path,
+    const char* content_path,
+    const void* (*custom_get_extension)(const char* name))
+    : library_path_(library_path),
+      content_path_(content_path),
+      custom_get_extension_(custom_get_extension) {}
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/raspi/2/mozjs/starboard_platform.gyp b/src/starboard/elf_loader/evergreen_config.gyp
similarity index 62%
rename from src/starboard/raspi/2/mozjs/starboard_platform.gyp
rename to src/starboard/elf_loader/evergreen_config.gyp
index 53d18eb..05846f4 100644
--- a/src/starboard/raspi/2/mozjs/starboard_platform.gyp
+++ b/src/starboard/elf_loader/evergreen_config.gyp
@@ -1,4 +1,4 @@
-# Copyright 2018 The Cobalt Authors. All Rights Reserved.
+# Copyright 2020 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.
@@ -11,8 +11,18 @@
 # 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.
+
+# This files contains all targets that should be created by gyp_cobalt by
+# default.
 {
-  'includes': [
-    '../../shared/starboard_platform.gypi',
+  'targets': [
+    {
+      'target_name': 'evergreen_config',
+      'type': 'static_library',
+      'sources': [
+        'evergreen_config.h',
+        'evergreen_config.cc',
+      ],
+    },
   ],
 }
diff --git a/src/starboard/elf_loader/evergreen_config.h b/src/starboard/elf_loader/evergreen_config.h
new file mode 100644
index 0000000..a2ca212
--- /dev/null
+++ b/src/starboard/elf_loader/evergreen_config.h
@@ -0,0 +1,57 @@
+// Copyright 2020 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 STARBOARD_ELF_LOADER_EVERGREEN_CONFIG_H_
+#define STARBOARD_ELF_LOADER_EVERGREEN_CONFIG_H_
+
+#include <string>
+
+#include "starboard/configuration.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// This is configuration published by the Evergreen loader.
+// The Starboard implementation may customize its behavior
+// based on it e.g. override the content location in SbSystemGetPath or
+// add an custom extension to SbSystemGetExtension.
+struct EvergreenConfig {
+  // Factory method to create the configuration.
+  static void Create(const char* library_path,
+                     const char* content_path,
+                     const void* (*custom_get_extension_)(const char* name));
+
+  // Retrieves the singleton instance of the configuration.
+  static const EvergreenConfig* GetInstance();
+
+  // Absolute path to the shared library binary.
+  const std::string library_path_;
+
+  // Absolute path to the content directory for this binary.
+  const std::string content_path_;
+
+  // Getter to a custom extension to be added to Starboard.
+  const void* (*custom_get_extension_)(const char* name);
+
+ private:
+  EvergreenConfig(const char* library_path,
+                  const char* content_path,
+                  const void* (*custom_get_extension_)(const char* name));
+  SB_DISALLOW_COPY_AND_ASSIGN(EvergreenConfig);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_EVERGREEN_CONFIG_H_
diff --git a/src/starboard/elf_loader/evergreen_info.cc b/src/starboard/elf_loader/evergreen_info.cc
new file mode 100644
index 0000000..875d215
--- /dev/null
+++ b/src/starboard/elf_loader/evergreen_info.cc
@@ -0,0 +1,70 @@
+// Copyright 2020 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/elf_loader/evergreen_info.h"
+
+#include "starboard/atomic.h"
+#include "starboard/common/log.h"
+#include "starboard/memory.h"
+#include "starboard/string.h"
+
+static EvergreenInfo g_evergreen_info;
+static bool g_valid_info = false;
+static SbAtomic32 g_busy = 0;
+
+bool SetEvergreenInfo(const EvergreenInfo* evergreen_info) {
+  // Set the busy flag or bail.
+  if (SbAtomicNoBarrier_CompareAndSwap(&g_busy, 0, 1) == 1) {
+    // Bailing out is OK as the process crashed
+    // before we launched the application and in that
+    // case the evergreen information is not needed.
+    return false;
+  }
+  if (evergreen_info != NULL && evergreen_info->base_address != 0 &&
+      SbStringGetLength(evergreen_info->file_path_buf) != 0) {
+    g_evergreen_info = *evergreen_info;
+    g_valid_info = true;
+  } else {
+    g_valid_info = false;
+  }
+  // Publish local memory changes to all threads.
+  SbAtomicMemoryBarrier();
+
+  // Clear the busy flag.
+  SbAtomicNoBarrier_CompareAndSwap(&g_busy, 1, 0);
+  return true;
+}
+
+bool GetEvergreenInfo(EvergreenInfo* evergreen_info) {
+  if (evergreen_info == NULL) {
+    return false;
+  }
+
+  // Set the busy flag or bail.
+  if (SbAtomicNoBarrier_CompareAndSwap(&g_busy, 0, 1) == 1) {
+    return false;
+  }
+
+  // Make sure all memory changes are visible to the current thread.
+  SbAtomicMemoryBarrier();
+  if (!g_valid_info) {
+    return false;
+  }
+
+  *evergreen_info = g_evergreen_info;
+
+  // Clear the busy flag.
+  SbAtomicNoBarrier_CompareAndSwap(&g_busy, 1, 0);
+  return true;
+}
diff --git a/src/starboard/linux/x64x11/mozjs/starboard_platform_tests.gyp b/src/starboard/elf_loader/evergreen_info.gyp
similarity index 62%
copy from src/starboard/linux/x64x11/mozjs/starboard_platform_tests.gyp
copy to src/starboard/elf_loader/evergreen_info.gyp
index 0ea8622..d6c66e3 100644
--- a/src/starboard/linux/x64x11/mozjs/starboard_platform_tests.gyp
+++ b/src/starboard/elf_loader/evergreen_info.gyp
@@ -1,4 +1,4 @@
-# Copyright 2018 The Cobalt Authors. All Rights Reserved.
+# Copyright 2020 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.
@@ -11,8 +11,18 @@
 # 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.
+
+# This files contains all targets that should be created by gyp_cobalt by
+# default.
 {
-  'includes': [
-    '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
+  'targets': [
+    {
+      'target_name': 'evergreen_info',
+      'type': 'static_library',
+      'sources': [
+        'evergreen_info.h',
+        'evergreen_info.cc',
+      ],
+    },
   ],
 }
diff --git a/src/starboard/elf_loader/evergreen_info.h b/src/starboard/elf_loader/evergreen_info.h
new file mode 100644
index 0000000..cca62e0
--- /dev/null
+++ b/src/starboard/elf_loader/evergreen_info.h
@@ -0,0 +1,73 @@
+// Copyright 2020 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.
+
+// An implementation of storage that just uses the filesystem. The User
+// implementation must implement a way to get the user's home directory.
+
+#ifndef STARBOARD_ELF_LOADER_EVERGREEN_INFO_H_
+#define STARBOARD_ELF_LOADER_EVERGREEN_INFO_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+// This is duplicate constant for use from signal-safe code in
+// the starboard implementation.
+#define EVERGREEN_FILE_PATH_MAX_SIZE 4096
+
+#define IS_EVERGREEN_ADDRESS(address, evergreen_info)                    \
+  (evergreen_info.base_address != 0 &&                                   \
+   reinterpret_cast<uint64_t>(address) >= evergreen_info.base_address && \
+   (reinterpret_cast<uint64_t>(address) - evergreen_info.base_address) < \
+       evergreen_info.load_size)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Evergreen shared library memory mapping information used for
+// stack unwinding and symbolizing.
+typedef struct EvergreenInfo {
+  // File path of the shared library file.
+  char file_path_buf[EVERGREEN_FILE_PATH_MAX_SIZE];
+
+  // Base memory address of the mapped library.
+  uint64_t base_address;
+
+  // Size of the mapped memory.
+  size_t load_size;
+
+  // Address of the Program Header Table for the library.
+  uint64_t phdr_table;
+
+  // Number of items in the Program Header Table.
+  size_t phdr_table_num;
+} EvergreenInfo;
+
+// Set the Evergreen information. Should be called only from the
+// elf_loader module. Passing NULL clears the currently stored
+// information.
+// Returns true if if successful.
+bool SetEvergreenInfo(const EvergreenInfo* evergreen_info);
+
+// Retrieve the Evergreen information. The implementation must be
+// signal-safe. The main clients for this call are stack unwinding
+// libraries and symbolization routines.
+// Returns true if the Evergreen info is available.
+bool GetEvergreenInfo(EvergreenInfo* evergreen_info);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // STARBOARD_ELF_LOADER_EVERGREEN_INFO_H_
diff --git a/src/starboard/elf_loader/exported_symbols.cc b/src/starboard/elf_loader/exported_symbols.cc
new file mode 100644
index 0000000..492731e
--- /dev/null
+++ b/src/starboard/elf_loader/exported_symbols.cc
@@ -0,0 +1,466 @@
+// 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 "starboard/elf_loader/exported_symbols.h"
+
+#include "starboard/accessibility.h"
+#include "starboard/audio_sink.h"
+#include "starboard/blitter.h"
+#include "starboard/byte_swap.h"
+#include "starboard/character.h"
+#include "starboard/condition_variable.h"
+#include "starboard/configuration.h"
+#include "starboard/configuration_constants.h"
+#include "starboard/cpu_features.h"
+#include "starboard/decode_target.h"
+#include "starboard/directory.h"
+#include "starboard/double.h"
+#include "starboard/egl.h"
+#include "starboard/event.h"
+#include "starboard/file.h"
+#include "starboard/gles.h"
+#include "starboard/image.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/memory_reporter.h"
+#include "starboard/microphone.h"
+#include "starboard/mutex.h"
+#include "starboard/once.h"
+#include "starboard/player.h"
+#include "starboard/socket.h"
+#include "starboard/socket_waiter.h"
+#include "starboard/speech_recognizer.h"
+#include "starboard/speech_synthesis.h"
+#include "starboard/storage.h"
+#include "starboard/string.h"
+#include "starboard/system.h"
+#include "starboard/thread.h"
+#include "starboard/time_zone.h"
+#include "starboard/ui_navigation.h"
+#include "starboard/window.h"
+
+#define REGISTER_SYMBOL(s)                        \
+  do {                                            \
+    map_[#s] = reinterpret_cast<const void*>(&s); \
+  } while (0)
+
+namespace starboard {
+namespace elf_loader {
+
+ExportedSymbols::ExportedSymbols(
+    const void* (*custom_get_extension)(const char* name)) {
+  REGISTER_SYMBOL(SbAccessibilityGetDisplaySettings);
+  REGISTER_SYMBOL(SbAccessibilityGetTextToSpeechSettings);
+  REGISTER_SYMBOL(SbAudioSinkCreate);
+  REGISTER_SYMBOL(SbAudioSinkDestroy);
+  REGISTER_SYMBOL(SbAudioSinkGetMaxChannels);
+  REGISTER_SYMBOL(SbAudioSinkGetNearestSupportedSampleFrequency);
+  REGISTER_SYMBOL(SbAudioSinkIsAudioFrameStorageTypeSupported);
+  REGISTER_SYMBOL(SbAudioSinkIsAudioSampleTypeSupported);
+  REGISTER_SYMBOL(SbAudioSinkIsValid);
+  REGISTER_SYMBOL(SbByteSwapS16);
+  REGISTER_SYMBOL(SbByteSwapS32);
+  REGISTER_SYMBOL(SbByteSwapS64);
+  REGISTER_SYMBOL(SbByteSwapU16);
+  REGISTER_SYMBOL(SbByteSwapU32);
+  REGISTER_SYMBOL(SbByteSwapU64);
+  REGISTER_SYMBOL(SbCharacterIsAlphanumeric);
+  REGISTER_SYMBOL(SbCharacterIsDigit);
+  REGISTER_SYMBOL(SbCharacterIsHexDigit);
+  REGISTER_SYMBOL(SbCharacterIsSpace);
+  REGISTER_SYMBOL(SbCharacterIsUpper);
+  REGISTER_SYMBOL(SbCharacterToLower);
+  REGISTER_SYMBOL(SbCharacterToUpper);
+  REGISTER_SYMBOL(SbConditionVariableBroadcast);
+  REGISTER_SYMBOL(SbConditionVariableCreate);
+  REGISTER_SYMBOL(SbConditionVariableDestroy);
+  REGISTER_SYMBOL(SbConditionVariableSignal);
+  REGISTER_SYMBOL(SbConditionVariableWait);
+  REGISTER_SYMBOL(SbConditionVariableWaitTimed);
+  REGISTER_SYMBOL(SbDecodeTargetGetInfo);
+  REGISTER_SYMBOL(SbDecodeTargetRelease);
+  REGISTER_SYMBOL(SbDirectoryCanOpen);
+  REGISTER_SYMBOL(SbDirectoryClose);
+  REGISTER_SYMBOL(SbDirectoryCreate);
+  REGISTER_SYMBOL(SbDirectoryGetNext);
+  REGISTER_SYMBOL(SbDirectoryOpen);
+  REGISTER_SYMBOL(SbDoubleAbsolute);
+  REGISTER_SYMBOL(SbDoubleExponent);
+  REGISTER_SYMBOL(SbDoubleFloor);
+  REGISTER_SYMBOL(SbDoubleIsFinite);
+  REGISTER_SYMBOL(SbDoubleIsNan);
+  REGISTER_SYMBOL(SbDrmCloseSession);
+  REGISTER_SYMBOL(SbDrmCreateSystem);
+  REGISTER_SYMBOL(SbDrmDestroySystem);
+  REGISTER_SYMBOL(SbDrmGenerateSessionUpdateRequest);
+  REGISTER_SYMBOL(SbDrmUpdateSession);
+  REGISTER_SYMBOL(SbEventCancel);
+  REGISTER_SYMBOL(SbEventSchedule);
+  REGISTER_SYMBOL(SbFileCanOpen);
+  REGISTER_SYMBOL(SbFileClose);
+  REGISTER_SYMBOL(SbFileDelete);
+  REGISTER_SYMBOL(SbFileExists);
+  REGISTER_SYMBOL(SbFileFlush);
+  REGISTER_SYMBOL(SbFileGetInfo);
+  REGISTER_SYMBOL(SbFileGetPathInfo);
+  REGISTER_SYMBOL(SbFileModeStringToFlags);
+  REGISTER_SYMBOL(SbFileOpen);
+  REGISTER_SYMBOL(SbFileRead);
+  REGISTER_SYMBOL(SbFileSeek);
+  REGISTER_SYMBOL(SbFileTruncate);
+  REGISTER_SYMBOL(SbFileWrite);
+  REGISTER_SYMBOL(SbMediaGetAudioConfiguration);
+  REGISTER_SYMBOL(SbMediaGetAudioOutputCount);
+  REGISTER_SYMBOL(SbMediaIsOutputProtected);
+  REGISTER_SYMBOL(SbMediaIsSupported);
+  REGISTER_SYMBOL(SbMediaSetOutputProtection);
+  REGISTER_SYMBOL(SbMemoryAllocate);
+  REGISTER_SYMBOL(SbMemoryAllocateAligned);
+  REGISTER_SYMBOL(SbMemoryAllocateNoReport);
+  REGISTER_SYMBOL(SbMemoryDeallocate);
+  REGISTER_SYMBOL(SbMemoryDeallocateAligned);
+  REGISTER_SYMBOL(SbMemoryDeallocateNoReport);
+  REGISTER_SYMBOL(SbMemoryReallocate);
+  REGISTER_SYMBOL(SbMemorySetReporter);
+  REGISTER_SYMBOL(SbImageDecode);
+  REGISTER_SYMBOL(SbImageIsDecodeSupported);
+  REGISTER_SYMBOL(SbLog);
+  REGISTER_SYMBOL(SbLogFlush);
+  REGISTER_SYMBOL(SbLogFormat);
+  REGISTER_SYMBOL(SbLogIsTty);
+  REGISTER_SYMBOL(SbLogRaw);
+  REGISTER_SYMBOL(SbLogRawDumpStack);
+  REGISTER_SYMBOL(SbLogRawFormat);
+  REGISTER_SYMBOL(SbMediaCanPlayMimeAndKeySystem);
+  REGISTER_SYMBOL(SbMemoryAllocateAlignedUnchecked);
+  REGISTER_SYMBOL(SbMemoryAllocateUnchecked);
+  REGISTER_SYMBOL(SbMemoryCompare);
+  REGISTER_SYMBOL(SbMemoryCopy);
+  REGISTER_SYMBOL(SbMemoryFindByte);
+  REGISTER_SYMBOL(SbMemoryFree);
+  REGISTER_SYMBOL(SbMemoryFreeAligned);
+  REGISTER_SYMBOL(SbMemoryGetStackBounds);
+  REGISTER_SYMBOL(SbMemoryMove);
+  REGISTER_SYMBOL(SbMemoryReallocateUnchecked);
+  REGISTER_SYMBOL(SbMemorySet);
+  REGISTER_SYMBOL(SbMutexAcquire);
+  REGISTER_SYMBOL(SbMutexAcquireTry);
+  REGISTER_SYMBOL(SbMutexCreate);
+  REGISTER_SYMBOL(SbMutexDestroy);
+  REGISTER_SYMBOL(SbMutexRelease);
+  REGISTER_SYMBOL(SbOnce);
+  REGISTER_SYMBOL(SbPlayerCreate);
+  REGISTER_SYMBOL(SbPlayerDestroy);
+  REGISTER_SYMBOL(SbPlayerGetCurrentFrame);
+  REGISTER_SYMBOL(SbPlayerSetBounds);
+  REGISTER_SYMBOL(SbPlayerSetPlaybackRate);
+  REGISTER_SYMBOL(SbPlayerSetVolume);
+  REGISTER_SYMBOL(SbPlayerWriteEndOfStream);
+  REGISTER_SYMBOL(SbSocketAccept);
+  REGISTER_SYMBOL(SbSocketBind);
+  REGISTER_SYMBOL(SbSocketClearLastError);
+  REGISTER_SYMBOL(SbSocketConnect);
+  REGISTER_SYMBOL(SbSocketCreate);
+  REGISTER_SYMBOL(SbSocketDestroy);
+  REGISTER_SYMBOL(SbSocketFreeResolution);
+  REGISTER_SYMBOL(SbSocketGetInterfaceAddress);
+  REGISTER_SYMBOL(SbSocketGetLastError);
+  REGISTER_SYMBOL(SbSocketGetLocalAddress);
+  REGISTER_SYMBOL(SbSocketIsConnected);
+  REGISTER_SYMBOL(SbSocketIsConnectedAndIdle);
+  REGISTER_SYMBOL(SbSocketJoinMulticastGroup);
+  REGISTER_SYMBOL(SbSocketListen);
+  REGISTER_SYMBOL(SbSocketReceiveFrom);
+  REGISTER_SYMBOL(SbSocketResolve);
+  REGISTER_SYMBOL(SbSocketSendTo);
+  REGISTER_SYMBOL(SbSocketSetBroadcast);
+  REGISTER_SYMBOL(SbSocketSetReceiveBufferSize);
+  REGISTER_SYMBOL(SbSocketSetReuseAddress);
+  REGISTER_SYMBOL(SbSocketSetSendBufferSize);
+  REGISTER_SYMBOL(SbSocketSetTcpKeepAlive);
+  REGISTER_SYMBOL(SbSocketSetTcpNoDelay);
+  REGISTER_SYMBOL(SbSocketSetTcpWindowScaling);
+  REGISTER_SYMBOL(SbSocketWaiterAdd);
+  REGISTER_SYMBOL(SbSocketWaiterCreate);
+  REGISTER_SYMBOL(SbSocketWaiterDestroy);
+  REGISTER_SYMBOL(SbSocketWaiterRemove);
+  REGISTER_SYMBOL(SbSocketWaiterWait);
+  REGISTER_SYMBOL(SbSocketWaiterWaitTimed);
+  REGISTER_SYMBOL(SbSocketWaiterWakeUp);
+  REGISTER_SYMBOL(SbStorageCloseRecord);
+  REGISTER_SYMBOL(SbStorageDeleteRecord);
+  REGISTER_SYMBOL(SbStorageGetRecordSize);
+  REGISTER_SYMBOL(SbStorageOpenRecord);
+  REGISTER_SYMBOL(SbStorageReadRecord);
+  REGISTER_SYMBOL(SbStorageWriteRecord);
+  REGISTER_SYMBOL(SbStringCompare);
+  REGISTER_SYMBOL(SbStringCompareAll);
+  REGISTER_SYMBOL(SbStringCompareNoCase);
+  REGISTER_SYMBOL(SbStringCompareNoCaseN);
+  REGISTER_SYMBOL(SbStringCompareWide);
+  REGISTER_SYMBOL(SbStringConcat);
+  REGISTER_SYMBOL(SbStringConcatWide);
+  REGISTER_SYMBOL(SbStringCopy);
+  REGISTER_SYMBOL(SbStringCopyWide);
+  REGISTER_SYMBOL(SbStringDuplicate);
+  REGISTER_SYMBOL(SbStringFindCharacter);
+  REGISTER_SYMBOL(SbStringFindLastCharacter);
+  REGISTER_SYMBOL(SbStringFindString);
+  REGISTER_SYMBOL(SbStringFormat);
+  REGISTER_SYMBOL(SbStringFormatWide);
+  REGISTER_SYMBOL(SbStringGetLength);
+  REGISTER_SYMBOL(SbStringGetLengthWide);
+  REGISTER_SYMBOL(SbStringParseDouble);
+  REGISTER_SYMBOL(SbStringParseSignedInteger);
+  REGISTER_SYMBOL(SbStringParseUInt64);
+  REGISTER_SYMBOL(SbStringParseUnsignedInteger);
+  REGISTER_SYMBOL(SbStringScan);
+  REGISTER_SYMBOL(SbSystemBinarySearch);
+  REGISTER_SYMBOL(SbSystemBreakIntoDebugger);
+  REGISTER_SYMBOL(SbSystemClearLastError);
+  REGISTER_SYMBOL(SbSystemGetConnectionType);
+  REGISTER_SYMBOL(SbSystemGetDeviceType);
+  REGISTER_SYMBOL(SbSystemGetErrorString);
+  REGISTER_SYMBOL(SbSystemGetLastError);
+  REGISTER_SYMBOL(SbSystemGetLocaleId);
+  REGISTER_SYMBOL(SbSystemGetNumberOfProcessors);
+  REGISTER_SYMBOL(SbSystemGetProperty);
+  REGISTER_SYMBOL(SbSystemGetRandomData);
+  REGISTER_SYMBOL(SbSystemGetRandomUInt64);
+  REGISTER_SYMBOL(SbSystemGetStack);
+  REGISTER_SYMBOL(SbSystemGetTotalCPUMemory);
+  REGISTER_SYMBOL(SbSystemGetTotalGPUMemory);
+  REGISTER_SYMBOL(SbSystemGetUsedCPUMemory);
+  REGISTER_SYMBOL(SbSystemGetUsedGPUMemory);
+  REGISTER_SYMBOL(SbSystemHasCapability);
+  REGISTER_SYMBOL(SbSystemHideSplashScreen);
+  REGISTER_SYMBOL(SbSystemIsDebuggerAttached);
+  REGISTER_SYMBOL(SbSystemRaisePlatformError);
+  REGISTER_SYMBOL(SbSystemRequestPause);
+  REGISTER_SYMBOL(SbSystemRequestStop);
+  REGISTER_SYMBOL(SbSystemRequestSuspend);
+  REGISTER_SYMBOL(SbSystemRequestUnpause);
+  REGISTER_SYMBOL(SbSystemSort);
+  REGISTER_SYMBOL(SbSystemSymbolize);
+  REGISTER_SYMBOL(SbThreadCreate);
+  REGISTER_SYMBOL(SbThreadCreateLocalKey);
+  REGISTER_SYMBOL(SbThreadDestroyLocalKey);
+  REGISTER_SYMBOL(SbThreadDetach);
+  REGISTER_SYMBOL(SbThreadGetCurrent);
+  REGISTER_SYMBOL(SbThreadGetId);
+  REGISTER_SYMBOL(SbThreadGetLocalValue);
+  REGISTER_SYMBOL(SbThreadGetName);
+  REGISTER_SYMBOL(SbThreadIsEqual);
+  REGISTER_SYMBOL(SbThreadJoin);
+  REGISTER_SYMBOL(SbThreadSetLocalValue);
+  REGISTER_SYMBOL(SbThreadSetName);
+  REGISTER_SYMBOL(SbThreadSleep);
+  REGISTER_SYMBOL(SbThreadYield);
+  REGISTER_SYMBOL(SbTimeGetMonotonicNow);
+  REGISTER_SYMBOL(SbTimeGetNow);
+  REGISTER_SYMBOL(SbTimeZoneGetCurrent);
+  REGISTER_SYMBOL(SbTimeZoneGetName);
+  REGISTER_SYMBOL(SbUserGetCurrent);
+  REGISTER_SYMBOL(SbUserGetProperty);
+  REGISTER_SYMBOL(SbUserGetPropertySize);
+  REGISTER_SYMBOL(SbUserGetSignedIn);
+  REGISTER_SYMBOL(SbWindowCreate);
+  REGISTER_SYMBOL(SbWindowDestroy);
+  REGISTER_SYMBOL(SbWindowGetPlatformHandle);
+  REGISTER_SYMBOL(SbWindowGetSize);
+  REGISTER_SYMBOL(SbWindowSetDefaultOptions);
+  REGISTER_SYMBOL(SbSystemGetPath);
+
+#if SB_API_VERSION >= 11
+  REGISTER_SYMBOL(SbGetEglInterface);
+  REGISTER_SYMBOL(SbGetGlesInterface);
+#endif  // SB_API_VERSION >= 11
+
+#if SB_API_VERSION >= SB_FILE_ATOMIC_REPLACE_VERSION
+  REGISTER_SYMBOL(SbFileAtomicReplace);
+#endif  // SB_API_VERSION >= SB_FILE_ATOMIC_REPLACE_VERSION
+
+#if SB_CAN(MAP_EXECUTABLE_MEMORY)
+  REGISTER_SYMBOL(SbMemoryFlush);
+#endif  // SB_CAN(MAP_EXECUTABLE_MEMORY)
+
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+  REGISTER_SYMBOL(SbPlayerGetPreferredOutputMode);
+#else
+  REGISTER_SYMBOL(SbPlayerOutputModeSupported);
+#endif
+
+#if SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP)
+  REGISTER_SYMBOL(SbMemoryMap);
+  REGISTER_SYMBOL(SbMemoryUnmap);
+  REGISTER_SYMBOL(SbMemoryProtect);
+#endif  // SB_HAS(MMAP)
+
+#if SB_API_VERSION >= SB_UI_NAVIGATION_VERSION
+  REGISTER_SYMBOL(SbUiNavGetInterface);
+#endif  // SB_API_VERSION >= SB_UI_NAVIGATION_VERSION
+
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
+  REGISTER_SYMBOL(SbWindowBlurOnScreenKeyboard);
+  REGISTER_SYMBOL(SbWindowFocusOnScreenKeyboard);
+  REGISTER_SYMBOL(SbWindowGetOnScreenKeyboardBoundingRect);
+  REGISTER_SYMBOL(SbWindowHideOnScreenKeyboard);
+  REGISTER_SYMBOL(SbWindowIsOnScreenKeyboardShown);
+  REGISTER_SYMBOL(SbWindowSetOnScreenKeyboardKeepFocus);
+  REGISTER_SYMBOL(SbWindowShowOnScreenKeyboard);
+#if SB_API_VERSION >= 11
+  REGISTER_SYMBOL(SbWindowOnScreenKeyboardSuggestionsSupported);
+  REGISTER_SYMBOL(SbWindowUpdateOnScreenKeyboardSuggestions);
+#endif  // SB_API_VERSION >= 11
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
+
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION
+  REGISTER_SYMBOL(SbWindowOnScreenKeyboardIsSupported);
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION
+
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
+  REGISTER_SYMBOL(SbAccessibilityGetCaptionSettings);
+  REGISTER_SYMBOL(SbAccessibilitySetCaptionsEnabled);
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
+
+#if SB_API_VERSION >= SB_MICROPHONE_REQUIRED_VERSION || SB_HAS(MICROPHONE)
+  REGISTER_SYMBOL(SbMicrophoneClose);
+  REGISTER_SYMBOL(SbMicrophoneCreate);
+  REGISTER_SYMBOL(SbMicrophoneDestroy);
+  REGISTER_SYMBOL(SbMicrophoneGetAvailable);
+  REGISTER_SYMBOL(SbMicrophoneIsSampleRateSupported);
+  REGISTER_SYMBOL(SbMicrophoneOpen);
+  REGISTER_SYMBOL(SbMicrophoneRead);
+#endif  // SB_API_VERSION >= SB_MICROPHONE_REQUIRED_VERSION ||
+        // SB_HAS(MICROPHONE)
+
+#if SB_API_VERSION >= SB_IPV6_REQUIRED_VERSION
+  REGISTER_SYMBOL(SbSocketIsIpv6Supported);
+#endif
+
+#if SB_API_VERSION >= SB_SPEECH_SYNTHESIS_REQUIRED_VERSION || \
+    SB_HAS(SPEECH_SYNTHESIS)
+  REGISTER_SYMBOL(SbSpeechSynthesisCancel);
+  REGISTER_SYMBOL(SbSpeechSynthesisSpeak);
+#endif  // SB_API_VERSION >= SB_SPEECH_SYNTHESIS_REQUIRED_VERSION ||
+        // SB_HAS(SPEECH_SYNTHESIS)
+
+#if SB_API_VERSION >= SB_TIME_THREAD_NOW_REQUIRED_VERSION || \
+    SB_HAS(TIME_THREAD_NOW)
+  REGISTER_SYMBOL(SbTimeGetMonotonicThreadNow);
+#endif  // SB_API_VERSION >= SB_TIME_THREAD_NOW_REQUIRED_VERSION ||
+        // SB_HAS(TIME_THREAD_NOW)
+
+#if SB_API_VERSION >= 5
+#if SB_API_VERSION >= SB_SPEECH_RECOGNIZER_REQUIRED_VERSION
+  REGISTER_SYMBOL(SbSpeechRecognizerIsSupported);
+#endif
+#if SB_API_VERSION >= SB_SPEECH_RECOGNIZER_REQUIRED_VERSION || \
+    SB_HAS(SPEECH_RECOGNIZER)
+  REGISTER_SYMBOL(SbSpeechRecognizerCreate);
+  REGISTER_SYMBOL(SbSpeechRecognizerDestroy);
+  REGISTER_SYMBOL(SbSpeechRecognizerStart);
+  REGISTER_SYMBOL(SbSpeechRecognizerStop);
+  REGISTER_SYMBOL(SbSpeechRecognizerCancel);
+#endif  // SB_API_VERSION >= SB_SPEECH_RECOGNIZER_REQUIRED_VERSION ||
+        // SB_HAS(SPEECH_RECOGNIZER)
+#endif  // SB_API_VERSION >= 5
+
+#if SB_API_VERSION >= SB_SPEECH_SYNTHESIS_REQUIRED_VERSION
+  REGISTER_SYMBOL(SbSpeechSynthesisIsSupported);
+#endif
+
+#if SB_API_VERSION >= SB_TIME_THREAD_NOW_REQUIRED_VERSION
+  REGISTER_SYMBOL(SbTimeIsTimeThreadNowSupported);
+#endif
+
+  REGISTER_SYMBOL(SbDrmIsServerCertificateUpdatable);
+  REGISTER_SYMBOL(SbDrmUpdateServerCertificate);
+  REGISTER_SYMBOL(SbMediaGetAudioBufferBudget);
+  REGISTER_SYMBOL(SbMediaGetBufferAlignment);
+  REGISTER_SYMBOL(SbMediaGetBufferAllocationUnit);
+  REGISTER_SYMBOL(SbMediaGetBufferGarbageCollectionDurationThreshold);
+  REGISTER_SYMBOL(SbMediaGetBufferPadding);
+  REGISTER_SYMBOL(SbMediaGetBufferStorageType);
+  REGISTER_SYMBOL(SbMediaGetInitialBufferCapacity);
+  REGISTER_SYMBOL(SbMediaGetMaxBufferCapacity);
+  REGISTER_SYMBOL(SbMediaGetProgressiveBufferBudget);
+  REGISTER_SYMBOL(SbMediaGetVideoBufferBudget);
+  REGISTER_SYMBOL(SbMediaIsBufferPoolAllocateOnDemand);
+  REGISTER_SYMBOL(SbMediaIsBufferUsingMemoryPool);
+  REGISTER_SYMBOL(SbPlayerGetInfo2);
+  REGISTER_SYMBOL(SbPlayerGetMaximumNumberOfSamplesPerWrite);
+  REGISTER_SYMBOL(SbPlayerSeek2);
+  REGISTER_SYMBOL(SbPlayerWriteSample2);
+  REGISTER_SYMBOL(SbSystemSupportsResume);
+
+#if SB_API_VERSION >= 11
+  REGISTER_SYMBOL(SbAudioSinkGetMinBufferSizeInFrames);
+  REGISTER_SYMBOL(SbCPUFeaturesGet);
+  REGISTER_SYMBOL(SbMediaSetAudioWriteDuration);
+  REGISTER_SYMBOL(SbSystemGetExtension);
+  REGISTER_SYMBOL(SbSystemSignWithCertificationSecretKey);
+  REGISTER_SYMBOL(SbThreadContextGetPointer);
+  REGISTER_SYMBOL(SbThreadSamplerCreate);
+  REGISTER_SYMBOL(SbThreadSamplerDestroy);
+  REGISTER_SYMBOL(SbThreadSamplerFreeze);
+  REGISTER_SYMBOL(SbThreadSamplerIsSupported);
+  REGISTER_SYMBOL(SbThreadSamplerThaw);
+  REGISTER_SYMBOL(SbWindowGetDiagonalSizeInInches);
+#endif  // SB_API_VERSION >= 11
+
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+  REGISTER_SYMBOL(kSbDefaultMmapThreshold);
+  REGISTER_SYMBOL(kSbFileMaxName);
+  REGISTER_SYMBOL(kSbFileMaxOpen);
+  REGISTER_SYMBOL(kSbFileAltSepChar);
+  REGISTER_SYMBOL(kSbFileAltSepString);
+  REGISTER_SYMBOL(kSbFileMaxPath);
+  REGISTER_SYMBOL(kSbFileSepChar);
+  REGISTER_SYMBOL(kSbFileSepString);
+  REGISTER_SYMBOL(kSbHasAc3Audio);
+  REGISTER_SYMBOL(kSbHasAsyncAudioFramesReporting);
+  REGISTER_SYMBOL(kSbHasMediaWebmVp9Support);
+  REGISTER_SYMBOL(kSbHasThreadPrioritySupport);
+  REGISTER_SYMBOL(kSbMallocAlignment);
+  REGISTER_SYMBOL(kSbMaxThreadLocalKeys);
+  REGISTER_SYMBOL(kSbMaxThreadNameLength);
+  REGISTER_SYMBOL(kSbMediaMaxAudioBitrateInBitsPerSecond);
+  REGISTER_SYMBOL(kSbMediaMaxVideoBitrateInBitsPerSecond);
+  REGISTER_SYMBOL(kSbMediaMaximumVideoFrames);
+  REGISTER_SYMBOL(kSbMediaMaximumVideoPrerollFrames);
+  REGISTER_SYMBOL(kSbMediaVideoFrameAlignment);
+  REGISTER_SYMBOL(kSbMemoryLogPath);
+  REGISTER_SYMBOL(kSbMemoryPageSize);
+  REGISTER_SYMBOL(kSbNetworkReceiveBufferSize);
+  REGISTER_SYMBOL(kSbMaxThreads);
+  REGISTER_SYMBOL(kSbPathSepChar);
+  REGISTER_SYMBOL(kSbPathSepString);
+  REGISTER_SYMBOL(kSbPreferredRgbaByteOrder);
+  REGISTER_SYMBOL(kSbUserMaxSignedIn);
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+}
+
+const void* ExportedSymbols::Lookup(const char* name) {
+  const void* address = map_[name];
+  // Any symbol that is not registered as part of the Starboard API in the
+  // constructor of this class is a leak, and is an error.
+  SB_CHECK(address) << "Failed to retrieve the address of '" << name << "'.";
+  return address;
+}
+
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/exported_symbols.h b/src/starboard/elf_loader/exported_symbols.h
new file mode 100644
index 0000000..1ffc732
--- /dev/null
+++ b/src/starboard/elf_loader/exported_symbols.h
@@ -0,0 +1,49 @@
+// 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.
+
+#ifndef STARBOARD_ELF_LOADER_EXPORTED_SYMBOLS_H_
+#define STARBOARD_ELF_LOADER_EXPORTED_SYMBOLS_H_
+
+#include <map>
+#include <string>
+
+#include "starboard/elf_loader/elf_hash_table.h"
+#include "starboard/elf_loader/gnu_hash_table.h"
+#include "starboard/file.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// class representing all exported symbols
+// by the starboard layer.
+
+// The elf_loader will not use any other symbols
+// outside of the set represented in this class.
+class ExportedSymbols {
+ public:
+  // An optional |custom_get_extension| function pointer can be passed in order
+  // to override the |SbSystemGetExtension| function.
+  explicit ExportedSymbols(
+      const void* (*custom_get_extension)(const char* name) = NULL);
+  const void* Lookup(const char* name);
+
+ private:
+  std::map<std::string, const void*> map_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(ExportedSymbols);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+#endif  // STARBOARD_ELF_LOADER_EXPORTED_SYMBOLS_H_
diff --git a/src/starboard/elf_loader/file.h b/src/starboard/elf_loader/file.h
new file mode 100644
index 0000000..90296e7
--- /dev/null
+++ b/src/starboard/elf_loader/file.h
@@ -0,0 +1,44 @@
+// 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.
+
+#ifndef STARBOARD_ELF_LOADER_FILE_H_
+#define STARBOARD_ELF_LOADER_FILE_H_
+
+#include "starboard/elf_loader/elf.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// File abstraction to be used by the ELF loader.
+// The main reason to introduce this class is to allow for
+// easy testing.
+class File {
+ public:
+  // Opens the file specified for reading.
+  virtual bool Open(const char* name) = 0;
+
+  // Reads a buffer from the file using the specified offset from the beginning
+  // of the file.
+  virtual bool ReadFromOffset(int64_t offset, char* buffer, int size) = 0;
+
+  // Closes the underlying file.
+  virtual void Close() = 0;
+
+  virtual ~File() {}
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_FILE_H_
diff --git a/src/starboard/elf_loader/file_impl.cc b/src/starboard/elf_loader/file_impl.cc
new file mode 100644
index 0000000..94ee465
--- /dev/null
+++ b/src/starboard/elf_loader/file_impl.cc
@@ -0,0 +1,76 @@
+// 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 "starboard/elf_loader/file_impl.h"
+
+#include "starboard/common/log.h"
+#include "starboard/elf_loader/log.h"
+
+namespace {
+void LogLastError(const char* msg) {
+  const int kErrorMessageBufferSize = 256;
+  char msgbuf[kErrorMessageBufferSize];
+  SbSystemError error_code = SbSystemGetLastError();
+  if (SbSystemGetErrorString(error_code, msgbuf, kErrorMessageBufferSize) > 0) {
+    SB_LOG(ERROR) << msg << ": " << msgbuf;
+  }
+}
+}  // namespace
+
+namespace starboard {
+namespace elf_loader {
+
+FileImpl::FileImpl() : file_(NULL) {}
+
+FileImpl::~FileImpl() {
+  Close();
+}
+
+bool FileImpl::Open(const char* name) {
+  SB_DLOG(INFO) << "Loading: " << name;
+  file_ = SbFileOpen(name, kSbFileOpenOnly | kSbFileRead, NULL, NULL);
+  if (!file_) {
+    return false;
+  }
+  return true;
+}
+
+bool FileImpl::ReadFromOffset(int64_t offset, char* buffer, int size) {
+  if (!file_) {
+    return false;
+  }
+  int64_t ret = SbFileSeek(file_, kSbFileFromBegin, offset);
+  SB_DLOG(INFO) << "SbFileSeek: ret=" << ret;
+  if (ret == -1) {
+    LogLastError("SbFileSeek: failed");
+    return false;
+  }
+
+  int count = SbFileReadAll(file_, buffer, size);
+  SB_DLOG(INFO) << "SbFileReadAll: count=" << count;
+  if (count == -1) {
+    LogLastError("SbFileReadAll failed");
+    return false;
+  }
+  return true;
+}
+
+void FileImpl::Close() {
+  if (file_) {
+    SbFileClose(file_);
+  }
+}
+
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/file_impl.h b/src/starboard/elf_loader/file_impl.h
new file mode 100644
index 0000000..9407c95
--- /dev/null
+++ b/src/starboard/elf_loader/file_impl.h
@@ -0,0 +1,44 @@
+// 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.
+
+#ifndef STARBOARD_ELF_LOADER_FILE_IMPL_H_
+#define STARBOARD_ELF_LOADER_FILE_IMPL_H_
+
+#include "starboard/elf_loader/elf.h"
+
+#include "starboard/elf_loader/file.h"
+#include "starboard/file.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// Starboard implementation for reading a file.
+class FileImpl : public File {
+ public:
+  FileImpl();
+  ~FileImpl() override;
+  bool Open(const char* name);
+  bool ReadFromOffset(int64_t offset, char* buffer, int size);
+  void Close();
+
+ private:
+  SbFile file_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(FileImpl);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_FILE_IMPL_H_
diff --git a/src/starboard/elf_loader/gnu_hash_table.cc b/src/starboard/elf_loader/gnu_hash_table.cc
new file mode 100644
index 0000000..9e413db
--- /dev/null
+++ b/src/starboard/elf_loader/gnu_hash_table.cc
@@ -0,0 +1,143 @@
+// 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 "starboard/elf_loader/gnu_hash_table.h"
+#include "starboard/common/log.h"
+#include "starboard/elf_loader/log.h"
+#include "starboard/string.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// Compute the GNU hash of a given symbol.
+// For more details on the hash function:
+//  https://blogs.oracle.com/solaris/gnu-hash-elf-sections-v2
+static uint32_t GnuHash(const char* name) {
+  uint32_t h = 5381;
+  const uint8_t* ptr = reinterpret_cast<const uint8_t*>(name);
+  while (*ptr) {
+    h = h * 33 + *ptr++;
+  }
+  return h;
+}
+
+GnuHashTable::GnuHashTable()
+    : num_buckets_(0),
+      sym_offset_(0),
+      sym_count_(0),
+      bloom_word_mask_(0),
+      bloom_shift_(0),
+      bloom_filter_(NULL),
+      buckets_(NULL),
+      chain_(NULL) {}
+void GnuHashTable::Init(uintptr_t dt_gnu_hash) {
+  SB_DLOG(INFO) << "GnuHashTable::Init 0x" << std::hex << dt_gnu_hash;
+  sym_count_ = 0;
+
+  const uint32_t* data = reinterpret_cast<const uint32_t*>(dt_gnu_hash);
+  num_buckets_ = data[0];
+  sym_offset_ = data[1];
+
+  SB_DLOG(INFO) << "GnuHashTable::Init num_buckets_=" << num_buckets_
+                << " sym_offset_" << sym_offset_;
+  if (!num_buckets_)
+    return;
+
+  const uint32_t bloom_size = data[2];
+  SB_DLOG(INFO) << "GnuHashTable::Init bloom_size=" << bloom_size;
+  if ((bloom_size & (bloom_size - 1U)) != 0)  // must be a power of 2
+    return;
+
+  bloom_word_mask_ = bloom_size - 1U;
+  bloom_shift_ = data[3];
+
+  SB_DLOG(INFO) << "GnuHashTable::Init bloom_word_mask_=" << bloom_word_mask_;
+  SB_DLOG(INFO) << "GnuHashTable::Init bloom_shift_=" << bloom_shift_;
+  bloom_filter_ = reinterpret_cast<const Addr*>(data + 4);
+  SB_DLOG(INFO) << "GnuHashTable::Init bloom_filter_=0x" << std::hex
+                << bloom_filter_;
+  buckets_ = reinterpret_cast<const uint32_t*>(bloom_filter_ + bloom_size);
+
+  SB_DLOG(INFO) << "GnuHashTable::Init buckets_=0x" << std::hex << buckets_;
+  chain_ = buckets_ + num_buckets_;
+
+  // Compute number of dynamic symbols by parsing the table.
+  if (num_buckets_ > 0) {
+    // First find the maximum index in the buckets table.
+    uint32_t max_index = buckets_[0];
+    for (size_t n = 1; n < num_buckets_; ++n) {
+      uint32_t sym_index = buckets_[n];
+      if (sym_index > max_index)
+        max_index = sym_index;
+    }
+    // Now start to look at the chain_ table from (max_index - sym_offset_)
+    // until there is a value with LSB set to 1, indicating the end of the
+    // last chain.
+    while ((chain_[max_index - sym_offset_] & 1) == 0)
+      max_index++;
+
+    sym_count_ = (max_index - sym_offset_) + 1;
+  }
+}
+
+bool GnuHashTable::IsValid() const {
+  return sym_count_ > 0;
+}
+
+const Sym* GnuHashTable::LookupByName(const char* symbol_name,
+                                      const Sym* symbol_table,
+                                      const char* string_table) const {
+  SB_DLOG(INFO) << "GnuHashTable::LookupByName: " << symbol_name;
+  uint32_t hash = GnuHash(symbol_name);
+
+  SB_DLOG(INFO) << "GnuHashTable::LookupByName: hash=" << hash;
+  SB_DLOG(INFO) << "GnuHashTable::LookupByName: ELF_BITS=" << ELF_BITS;
+
+  // First, bloom filter test.
+  Addr word = bloom_filter_[(hash / ELF_BITS) & bloom_word_mask_];
+
+  SB_DLOG(INFO) << "GnuHashTable::LookupByName: word=" << word;
+  Addr mask = (Addr(1) << (hash % ELF_BITS)) |
+              (Addr(1) << ((hash >> bloom_shift_) % ELF_BITS));
+
+  SB_DLOG(INFO) << "GnuHashTable::LookupByName: mask=" << mask;
+  if ((word & mask) != mask)
+    return NULL;
+
+  uint32_t sym_index = buckets_[hash % num_buckets_];
+  if (sym_index < sym_offset_)
+    return NULL;
+
+  // TODO: add validations of the syn_index
+  while (true) {
+    const Sym* sym = symbol_table + sym_index;
+    const uint32_t sym_hash = chain_[sym_index - sym_offset_];
+    const char* sym_name = string_table + sym->st_name;
+
+    if ((sym_hash | 1) == (hash | 1) &&
+        !SbStringCompareAll(sym_name, symbol_name)) {
+      return sym;
+    }
+
+    if (sym_hash & 1)
+      break;
+
+    sym_index++;
+  }
+
+  return NULL;
+}
+
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/gnu_hash_table.h b/src/starboard/elf_loader/gnu_hash_table.h
new file mode 100644
index 0000000..5bdf199
--- /dev/null
+++ b/src/starboard/elf_loader/gnu_hash_table.h
@@ -0,0 +1,66 @@
+// 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.
+
+#ifndef STARBOARD_ELF_LOADER_GNU_HASH_TABLE_H_
+#define STARBOARD_ELF_LOADER_GNU_HASH_TABLE_H_
+
+#include <stddef.h>
+#include "starboard/elf_loader/elf.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// Models the hash table used to map symbol names to symbol entries using
+// the GNU format. This one is smaller and faster than the standard ELF one.
+class GnuHashTable {
+ public:
+  GnuHashTable();
+  // Initialize instance. |dt_gnu_hash| should be the address that the
+  // DT_GNU_HASH entry points to in the input ELF dynamic section. Call
+  // IsValid() to determine whether the table was well-formed.
+  void Init(uintptr_t dt_gnu_hash);
+
+  // Returns true iff the content of the table is valid.
+  bool IsValid() const;
+
+  // Return the index of the first dynamic symbol within the ELF symbol table.
+  size_t dyn_symbols_offset() const { return sym_offset_; }
+
+  // Number of dynamic symbols in the ELF symbol table.
+  size_t dyn_symbols_count() const { return sym_count_; }
+
+  // Lookup |symbol_name| in the table. |symbol_table| should point to the
+  // ELF symbol table, and |string_table| to the start of its string table.
+  // Returns NULL on failure.
+  const Sym* LookupByName(const char* symbol_name,
+                          const Sym* symbol_table,
+                          const char* string_table) const;
+
+ private:
+  uint32_t num_buckets_;
+  uint32_t sym_offset_;
+  uint32_t sym_count_;
+  uint32_t bloom_word_mask_;
+  uint32_t bloom_shift_;
+  const Addr* bloom_filter_;
+  const uint32_t* buckets_;
+  const uint32_t* chain_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(GnuHashTable);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_GNU_HASH_TABLE_H_
diff --git a/src/starboard/linux/x64x11/mozjs/thread_types_public.h b/src/starboard/elf_loader/log.h
similarity index 64%
rename from src/starboard/linux/x64x11/mozjs/thread_types_public.h
rename to src/starboard/elf_loader/log.h
index 32fbba2..9736465 100644
--- a/src/starboard/linux/x64x11/mozjs/thread_types_public.h
+++ b/src/starboard/elf_loader/log.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Cobalt Authors. All Rights Reserved.
+// Copyright 2020 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.
@@ -12,9 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_LINUX_X64X11_MOZJS_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_MOZJS_THREAD_TYPES_PUBLIC_H_
+#ifndef STARBOARD_ELF_LOADER_LOG_H_
+#define STARBOARD_ELF_LOADER_LOG_H_
 
-#include "starboard/linux/shared/thread_types_public.h"
+// Disable the verbose logging which is designed only for
+// debugging.
+#undef SB_DLOG
+#define SB_DLOG(severity) SB_EAT_STREAM_PARAMETERS
 
-#endif  // STARBOARD_LINUX_X64X11_MOZJS_THREAD_TYPES_PUBLIC_H_
+#endif  // STARBOARD_ELF_LOADER_LOG_H_
diff --git a/src/starboard/elf_loader/program_table.cc b/src/starboard/elf_loader/program_table.cc
new file mode 100644
index 0000000..aa1e49f
--- /dev/null
+++ b/src/starboard/elf_loader/program_table.cc
@@ -0,0 +1,380 @@
+// 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 "starboard/elf_loader/program_table.h"
+
+#include "starboard/common/log.h"
+#include "starboard/elf_loader/evergreen_info.h"
+#include "starboard/elf_loader/log.h"
+#include "starboard/memory.h"
+#include "starboard/string.h"
+
+#define MAYBE_MAP_FLAG(x, from, to) (((x) & (from)) ? (to) : 0)
+
+#if SB_CAN(MAP_EXECUTABLE_MEMORY)
+#define PFLAGS_TO_PROT(x)                               \
+  (MAYBE_MAP_FLAG((x), PF_X, kSbMemoryMapProtectExec) | \
+   MAYBE_MAP_FLAG((x), PF_R, kSbMemoryMapProtectRead) | \
+   MAYBE_MAP_FLAG((x), PF_W, kSbMemoryMapProtectWrite))
+#endif
+
+#define MAP_FAILED ((void*)-1)
+
+namespace starboard {
+namespace elf_loader {
+
+ProgramTable::ProgramTable()
+    : phdr_num_(0),
+      phdr_mmap_(NULL),
+      phdr_table_(NULL),
+      phdr_size_(0),
+      load_start_(NULL),
+      load_size_(0),
+      base_memory_address_(0) {}
+
+bool ProgramTable::LoadProgramHeader(const Ehdr* elf_header, File* elf_file) {
+  if (!elf_header) {
+    SB_LOG(ERROR) << "Ehdr is required";
+    return false;
+  }
+  if (!elf_file) {
+    SB_LOG(ERROR) << "File is required";
+    return false;
+  }
+  phdr_num_ = elf_header->e_phnum;
+
+  SB_DLOG(INFO) << "Program Header count=" << phdr_num_;
+  // Like the kernel, only accept program header tables smaller than 64 KB.
+  if (phdr_num_ < 1 || phdr_num_ > 65536 / elf_header->e_phentsize) {
+    SB_LOG(ERROR) << "Invalid program header count: " << phdr_num_;
+    return false;
+  }
+
+  SB_DLOG(INFO) << "elf_header->e_phoff=" << elf_header->e_phoff;
+  SB_DLOG(INFO) << "elf_header->e_phnum=" << elf_header->e_phnum;
+
+  Addr page_min = PAGE_START(elf_header->e_phoff);
+  Addr page_max = PAGE_END(elf_header->e_phoff + (phdr_num_ * elf_header->e_phentsize));
+  Addr page_offset = PAGE_OFFSET(elf_header->e_phoff);
+
+  SB_DLOG(INFO) << "page_min=" << page_min;
+  SB_DLOG(INFO) << "page_max=" << page_max;
+
+  phdr_size_ = page_max - page_min;
+
+  SB_DLOG(INFO) << "page_max - page_min=" << page_max - page_min;
+
+#if SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP)
+  phdr_mmap_ =
+      SbMemoryMap(phdr_size_, kSbMemoryMapProtectWrite, "program_header");
+  if (!phdr_mmap_) {
+    SB_LOG(ERROR) << "Failed to allocate memory";
+    return false;
+  }
+
+  SB_DLOG(INFO) << "Allocated address=" << phdr_mmap_;
+#else
+  SB_CHECK(false);
+#endif
+  if (!elf_file->ReadFromOffset(page_min, reinterpret_cast<char*>(phdr_mmap_),
+                                phdr_size_)) {
+    SB_LOG(ERROR) << "Failed to read program header from file offset: "
+                  << page_min;
+    return false;
+  }
+#if SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP)
+  bool mp_result =
+      SbMemoryProtect(phdr_mmap_, phdr_size_, kSbMemoryMapProtectRead);
+  SB_DLOG(INFO) << "mp_result=" << mp_result;
+  if (!mp_result) {
+    SB_LOG(ERROR) << "Failed to protect program header";
+    return false;
+  }
+#else
+  SB_CHECK(false);
+#endif
+
+  phdr_table_ = reinterpret_cast<Phdr*>(reinterpret_cast<char*>(phdr_mmap_) +
+                                        page_offset);
+
+  return true;
+}
+
+bool ProgramTable::LoadSegments(File* elf_file) {
+  for (size_t i = 0; i < phdr_num_; ++i) {
+    const Phdr* phdr = &phdr_table_[i];
+
+    if (phdr->p_type != PT_LOAD) {
+      continue;
+    }
+
+    // Segment byte addresses in memory.
+    Addr seg_start = phdr->p_vaddr + base_memory_address_;
+    Addr seg_end = seg_start + phdr->p_memsz;
+
+    // Segment page addresses in memory.
+    Addr seg_page_start = PAGE_START(seg_start);
+    Addr seg_page_end = PAGE_END(seg_end);
+
+    // File offsets.
+    Addr seg_file_end = seg_start + phdr->p_filesz;
+    Addr file_start = phdr->p_offset;
+    Addr file_end = file_start + phdr->p_filesz;
+
+    SB_DLOG(INFO) << " phdr->p_offset=" << phdr->p_offset
+                  << " phdr->p_filesz=" << phdr->p_filesz;
+
+    Addr file_page_start = PAGE_START(file_start);
+    Addr file_length = file_end - file_page_start;
+
+    SB_DLOG(INFO) << "Mapping segment: "
+                  << " file_page_start=" << file_page_start
+                  << " file_length=" << file_length << " seg_page_start=0x"
+                  << std::hex << seg_page_start;
+
+#if (SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP)) && \
+    SB_CAN(MAP_EXECUTABLE_MEMORY)
+    if (file_length != 0) {
+      const int prot_flags = PFLAGS_TO_PROT(phdr->p_flags);
+      SB_DLOG(INFO) << "segment prot_flags=" << std::hex << prot_flags;
+
+      void* seg_addr = reinterpret_cast<void*>(seg_page_start);
+      bool mp_ret =
+          SbMemoryProtect(seg_addr, file_length, kSbMemoryMapProtectWrite);
+      SB_DLOG(INFO) << "segment vaddress=" << seg_addr;
+
+      if (!mp_ret) {
+        SB_LOG(ERROR) << "Failed to unprotect segment";
+        return false;
+      }
+      if (!elf_file->ReadFromOffset(file_page_start,
+                                    reinterpret_cast<char*>(seg_addr),
+                                    file_length)) {
+        SB_DLOG(INFO) << "Failed to read segment from file offset: "
+                      << file_page_start;
+        return false;
+      }
+      mp_ret = SbMemoryProtect(seg_addr, file_length, prot_flags);
+      SB_DLOG(INFO) << "mp_ret=" << mp_ret;
+      if (!mp_ret) {
+        SB_LOG(ERROR) << "Failed to protect segment";
+        return false;
+      }
+      if (!seg_addr) {
+        SB_LOG(ERROR) << "Could not map segment " << i;
+        return false;
+      }
+    }
+#else
+    SB_CHECK(false);
+#endif
+
+    // if the segment is writable, and does not end on a page boundary,
+    // zero-fill it until the page limit.
+    if ((phdr->p_flags & PF_W) != 0 && PAGE_OFFSET(seg_file_end) > 0) {
+      SbMemorySet(reinterpret_cast<void*>(seg_file_end), 0,
+                  PAGE_SIZE - PAGE_OFFSET(seg_file_end));
+    }
+
+    seg_file_end = PAGE_END(seg_file_end);
+
+    // seg_file_end is now the first page address after the file
+    // content. If seg_page_end is larger, we need to zero anything
+    // between them. This is done by using a private anonymous
+    // map for all extra pages.
+    if (seg_page_end > seg_file_end) {
+#if (SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP)) && \
+    SB_CAN(MAP_EXECUTABLE_MEMORY)
+      bool mprotect_fix = SbMemoryProtect(reinterpret_cast<void*>(seg_file_end),
+                                          seg_page_end - seg_file_end,
+                                          kSbMemoryMapProtectWrite);
+      SB_DLOG(INFO) << "mprotect_fix=" << mprotect_fix;
+      if (!mprotect_fix) {
+        SB_LOG(ERROR) << "Failed to unprotect end of segment";
+        return false;
+      }
+#else
+      SB_CHECK(false);
+#endif
+
+      SbMemorySet(reinterpret_cast<void*>(seg_file_end), 0,
+                  seg_page_end - seg_file_end);
+#if (SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP)) && \
+    SB_CAN(MAP_EXECUTABLE_MEMORY)
+      SbMemoryProtect(reinterpret_cast<void*>(seg_file_end),
+                      seg_page_end - seg_file_end,
+                      PFLAGS_TO_PROT(phdr->p_flags));
+      SB_DLOG(INFO) << "mprotect_fix=" << mprotect_fix;
+      if (!mprotect_fix) {
+        SB_LOG(ERROR) << "Failed to protect end of segment";
+        return false;
+      }
+#else
+      SB_CHECK(false);
+#endif
+    }
+  }
+  return true;
+}
+
+size_t ProgramTable::GetLoadMemorySize() {
+  Addr min_vaddr = ~static_cast<Addr>(0);
+  Addr max_vaddr = 0x00000000U;
+
+  bool found_pt_load = false;
+  for (size_t i = 0; i < phdr_num_; ++i) {
+    const Phdr* phdr = &phdr_table_[i];
+
+    if (phdr->p_type != PT_LOAD) {
+      SB_DLOG(INFO) << "GetLoadMemorySize: ignoring segment with type: "
+                    << phdr->p_type;
+      continue;
+    }
+    found_pt_load = true;
+
+    if (phdr->p_vaddr < min_vaddr) {
+      SB_DLOG(INFO) << "p_vaddr=" << std::hex << phdr->p_vaddr;
+      min_vaddr = phdr->p_vaddr;
+    }
+
+    if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) {
+      max_vaddr = phdr->p_vaddr + phdr->p_memsz;
+      SB_DLOG(INFO) << "phdr->p_vaddr=" << phdr->p_vaddr
+                    << " phdr->p_memsz=" << phdr->p_memsz;
+      SB_DLOG(INFO) << " max_vaddr=0x" << std::hex << max_vaddr;
+    }
+  }
+  if (!found_pt_load) {
+    min_vaddr = 0x00000000U;
+  }
+
+  min_vaddr = PAGE_START(min_vaddr);
+  max_vaddr = PAGE_END(max_vaddr);
+
+  return max_vaddr - min_vaddr;
+}
+
+void ProgramTable::GetDynamicSection(Dyn** dynamic,
+                                     size_t* dynamic_count,
+                                     Word* dynamic_flags) {
+  const Phdr* phdr = phdr_table_;
+  const Phdr* phdr_limit = phdr + phdr_num_;
+
+  for (phdr = phdr_table_; phdr < phdr_limit; phdr++) {
+    if (phdr->p_type != PT_DYNAMIC) {
+      SB_DLOG(INFO) << "Ignore section with type: " << phdr->p_type;
+      continue;
+    }
+
+    SB_DLOG(INFO) << "Reading at vaddr: " << phdr->p_vaddr;
+    *dynamic = reinterpret_cast<Dyn*>(base_memory_address_ + phdr->p_vaddr);
+    if (dynamic_count) {
+      *dynamic_count = (size_t)(phdr->p_memsz / sizeof(Dyn));
+    }
+    if (dynamic_flags) {
+      *dynamic_flags = phdr->p_flags;
+    }
+    return;
+  }
+  *dynamic = NULL;
+  if (dynamic_count) {
+    *dynamic_count = 0;
+  }
+}
+
+int ProgramTable::AdjustMemoryProtectionOfReadOnlySegments(
+    int extra_prot_flags) {
+  const Phdr* phdr = phdr_table_;
+  const Phdr* phdr_limit = phdr + phdr_num_;
+
+  for (; phdr < phdr_limit; phdr++) {
+    if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0)
+      continue;
+
+    Addr seg_page_start = PAGE_START(phdr->p_vaddr) + base_memory_address_;
+    Addr seg_page_end =
+        PAGE_END(phdr->p_vaddr + phdr->p_memsz) + base_memory_address_;
+#if (SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP)) && \
+    SB_CAN(MAP_EXECUTABLE_MEMORY)
+    int ret = SbMemoryProtect(reinterpret_cast<void*>(seg_page_start),
+                              seg_page_end - seg_page_start,
+                              PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags);
+    if (ret < 0) {
+      return -1;
+    }
+#else
+    SB_CHECK(false);
+#endif
+  }
+  return 0;
+}
+
+bool ProgramTable::ReserveLoadMemory() {
+  load_size_ = GetLoadMemorySize();
+  if (load_size_ == 0) {
+    SB_LOG(ERROR) << "No loadable segments";
+    return false;
+  }
+
+  SB_DLOG(INFO) << "Load size=" << load_size_;
+
+#if (SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP))
+  load_start_ =
+      SbMemoryMap(load_size_, kSbMemoryMapProtectReserved, "reserved_mem");
+  if (load_start_ == MAP_FAILED) {
+    SB_LOG(ERROR) << "Could not reserve " << load_size_
+                  << " bytes of address space";
+    return false;
+  }
+#else
+  SB_CHECK(false);
+#endif
+  base_memory_address_ = reinterpret_cast<Addr>(load_start_);
+  SB_LOG(INFO) << "Load start=" << std::hex << load_start_
+               << " base_memory_address=0x" << base_memory_address_;
+  return true;
+}
+
+void ProgramTable::PublishEvergreenInfo(const char* file_path) {
+  EvergreenInfo evergreen_info;
+  SbMemorySet(&evergreen_info, sizeof(EvergreenInfo), 0);
+  SbStringCopy(evergreen_info.file_path_buf, file_path,
+               EVERGREEN_FILE_PATH_MAX_SIZE);
+  evergreen_info.base_address = base_memory_address_;
+  evergreen_info.load_size = load_size_;
+  evergreen_info.phdr_table = (uint64_t)phdr_table_;
+  evergreen_info.phdr_table_num = phdr_num_;
+  SetEvergreenInfo(&evergreen_info);
+}
+
+Addr ProgramTable::GetBaseMemoryAddress() {
+  return base_memory_address_;
+}
+
+ProgramTable::~ProgramTable() {
+  SetEvergreenInfo(NULL);
+#if SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP)
+  if (load_start_) {
+    SbMemoryUnmap(load_start_, load_size_);
+  }
+  if (phdr_mmap_) {
+    SbMemoryUnmap(phdr_mmap_, phdr_size_);
+  }
+#else
+  SB_CHECK(false);
+#endif
+}
+
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/program_table.h b/src/starboard/elf_loader/program_table.h
new file mode 100644
index 0000000..9512b5e
--- /dev/null
+++ b/src/starboard/elf_loader/program_table.h
@@ -0,0 +1,95 @@
+// 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.
+
+#ifndef STARBOARD_ELF_LOADER_PROGRAM_TABLE_H_
+#define STARBOARD_ELF_LOADER_PROGRAM_TABLE_H_
+
+#include "starboard/elf_loader/elf.h"
+#include "starboard/elf_loader/file.h"
+
+namespace starboard {
+namespace elf_loader {
+
+// Loads the ELF's binary program table and memory maps
+// the loadable segments.
+//
+// To properly initialize the program table and the segments
+// the following calls are required:
+//  1. LoadProgramHeader()
+//  2. LoadSegments()
+//
+// After those calls the ProgramTable class is fully functional and
+// the segments properly loaded.
+
+class ProgramTable {
+ public:
+  ProgramTable();
+
+  // Loads the program header.
+  bool LoadProgramHeader(const Ehdr* elf_header, File* elf_file);
+
+  // Loads the segments.
+  bool LoadSegments(File* elf_file);
+
+  // Retrieves the dynamic section table.
+  void GetDynamicSection(Dyn** dynamic,
+                         size_t* dynamic_count,
+                         Word* dynamic_flags);
+
+  // Adjusts the memory protection of read only segments.
+  // This call is used to make text segments writable in order
+  // to apply relocations. After the relocations are done the
+  // protection is restored to its original read only state.
+  int AdjustMemoryProtectionOfReadOnlySegments(int extra_prot_flags);
+
+  // Reserves a contiguous block of memory, page aligned for mapping all
+  // the segments of the binary.
+  bool ReserveLoadMemory();
+
+  // Retrieves the base load address for the binary.
+  Addr GetBaseMemoryAddress();
+
+  // Publish the memory mapping information of the library to
+  // the EvergreenInfo API.
+  void PublishEvergreenInfo(const char* file_path);
+
+  ~ProgramTable();
+
+ private:
+  // Calculates the memory size of the binary.
+  size_t GetLoadMemorySize();
+
+ private:
+  size_t phdr_num_;
+  void* phdr_mmap_;
+  Phdr* phdr_table_;
+  Addr phdr_size_;
+
+  // First page of reserved address space.
+  void* load_start_;
+
+  // Size in bytes of reserved address space.
+  Addr load_size_;
+
+  // The base memory address. All virtual addresses
+  // from the ELF file are offsets from this address.
+  Addr base_memory_address_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(ProgramTable);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_PROGRAM_TABLE_H_
diff --git a/src/starboard/elf_loader/program_table_test.cc b/src/starboard/elf_loader/program_table_test.cc
new file mode 100644
index 0000000..b2b6efa
--- /dev/null
+++ b/src/starboard/elf_loader/program_table_test.cc
@@ -0,0 +1,188 @@
+// 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 "starboard/elf_loader/program_table.h"
+
+#include <vector>
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/elf_loader/file.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_API_VERSION >= 12 &&                                         \
+    (SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP)) && \
+    SB_CAN(MAP_EXECUTABLE_MEMORY)
+namespace starboard {
+namespace elf_loader {
+
+namespace {
+
+class DummyFile : public File {
+ public:
+  typedef struct FileChunk {
+    FileChunk(int file_offset, const char* buffer, int size)
+        : file_offset_(file_offset), buffer_(buffer), size_(size) {}
+    int file_offset_;
+    const char* buffer_;
+    int size_;
+  } FileChunk;
+
+  explicit DummyFile(const std::vector<FileChunk>& file_chunks)
+      : file_chunks_(file_chunks), read_index_(0) {}
+
+  bool Open(const char* name) { return true; }
+  bool ReadFromOffset(int64_t offset, char* buffer, int size) {
+    SB_LOG(INFO) << "ReadFromOffset offset=" << offset << " size=" << size
+                 << " read_index_=" << read_index_;
+    if (read_index_ >= file_chunks_.size()) {
+      SB_LOG(INFO) << "ReadFromOffset EOF";
+      return false;
+    }
+    const FileChunk& file_chunk = file_chunks_[read_index_++];
+    if (offset != file_chunk.file_offset_) {
+      SB_LOG(ERROR) << "ReadFromOffset: Invalid offset " << offset
+                    << " expected " << file_chunk.file_offset_;
+      return false;
+    }
+    if (size > file_chunk.size_) {
+      SB_LOG(ERROR) << "ReadFromOffset: Invalid size " << size << " expected < "
+                    << file_chunk.size_;
+      return false;
+    }
+    SbMemoryCopy(buffer, file_chunk.buffer_, size);
+    return true;
+  }
+  void Close() {}
+
+ private:
+  int file_offset_;
+  const char* buffer_;
+  int size_;
+  std::vector<FileChunk> file_chunks_;
+  int read_index_;
+};
+
+class ProgramTableTest : public ::testing::Test {
+ protected:
+  ProgramTableTest() { program_table_.reset(new ProgramTable()); }
+  ~ProgramTableTest() {}
+
+  void HelperMethod() {}
+
+ protected:
+  scoped_ptr<ProgramTable> program_table_;
+};
+
+TEST_F(ProgramTableTest, LoadSegments) {
+  // File structure
+  // [Phdr1]
+  // [Phdr2]
+  // [200, 300) sement for phdr1
+  // [250, 300) dyanmic section in segment for phdr1
+  // [400, 500) sement for phdr2
+  Ehdr ehdr;
+  ehdr.e_phnum = 3;
+  ehdr.e_phoff = 0;
+  ehdr.e_phentsize = sizeof(Phdr);
+
+  Phdr ent1;
+  Phdr ent2;
+  Phdr ent3;
+  SbMemorySet(&ent1, 0, sizeof(Phdr));
+  SbMemorySet(&ent2, 0, sizeof(Phdr));
+  SbMemorySet(&ent3, 0, sizeof(Phdr));
+
+  ent1.p_type = PT_LOAD;
+  ent1.p_vaddr = 0;
+  ent1.p_memsz = 2 * PAGE_SIZE;
+  ent1.p_offset = 200;
+  ent1.p_filesz = 100;
+  ent1.p_flags = kSbMemoryMapProtectRead;
+
+  ent2.p_type = PT_LOAD;
+  ent2.p_vaddr = 2 * PAGE_SIZE;
+  ent2.p_memsz = 3 * PAGE_SIZE;
+  ent2.p_offset = 400;
+  ent2.p_filesz = 100;
+  ent1.p_flags = kSbMemoryMapProtectRead | kSbMemoryMapProtectExec;
+
+  ent3.p_type = PT_DYNAMIC;
+  ent3.p_vaddr = 250;
+  ent3.p_memsz = 3 * sizeof(Dyn);
+  ent3.p_offset = 250;
+  ent3.p_filesz = 5 * sizeof(Dyn);
+  ent3.p_flags = 0x42;
+
+  Phdr program_table_data[3];
+  program_table_data[0] = ent1;
+  program_table_data[1] = ent2;
+  program_table_data[2] = ent3;
+
+  Dyn dynamic_table_data[3];
+  dynamic_table_data[0].d_tag = DT_DEBUG;
+  dynamic_table_data[1].d_tag = DT_DEBUG;
+  dynamic_table_data[2].d_tag = DT_DEBUG;
+
+  char program_table_page[PAGE_SIZE];
+  SbMemorySet(program_table_page, 0, sizeof(program_table_page));
+  SbMemoryCopy(program_table_page, program_table_data,
+               sizeof(program_table_data));
+
+  char segment_file_data1[2 * PAGE_SIZE];
+  char segment_file_data2[3 * PAGE_SIZE];
+
+  SbMemoryCopy(segment_file_data1 + 250, dynamic_table_data,
+               sizeof(dynamic_table_data));
+
+  std::vector<DummyFile::FileChunk> file_chunks;
+  file_chunks.push_back(
+      DummyFile::FileChunk(0, program_table_page, sizeof(program_table_page)));
+  file_chunks.push_back(
+      DummyFile::FileChunk(0, segment_file_data1, sizeof(segment_file_data1)));
+  file_chunks.push_back(
+      DummyFile::FileChunk(0, segment_file_data2, sizeof(segment_file_data2)));
+
+  DummyFile file(file_chunks);
+
+  EXPECT_TRUE(program_table_->LoadProgramHeader(&ehdr, &file));
+
+  EXPECT_EQ(program_table_->GetBaseMemoryAddress(), 0);
+
+  EXPECT_TRUE(program_table_->ReserveLoadMemory());
+
+  EXPECT_NE(program_table_->GetBaseMemoryAddress(), 0);
+
+  EXPECT_TRUE(program_table_->LoadSegments(&file));
+
+  Dyn* dynamic = NULL;
+  size_t dynamic_count = 0;
+  Word dynamic_flags = 0;
+
+  program_table_->GetDynamicSection(&dynamic, &dynamic_count, &dynamic_flags);
+  Dyn* expected_dyn = reinterpret_cast<Dyn*>(
+      program_table_->GetBaseMemoryAddress() + ent3.p_vaddr);
+  EXPECT_TRUE(dynamic != NULL);
+  EXPECT_EQ(dynamic[0].d_tag, DT_DEBUG);
+  EXPECT_EQ(dynamic[1].d_tag, DT_DEBUG);
+  EXPECT_EQ(dynamic[2].d_tag, DT_DEBUG);
+  EXPECT_EQ(dynamic_count, 3);
+  EXPECT_EQ(dynamic_flags, 0x42);
+}
+
+}  // namespace
+}  // namespace elf_loader
+}  // namespace starboard
+#endif  // SB_API_VERSION >= 12 && (SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION
+        // || SB_HAS(MMAP)) && SB_CAN(MAP_EXECUTABLE_MEMORY)
diff --git a/src/starboard/elf_loader/relocations.cc b/src/starboard/elf_loader/relocations.cc
new file mode 100644
index 0000000..80c9069
--- /dev/null
+++ b/src/starboard/elf_loader/relocations.cc
@@ -0,0 +1,478 @@
+// 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 "starboard/elf_loader/relocations.h"
+
+#include "starboard/common/log.h"
+#include "starboard/elf_loader/log.h"
+
+namespace starboard {
+namespace elf_loader {
+
+Relocations::Relocations(Addr base_memory_address,
+                         DynamicSection* dynamic_section,
+                         ExportedSymbols* exported_symbols)
+    : base_memory_address_(base_memory_address),
+      dynamic_section_(dynamic_section),
+      plt_relocations_(0),
+      plt_relocations_size_(0),
+      plt_got_(NULL),
+      relocations_(0),
+      relocations_size_(0),
+      has_text_relocations_(false),
+      has_symbolic_(false),
+      exported_symbols_(exported_symbols) {}
+
+bool Relocations::HasTextRelocations() {
+  return has_text_relocations_;
+}
+
+bool Relocations::InitRelocations() {
+  SB_DLOG(INFO) << "InitRelocations: dynamic_count="
+                << dynamic_section_->GetDynamicTableSize();
+  const Dyn* dynamic = dynamic_section_->GetDynamicTable();
+  for (int i = 0; i < dynamic_section_->GetDynamicTableSize(); i++) {
+    Addr dyn_value = dynamic[i].d_un.d_val;
+    uintptr_t dyn_addr = base_memory_address_ + dynamic[i].d_un.d_ptr;
+    Addr tag = dynamic[i].d_tag;
+    SB_DLOG(INFO) << "InitRelocations: tag=" << tag;
+    switch (tag) {
+#if defined(USE_RELA)
+      case DT_REL:
+      case DT_RELSZ:
+#else
+      case DT_RELA:
+      case DT_RELASZ:
+#endif
+        SB_LOG(ERROR) << "unsupported relocation type";
+        return false;
+      case DT_PLTREL:
+        SB_DLOG(INFO) << "  DT_PLTREL value=" << dyn_value;
+#if defined(USE_RELA)
+        if (dyn_value != DT_RELA) {
+          SB_LOG(ERROR) << "unsupported DT_PLTREL  expected DT_RELA";
+          return false;
+        }
+#else
+        if (dyn_value != DT_REL) {
+          SB_LOG(ERROR) << "unsupported DT_PLTREL expected DT_REL";
+          return false;
+        }
+#endif
+        break;
+      case DT_JMPREL:
+        SB_DLOG(INFO) << "  DT_JMPREL addr=0x" << std::hex
+                      << (dyn_addr - base_memory_address_);
+        plt_relocations_ = dyn_addr;
+        break;
+      case DT_PLTRELSZ:
+        plt_relocations_size_ = dyn_value;
+        SB_DLOG(INFO) << "  DT_PLTRELSZ size=" << dyn_value;
+        break;
+#if defined(USE_RELA)
+      case DT_RELA:
+#else
+      case DT_REL:
+#endif
+        SB_DLOG(INFO) << "  " << ((tag == DT_RELA) ? "DT_RELA" : "DT_REL")
+                      << " addr=" << std::hex
+                      << (dyn_addr - base_memory_address_);
+        if (relocations_) {
+          SB_LOG(ERROR)
+              << "Unsupported DT_RELA/DT_REL combination in dynamic section";
+          return false;
+        }
+        relocations_ = dyn_addr;
+        break;
+#if defined(USE_RELA)
+      case DT_RELASZ:
+#else
+      case DT_RELSZ:
+#endif
+        SB_DLOG(INFO) << "  " << ((tag == DT_RELASZ) ? "DT_RELASZ" : "DT_RELSZ")
+                      << " size=" << dyn_value;
+        relocations_size_ = dyn_value;
+        break;
+      case DT_RELR:
+      case DT_RELRSZ:
+        SB_LOG(ERROR) << "  DT_RELRSZ NOT IMPELMENTED";
+        break;
+      case DT_RELRENT:
+      case DT_PLTGOT:
+        SB_DLOG(INFO) << "DT_PLTGOT addr=0x" << std::hex
+                      << (dyn_addr - base_memory_address_);
+        plt_got_ = reinterpret_cast<Addr*>(dyn_addr);
+        break;
+      case DT_TEXTREL:
+        SB_DLOG(INFO) << "  DT_TEXTREL";
+        has_text_relocations_ = true;
+        break;
+      case DT_SYMBOLIC:
+        SB_DLOG(INFO) << "  DT_SYMBOLIC";
+        has_symbolic_ = true;
+        break;
+      case DT_FLAGS:
+        if (dyn_value & DF_TEXTREL)
+          has_text_relocations_ = true;
+        if (dyn_value & DF_SYMBOLIC)
+          has_symbolic_ = true;
+
+        SB_DLOG(INFO) << "  DT_FLAGS has_text_relocations="
+                      << has_text_relocations_
+                      << " has_symbolic=" << has_symbolic_;
+
+        break;
+      default:
+        break;
+    }
+  }
+
+  return true;
+}
+
+bool Relocations::ApplyAllRelocations() {
+  SB_DLOG(INFO) << "Applying regular relocations";
+  if (!ApplyRelocations(reinterpret_cast<rel_t*>(relocations_),
+                        relocations_size_ / sizeof(rel_t))) {
+    SB_LOG(ERROR) << "regular relocations failed";
+    return false;
+  }
+
+  SB_DLOG(INFO) << "Applying PLT relocations";
+  if (!ApplyRelocations(reinterpret_cast<rel_t*>(plt_relocations_),
+                        plt_relocations_size_ / sizeof(rel_t))) {
+    SB_LOG(ERROR) << "PLT relocations failed";
+    return false;
+  }
+  return true;
+}
+
+bool Relocations::ApplyRelocations(const rel_t* rel, size_t rel_count) {
+  SB_DLOG(INFO) << "rel=" << std::hex << rel << std::dec
+                << " rel_count=" << rel_count;
+
+  if (!rel)
+    return true;
+
+  for (size_t rel_n = 0; rel_n < rel_count; rel++, rel_n++) {
+    SB_DLOG(INFO) << "  Relocation " << rel_n + 1 << " of " << rel_count;
+
+    if (!ApplyRelocation(rel))
+      return false;
+  }
+
+  return true;
+}
+
+bool Relocations::ApplyRelocation(const rel_t* rel) {
+  const Word rel_type = ELF_R_TYPE(rel->r_info);
+  const Word rel_symbol = ELF_R_SYM(rel->r_info);
+
+  Addr sym_addr = 0;
+  Addr reloc = static_cast<Addr>(rel->r_offset + base_memory_address_);
+  SB_DLOG(INFO) << "  offset=0x" << std::hex << rel->r_offset
+                << " type=" << std::dec << rel_type << " reloc=0x" << std::hex
+                << reloc << " symbol=" << rel_symbol;
+
+  if (rel_type == 0)
+    return true;
+
+  if (rel_symbol != 0) {
+    if (!ResolveSymbol(rel_type, rel_symbol, reloc, &sym_addr)) {
+      SB_LOG(ERROR) << "Failed to resolve symbol: " << rel_symbol;
+      return false;
+    }
+  }
+
+  return ApplyResolvedReloc(rel, sym_addr);
+}
+
+#if defined(USE_RELA)
+bool Relocations::ApplyResolvedReloc(const Rela* rela, Addr sym_addr) {
+  const Word rela_type = ELF_R_TYPE(rela->r_info);
+  const Sword addend = rela->r_addend;
+  const Addr reloc = static_cast<Addr>(rela->r_offset + base_memory_address_);
+
+  SB_DLOG(INFO) << "  rela reloc=0x" << std::hex << reloc << " offset=0x"
+                << rela->r_offset << " type=" << std::dec << rela_type
+                << " addend=0x" << std::hex << addend;
+  Addr* target = reinterpret_cast<Addr*>(reloc);
+  switch (rela_type) {
+#if SB_IS(ARCH_ARM64)
+    case R_AARCH64_JUMP_SLOT:
+      SB_DLOG(INFO) << "  R_AARCH64_JUMP_SLOT target=" << std::hex << target
+                    << " addr=" << (sym_addr + addend);
+      *target = sym_addr + addend;
+      break;
+
+    case R_AARCH64_GLOB_DAT:
+      SB_DLOG(INFO) << " R_AARCH64_GLOB_DAT target=" << std::hex << target
+                    << " addr=" << (sym_addr + addend);
+      *target = sym_addr + addend;
+      break;
+
+    case R_AARCH64_ABS64:
+      SB_DLOG(INFO) << "  R_AARCH64_ABS64 target=" << std::hex << target << " "
+                    << *target << " addr=" << sym_addr + addend;
+      *target += sym_addr + addend;
+      break;
+
+    case R_AARCH64_RELATIVE:
+      SB_DLOG(INFO) << "  R_AARCH64_RELATIVE target=" << std::hex << target
+                    << " " << *target
+                    << " bias=" << base_memory_address_ + addend;
+      *target = base_memory_address_ + addend;
+      break;
+#endif
+
+#if SB_IS(ARCH_X64)
+    case R_X86_64_JMP_SLOT:
+      SB_DLOG(INFO) << "  R_X86_64_JMP_SLOT target=" << std::hex << target
+                    << " addr=" << (sym_addr + addend);
+      *target = sym_addr + addend;
+      break;
+
+    case R_X86_64_GLOB_DAT:
+      SB_DLOG(INFO) << "  R_X86_64_GLOB_DAT target=" << std::hex << target
+                    << " addr=" << (sym_addr + addend);
+
+      *target = sym_addr + addend;
+      break;
+
+    case R_X86_64_RELATIVE:
+      SB_DLOG(INFO) << "  R_X86_64_RELATIVE target=" << std::hex << target
+                    << " " << *target
+                    << " bias=" << base_memory_address_ + addend;
+      *target = base_memory_address_ + addend;
+      break;
+
+    case R_X86_64_64:
+      *target = sym_addr + addend;
+      break;
+
+    case R_X86_64_PC32:
+      *target = sym_addr + (addend - reloc);
+      break;
+#endif
+
+    default:
+      SB_LOG(ERROR) << "Invalid relocation type: " << rela_type;
+      return false;
+  }
+
+  return true;
+}
+#else
+bool Relocations::ApplyResolvedReloc(const Rel* rel, Addr sym_addr) {
+  const Word rel_type = ELF_R_TYPE(rel->r_info);
+  const Addr reloc = static_cast<Addr>(rel->r_offset + base_memory_address_);
+
+  SB_DLOG(INFO) << "  rel reloc=0x" << std::hex << reloc << " offset=0x"
+                << rel->r_offset << " type=" << std::dec << rel_type;
+
+  Addr* target = reinterpret_cast<Addr*>(reloc);
+  switch (rel_type) {
+#if SB_IS(ARCH_ARM)
+    case R_ARM_JUMP_SLOT:
+      SB_DLOG(INFO) << "  R_ARM_JUMP_SLOT target=" << std::hex << target
+                    << " addr=" << sym_addr;
+      *target = sym_addr;
+      break;
+
+    case R_ARM_GLOB_DAT:
+      SB_DLOG(INFO) << "  R_ARM_GLOB_DAT target=" << std::hex << target
+                    << " addr=" << sym_addr;
+      *target = sym_addr;
+      break;
+
+    case R_ARM_ABS32:
+      SB_DLOG(INFO) << "  R_ARM_ABS32 target=" << std::hex << target << " "
+                    << *target << " addr=" << sym_addr;
+      *target += sym_addr;
+      break;
+
+    case R_ARM_REL32:
+      SB_DLOG(INFO) << "  R_ARM_REL32 target=" << std::hex << target << " "
+                    << *target << " addr=" << sym_addr
+                    << " offset=" << rel->r_offset;
+      *target += sym_addr - rel->r_offset;
+      break;
+
+    case R_ARM_RELATIVE:
+      SB_DLOG(INFO) << "  RR_ARM_RELATIVE target=" << std::hex << target << " "
+                    << *target << " bias=" << base_memory_address_;
+      *target += base_memory_address_;
+      break;
+#endif
+
+#if SB_IS(ARCH_X86)
+    case R_386_JMP_SLOT:
+      SB_DLOG(INFO) << "  R_386_JMP_SLOT target=" << std::hex << target
+                    << " addr=" << sym_addr;
+
+      *target = sym_addr;
+      break;
+
+    case R_386_GLOB_DAT:
+      SB_DLOG(INFO) << "  R_386_GLOB_DAT target=" << std::hex << target
+                    << " addr=" << sym_addr;
+      *target = sym_addr;
+
+      break;
+
+    case R_386_RELATIVE:
+      SB_DLOG(INFO) << "  R_386_RELATIVE target=" << std::hex << target << " "
+                    << *target << " bias=" << base_memory_address_;
+
+      *target += base_memory_address_;
+      break;
+
+    case R_386_32:
+      SB_DLOG(INFO) << "  R_386_32 target=" << std::hex << target << " "
+                    << *target << " addr=" << sym_addr;
+      *target += sym_addr;
+      break;
+
+    case R_386_PC32:
+      SB_DLOG(INFO) << "  R_386_PC32 target=" << std::hex << target << " "
+                    << *target << " addr=" << sym_addr << " reloc=" << reloc;
+      *target += (sym_addr - reloc);
+      break;
+#endif
+
+    default:
+      SB_LOG(ERROR) << "Invalid relocation type: " << rel_type;
+      return false;
+  }
+
+  return true;
+}
+#endif
+
+RelocationType Relocations::GetRelocationType(Word r_type) {
+  switch (r_type) {
+#if SB_IS(ARCH_ARM)
+    case R_ARM_JUMP_SLOT:
+    case R_ARM_GLOB_DAT:
+    case R_ARM_ABS32:
+      return RELOCATION_TYPE_ABSOLUTE;
+
+    case R_ARM_REL32:
+    case R_ARM_RELATIVE:
+      return RELOCATION_TYPE_RELATIVE;
+
+    case R_ARM_COPY:
+      return RELOCATION_TYPE_COPY;
+#endif
+
+#if SB_IS(ARCH_ARM64)
+    case R_AARCH64_JUMP_SLOT:
+    case R_AARCH64_GLOB_DAT:
+    case R_AARCH64_ABS64:
+      return RELOCATION_TYPE_ABSOLUTE;
+
+    case R_AARCH64_RELATIVE:
+      return RELOCATION_TYPE_RELATIVE;
+
+    case R_AARCH64_COPY:
+      return RELOCATION_TYPE_COPY;
+#endif
+
+#if SB_IS(ARCH_X86)
+    case R_386_JMP_SLOT:
+    case R_386_GLOB_DAT:
+    case R_386_32:
+      return RELOCATION_TYPE_ABSOLUTE;
+
+    case R_386_RELATIVE:
+      return RELOCATION_TYPE_RELATIVE;
+
+    case R_386_PC32:
+      return RELOCATION_TYPE_PC_RELATIVE;
+#endif
+
+#if SB_IS(ARCH_X64)
+    case R_X86_64_JMP_SLOT:
+    case R_X86_64_GLOB_DAT:
+    case R_X86_64_64:
+      return RELOCATION_TYPE_ABSOLUTE;
+
+    case R_X86_64_RELATIVE:
+      return RELOCATION_TYPE_RELATIVE;
+
+    case R_X86_64_PC32:
+      return RELOCATION_TYPE_PC_RELATIVE;
+#endif
+    default:
+      return RELOCATION_TYPE_UNKNOWN;
+  }
+}
+
+bool Relocations::ResolveSymbol(Word rel_type,
+                                Word rel_symbol,
+                                Addr reloc,
+                                Addr* sym_addr) {
+  const char* sym_name = dynamic_section_->LookupNameById(rel_symbol);
+  SB_DLOG(INFO) << "Resolve: " << sym_name;
+  const void* address = NULL;
+
+  const Sym* sym = dynamic_section_->LookupByName(sym_name);
+  if (sym) {
+    address = reinterpret_cast<void*>(base_memory_address_ + sym->st_value);
+  } else {
+    address = exported_symbols_->Lookup(sym_name);
+  }
+
+  SB_DLOG(INFO) << "Resolve: address=0x" << std::hex << address;
+
+  if (address) {
+    // The symbol was found, so compute its address.
+    *sym_addr = reinterpret_cast<Addr>(address);
+    return true;
+  }
+
+  // The symbol was not found. Normally this is an error except
+  // if this is a weak reference.
+  if (!dynamic_section_->IsWeakById(rel_symbol)) {
+    SB_LOG(ERROR) << "Could not find symbol: " << sym_name;
+    return false;
+  }
+
+  // IHI0044C AAELF 4.5.1.1:
+  // Libraries are not searched to resolve weak references.
+  // It is not an error for a weak reference to remain
+  // unsatisfied.
+  //
+  // During linking, the value of an undefined weak reference is:
+  // - Zero if the relocation type is absolute
+  // - The address of the place if the relocation is pc-relative
+  // - The address of nominal base address if the relocation
+  //   type is base-relative.
+  RelocationType r = GetRelocationType(rel_type);
+  if (r == RELOCATION_TYPE_ABSOLUTE || r == RELOCATION_TYPE_RELATIVE) {
+    *sym_addr = 0;
+    return true;
+  }
+
+  if (r == RELOCATION_TYPE_PC_RELATIVE) {
+    *sym_addr = reloc;
+    return true;
+  }
+
+  SB_LOG(ERROR) << "Invalid weak relocation type (" << r
+                << ") for unknown symbol '" << sym_name << "'";
+  return false;
+}
+}  // namespace elf_loader
+}  // namespace starboard
diff --git a/src/starboard/elf_loader/relocations.h b/src/starboard/elf_loader/relocations.h
new file mode 100644
index 0000000..fad8a4e
--- /dev/null
+++ b/src/starboard/elf_loader/relocations.h
@@ -0,0 +1,93 @@
+// 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.
+
+#ifndef STARBOARD_ELF_LOADER_RELOCATIONS_H_
+#define STARBOARD_ELF_LOADER_RELOCATIONS_H_
+
+#include "starboard/elf_loader/elf.h"
+
+#include "starboard/elf_loader/dynamic_section.h"
+#include "starboard/elf_loader/program_table.h"
+
+namespace starboard {
+namespace elf_loader {
+
+enum RelocationType {
+  RELOCATION_TYPE_UNKNOWN = 0,
+  RELOCATION_TYPE_ABSOLUTE = 1,
+  RELOCATION_TYPE_RELATIVE = 2,
+  RELOCATION_TYPE_PC_RELATIVE = 3,
+  RELOCATION_TYPE_COPY = 4,
+};
+
+// class representing the ELF relocations.
+class Relocations {
+ public:
+  Relocations(Addr base_memory_adddress,
+              DynamicSection* dynamic_section,
+              ExportedSymbols* exported_symbols);
+
+  // Initialize the relocation tables.
+  bool InitRelocations();
+
+  // Apply all the relocations.
+  bool ApplyAllRelocations();
+
+  // Apply a set of relocations.
+  bool ApplyRelocations(const rel_t* rel, size_t rel_count);
+
+  // Apply an individual relocation.
+  bool ApplyRelocation(const rel_t* rel);
+
+// Apply a resolved symbol relocation.
+#if defined(USE_RELA)
+  bool ApplyResolvedReloc(const Rela* rela, Addr sym_addr);
+#else
+  bool ApplyResolvedReloc(const Rel* rel, Addr sym_addr);
+#endif
+
+  // Convert an ELF relocation type info a RelocationType value.
+  RelocationType GetRelocationType(Word r_type);
+
+  // Resolve a symbol address.
+  bool ResolveSymbol(Word rel_type,
+                     Word rel_symbol,
+                     Addr reloc,
+                     Addr* sym_addr);
+
+  // Checks if there are any text relocations.
+  bool HasTextRelocations();
+
+ private:
+  Addr base_memory_address_;
+  DynamicSection* dynamic_section_;
+  Addr plt_relocations_;
+  size_t plt_relocations_size_;
+  Addr* plt_got_;
+
+  Addr relocations_;
+  size_t relocations_size_;
+
+  bool has_text_relocations_;
+  bool has_symbolic_;
+
+  ExportedSymbols* exported_symbols_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(Relocations);
+};
+
+}  // namespace elf_loader
+}  // namespace starboard
+
+#endif  // STARBOARD_ELF_LOADER_RELOCATIONS_H_
diff --git a/src/starboard/elf_loader/relocations_test.cc b/src/starboard/elf_loader/relocations_test.cc
new file mode 100644
index 0000000..1bb2b7b
--- /dev/null
+++ b/src/starboard/elf_loader/relocations_test.cc
@@ -0,0 +1,388 @@
+// 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 "starboard/elf_loader/relocations.h"
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/elf_loader/elf.h"
+#include "starboard/elf_loader/file_impl.h"
+#include "starboard/string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_API_VERSION >= 12 &&                                         \
+    (SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP)) && \
+    SB_CAN(MAP_EXECUTABLE_MEMORY)
+namespace starboard {
+namespace elf_loader {
+
+namespace {
+
+// Test constants used as sample data.
+const Addr kTestAddress = 34;
+const Sword kTestAddend = 5;
+
+class RelocationsTest : public ::testing::Test {
+ protected:
+  RelocationsTest() {
+    SbMemorySet(buf_, 'A', sizeof(buf_));
+    base_addr_ = reinterpret_cast<Addr>(&buf_);
+    dynamic_table_[0].d_tag = DT_REL;
+
+    dynamic_section_.reset(
+        new DynamicSection(base_addr_, dynamic_table_, 1, 0));
+    dynamic_section_->InitDynamicSection();
+
+    exported_symbols_.reset(new ExportedSymbols());
+    relocations_.reset(new Relocations(base_addr_, dynamic_section_.get(),
+                                       exported_symbols_.get()));
+  }
+  ~RelocationsTest() {}
+
+  void VerifySymAddress(const rel_t* rel, Addr sym_addr) {
+    Addr target_addr = base_addr_ + rel->r_offset;
+    Addr target_value = sym_addr;
+    EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
+  }
+
+#ifdef USE_RELA
+  void VerifySymAddressPlusAddend(const rel_t* rel, Addr sym_addr) {
+    Addr target_addr = base_addr_ + rel->r_offset;
+    Addr target_value = sym_addr + rel->r_addend;
+    EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
+  }
+
+  void VerifyBaseAddressPlusAddend(const rel_t* rel) {
+    Addr target_addr = base_addr_ + rel->r_offset;
+    Addr target_value = base_addr_ + rel->r_addend;
+    EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
+  }
+
+  void VerifySymAddressPlusAddendDelta(const rel_t* rel, Addr sym_addr) {
+    Addr target_addr = base_addr_ + rel->r_offset;
+    Addr offset_rel = rel->r_offset + base_addr_;
+    Addr target_value = sym_addr + (rel->r_addend - offset_rel);
+    EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
+  }
+#endif
+
+ protected:
+  scoped_ptr<Relocations> relocations_;
+  Addr base_addr_;
+
+ private:
+  char buf_[128];
+  Dyn dynamic_table_[10];
+  scoped_ptr<DynamicSection> dynamic_section_;
+  scoped_ptr<ExportedSymbols> exported_symbols_;
+};
+
+#if SB_IS(ARCH_ARM)
+TEST_F(RelocationsTest, R_ARM_JUMP_SLOT) {
+  rel_t rel;
+  rel.r_offset = 0;
+  rel.r_info = R_ARM_JUMP_SLOT;
+  Addr sym_addr = kTestAddress;
+
+  // Expected relocation calculation:
+  //   *target = sym_addr;
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  VerifySymAddress(&rel, sym_addr);
+}
+
+TEST_F(RelocationsTest, R_ARM_GLOB_DAT) {
+  rel_t rel;
+  rel.r_offset = 1;
+  rel.r_info = R_ARM_GLOB_DAT;
+  Addr sym_addr = kTestAddress;
+
+  // Expected relocation calculation:
+  //   *target = sym_addr;
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  VerifySymAddress(&rel, sym_addr);
+}
+
+TEST_F(RelocationsTest, R_ARM_ABS32) {
+  rel_t rel;
+  rel.r_offset = 2;
+  rel.r_info = R_ARM_ABS32;
+  Addr sym_addr = kTestAddress;
+
+  Addr target_addr = base_addr_ + rel.r_offset;
+  Addr target_value = *reinterpret_cast<Addr*>(target_addr);
+  target_value += sym_addr;
+
+  // Expected relocation calculation:
+  //   *target += sym_addr;
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
+}
+
+TEST_F(RelocationsTest, R_ARM_REL32) {
+  rel_t rel;
+  rel.r_offset = 3;
+  rel.r_info = R_ARM_REL32;
+  Addr sym_addr = kTestAddress;
+
+  Addr target_addr = base_addr_ + rel.r_offset;
+  Addr target_value = *reinterpret_cast<Addr*>(target_addr);
+  target_value += sym_addr - rel.r_offset;
+
+  // Expected relocation calculation:
+  //   *target += sym_addr - rel->r_offset;
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
+}
+
+TEST_F(RelocationsTest, R_ARM_RELATIVE) {
+  rel_t rel;
+  rel.r_offset = 4;
+  rel.r_info = R_ARM_RELATIVE;
+  Addr sym_addr = kTestAddress;
+
+  Addr target_addr = base_addr_ + rel.r_offset;
+  Addr target_value = *reinterpret_cast<Addr*>(target_addr);
+  target_value += base_addr_;
+
+  // Expected relocation calculation:
+  //   *target += base_memory_address_;
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
+}
+#endif  // SB_IS(ARCH_ARM)
+
+#if SB_IS(ARCH_ARM64) && defined(USE_RELA)
+TEST_F(RelocationsTest, R_AARCH64_JUMP_SLOT) {
+  rel_t rel;
+  rel.r_offset = 0;
+  rel.r_info = R_AARCH64_JUMP_SLOT;
+  rel.r_addend = kTestAddend;
+  Addr sym_addr = kTestAddress;
+
+  // Expected relocation calculation:
+  //   *target = sym_addr + addend;
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  VerifySymAddressPlusAddend(&rel, sym_addr);
+}
+
+TEST_F(RelocationsTest, R_AARCH64_GLOB_DAT) {
+  rel_t rel;
+  rel.r_offset = 1;
+  rel.r_info = R_AARCH64_GLOB_DAT;
+  rel.r_addend = kTestAddend;
+  Addr sym_addr = kTestAddress;
+
+  // Expected relocation calculation:
+  //   *target = sym_addr + addend;
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  VerifySymAddressPlusAddend(&rel, sym_addr);
+}
+
+TEST_F(RelocationsTest, R_AARCH64_ABS64) {
+  rel_t rel;
+  rel.r_offset = 2;
+  rel.r_info = R_AARCH64_ABS64;
+  rel.r_addend = kTestAddend;
+  Addr sym_addr = kTestAddress;
+
+  Addr target_addr = base_addr_ + rel.r_offset;
+  Addr target_value = *reinterpret_cast<Addr*>(target_addr);
+  target_value += sym_addr + rel.r_addend;
+
+  // Expected relocation calculation:
+  //   *target += sym_addr + addend;
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
+}
+
+TEST_F(RelocationsTest, R_AARCH64_RELATIVE) {
+  rel_t rel;
+  rel.r_offset = 3;
+  rel.r_info = R_AARCH64_RELATIVE;
+  rel.r_addend = kTestAddend;
+  Addr sym_addr = kTestAddress;
+
+  // Expected relocation calculation:
+  //   *target = base_memory_address_ + addend;
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  VerifyBaseAddressPlusAddend(&rel);
+}
+
+#endif  // SB_IS(ARCH_ARM64) && defined(USE_RELA)
+
+#if SB_IS(ARCH_X86)
+TEST_F(RelocationsTest, R_386_JMP_SLOT) {
+  rel_t rel;
+  rel.r_offset = 0;
+  rel.r_info = R_386_JMP_SLOT;
+  Addr sym_addr = kTestAddress;
+
+  // Expected relocation calculation:
+  //   *target = sym_addr;
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  VerifySymAddress(&rel, sym_addr);
+}
+
+TEST_F(RelocationsTest, R_386_GLOB_DAT) {
+  rel_t rel;
+  rel.r_offset = 1;
+  rel.r_info = R_386_GLOB_DAT;
+  Addr sym_addr = kTestAddress;
+
+  // Expected relocation calculation:
+  //   *target = sym_addr;
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  VerifySymAddress(&rel, sym_addr);
+}
+
+TEST_F(RelocationsTest, R_386_RELATIVE) {
+  rel_t rel;
+  rel.r_offset = 2;
+  rel.r_info = R_386_RELATIVE;
+  Addr sym_addr = kTestAddress;
+
+  Addr target_addr = base_addr_ + rel.r_offset;
+  Addr target_value = *reinterpret_cast<Addr*>(target_addr);
+  target_value += base_addr_;
+
+  // Expected relocation calculation:
+  //   *target += base_memory_address_;
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
+}
+
+TEST_F(RelocationsTest, R_386_32) {
+  rel_t rel;
+  rel.r_offset = 3;
+  rel.r_info = R_386_32;
+  Addr sym_addr = kTestAddress;
+
+  Addr target_addr = base_addr_ + rel.r_offset;
+  Addr target_value = *reinterpret_cast<Addr*>(target_addr);
+  target_value += sym_addr;
+
+  // Expected relocation calculation:
+  //   *target += sym_addr;
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
+}
+
+TEST_F(RelocationsTest, R_386_PC32) {
+  rel_t rel;
+  rel.r_offset = 4;
+  rel.r_info = R_386_PC32;
+  Addr sym_addr = kTestAddress;
+
+  Addr target_addr = base_addr_ + rel.r_offset;
+  Addr target_value = *reinterpret_cast<Addr*>(target_addr);
+  Addr reloc = static_cast<Addr>(rel.r_offset + base_addr_);
+  target_value += (sym_addr - reloc);
+
+  // Expected relocation calculation:
+  //   *target += (sym_addr - reloc);
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  EXPECT_EQ(target_value, *reinterpret_cast<Addr*>(target_addr));
+}
+#endif  // SB_IS(ARCH_X86)
+
+#if SB_IS(ARCH_X64) && defined(USE_RELA)
+TEST_F(RelocationsTest, R_X86_64_JMP_SLOT) {
+  rel_t rel;
+  rel.r_offset = 0;
+  rel.r_info = R_X86_64_JMP_SLOT;
+  rel.r_addend = kTestAddend;
+  Addr sym_addr = kTestAddress;
+
+  // Expected relocation calculation:
+  //   *target = sym_addr + addend;
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  VerifySymAddressPlusAddend(&rel, sym_addr);
+}
+
+TEST_F(RelocationsTest, R_X86_64_GLOB_DAT) {
+  rel_t rel;
+  rel.r_offset = 1;
+  rel.r_info = R_X86_64_GLOB_DAT;
+  rel.r_addend = kTestAddend;
+  Addr sym_addr = kTestAddress;
+
+  // Expected relocation calculation:
+  //   *target = sym_addr + addend;
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  VerifySymAddressPlusAddend(&rel, sym_addr);
+}
+
+TEST_F(RelocationsTest, R_X86_64_RELATIVE) {
+  rel_t rel;
+  rel.r_offset = 2;
+  rel.r_info = R_X86_64_RELATIVE;
+  rel.r_addend = kTestAddend;
+  Addr sym_addr = kTestAddress;
+
+  // Expected relocation calculation:
+  //   *target = base_memory_address_ + addend;
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  VerifyBaseAddressPlusAddend(&rel);
+}
+
+TEST_F(RelocationsTest, R_X86_64_64) {
+  rel_t rel;
+  rel.r_offset = 3;
+  rel.r_info = R_X86_64_64;
+  rel.r_addend = kTestAddend;
+  Addr sym_addr = kTestAddress;
+
+  // Expected relocation calculation:
+  //   *target = sym_addr + addend;
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  VerifySymAddressPlusAddend(&rel, sym_addr);
+}
+
+TEST_F(RelocationsTest, R_X86_64_PC32) {
+  rel_t rel;
+  rel.r_offset = 4;
+  rel.r_info = R_X86_64_PC32;
+  rel.r_addend = kTestAddend;
+  Addr sym_addr = kTestAddress;
+
+  relocations_->ApplyResolvedReloc(&rel, sym_addr);
+
+  // Expected relocation calculation:
+  //   *target = sym_addr + (addend - reloc);
+  VerifySymAddressPlusAddendDelta(&rel, sym_addr);
+}
+#endif  // SB_IS(ARCH_X64) && defined(USE_RELA)
+
+}  // namespace
+}  // namespace elf_loader
+}  // namespace starboard
+#endif  // SB_API_VERSION >= 12 && (SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION
+        // || SB_HAS(MMAP)) && SB_CAN(MAP_EXECUTABLE_MEMORY)
diff --git a/src/starboard/elf_loader/sandbox.cc b/src/starboard/elf_loader/sandbox.cc
new file mode 100644
index 0000000..8b6e9da
--- /dev/null
+++ b/src/starboard/elf_loader/sandbox.cc
@@ -0,0 +1,84 @@
+// 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 <string>
+
+#include "starboard/common/log.h"
+#include "starboard/elf_loader/elf_loader.h"
+#include "starboard/elf_loader/elf_loader_switches.h"
+#include "starboard/event.h"
+#include "starboard/mutex.h"
+#include "starboard/shared/starboard/command_line.h"
+#include "starboard/thread_types.h"
+
+starboard::elf_loader::ElfLoader g_elf_loader;
+
+void (*g_sb_event_func)(const SbEvent*) = NULL;
+
+void LoadLibraryAndInitialize(const std::string& library_path,
+                              const std::string& content_path) {
+  if (library_path.empty()) {
+    SB_LOG(ERROR) << "Library must be specified with --"
+                  << starboard::elf_loader::kEvergreenLibrary
+                  << "=path/to/library/relative/to/loader/content.";
+    return;
+  }
+  if (content_path.empty()) {
+    SB_LOG(ERROR) << "Content must be specified with --"
+                  << starboard::elf_loader::kEvergreenContent
+                  << "=path/to/content/relative/to/loader/content.";
+    return;
+  }
+  if (!g_elf_loader.Load(library_path, content_path, true)) {
+    SB_NOTREACHED() << "Failed to load library at '"
+                    << g_elf_loader.GetLibraryPath() << "'.";
+    return;
+  }
+
+  SB_LOG(INFO) << "Successfully loaded '" << g_elf_loader.GetLibraryPath()
+               << "'.";
+
+  g_sb_event_func = reinterpret_cast<void (*)(const SbEvent*)>(
+      g_elf_loader.LookupSymbol("SbEventHandle"));
+
+  if (!g_sb_event_func) {
+    SB_LOG(ERROR) << "Failed to find SbEventHandle.";
+    return;
+  }
+
+  SB_LOG(INFO) << "Found SbEventHandle at address: "
+               << reinterpret_cast<void*>(g_sb_event_func);
+}
+
+void SbEventHandle(const SbEvent* event) {
+  static SbMutex mutex = SB_MUTEX_INITIALIZER;
+
+  SB_CHECK(SbMutexAcquire(&mutex) == kSbMutexAcquired);
+
+  if (!g_sb_event_func) {
+    const SbEventStartData* data =
+        static_cast<SbEventStartData*>(event->data);
+    const starboard::shared::starboard::CommandLine command_line(
+        data->argument_count,
+        const_cast<const char**>(data->argument_values));
+    LoadLibraryAndInitialize(
+        command_line.GetSwitchValue(starboard::elf_loader::kEvergreenLibrary),
+        command_line.GetSwitchValue(starboard::elf_loader::kEvergreenContent));
+    SB_CHECK(g_sb_event_func);
+  }
+
+  g_sb_event_func(event);
+
+  SB_CHECK(SbMutexRelease(&mutex) == true);
+}
diff --git a/src/starboard/linux/x64x11/sbversion/6/__init__.py b/src/starboard/evergreen/__init__.py
similarity index 100%
copy from src/starboard/linux/x64x11/sbversion/6/__init__.py
copy to src/starboard/evergreen/__init__.py
diff --git a/src/starboard/linux/x64x11/sbversion/6/__init__.py b/src/starboard/evergreen/arm/__init__.py
similarity index 100%
copy from src/starboard/linux/x64x11/sbversion/6/__init__.py
copy to src/starboard/evergreen/arm/__init__.py
diff --git a/src/starboard/linux/x64x11/sbversion/6/__init__.py b/src/starboard/evergreen/arm/hardfp/__init__.py
similarity index 100%
copy from src/starboard/linux/x64x11/sbversion/6/__init__.py
copy to src/starboard/evergreen/arm/hardfp/__init__.py
diff --git a/src/starboard/linux/x64x11/mozjs/atomic_public.h b/src/starboard/evergreen/arm/hardfp/atomic_public.h
similarity index 64%
rename from src/starboard/linux/x64x11/mozjs/atomic_public.h
rename to src/starboard/evergreen/arm/hardfp/atomic_public.h
index 503b0a1..d9786c1 100644
--- a/src/starboard/linux/x64x11/mozjs/atomic_public.h
+++ b/src/starboard/evergreen/arm/hardfp/atomic_public.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Cobalt Authors. All Rights Reserved.
+// 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.
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_LINUX_X64X11_MOZJS_ATOMIC_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_MOZJS_ATOMIC_PUBLIC_H_
+#ifndef STARBOARD_EVERGREEN_ARM_HARDFP_ATOMIC_PUBLIC_H_
+#define STARBOARD_EVERGREEN_ARM_HARDFP_ATOMIC_PUBLIC_H_
 
-#include "starboard/linux/shared/atomic_public.h"
+#include "starboard/atomic.h"
+#include "starboard/shared/gcc/atomic_gcc_public.h"
 
-#endif  // STARBOARD_LINUX_X64X11_MOZJS_ATOMIC_PUBLIC_H_
+#endif  // STARBOARD_EVERGREEN_ARM_HARDFP_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/evergreen/arm/hardfp/configuration_public.h b/src/starboard/evergreen/arm/hardfp/configuration_public.h
new file mode 100644
index 0000000..b813c19
--- /dev/null
+++ b/src/starboard/evergreen/arm/hardfp/configuration_public.h
@@ -0,0 +1,200 @@
+// 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.
+
+// The Starboard configuration for X86_64. Other devices will have
+// specific Starboard implementations.
+
+// Other source files should never include this header directly, but should
+// include the generic "starboard/configuration.h" instead.
+
+#ifndef STARBOARD_EVERGREEN_ARM_HARDFP_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_EVERGREEN_ARM_HARDFP_CONFIGURATION_PUBLIC_H_
+
+// --- System Header Configuration -------------------------------------------
+
+// Any system headers listed here that are not provided by the platform will be
+// emulated in starboard/types.h.
+
+// Whether the current platform provides the standard header stdarg.h.
+#define SB_HAS_STDARG_H 1
+
+// Whether the current platform provides the standard header stdbool.h.
+#define SB_HAS_STDBOOL_H 1
+
+// Whether the current platform provides the standard header stddef.h.
+#define SB_HAS_STDDEF_H 1
+
+// Whether the current platform provides the standard header stdint.h.
+#define SB_HAS_STDINT_H 1
+
+// Whether the current platform provides the standard header inttypes.h.
+#define SB_HAS_INTTYPES_H 1
+
+// Whether the current platform provides the standard header sys/types.h.
+#define SB_HAS_SYS_TYPES_H 1
+
+// Whether the current platform provides the standard header wchar.h.
+#define SB_HAS_WCHAR_H 1
+
+// Whether the current platform provides the standard header limits.h.
+#define SB_HAS_LIMITS_H 1
+
+// Whether the current platform provides the standard header float.h.
+#define SB_HAS_FLOAT_H 1
+
+// Whether the current platform provides ssize_t.
+#define SB_HAS_SSIZE_T 1
+
+// Type detection for wchar_t.
+#if defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
+#define SB_IS_WCHAR_T_UTF32 1
+#elif defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff)
+#define SB_IS_WCHAR_T_UTF16 1
+#endif
+
+// Chrome only defines these two if ARMEL or MIPSEL are defined.
+#if defined(__ARMEL__)
+// Chrome has an exclusion for iOS here, we should too when we support iOS.
+#define SB_IS_WCHAR_T_UNSIGNED 1
+#elif defined(__MIPSEL__)
+#define SB_IS_WCHAR_T_SIGNED 1
+#endif
+
+// --- Compiler Configuration ------------------------------------------------
+
+// The platform's annotation for forcing a C function to be inlined.
+#define SB_C_FORCE_INLINE __inline__ __attribute__((always_inline))
+
+// The platform's annotation for marking a C function as suggested to be
+// inlined.
+#define SB_C_INLINE inline
+
+// The platform's annotation for marking a C function as forcibly not
+// inlined.
+#define SB_C_NOINLINE __attribute__((noinline))
+
+// The platform's annotation for marking a symbol as exported outside of the
+// current shared library.
+#define SB_EXPORT_PLATFORM __attribute__((visibility("default")))
+
+// The platform's annotation for marking a symbol as imported from outside of
+// the current linking unit.
+#define SB_IMPORT_PLATFORM
+
+// --- Extensions Configuration ----------------------------------------------
+
+// Do not use <unordered_map> and <unordered_set> for the hash table types.
+#define SB_HAS_STD_UNORDERED_HASH 0
+
+// GCC/Clang doesn't define a long long hash function, except for Android and
+// Game consoles.
+#define SB_HAS_LONG_LONG_HASH 0
+
+// GCC/Clang doesn't define a string hash function, except for Game Consoles.
+#define SB_HAS_STRING_HASH 0
+
+// Desktop Linux needs a using statement for the hash functions.
+#define SB_HAS_HASH_USING 0
+
+// Set this to 1 if hash functions for custom types can be defined as a
+// hash_value() function. Otherwise, they need to be placed inside a
+// partially-specified hash struct template with an operator().
+#define SB_HAS_HASH_VALUE 0
+
+// Set this to 1 if use of hash_map or hash_set causes a deprecation warning
+// (which then breaks the build).
+#define SB_HAS_HASH_WARNING 1
+
+// The location to include hash_map on this platform.
+#define SB_HASH_MAP_INCLUDE <ext/hash_map>
+
+// C++'s hash_map and hash_set are often found in different namespaces depending
+// on the compiler.
+#define SB_HASH_NAMESPACE __gnu_cxx
+
+// The location to include hash_set on this platform.
+#define SB_HASH_SET_INCLUDE <ext/hash_set>
+
+// Define this to how this platform copies varargs blocks.
+#define SB_VA_COPY(dest, source) va_copy(dest, source)
+
+// --- Graphics Configuration ------------------------------------------------
+
+// Specifies whether this platform supports a performant accelerated blitter
+// API. The basic requirement is a scaled, clipped, alpha-blended blit.
+#define SB_HAS_BLITTER 0
+
+// Indicates whether or not the given platform supports bilinear filtering.
+// This can be checked to enable/disable renderer tests that verify that this is
+// working properly.
+#define SB_HAS_BILINEAR_FILTERING_SUPPORT 1
+
+// Indicates whether or not the given platform supports rendering of NV12
+// textures. These textures typically originate from video decoders.
+#define SB_HAS_NV12_TEXTURE_SUPPORT 1
+
+// --- I/O Configuration -----------------------------------------------------
+
+// Whether the current platform implements the on screen keyboard interface.
+#define SB_HAS_ON_SCREEN_KEYBOARD 0
+
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 0
+
+// Whether the current platform has speech synthesis.
+#define SB_HAS_SPEECH_SYNTHESIS 0
+
+// --- Decoder-only Params ---
+
+// --- Memory Configuration --------------------------------------------------
+
+// Whether this platform has and should use an MMAP function to map physical
+// memory to the virtual address space.
+#if SB_API_VERSION < SB_MMAP_REQUIRED_VERSION
+#define SB_HAS_MMAP 1
+#endif
+
+// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
+// required for platforms that want to JIT.
+#define SB_CAN_MAP_EXECUTABLE_MEMORY 1
+
+// --- Network Configuration -------------------------------------------------
+
+// Specifies whether this platform supports IPV6.
+#define SB_HAS_IPV6 1
+
+// Specifies whether this platform supports pipe.
+#define SB_HAS_PIPE 1
+
+// --- Thread Configuration --------------------------------------------------
+
+// --- Timing API ------------------------------------------------------------
+
+// Whether this platform has an API to retrieve how long the current thread
+// has spent in the executing state.
+#define SB_HAS_TIME_THREAD_NOW 1
+
+// --- Tuneable Parameters ---------------------------------------------------
+
+// --- User Configuration ----------------------------------------------------
+
+// --- Platform Specific Audits ----------------------------------------------
+
+#if !defined(__GNUC__)
+#error "Evergreen-arm builds need a GCC-like compiler (for the moment)."
+#endif
+
+#endif  // STARBOARD_EVERGREEN_ARM_HARDFP_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/6/gyp_configuration.gypi b/src/starboard/evergreen/arm/hardfp/gyp_configuration.gypi
similarity index 73%
rename from src/starboard/linux/x64x11/blittergles/sbversion/6/gyp_configuration.gypi
rename to src/starboard/evergreen/arm/hardfp/gyp_configuration.gypi
index 3b6dac2..4ba76c1 100644
--- a/src/starboard/linux/x64x11/blittergles/sbversion/6/gyp_configuration.gypi
+++ b/src/starboard/evergreen/arm/hardfp/gyp_configuration.gypi
@@ -16,25 +16,27 @@
 # though it may have been modified since its creation.
 
 {
+  'variables': {
+    'sb_target_platform': 'evergreen-arm-hardfp',
+  },
   'target_defaults': {
-    'default_configuration': 'linux-x64x11-blittergles-sbversion-6_debug',
+    'default_configuration': 'evergreen-arm-hardfp_debug',
     'configurations': {
-      'linux-x64x11-blittergles-sbversion-6_debug': {
+      'evergreen-arm-hardfp_debug': {
         'inherit_from': ['debug_base'],
       },
-      'linux-x64x11-blittergles-sbversion-6_devel': {
+      'evergreen-arm-hardfp_devel': {
         'inherit_from': ['devel_base'],
       },
-      'linux-x64x11-blittergles-sbversion-6_qa': {
+      'evergreen-arm-hardfp_qa': {
         'inherit_from': ['qa_base'],
       },
-      'linux-x64x11-blittergles-sbversion-6_gold': {
+      'evergreen-arm-hardfp_gold': {
         'inherit_from': ['gold_base'],
       },
     }, # end of configurations
   },
-
   'includes': [
-    '<(DEPTH)/starboard/linux/x64x11/blittergles/shared/gyp_configuration.gypi',
+    '<(DEPTH)/starboard/evergreen/arm/shared/gyp_configuration.gypi',
   ],
 }
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform_tests.gyp b/src/starboard/evergreen/arm/hardfp/gyp_configuration.py
similarity index 68%
rename from src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform_tests.gyp
rename to src/starboard/evergreen/arm/hardfp/gyp_configuration.py
index 94e7cc2..467ce05 100644
--- a/src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform_tests.gyp
+++ b/src/starboard/evergreen/arm/hardfp/gyp_configuration.py
@@ -14,11 +14,12 @@
 
 # This file was initially generated by starboard/tools/create_derived_build.py,
 # though it may have been modified since its creation.
+"""Starboard evergreen-arm-hardfp platform configuration for gyp_cobalt."""
 
-{
-  'includes': [
-    # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file.  The idea
-    # is that we just want this file to *be* the parent gyp file.
-    '<(DEPTH)/starboard/linux/x64x11/blittergles/starboard_platform_tests.gyp',
-  ],
-}
+from starboard.evergreen.arm.shared import gyp_configuration as parent_configuration
+
+
+def CreatePlatformConfig():
+  return parent_configuration.EvergreenArmConfiguration(
+      'evergreen-arm-hardfp',
+      sabi_json_path='starboard/sabi/arm/hardfp/sabi.json')
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform.gyp b/src/starboard/evergreen/arm/hardfp/starboard_platform.gyp
similarity index 92%
rename from src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform.gyp
rename to src/starboard/evergreen/arm/hardfp/starboard_platform.gyp
index 8163408..8787f61 100644
--- a/src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform.gyp
+++ b/src/starboard/evergreen/arm/hardfp/starboard_platform.gyp
@@ -19,6 +19,7 @@
   'includes': [
     # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file.  The idea
     # is that we just want this file to *be* the parent gyp file.
-    '<(DEPTH)/starboard/linux/x64x11/blittergles/starboard_platform.gyp',
+    '<(DEPTH)/starboard/evergreen/shared/starboard_platform.gyp',
   ],
 }
+
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/6/atomic_public.h b/src/starboard/evergreen/arm/hardfp/thread_types_public.h
similarity index 72%
rename from src/starboard/linux/x64x11/blittergles/sbversion/6/atomic_public.h
rename to src/starboard/evergreen/arm/hardfp/thread_types_public.h
index d2414dd..fda18eb 100644
--- a/src/starboard/linux/x64x11/blittergles/sbversion/6/atomic_public.h
+++ b/src/starboard/evergreen/arm/hardfp/thread_types_public.h
@@ -15,9 +15,9 @@
 // This file was initially generated by starboard/tools/create_derived_build.py,
 // though it may have been modified since its creation.
 
-#ifndef STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_ATOMIC_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_ATOMIC_PUBLIC_H_
+#ifndef STARBOARD_EVERGREEN_ARM_HARDFP_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_EVERGREEN_ARM_HARDFP_THREAD_TYPES_PUBLIC_H_
 
-#include "starboard/linux/x64x11/blittergles/atomic_public.h"
+#include "starboard/shared/pthread/types_public.h"
 
-#endif  // STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_ATOMIC_PUBLIC_H_
+#endif  // STARBOARD_EVERGREEN_ARM_HARDFP_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/sbversion/6/__init__.py b/src/starboard/evergreen/arm/shared/__init__.py
similarity index 100%
copy from src/starboard/linux/x64x11/sbversion/6/__init__.py
copy to src/starboard/evergreen/arm/shared/__init__.py
diff --git a/src/starboard/linux/x64x11/sbversion/6/__init__.py b/src/starboard/evergreen/arm/shared/cobalt/__init__.py
similarity index 100%
copy from src/starboard/linux/x64x11/sbversion/6/__init__.py
copy to src/starboard/evergreen/arm/shared/cobalt/__init__.py
diff --git a/src/starboard/linux/x64x11/mozjs/starboard_platform_tests.gyp b/src/starboard/evergreen/arm/shared/cobalt/configuration.gypi
similarity index 75%
rename from src/starboard/linux/x64x11/mozjs/starboard_platform_tests.gyp
rename to src/starboard/evergreen/arm/shared/cobalt/configuration.gypi
index 0ea8622..89aca9a 100644
--- a/src/starboard/linux/x64x11/mozjs/starboard_platform_tests.gyp
+++ b/src/starboard/evergreen/arm/shared/cobalt/configuration.gypi
@@ -1,4 +1,4 @@
-# Copyright 2018 The Cobalt Authors. All Rights Reserved.
+# Copyright 2020 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.
@@ -11,8 +11,11 @@
 # 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.
+
+# Cobalt Evergreen ARM specific configuration.
+
 {
-  'includes': [
-    '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
-  ],
+  'variables': {
+    'in_app_dial%': 0,
+  }, # end of variables
 }
diff --git a/src/starboard/evergreen/arm/shared/cobalt/configuration.py b/src/starboard/evergreen/arm/shared/cobalt/configuration.py
new file mode 100644
index 0000000..0d71086
--- /dev/null
+++ b/src/starboard/evergreen/arm/shared/cobalt/configuration.py
@@ -0,0 +1,83 @@
+# Copyright 2020 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.
+"""Starboard Cobalt Evergreen ARM configuration."""
+
+import os
+
+from cobalt.build import cobalt_configuration
+from starboard.tools.testing import test_filter
+
+
+class CobaltARMConfiguration(cobalt_configuration.CobaltConfiguration):
+  """Starboard Cobalt Evergreen ARM configuration."""
+
+  def __init__(self, platform_configuration, application_name,
+               application_directory):
+    super(CobaltARMConfiguration,
+          self).__init__(platform_configuration, application_name,
+                         application_directory)
+
+  def GetPostIncludes(self):
+    # If there isn't a configuration.gypi found in the usual place, we'll
+    # supplement with our shared implementation.
+    includes = super(CobaltARMConfiguration, self).GetPostIncludes()
+    for include in includes:
+      if os.path.basename(include) == 'configuration.gypi':
+        return includes
+
+    shared_gypi_path = os.path.join(
+        os.path.dirname(__file__), 'configuration.gypi')
+    if os.path.isfile(shared_gypi_path):
+      includes.append(shared_gypi_path)
+    return includes
+
+  def WebdriverBenchmarksEnabled(self):
+    return True
+
+  def GetTestFilters(self):
+    filters = super(CobaltARMConfiguration, self).GetTestFilters()
+    for target, tests in self.__FILTERED_TESTS.iteritems():
+      filters.extend(test_filter.TestFilter(target, test) for test in tests)
+    return filters
+
+  def GetWebPlatformTestFilters(self):
+    filters = super(CobaltARMConfiguration, self).GetWebPlatformTestFilters()
+    filters += [
+        ('csp/WebPlatformTest.Run/'
+         'content_security_policy_media_src_media_src_allowed_html'),
+        ('websockets/WebPlatformTest.Run/websockets_*'),
+    ]
+    return filters
+
+  def GetTestEnvVariables(self):
+    return {
+        'base_unittests': {
+            'ASAN_OPTIONS': 'detect_leaks=0'
+        },
+        'crypto_unittests': {
+            'ASAN_OPTIONS': 'detect_leaks=0'
+        },
+        'net_unittests': {
+            'ASAN_OPTIONS': 'detect_leaks=0'
+        }
+    }
+
+  __FILTERED_TESTS = {
+      'base_unittests': [test_filter.FILTER_ALL],
+      'bindings_test': ['DateBindingsTest.PosixEpoch'],
+      'net_unittests': [test_filter.FILTER_ALL],
+      'renderer_test': [
+          'PixelTest.CircularSubPixelBorder', 'PixelTest.FilterBlurred100PxText'
+      ],
+  }
diff --git a/src/starboard/evergreen/arm/shared/gyp_configuration.gypi b/src/starboard/evergreen/arm/shared/gyp_configuration.gypi
new file mode 100644
index 0000000..404d3a3
--- /dev/null
+++ b/src/starboard/evergreen/arm/shared/gyp_configuration.gypi
@@ -0,0 +1,50 @@
+# 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.
+
+# This file was initially generated by starboard/tools/create_derived_build.py,
+# though it may have been modified since its creation.
+
+{
+  'variables': {
+    'yasm_exists': 1,
+
+    'compiler_flags': [
+      '-isystem<(cobalt_repo_root)/third_party/musl/arch/arm',
+
+      # Force char to be signed.
+      '-fsigned-char',
+    ],
+
+    'linker_flags': [
+      '-fuse-ld=lld',
+    ],
+  },
+  'target_defaults': {
+    'defines' : [
+      # Ensure that the Starboardized __external_threading file is included.
+      '_LIBCPP_HAS_THREAD_API_EXTERNAL',
+
+      # Ensure that only the forward declarations and type definitions are included
+      # in __external_threading.
+      '_LIBCPP_HAS_THREAD_LIBRARY_EXTERNAL',
+
+      # Enable GNU extensions to get prototypes like ffsl.
+      '_GNU_SOURCE=1',
+    ],
+  },
+  'includes': [
+    '<(DEPTH)/starboard/evergreen/shared/compiler_flags.gypi',
+    '<(DEPTH)/starboard/evergreen/shared/gyp_configuration.gypi',
+  ],
+}
diff --git a/src/starboard/evergreen/arm/shared/gyp_configuration.py b/src/starboard/evergreen/arm/shared/gyp_configuration.py
new file mode 100644
index 0000000..d442333
--- /dev/null
+++ b/src/starboard/evergreen/arm/shared/gyp_configuration.py
@@ -0,0 +1,103 @@
+# 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.
+"""Starboard Evergreen ARM shared platform configuration for gyp_cobalt."""
+
+import os.path
+
+from starboard.build import clang as clang_build
+from starboard.evergreen.shared import gyp_configuration as shared_configuration
+from starboard.tools import build
+from starboard.tools.testing import test_filter
+from starboard.tools.toolchain import ar
+from starboard.tools.toolchain import bash
+from starboard.tools.toolchain import clang
+from starboard.tools.toolchain import clangxx
+from starboard.tools.toolchain import cp
+from starboard.tools.toolchain import evergreen_linker
+from starboard.tools.toolchain import touch
+
+
+class EvergreenArmConfiguration(shared_configuration.EvergreenConfiguration):
+  """Starboard Evergreen ARM platform configuration."""
+
+  def __init__(self,
+               platform_name='evergreen-arm',
+               asan_enabled_by_default=False,
+               goma_supports_compiler=True,
+               sabi_json_path=None):
+    # pylint: disable=useless-super-delegation
+    super(EvergreenArmConfiguration,
+          self).__init__(platform_name, asan_enabled_by_default,
+                         goma_supports_compiler, sabi_json_path)
+    self.AppendApplicationConfigurationPath(os.path.dirname(__file__))
+    self._host_toolchain = None
+
+  def GetTargetToolchain(self):
+    return self.GetHostToolchain()
+
+  def GetHostToolchain(self):
+    if not self._host_toolchain:
+      if not hasattr(self, 'host_compiler_environment'):
+        self.host_compiler_environment = build.GetHostCompilerEnvironment(
+            clang_build.GetClangSpecification(), False)
+      cc_path = self.host_compiler_environment['CC_host']
+      cxx_path = self.host_compiler_environment['CXX_host']
+
+      # Takes the provided value of CXX_HOST with a prepended 'gomacc' and an
+      # appended 'bin/clang++' and strips them off, leaving us with an absolute
+      # path to the root directory of our toolchain.
+      begin_path_index = cxx_path.find('/')
+      end_path_index = cxx_path.rfind('/', 0, cxx_path.rfind('/')) + 1
+
+      cxx_path_root = cxx_path[begin_path_index:end_path_index]
+
+      self._host_toolchain = [
+          clang.CCompiler(path=cc_path),
+          clang.CxxCompiler(path=cxx_path),
+          clang.AssemblerWithCPreprocessor(path=cc_path),
+          ar.StaticThinLinker(),
+          ar.StaticLinker(),
+          clangxx.ExecutableLinker(path=cxx_path),
+          evergreen_linker.SharedLibraryLinker(
+              path=cxx_path_root, extra_flags=['-m armelf']),
+          cp.Copy(),
+          touch.Stamp(),
+          bash.Shell(),
+      ]
+    return self._host_toolchain
+
+  def GetTestFilters(self):
+    filters = super(EvergreenArmConfiguration, self).GetTestFilters()
+    for target, tests in self.__FILTERED_TESTS.iteritems():
+      filters.extend(test_filter.TestFilter(target, test) for test in tests)
+    return filters
+
+  def GetVariables(self, configuration):
+    variables = super(EvergreenArmConfiguration, self).GetVariables(configuration)
+    variables.update({
+        'include_path_platform_deploy_gypi':
+            'starboard/evergreen/arm/shared/platform_deploy.gypi',
+    })
+    return variables
+
+  __FILTERED_TESTS = {  # pylint: disable=invalid-name
+      'nplb': ['SbSystemGetStackTest.SunnyDayStackDirection',
+               'SbSystemGetStackTest.SunnyDay',
+               'SbSystemGetStackTest.SunnyDayShortStack',
+               'SbSystemSymbolizeTest.SunnyDay'],
+  }
+
+
+def CreatePlatformConfig():
+  return EvergreenArmConfiguration()
diff --git a/src/starboard/evergreen/arm/shared/platform_deploy.gypi b/src/starboard/evergreen/arm/shared/platform_deploy.gypi
new file mode 100644
index 0000000..fef9168
--- /dev/null
+++ b/src/starboard/evergreen/arm/shared/platform_deploy.gypi
@@ -0,0 +1,37 @@
+# Copyright 2020 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.
+
+{
+  'variables': {
+    'deploy_executable_file': '<(target_deploy_dir)/lib/lib<(executable_name).so',
+    'executable_file': '<(PRODUCT_DIR)/lib/lib<(executable_name).so',
+  },
+  'includes': [ '<(DEPTH)/starboard/build/collect_deploy_content.gypi' ],
+  'actions': [
+    {
+      'action_name': 'deploy_executable',
+      'message': 'Strip executable: <(deploy_executable_file)',
+      'inputs': [
+        '<(executable_file)',
+        '<(content_deploy_stamp_file)',
+      ],
+      'outputs': [ '<(deploy_executable_file)' ],
+      'action': [
+        'arm-linux-gnueabi-strip',
+        '-o', '<(deploy_executable_file)',
+        '<(executable_file)',
+      ],
+    },
+  ],
+}
diff --git a/src/starboard/linux/x64x11/sbversion/6/__init__.py b/src/starboard/evergreen/arm/softfp/__init__.py
similarity index 100%
copy from src/starboard/linux/x64x11/sbversion/6/__init__.py
copy to src/starboard/evergreen/arm/softfp/__init__.py
diff --git a/src/starboard/linux/x64x11/mozjs/atomic_public.h b/src/starboard/evergreen/arm/softfp/atomic_public.h
similarity index 64%
copy from src/starboard/linux/x64x11/mozjs/atomic_public.h
copy to src/starboard/evergreen/arm/softfp/atomic_public.h
index 503b0a1..48e2e37 100644
--- a/src/starboard/linux/x64x11/mozjs/atomic_public.h
+++ b/src/starboard/evergreen/arm/softfp/atomic_public.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Cobalt Authors. All Rights Reserved.
+// 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.
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_LINUX_X64X11_MOZJS_ATOMIC_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_MOZJS_ATOMIC_PUBLIC_H_
+#ifndef STARBOARD_EVERGREEN_ARM_SOFTFP_ATOMIC_PUBLIC_H_
+#define STARBOARD_EVERGREEN_ARM_SOFTFP_ATOMIC_PUBLIC_H_
 
-#include "starboard/linux/shared/atomic_public.h"
+#include "starboard/atomic.h"
+#include "starboard/shared/gcc/atomic_gcc_public.h"
 
-#endif  // STARBOARD_LINUX_X64X11_MOZJS_ATOMIC_PUBLIC_H_
+#endif  // STARBOARD_EVERGREEN_ARM_SOFTFP_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/evergreen/arm/softfp/configuration_public.h b/src/starboard/evergreen/arm/softfp/configuration_public.h
new file mode 100644
index 0000000..1743bba
--- /dev/null
+++ b/src/starboard/evergreen/arm/softfp/configuration_public.h
@@ -0,0 +1,203 @@
+// 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.
+
+// The Starboard configuration for X86_64. Other devices will have
+// specific Starboard implementations.
+
+// Other source files should never include this header directly, but should
+// include the generic "starboard/configuration.h" instead.
+
+#ifndef STARBOARD_EVERGREEN_ARM_SOFTFP_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_EVERGREEN_ARM_SOFTFP_CONFIGURATION_PUBLIC_H_
+
+// --- System Header Configuration -------------------------------------------
+
+// Any system headers listed here that are not provided by the platform will be
+// emulated in starboard/types.h.
+
+// Whether the current platform provides the standard header stdarg.h.
+#define SB_HAS_STDARG_H 1
+
+// Whether the current platform provides the standard header stdbool.h.
+#define SB_HAS_STDBOOL_H 1
+
+// Whether the current platform provides the standard header stddef.h.
+#define SB_HAS_STDDEF_H 1
+
+// Whether the current platform provides the standard header stdint.h.
+#define SB_HAS_STDINT_H 1
+
+// Whether the current platform provides the standard header inttypes.h.
+#define SB_HAS_INTTYPES_H 1
+
+// Whether the current platform provides the standard header sys/types.h.
+#define SB_HAS_SYS_TYPES_H 1
+
+// Whether the current platform provides the standard header wchar.h.
+#define SB_HAS_WCHAR_H 1
+
+// Whether the current platform provides the standard header limits.h.
+#define SB_HAS_LIMITS_H 1
+
+// Whether the current platform provides the standard header float.h.
+#define SB_HAS_FLOAT_H 1
+
+// Whether the current platform provides ssize_t.
+#define SB_HAS_SSIZE_T 1
+
+// Type detection for wchar_t.
+#if defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
+#define SB_IS_WCHAR_T_UTF32 1
+#elif defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff)
+#define SB_IS_WCHAR_T_UTF16 1
+#endif
+
+// Chrome only defines these two if ARMEL or MIPSEL are defined.
+#if defined(__ARMEL__)
+// Chrome has an exclusion for iOS here, we should too when we support iOS.
+#define SB_IS_WCHAR_T_UNSIGNED 1
+#elif defined(__MIPSEL__)
+#define SB_IS_WCHAR_T_SIGNED 1
+#endif
+
+// --- Compiler Configuration ------------------------------------------------
+
+// The platform's annotation for forcing a C function to be inlined.
+#define SB_C_FORCE_INLINE __inline__ __attribute__((always_inline))
+
+// The platform's annotation for marking a C function as suggested to be
+// inlined.
+#define SB_C_INLINE inline
+
+// The platform's annotation for marking a C function as forcibly not
+// inlined.
+#define SB_C_NOINLINE __attribute__((noinline))
+
+// The platform's annotation for marking a symbol as exported outside of the
+// current shared library.
+#define SB_EXPORT_PLATFORM __attribute__((visibility("default")))
+
+// The platform's annotation for marking a symbol as imported from outside of
+// the current linking unit.
+#define SB_IMPORT_PLATFORM
+
+// --- Extensions Configuration ----------------------------------------------
+
+// Do not use <unordered_map> and <unordered_set> for the hash table types.
+#define SB_HAS_STD_UNORDERED_HASH 0
+
+// GCC/Clang doesn't define a long long hash function, except for Android and
+// Game consoles.
+#define SB_HAS_LONG_LONG_HASH 0
+
+// GCC/Clang doesn't define a string hash function, except for Game Consoles.
+#define SB_HAS_STRING_HASH 0
+
+// Desktop Linux needs a using statement for the hash functions.
+#define SB_HAS_HASH_USING 0
+
+// Set this to 1 if hash functions for custom types can be defined as a
+// hash_value() function. Otherwise, they need to be placed inside a
+// partially-specified hash struct template with an operator().
+#define SB_HAS_HASH_VALUE 0
+
+// Set this to 1 if use of hash_map or hash_set causes a deprecation warning
+// (which then breaks the build).
+#define SB_HAS_HASH_WARNING 1
+
+// The location to include hash_map on this platform.
+#define SB_HASH_MAP_INCLUDE <ext/hash_map>
+
+// C++'s hash_map and hash_set are often found in different namespaces depending
+// on the compiler.
+#define SB_HASH_NAMESPACE __gnu_cxx
+
+// The location to include hash_set on this platform.
+#define SB_HASH_SET_INCLUDE <ext/hash_set>
+
+// Define this to how this platform copies varargs blocks.
+#define SB_VA_COPY(dest, source) va_copy(dest, source)
+
+// --- Graphics Configuration ------------------------------------------------
+
+// Specifies whether this platform supports a performant accelerated blitter
+// API. The basic requirement is a scaled, clipped, alpha-blended blit.
+#define SB_HAS_BLITTER 0
+
+// Indicates whether or not the given platform supports bilinear filtering.
+// This can be checked to enable/disable renderer tests that verify that this is
+// working properly.
+#define SB_HAS_BILINEAR_FILTERING_SUPPORT 1
+
+// Indicates whether or not the given platform supports rendering of NV12
+// textures. These textures typically originate from video decoders.
+#define SB_HAS_NV12_TEXTURE_SUPPORT 1
+
+// --- I/O Configuration -----------------------------------------------------
+
+// Whether the current platform has microphone supported.
+#define SB_HAS_MICROPHONE 0
+
+// Whether the current platform implements the on screen keyboard interface.
+#define SB_HAS_ON_SCREEN_KEYBOARD 0
+
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 0
+
+// Whether the current platform has speech synthesis.
+#define SB_HAS_SPEECH_SYNTHESIS 0
+
+// --- Decoder-only Params ---
+
+// --- Memory Configuration --------------------------------------------------
+
+// Whether this platform has and should use an MMAP function to map physical
+// memory to the virtual address space.
+#if SB_API_VERSION < SB_MMAP_REQUIRED_VERSION
+#define SB_HAS_MMAP 1
+#endif
+
+// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
+// required for platforms that want to JIT.
+#define SB_CAN_MAP_EXECUTABLE_MEMORY 1
+
+// --- Network Configuration -------------------------------------------------
+
+// Specifies whether this platform supports IPV6.
+#define SB_HAS_IPV6 1
+
+// Specifies whether this platform supports pipe.
+#define SB_HAS_PIPE 1
+
+// --- Thread Configuration --------------------------------------------------
+
+// --- Timing API ------------------------------------------------------------
+
+// Whether this platform has an API to retrieve how long the current thread
+// has spent in the executing state.
+#define SB_HAS_TIME_THREAD_NOW 1
+
+// --- Tuneable Parameters ---------------------------------------------------
+
+// --- User Configuration ----------------------------------------------------
+
+// --- Platform Specific Audits ----------------------------------------------
+
+#if !defined(__GNUC__)
+#error "Evergreen-arm builds need a GCC-like compiler (for the moment)."
+#endif
+
+#endif  // STARBOARD_EVERGREEN_ARM_SOFTFP_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/6/gyp_configuration.gypi b/src/starboard/evergreen/arm/softfp/gyp_configuration.gypi
similarity index 73%
copy from src/starboard/linux/x64x11/blittergles/sbversion/6/gyp_configuration.gypi
copy to src/starboard/evergreen/arm/softfp/gyp_configuration.gypi
index 3b6dac2..cd963f7 100644
--- a/src/starboard/linux/x64x11/blittergles/sbversion/6/gyp_configuration.gypi
+++ b/src/starboard/evergreen/arm/softfp/gyp_configuration.gypi
@@ -16,25 +16,27 @@
 # though it may have been modified since its creation.
 
 {
+  'variables': {
+    'sb_target_platform': 'evergreen-arm-softfp',
+  },
   'target_defaults': {
-    'default_configuration': 'linux-x64x11-blittergles-sbversion-6_debug',
+    'default_configuration': 'evergreen-arm-softfp_debug',
     'configurations': {
-      'linux-x64x11-blittergles-sbversion-6_debug': {
+      'evergreen-arm-softfp_debug': {
         'inherit_from': ['debug_base'],
       },
-      'linux-x64x11-blittergles-sbversion-6_devel': {
+      'evergreen-arm-softfp_devel': {
         'inherit_from': ['devel_base'],
       },
-      'linux-x64x11-blittergles-sbversion-6_qa': {
+      'evergreen-arm-softfp_qa': {
         'inherit_from': ['qa_base'],
       },
-      'linux-x64x11-blittergles-sbversion-6_gold': {
+      'evergreen-arm-softfp_gold': {
         'inherit_from': ['gold_base'],
       },
     }, # end of configurations
   },
-
   'includes': [
-    '<(DEPTH)/starboard/linux/x64x11/blittergles/shared/gyp_configuration.gypi',
+    '<(DEPTH)/starboard/evergreen/arm/shared/gyp_configuration.gypi',
   ],
 }
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform_tests.gyp b/src/starboard/evergreen/arm/softfp/gyp_configuration.py
similarity index 68%
copy from src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform_tests.gyp
copy to src/starboard/evergreen/arm/softfp/gyp_configuration.py
index 94e7cc2..8c649b9 100644
--- a/src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform_tests.gyp
+++ b/src/starboard/evergreen/arm/softfp/gyp_configuration.py
@@ -14,11 +14,12 @@
 
 # This file was initially generated by starboard/tools/create_derived_build.py,
 # though it may have been modified since its creation.
+"""Starboard evergreen-arm-softfp platform configuration for gyp_cobalt."""
 
-{
-  'includes': [
-    # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file.  The idea
-    # is that we just want this file to *be* the parent gyp file.
-    '<(DEPTH)/starboard/linux/x64x11/blittergles/starboard_platform_tests.gyp',
-  ],
-}
+from starboard.evergreen.arm.shared import gyp_configuration as parent_configuration
+
+
+def CreatePlatformConfig():
+  return parent_configuration.EvergreenArmConfiguration(
+      'evergreen-arm-softfp',
+      sabi_json_path='starboard/sabi/arm/softfp/sabi.json')
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform.gyp b/src/starboard/evergreen/arm/softfp/starboard_platform.gyp
similarity index 92%
copy from src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform.gyp
copy to src/starboard/evergreen/arm/softfp/starboard_platform.gyp
index 8163408..8787f61 100644
--- a/src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform.gyp
+++ b/src/starboard/evergreen/arm/softfp/starboard_platform.gyp
@@ -19,6 +19,7 @@
   'includes': [
     # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file.  The idea
     # is that we just want this file to *be* the parent gyp file.
-    '<(DEPTH)/starboard/linux/x64x11/blittergles/starboard_platform.gyp',
+    '<(DEPTH)/starboard/evergreen/shared/starboard_platform.gyp',
   ],
 }
+
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/6/atomic_public.h b/src/starboard/evergreen/arm/softfp/thread_types_public.h
similarity index 72%
copy from src/starboard/linux/x64x11/blittergles/sbversion/6/atomic_public.h
copy to src/starboard/evergreen/arm/softfp/thread_types_public.h
index d2414dd..d739119 100644
--- a/src/starboard/linux/x64x11/blittergles/sbversion/6/atomic_public.h
+++ b/src/starboard/evergreen/arm/softfp/thread_types_public.h
@@ -15,9 +15,9 @@
 // This file was initially generated by starboard/tools/create_derived_build.py,
 // though it may have been modified since its creation.
 
-#ifndef STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_ATOMIC_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_ATOMIC_PUBLIC_H_
+#ifndef STARBOARD_EVERGREEN_ARM_SOFTFP_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_EVERGREEN_ARM_SOFTFP_THREAD_TYPES_PUBLIC_H_
 
-#include "starboard/linux/x64x11/blittergles/atomic_public.h"
+#include "starboard/shared/pthread/types_public.h"
 
-#endif  // STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_ATOMIC_PUBLIC_H_
+#endif  // STARBOARD_EVERGREEN_ARM_SOFTFP_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/sbversion/6/__init__.py b/src/starboard/evergreen/arm64/__init__.py
similarity index 100%
copy from src/starboard/linux/x64x11/sbversion/6/__init__.py
copy to src/starboard/evergreen/arm64/__init__.py
diff --git a/src/starboard/raspi/2/mozjs/thread_types_public.h b/src/starboard/evergreen/arm64/atomic_public.h
similarity index 65%
rename from src/starboard/raspi/2/mozjs/thread_types_public.h
rename to src/starboard/evergreen/arm64/atomic_public.h
index 96c08d1..1b5fd76 100644
--- a/src/starboard/raspi/2/mozjs/thread_types_public.h
+++ b/src/starboard/evergreen/arm64/atomic_public.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Cobalt Authors. All Rights Reserved.
+// 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.
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_RASPI_2_MOZJS_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_RASPI_2_MOZJS_THREAD_TYPES_PUBLIC_H_
+#ifndef STARBOARD_EVERGREEN_ARM64_ATOMIC_PUBLIC_H_
+#define STARBOARD_EVERGREEN_ARM64_ATOMIC_PUBLIC_H_
 
-#include "starboard/raspi/2/thread_types_public.h"
+#include "starboard/atomic.h"
+#include "starboard/shared/gcc/atomic_gcc_public.h"
 
-#endif  // STARBOARD_RASPI_2_MOZJS_THREAD_TYPES_PUBLIC_H_
+#endif  // STARBOARD_EVERGREEN_ARM64_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/evergreen/arm64/configuration_public.h b/src/starboard/evergreen/arm64/configuration_public.h
new file mode 100644
index 0000000..9d5aac5
--- /dev/null
+++ b/src/starboard/evergreen/arm64/configuration_public.h
@@ -0,0 +1,219 @@
+// 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.
+
+// The Starboard configuration for X86_64. Other devices will have
+// specific Starboard implementations.
+
+// Other source files should never include this header directly, but should
+// include the generic "starboard/configuration.h" instead.
+
+#ifndef STARBOARD_EVERGREEN_ARM64_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_EVERGREEN_ARM64_CONFIGURATION_PUBLIC_H_
+
+// Configuration parameters that allow the application to make some general
+// compile-time decisions with respect to the the number of cores likely to be
+// available on this platform. For a definitive measure, the application should
+// still call SbSystemGetNumberOfProcessors at runtime.
+
+// Whether the current platform's thread scheduler will automatically balance
+// threads between cores, as opposed to systems where threads will only ever run
+// on the specifically pinned core.
+#define SB_HAS_CROSS_CORE_SCHEDULER 1
+
+// Indicates that there is no support for alignment at greater than 16 bytes for
+// items on the stack.
+#define SB_HAS_QUIRK_DOES_NOT_STACK_ALIGN_OVER_16_BYTES 1
+
+// This quirk is used to fix an issue caused by the rewriting of memset to
+// SbMemorySet in third_party/protobuf/src/google/protobuf/stubs/port.h.
+#define SB_HAS_QUIRK_MEMSET_IN_SYSTEM_HEADERS 1
+
+// --- System Header Configuration -------------------------------------------
+
+// Any system headers listed here that are not provided by the platform will be
+// emulated in starboard/types.h.
+
+// Whether the current platform provides the standard header stdarg.h.
+#define SB_HAS_STDARG_H 1
+
+// Whether the current platform provides the standard header stdbool.h.
+#define SB_HAS_STDBOOL_H 1
+
+// Whether the current platform provides the standard header stddef.h.
+#define SB_HAS_STDDEF_H 1
+
+// Whether the current platform provides the standard header stdint.h.
+#define SB_HAS_STDINT_H 1
+
+// Whether the current platform provides the standard header inttypes.h.
+#define SB_HAS_INTTYPES_H 1
+
+// Whether the current platform provides the standard header limits.h.
+#define SB_HAS_LIMITS_H 1
+
+// Whether the current platform provides the standard header float.h.
+#define SB_HAS_FLOAT_H 1
+
+// Whether the current platform provides ssize_t.
+#define SB_HAS_SSIZE_T 0
+
+// Type detection for wchar_t.
+#if defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
+#define SB_IS_WCHAR_T_UTF32 1
+#elif defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff)
+#define SB_IS_WCHAR_T_UTF16 1
+#endif
+
+// Chrome only defines these two if ARMEL or MIPSEL are defined.
+#if defined(__ARMEL__)
+// Chrome has an exclusion for iOS here, we should too when we support iOS.
+#define SB_IS_WCHAR_T_UNSIGNED 1
+#elif defined(__MIPSEL__)
+#define SB_IS_WCHAR_T_SIGNED 1
+#endif
+
+// This quirk is used to fix an issue caused by the rewriting of memset to
+// SbMemorySet in third_party/protobuf/src/google/protobuf/stubs/port.h.
+#define SB_HAS_QUIRK_MEMSET_IN_SYSTEM_HEADERS 1
+
+// --- Compiler Configuration ------------------------------------------------
+
+// The platform's annotation for forcing a C function to be inlined.
+#define SB_C_FORCE_INLINE __inline__ __attribute__((always_inline))
+
+// The platform's annotation for marking a C function as suggested to be
+// inlined.
+#define SB_C_INLINE inline
+
+// The platform's annotation for marking a C function as forcibly not
+// inlined.
+#define SB_C_NOINLINE __attribute__((noinline))
+
+// The platform's annotation for marking a symbol as exported outside of the
+// current shared library.
+#define SB_EXPORT_PLATFORM __attribute__((visibility("default")))
+
+// The platform's annotation for marking a symbol as imported from outside of
+// the current linking unit.
+#define SB_IMPORT_PLATFORM
+
+// --- Extensions Configuration ----------------------------------------------
+
+// Do not use <unordered_map> and <unordered_set> for the hash table types.
+#define SB_HAS_STD_UNORDERED_HASH 0
+
+// GCC/Clang doesn't define a long long hash function, except for Android and
+// Game consoles.
+#define SB_HAS_LONG_LONG_HASH 0
+
+// GCC/Clang doesn't define a string hash function, except for Game Consoles.
+#define SB_HAS_STRING_HASH 0
+
+// Desktop Linux needs a using statement for the hash functions.
+#define SB_HAS_HASH_USING 0
+
+// Set this to 1 if hash functions for custom types can be defined as a
+// hash_value() function. Otherwise, they need to be placed inside a
+// partially-specified hash struct template with an operator().
+#define SB_HAS_HASH_VALUE 0
+
+// Set this to 1 if use of hash_map or hash_set causes a deprecation warning
+// (which then breaks the build).
+#define SB_HAS_HASH_WARNING 1
+
+// The location to include hash_map on this platform.
+#define SB_HASH_MAP_INCLUDE <ext/hash_map>
+
+// C++'s hash_map and hash_set are often found in different namespaces depending
+// on the compiler.
+#define SB_HASH_NAMESPACE __gnu_cxx
+
+// The location to include hash_set on this platform.
+#define SB_HASH_SET_INCLUDE <ext/hash_set>
+
+// Define this to how this platform copies varargs blocks.
+#define SB_VA_COPY(dest, source) va_copy(dest, source)
+
+// --- Graphics Configuration ------------------------------------------------
+
+// Specifies whether this platform supports a performant accelerated blitter
+// API. The basic requirement is a scaled, clipped, alpha-blended blit.
+#define SB_HAS_BLITTER 0
+
+// Indicates whether or not the given platform supports bilinear filtering.
+// This can be checked to enable/disable renderer tests that verify that this is
+// working properly.
+#define SB_HAS_BILINEAR_FILTERING_SUPPORT 1
+
+// Indicates whether or not the given platform supports rendering of NV12
+// textures. These textures typically originate from video decoders.
+#define SB_HAS_NV12_TEXTURE_SUPPORT 1
+
+// --- I/O Configuration -----------------------------------------------------
+
+// Whether the current platform has microphone supported.
+#define SB_HAS_MICROPHONE 1
+
+// Whether the current platform implements the on screen keyboard interface.
+#define SB_HAS_ON_SCREEN_KEYBOARD 0
+
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 0
+
+// Whether the current platform has speech synthesis.
+#define SB_HAS_SPEECH_SYNTHESIS 0
+
+// --- Decoder-only Params ---
+
+// --- Memory Configuration --------------------------------------------------
+
+// Whether this platform has and should use an MMAP function to map physical
+// memory to the virtual address space.
+#if SB_API_VERSION < SB_MMAP_REQUIRED_VERSION
+#define SB_HAS_MMAP 1
+#endif
+
+// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
+// required for platforms that want to JIT.
+#define SB_CAN_MAP_EXECUTABLE_MEMORY 1
+
+// --- Network Configuration -------------------------------------------------
+
+// Specifies whether this platform supports IPV6.
+#define SB_HAS_IPV6 1
+
+// Specifies whether this platform supports pipe.
+#define SB_HAS_PIPE 1
+
+// --- Thread Configuration --------------------------------------------------
+
+// --- Timing API ------------------------------------------------------------
+
+// Whether this platform has an API to retrieve how long the current thread
+// has spent in the executing state.
+#define SB_HAS_TIME_THREAD_NOW 1
+
+// --- Tuneable Parameters ---------------------------------------------------
+
+// --- User Configuration ----------------------------------------------------
+
+// --- Platform Specific Audits ----------------------------------------------
+
+#if !defined(__GNUC__)
+#error "Evergreen-arm64 builds need a GCC-like compiler (for the moment)."
+#endif
+
+#endif  // STARBOARD_EVERGREEN_ARM64_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/evergreen/arm64/gyp_configuration.gypi b/src/starboard/evergreen/arm64/gyp_configuration.gypi
new file mode 100644
index 0000000..b2a0525
--- /dev/null
+++ b/src/starboard/evergreen/arm64/gyp_configuration.gypi
@@ -0,0 +1,58 @@
+# 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.
+
+# This file was initially generated by starboard/tools/create_derived_build.py,
+# though it may have been modified since its creation.
+
+{
+  'variables': {
+    'yasm_exists': 1,
+    'sb_target_platform': 'evergreen-arm64',
+
+    'compiler_flags': [
+      '-isystem<(cobalt_repo_root)/third_party/musl/arch/aarch64',
+    ],
+  },
+
+  'target_defaults': {
+    'defines' : [
+      # Ensure that the Starboardized __external_threading file is included.
+      '_LIBCPP_HAS_THREAD_API_EXTERNAL',
+      # Ensure that only the forward declarations and type definitions are included
+      # in __external_threading.
+      '_LIBCPP_HAS_THREAD_LIBRARY_EXTERNAL',
+      # Enable GNU extensions to get prototypes like ffsl.
+      '_GNU_SOURCE=1',
+    ],
+    'default_configuration': 'evergreen-arm64_debug',
+    'configurations': {
+      'evergreen-arm64_debug': {
+        'inherit_from': ['debug_base'],
+      },
+      'evergreen-arm64_devel': {
+        'inherit_from': ['devel_base'],
+      },
+      'evergreen-arm64_qa': {
+        'inherit_from': ['qa_base'],
+      },
+      'evergreen-arm64_gold': {
+        'inherit_from': ['gold_base'],
+      },
+    }, # end of configurations
+  },
+  'includes': [
+    '<(DEPTH)/starboard/evergreen/shared/compiler_flags.gypi',
+    '<(DEPTH)/starboard/evergreen/shared/gyp_configuration.gypi',
+  ],
+}
diff --git a/src/starboard/evergreen/arm64/gyp_configuration.py b/src/starboard/evergreen/arm64/gyp_configuration.py
new file mode 100644
index 0000000..a607170
--- /dev/null
+++ b/src/starboard/evergreen/arm64/gyp_configuration.py
@@ -0,0 +1,91 @@
+# 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.
+"""Starboard evergreen-arm64 platform configuration for gyp_cobalt."""
+
+from starboard.build import clang as clang_build
+from starboard.evergreen.shared import gyp_configuration as shared_configuration
+from starboard.tools import build
+from starboard.tools.toolchain import ar
+from starboard.tools.toolchain import bash
+from starboard.tools.toolchain import clang
+from starboard.tools.toolchain import clangxx
+from starboard.tools.toolchain import cp
+from starboard.tools.toolchain import evergreen_linker
+from starboard.tools.toolchain import touch
+
+
+class EvergreenArm64Configuration(shared_configuration.EvergreenConfiguration):
+  """Starboard Evergreen 64-bit ARM platform configuration."""
+
+  def __init__(self,
+               platform_name='evergreen-arm64',
+               asan_enabled_by_default=False,
+               goma_supports_compiler=True,
+               sabi_json_path=None):
+    # pylint: disable=useless-super-delegation
+    super(EvergreenArm64Configuration,
+          self).__init__(platform_name, asan_enabled_by_default,
+                         goma_supports_compiler, sabi_json_path)
+    self._host_toolchain = None
+
+  def GetTargetToolchain(self):
+    return self.GetHostToolchain()
+
+  def GetHostToolchain(self):
+    if not self._host_toolchain:
+      if not hasattr(self, 'host_compiler_environment'):
+        self.host_compiler_environment = build.GetHostCompilerEnvironment(
+            clang_build.GetClangSpecification(), False)
+      cc_path = self.host_compiler_environment['CC_host']
+      cxx_path = self.host_compiler_environment['CXX_host']
+
+      # Takes the provided value of CXX_HOST with a prepended 'gomacc' and an
+      # appended 'bin/clang++' and strips them off, leaving us with an absolute
+      # path to the root directory of our toolchain.
+      begin_path_index = cxx_path.find('/')
+      end_path_index = cxx_path.rfind('/', 0, cxx_path.rfind('/')) + 1
+
+      cxx_path_root = cxx_path[begin_path_index:end_path_index]
+
+      self._host_toolchain = [
+          clang.CCompiler(path=cc_path),
+          clang.CxxCompiler(path=cxx_path),
+          clang.AssemblerWithCPreprocessor(path=cc_path),
+          ar.StaticThinLinker(),
+          ar.StaticLinker(),
+          clangxx.ExecutableLinker(path=cxx_path),
+          evergreen_linker.SharedLibraryLinker(
+              path=cxx_path_root, extra_flags=['-m aarch64elf']),
+          cp.Copy(),
+          touch.Stamp(),
+          bash.Shell(),
+      ]
+    return self._host_toolchain
+
+  def GetTestFilters(self):
+    # pylint: disable=useless-super-delegation
+    return super(EvergreenArm64Configuration, self).GetTestFilters()
+
+  def GetVariables(self, configuration):
+    variables = super(EvergreenArm64Configuration, self).GetVariables(configuration)
+    variables.update({
+        'include_path_platform_deploy_gypi':
+            'starboard/evergreen/arm64/platform_deploy.gypi',
+    })
+    return variables
+
+
+def CreatePlatformConfig():
+  return EvergreenArm64Configuration(
+      sabi_json_path='starboard/sabi/arm64/sabi.json')
diff --git a/src/starboard/evergreen/arm64/platform_deploy.gypi b/src/starboard/evergreen/arm64/platform_deploy.gypi
new file mode 100644
index 0000000..f32c77f
--- /dev/null
+++ b/src/starboard/evergreen/arm64/platform_deploy.gypi
@@ -0,0 +1,37 @@
+# Copyright 2020 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.
+
+{
+  'variables': {
+    'deploy_executable_file': '<(target_deploy_dir)/lib/lib<(executable_name).so',
+    'executable_file': '<(PRODUCT_DIR)/lib/lib<(executable_name).so',
+  },
+  'includes': [ '<(DEPTH)/starboard/build/collect_deploy_content.gypi' ],
+  'actions': [
+    {
+      'action_name': 'deploy_executable',
+      'message': 'Strip executable: <(deploy_executable_file)',
+      'inputs': [
+        '<(executable_file)',
+        '<(content_deploy_stamp_file)',
+      ],
+      'outputs': [ '<(deploy_executable_file)' ],
+      'action': [
+        'aarch64-linux-gnu-strip',
+        '-o', '<(deploy_executable_file)',
+        '<(executable_file)',
+      ],
+    },
+  ],
+}
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform.gyp b/src/starboard/evergreen/arm64/starboard_platform.gyp
similarity index 78%
copy from src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform.gyp
copy to src/starboard/evergreen/arm64/starboard_platform.gyp
index 8163408..8fa33ef 100644
--- a/src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform.gyp
+++ b/src/starboard/evergreen/arm64/starboard_platform.gyp
@@ -12,13 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# This file was initially generated by starboard/tools/create_derived_build.py,
-# though it may have been modified since its creation.
-
 {
   'includes': [
     # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file.  The idea
     # is that we just want this file to *be* the parent gyp file.
-    '<(DEPTH)/starboard/linux/x64x11/blittergles/starboard_platform.gyp',
+    '<(DEPTH)/starboard/evergreen/shared/starboard_platform.gyp',
   ],
 }
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/6/atomic_public.h b/src/starboard/evergreen/arm64/thread_types_public.h
similarity index 72%
copy from src/starboard/linux/x64x11/blittergles/sbversion/6/atomic_public.h
copy to src/starboard/evergreen/arm64/thread_types_public.h
index d2414dd..c156579 100644
--- a/src/starboard/linux/x64x11/blittergles/sbversion/6/atomic_public.h
+++ b/src/starboard/evergreen/arm64/thread_types_public.h
@@ -15,9 +15,9 @@
 // This file was initially generated by starboard/tools/create_derived_build.py,
 // though it may have been modified since its creation.
 
-#ifndef STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_ATOMIC_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_ATOMIC_PUBLIC_H_
+#ifndef STARBOARD_EVERGREEN_ARM64_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_EVERGREEN_ARM64_THREAD_TYPES_PUBLIC_H_
 
-#include "starboard/linux/x64x11/blittergles/atomic_public.h"
+#include "starboard/shared/pthread/types_public.h"
 
-#endif  // STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_ATOMIC_PUBLIC_H_
+#endif  // STARBOARD_EVERGREEN_ARM64_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/evergreen/sabi/sabi_flags.gypi b/src/starboard/evergreen/sabi/sabi_flags.gypi
new file mode 100644
index 0000000..160ef7f
--- /dev/null
+++ b/src/starboard/evergreen/sabi/sabi_flags.gypi
@@ -0,0 +1,80 @@
+# 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.
+
+# Starboard Application Binary Interface
+#
+# This file translates the set of ABI variables, which should be overridden by
+# each platform, into platform-specific sets of compiler flags.
+
+{
+  'variables': {
+    'conditions': [
+      # arm
+      ['target_arch=="arm"', {
+        'compiler_flags': [
+          '-march=arm<(target_arch_sub)',
+        ],
+        'conditions': [
+          ['floating_point_abi=="soft"', {
+            'compiler_flags': [
+              '-mfloat-abi=soft',
+            ],
+          }, {
+            'compiler_flags': [
+              '-mfloat-abi=<(floating_point_abi)',
+              '-mfpu=<(floating_point_fpu)',
+            ],
+          }],
+          ['floating_point_abi=="hard"', {
+            'compiler_flags': [
+              '-target', 'arm<(target_arch_sub)-none-eabihf',
+            ],
+          }, {
+            'compiler_flags': [
+              '-target', 'arm<(target_arch_sub)-none-eabi',
+            ],
+          }],
+        ],
+      }],
+      # arm64
+      ['target_arch=="arm64"', {
+        'compiler_flags': [
+          '-march=arm64<(target_arch_sub)',
+          '-target', 'arm64<(target_arch_sub)-unknown-unknown-elf',
+
+          # There is no software floating point support for arm64 / aarch64
+          # architectures. Thus, -mfloat-abi and -mfpu would simply be ignored.
+
+          # char is unsigned by default for arm64.
+          '-fsigned-char',
+        ],
+      }],
+      # x86
+      ['target_arch=="x86"', {
+        'compiler_flags': [
+          '-march=i686',
+          '-target', 'i686-unknown-unknown-elf',
+        ],
+      }],
+      # x64
+      ['target_arch=="x64"', {
+        'compiler_flags': [
+          '-march=x86-64',
+          '-target', 'x86_64-unknown-linux-elf',
+        ],
+      }],
+    ],
+  },
+}
+
diff --git a/src/starboard/linux/x64x11/sbversion/6/__init__.py b/src/starboard/evergreen/shared/__init__.py
similarity index 100%
copy from src/starboard/linux/x64x11/sbversion/6/__init__.py
copy to src/starboard/evergreen/shared/__init__.py
diff --git a/src/starboard/evergreen/shared/compiler_flags.gypi b/src/starboard/evergreen/shared/compiler_flags.gypi
new file mode 100644
index 0000000..91af007
--- /dev/null
+++ b/src/starboard/evergreen/shared/compiler_flags.gypi
@@ -0,0 +1,192 @@
+# 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.
+
+# Platform specific compiler flags for Evergreen. Included from
+# gyp_configuration.gypi.
+
+{
+  'variables': {
+    'compiler_flags_host': [
+      '-O2',
+    ],
+    'compiler_flags_debug': [
+      '-frtti',
+      '-O0',
+    ],
+    'compiler_flags_devel': [
+      '-frtti',
+      '-O2',
+    ],
+    'compiler_flags_qa': [
+      '-fno-rtti',
+      '-gline-tables-only',
+    ],
+    'compiler_flags_qa_size': [
+      '-Os',
+    ],
+    'compiler_flags_qa_speed': [
+      '-O2',
+    ],
+    'compiler_flags_gold': [
+      '-fno-rtti',
+      '-gline-tables-only',
+    ],
+    'compiler_flags_gold_size': [
+      '-Os',
+    ],
+    'compiler_flags_gold_speed': [
+      '-O2',
+    ],
+    'conditions': [
+      ['clang==1', {
+        'linker_flags': [
+          '-fuse-ld=lld',
+          '-nostdlib',
+        ],
+        'common_clang_flags': [
+          '-fcolor-diagnostics',
+          # Default visibility to hidden, to enable dead stripping.
+          '-fvisibility=hidden',
+          '-Wno-c++11-compat',
+          # This complains about 'override', which we use heavily.
+          '-Wno-c++11-extensions',
+          # Warns on switches on enums that cover all enum values but
+          # also contain a default: branch. Chrome is full of that.
+          '-Wno-covered-switch-default',
+          # protobuf uses hash_map.
+          '-Wno-deprecated',
+          '-fno-exceptions',
+          # Enable unwind tables used by libunwind for stack traces.
+          '-funwind-tables',
+          # Disable usage of frame pointers.
+          '-fomit-frame-pointer',
+          # Don't warn about the "struct foo f = {0};" initialization pattern.
+          '-Wno-missing-field-initializers',
+          # Do not warn for implicit sign conversions.
+          '-Wno-sign-conversion',
+          '-fno-strict-aliasing',  # See http://crbug.com/32204
+          '-Wno-unnamed-type-template-args',
+          # Triggered by the COMPILE_ASSERT macro.
+          '-Wno-unused-local-typedef',
+          # Do not warn if a function or variable cannot be implicitly
+          # instantiated.
+          '-Wno-undefined-var-template',
+          # Do not warn about an implicit exception spec mismatch.
+          '-Wno-implicit-exception-spec-mismatch',
+          # It's OK not to use some input parameters.
+          '-Wno-unused-parameter',
+          '-Wno-conversion',
+          '-Wno-bitwise-op-parentheses',
+          '-Wno-shift-op-parentheses',
+          '-Wno-shorten-64-to-32',
+          '-fno-use-cxa-atexit',
+        ],
+      }],
+      ['cobalt_fastbuild==0', {
+        'compiler_flags_debug': [
+          '-g',
+        ],
+        'compiler_flags_devel': [
+          '-g',
+        ],
+        'compiler_flags_qa': [
+          '-gline-tables-only',
+        ],
+        'compiler_flags_gold': [
+          '-gline-tables-only',
+        ],
+      }],
+    ],
+  },
+
+  'target_defaults': {
+    'defines': [
+      # By default, <EGL/eglplatform.h> pulls in some X11 headers that have some
+      # nasty macros (|Status|, for example) that conflict with Chromium base.
+      'MESA_EGL_NO_X11_HEADERS'
+    ],
+    'cflags': [
+      '-fPIC',
+      '-nostdlibinc',
+      '-isystem<(cobalt_repo_root)/third_party/llvm-project/libcxxabi/include',
+      '-isystem<(cobalt_repo_root)/third_party/llvm-project/libunwind/include',
+      '-isystem<(cobalt_repo_root)/third_party/llvm-project/libcxx/include',
+      '-isystem<(cobalt_repo_root)/third_party/musl/include',
+      '-isystem<(cobalt_repo_root)/third_party/musl/arch/generic',
+    ],
+    'cflags_cc': [
+      '-nostdinc++',
+      '-std=c++11',
+    ],
+    'target_conditions': [
+      ['sb_pedantic_warnings==1', {
+        'cflags': [
+          '-Wall',
+          '-Wextra',
+          '-Wunreachable-code',
+          '<@(common_clang_flags)',
+        ],
+      },{
+        'cflags': [
+          '<@(common_clang_flags)',
+          # 'this' pointer cannot be NULL...pointer may be assumed
+          # to always convert to true.
+          '-Wno-undefined-bool-conversion',
+          # Skia doesn't use overrides.
+          '-Wno-inconsistent-missing-override',
+          # Do not warn for implicit type conversions that may change a value.
+          '-Wno-conversion',
+          # shifting a negative signed value is undefined
+          '-Wno-shift-negative-value',
+          # Width of bit-field exceeds width of its type- value will be truncated
+          '-Wno-bitfield-width',
+          '-Wno-undefined-var-template',
+        ],
+      }],
+      ['use_asan==1', {
+        'cflags': [
+          '-fsanitize=address',
+          '-fno-omit-frame-pointer',
+        ],
+        'ldflags': [
+          '-fsanitize=address',
+          # Force linking of the helpers in sanitizer_options.cc
+          '-Wl,-u_sanitizer_options_link_helper',
+        ],
+        'defines': [
+          'ADDRESS_SANITIZER',
+        ],
+        'conditions': [
+          ['asan_symbolizer_path!=""', {
+            'defines': [
+              'ASAN_SYMBOLIZER_PATH="<@(asan_symbolizer_path)"',
+            ],
+          }],
+        ],
+      }],
+      ['use_tsan==1', {
+        'cflags': [
+          '-fsanitize=thread',
+          '-fno-omit-frame-pointer',
+        ],
+        'ldflags': [
+          '-fsanitize=thread',
+        ],
+        'defines': [
+          'THREAD_SANITIZER',
+        ],
+      }],
+    ],
+  }, # end of target_defaults
+}
diff --git a/src/starboard/evergreen/shared/gyp_configuration.gypi b/src/starboard/evergreen/shared/gyp_configuration.gypi
new file mode 100644
index 0000000..e6a8911
--- /dev/null
+++ b/src/starboard/evergreen/shared/gyp_configuration.gypi
@@ -0,0 +1,70 @@
+# 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.
+
+# This file was initially generated by starboard/tools/create_derived_build.py,
+# though it may have been modified since its creation.
+
+{
+  'variables': {
+    'sb_evergreen': 1,
+    'sb_speech_supported': 0,
+
+    'enable_vr': 0,
+    'default_renderer_options_dependency': '<(DEPTH)/cobalt/renderer/default_options_starboard.gyp:default_options',
+    'javascript_engine': 'v8',
+    'cobalt_v8_buildtime_snapshot': 1,
+    'cobalt_v8_enable_embedded_builtins': 1,
+
+    # Override that omits the "data" subdirectory.
+    # TODO: Remove when omitted for all platforms in base_configuration.gypi.
+    'sb_static_contents_output_data_dir': '<(PRODUCT_DIR)/content',
+
+    'final_executable_type': 'shared_library',
+    'gtest_target_type': 'shared_library',
+
+    'compiler_flags': [
+      # We'll pretend not to be Linux, but Starboard instead.
+      '-U__linux__',
+
+      # Pretend not to be unix, so that _LIBCPP_HAS_CATOPEN is not defined in
+      # third_party/llvm-project/libcxx/include/__config. Then leaks catopen,
+      # catgets and catclose are plugged.
+      '-U__unix__',
+    ],
+
+    # Using an inner scope for 'variables' so that it can be made a default
+    # (and so overridden elsewhere), but yet still used immediately in this
+    # file.
+    'variables': {
+      'use_dlmalloc_allocator%': 0,
+    },
+  },
+  'target_defaults': {
+    'defines' : [
+      '_LIBCPP_HAS_MUSL_LIBC',
+      '__STDC_FORMAT_MACROS', # so that we get PRI*
+
+      # File format of the shared object we will generate.
+      '__ELF__',
+
+      # Use scalar portable implementations instead of Clang/GCC vector
+      # extensions in SkVx.h.
+      'SKNX_NO_SIMD',
+    ],
+  },
+  'includes': [
+    '<(DEPTH)/starboard/evergreen/sabi/sabi_flags.gypi',
+    '<(DEPTH)/starboard/sabi/sabi.gypi',
+  ],
+}
diff --git a/src/starboard/evergreen/shared/gyp_configuration.py b/src/starboard/evergreen/shared/gyp_configuration.py
new file mode 100644
index 0000000..3d9860f
--- /dev/null
+++ b/src/starboard/evergreen/shared/gyp_configuration.py
@@ -0,0 +1,117 @@
+# 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.
+"""Starboard Evergreen shared platform configuration."""
+
+import os
+
+from starboard.build import clang
+from starboard.build import platform_configuration
+from starboard.tools import build
+from starboard.tools import paths
+from starboard.tools.testing import test_filter
+
+
+class EvergreenConfiguration(platform_configuration.PlatformConfiguration):
+  """Starboard Evergreen platform configuration."""
+
+  def __init__(self,
+               platform,
+               asan_enabled_by_default=True,
+               goma_supports_compiler=True,
+               sabi_json_path=None):
+    self.goma_supports_compiler = goma_supports_compiler
+    self.sabi_json_path = sabi_json_path
+    super(EvergreenConfiguration, self).__init__(platform,
+                                                 asan_enabled_by_default)
+
+  def GetBuildFormat(self):
+    """Returns the desired build format."""
+    # The comma means that ninja and qtcreator_ninja will be chained and use the
+    # same input information so that .gyp files will only have to be parsed
+    # once.
+    return 'ninja,qtcreator_ninja'
+
+  def GetVariables(self, config_name):
+    variables = super(EvergreenConfiguration, self).GetVariables(
+        config_name, use_clang=1)
+    variables.update({
+        'javascript_engine':
+            'v8',
+        'cobalt_enable_jit':
+            1,
+        'cobalt_repo_root':
+            paths.REPOSITORY_ROOT,
+        'include_path_platform_deploy_gypi':
+            'starboard/evergreen/shared/platform_deploy.gypi',
+    })
+    return variables
+
+  def GetLauncherPath(self):
+    """Gets the path to the launcher module for this platform."""
+    return os.path.dirname(__file__)
+
+  def GetGeneratorVariables(self, config_name):
+    del config_name
+    generator_variables = {
+        'qtcreator_session_name_prefix': 'cobalt',
+    }
+    return generator_variables
+
+  def GetEnvironmentVariables(self):
+    if not hasattr(self, 'host_compiler_environment'):
+      self.host_compiler_environment = build.GetHostCompilerEnvironment(
+          clang.GetClangSpecification(), self.goma_supports_compiler)
+
+    env_variables = self.host_compiler_environment
+    env_variables.update({
+        'CC': self.host_compiler_environment['CC_host'],
+        'CXX': self.host_compiler_environment['CXX_host'],
+    })
+    return env_variables
+
+  def GetPathToSabiJsonFile(self):
+    return self.sabi_json_path
+
+  def GetTestFilters(self):
+    filters = super(EvergreenConfiguration, self).GetTestFilters()
+    for target, tests in self.__FILTERED_TESTS.iteritems():
+      filters.extend(test_filter.TestFilter(target, test) for test in tests)
+    return filters
+
+  __FILTERED_TESTS = {  # pylint: disable=invalid-name
+      'nplb': ['MemoryReportingTest.CapturesMemMapUnmap',
+               'MemoryReportingTest.CapturesOperatorDeleteNothrow',
+               'SbAudioSinkTest.*',
+               'SbDrmTest.AnySupportedKeySystems'],
+
+      # player_filter_tests test the platform's Starboard implementation of
+      # the filter-based player, which is not exposed through the Starboard
+      # interface. Since Evergreen has no visibility of the platform's
+      # specific Starboard implementation, rely on the platform to test this
+      # directly instead.
+      'player_filter_tests': [test_filter.FILTER_ALL],
+  }
+
+  def GetTestTargets(self):
+    tests = super(EvergreenConfiguration, self).GetTestTargets()
+    return [test for test in tests if test not in self.__BLACKLISTED_TESTS]
+
+  __BLACKLISTED_TESTS = [  # pylint: disable=invalid-name
+      # elf_loader_test and installation_manager_test are explicitly tests that
+      # validate the correctness of the underlying platform. We should not be
+      # running these tests in Evergreen mode, and instead will rely on the
+      # platform to test this directly instead.
+      'elf_loader_test',
+      'installation_manager_test',
+  ]
diff --git a/src/starboard/evergreen/shared/launcher.py b/src/starboard/evergreen/shared/launcher.py
new file mode 100644
index 0000000..5f18643
--- /dev/null
+++ b/src/starboard/evergreen/shared/launcher.py
@@ -0,0 +1,192 @@
+# 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.
+"""Evergreen implementation of Starboard launcher abstraction."""
+
+import logging
+import os
+import shutil
+
+import _env  # pylint: disable=unused-import
+from starboard.tools import abstract_launcher
+from starboard.tools import paths
+from starboard.tools import port_symlink
+
+_BASE_STAGING_DIRECTORY = 'evergreen_staging'
+_LOADER_TARGET = 'elf_loader_sandbox'
+
+
+class Launcher(abstract_launcher.AbstractLauncher):
+  """Class for launching Evergreen targets on arbitrary platforms.
+
+  This Evergreen launcher leverages the platform-specific launchers to start the
+  Evergreen loader on a particular device. A staging directory is built that
+  resembles a typical deploy directory, with the Evergreen target and its
+  content included in the Evergreen loader's content. The platform-specific
+  launcher, given command-line switches to tell the Evergreen loader where the
+  Evergreen target and its content are, can run the loader without needing to
+  know about Evergreen per-se.
+
+  The Evergreen launcher does not require its own implementation for many
+  abstract launcher methods and trampolines them to the platform-specific
+  launcher.
+  """
+
+  def __init__(self, platform, target_name, config, device_id, **kwargs):
+    super(Launcher, self).__init__(platform, target_name, config, device_id,
+                                   **kwargs)
+    self.loader_platform = kwargs.get('loader_platform')
+    if not self.loader_platform:
+      raise ValueError('|loader_platform| cannot be |None|.')
+
+    self.loader_config = kwargs.get('loader_config')
+    if not self.loader_config:
+      raise ValueError('|loader_config| cannot be |None|.')
+
+    self.loader_out_directory = kwargs.get('loader_out_directory')
+    if not self.loader_out_directory:
+      self.loader_out_directory = paths.BuildOutputDirectory(
+          self.loader_platform, self.loader_config)
+
+    # The relationship of loader platforms and configurations to evergreen
+    # platforms and configurations is many-to-many. We need a separate directory
+    # for each of them, i.e. linux-x64x11_debug__evergreen-x64_gold.
+    self.combined_config = '{}_{}__{}_{}'.format(self.loader_platform,
+                                                 self.loader_config, platform,
+                                                 config)
+
+    self.staging_directory = os.path.join(
+        os.path.dirname(self.out_directory), _BASE_STAGING_DIRECTORY,
+        self.combined_config)
+
+    # TODO: Figure out a place for the launcher to do this prep work that is not
+    # part of the constructor.
+    self._StageTargetsAndContents()
+
+    # Ensure the path, relative to the content of the ELF Loader, to the
+    # Evergreen target and its content are passed as command line switches.
+    target_command_line_params = [
+        '--evergreen_library=app/{}/lib/lib{}.so'.format(
+            self.target_name, self.target_name),
+        '--evergreen_content=app/{}/content'.format(self.target_name)
+    ]
+
+    if self.target_command_line_params:
+      target_command_line_params.extend(self.target_command_line_params)
+
+    self.launcher = abstract_launcher.LauncherFactory(
+        self.loader_platform,
+        _LOADER_TARGET,
+        self.loader_config,
+        device_id,
+        target_params=target_command_line_params,
+        output_file=self.output_file,
+        out_directory=self.staging_directory,
+        coverage_directory=self.coverage_directory,
+        env_variables=self.env_variables)
+
+  def Run(self):
+    """Redirects to the ELF Loader platform's abstract loader implementation."""
+
+    return_code = 1
+
+    try:
+      return_code = self.launcher.Run()
+    except Exception:  # pylint: disable=broad-except
+      logging.exception('Error occurred while running test.')
+
+    return return_code
+
+  def Kill(self):
+    """Redirects to the ELF Loader platform's abstract loader implementation."""
+    self.launcher.Kill()
+
+  def _StageTargetsAndContents(self):
+    """Stage targets and their contents in our staging directory.
+
+    The existing launcher infrastructure expects a specific directory structure
+    regardless of whether or not we are running locally:
+
+             platform:    raspi-2          raspi-2_devel
+             config:      devel             +-- deploy
+             target_name: nplb                   +-- nplb
+                                                      +-- content
+                                                      +-- nplb
+
+    This function effectively builds a directory structure that matches both of
+    these expectations while minimizing the hard copies made.
+
+    Note: The Linux launcher does not yet look in the deploy directory and
+          instead runs the target from the top of its out-directory. This will
+          be changed in the future.
+    """
+
+    if os.path.exists(self.staging_directory):
+      shutil.rmtree(self.staging_directory)
+    os.makedirs(self.staging_directory)
+
+    # out/evergreen_staging/linux-x64x11_devel__evergreen-x64_devel/deploy/elf_loader_sandbox
+    staging_directory_loader = os.path.join(self.staging_directory, 'deploy',
+                                            _LOADER_TARGET)
+
+    # out/evergreen_staging/linux-x64x11_devel__evergreen-x64_devel/deploy/elf_loader_sandbox/content/app/nplb/
+    staging_directory_evergreen = os.path.join(staging_directory_loader,
+                                               'content', 'app',
+                                               self.target_name)
+
+    # Make a hard copy of the ELF Loader's deploy directory in the location
+    # specified by |staging_directory_loader|. A symbolic link here would cause
+    # future symbolic links to fall through to the original out-directories.
+    shutil.copytree(
+        os.path.join(self.loader_out_directory, 'deploy', _LOADER_TARGET),
+        staging_directory_loader)
+
+    port_symlink.MakeSymLink(
+        os.path.join(self.out_directory, 'deploy', self.target_name),
+        staging_directory_evergreen)
+
+    # TODO: Make the Linux launcher run from the deploy directory, no longer
+    #       create these symlinks, and remove the NOTE from the docstring.
+    port_symlink.MakeSymLink(
+        os.path.join(staging_directory_loader, _LOADER_TARGET),
+        os.path.join(self.staging_directory, _LOADER_TARGET))
+    port_symlink.MakeSymLink(
+        os.path.join(staging_directory_loader, 'content'),
+        os.path.join(self.staging_directory, 'content'))
+
+  def SupportsSuspendResume(self):
+    return self.launcher.SupportsSuspendResume()
+
+  def SendResume(self):
+    return self.launcher.SendResume()
+
+  def SendSuspend(self):
+    return self.launcher.SendSuspend()
+
+  def SupportsDeepLink(self):
+    return self.launcher.SupportsDeepLink()
+
+  def SendDeepLink(self, link):
+    return self.launcher.SendDeepLink(link)
+
+  def GetStartupTimeout(self):
+    return self.launcher.GetStartupTimeout()
+
+  def GetHostAndPortGivenPort(self, port):
+    return self.launcher.GetHostAndPortGivenPort(port)
+
+  def GetTargetPath(self):
+    return self.launcher.GetTargetPath()
+
+  def GetDeviceIp(self):
+    return self.launcher.GetDeviceIp()
diff --git a/src/starboard/evergreen/shared/platform_deploy.gypi b/src/starboard/evergreen/shared/platform_deploy.gypi
new file mode 100644
index 0000000..3ad944a
--- /dev/null
+++ b/src/starboard/evergreen/shared/platform_deploy.gypi
@@ -0,0 +1,44 @@
+# 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.
+
+{
+  'variables': {
+    'deploy_executable_file': '<(target_deploy_dir)/lib/lib<(executable_name).so',
+    'executable_file': '<(PRODUCT_DIR)/lib/lib<(executable_name).so',
+  },
+  'includes': [
+    '<(DEPTH)/starboard/build/collect_deploy_content.gypi',
+  ],
+  'actions': [
+    {
+      'action_name': 'deploy_executable',
+      'message': 'Deploy <(deploy_executable_file)',
+      'inputs': [
+        '<(content_deploy_stamp_file)',
+        '<(executable_file)',
+      ],
+      'outputs': [ '<(deploy_executable_file)' ],
+      'action': [
+        'python',
+        '<(DEPTH)/starboard/tools/port_symlink.py',
+        '-f',
+        '-r',
+        '--link',
+        '<(executable_file)',
+        '<(deploy_executable_file)',
+      ],
+    },
+  ],
+}
+
diff --git a/src/starboard/evergreen/shared/starboard_platform.gyp b/src/starboard/evergreen/shared/starboard_platform.gyp
new file mode 100644
index 0000000..768b46b
--- /dev/null
+++ b/src/starboard/evergreen/shared/starboard_platform.gyp
@@ -0,0 +1,44 @@
+# 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.
+
+{
+  'includes': [
+    '<(DEPTH)/starboard/stub/stub_sources.gypi',
+    '<(DEPTH)/starboard/evergreen/shared/starboard_platform.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'starboard_platform',
+      'type': 'static_library',
+      'sources': [
+        '<@(stub_sources)',
+        'atomic_public.h',
+        'configuration_public.h',
+        'thread_types_public.h',
+      ],
+      'conditions': [
+        ['sb_speech_supported == 0', {
+          'sources!': [
+            '<@(speech_stub_sources)',
+          ],
+        }],
+      ],
+      'defines': [
+        # This must be defined when building Starboard, and must not when
+        # building Starboard client code.
+        'STARBOARD_IMPLEMENTATION',
+      ],
+    },
+  ],
+}
diff --git a/src/starboard/evergreen/shared/starboard_platform.gypi b/src/starboard/evergreen/shared/starboard_platform.gypi
new file mode 100644
index 0000000..1a205c7
--- /dev/null
+++ b/src/starboard/evergreen/shared/starboard_platform.gypi
@@ -0,0 +1,28 @@
+# Copyright 2016 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.
+{
+  'variables':  {
+    'speech_stub_sources': [
+      '<(DEPTH)/starboard/shared/stub/speech_recognizer_cancel.cc',
+      '<(DEPTH)/starboard/shared/stub/speech_recognizer_create.cc',
+      '<(DEPTH)/starboard/shared/stub/speech_recognizer_destroy.cc',
+      '<(DEPTH)/starboard/shared/stub/speech_recognizer_is_supported.cc',
+      '<(DEPTH)/starboard/shared/stub/speech_recognizer_start.cc',
+      '<(DEPTH)/starboard/shared/stub/speech_recognizer_stop.cc',
+      '<(DEPTH)/starboard/shared/stub/speech_synthesis_cancel.cc',
+      '<(DEPTH)/starboard/shared/stub/speech_synthesis_is_supported.cc',
+      '<(DEPTH)/starboard/shared/stub/speech_synthesis_speak.cc',
+    ],
+  },
+}
diff --git a/src/starboard/linux/x64x11/sbversion/6/__init__.py b/src/starboard/evergreen/x64/__init__.py
similarity index 100%
copy from src/starboard/linux/x64x11/sbversion/6/__init__.py
copy to src/starboard/evergreen/x64/__init__.py
diff --git a/src/starboard/raspi/2/mozjs/thread_types_public.h b/src/starboard/evergreen/x64/atomic_public.h
similarity index 65%
copy from src/starboard/raspi/2/mozjs/thread_types_public.h
copy to src/starboard/evergreen/x64/atomic_public.h
index 96c08d1..c3fc6c7 100644
--- a/src/starboard/raspi/2/mozjs/thread_types_public.h
+++ b/src/starboard/evergreen/x64/atomic_public.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Cobalt Authors. All Rights Reserved.
+// 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.
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_RASPI_2_MOZJS_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_RASPI_2_MOZJS_THREAD_TYPES_PUBLIC_H_
+#ifndef STARBOARD_EVERGREEN_X64_ATOMIC_PUBLIC_H_
+#define STARBOARD_EVERGREEN_X64_ATOMIC_PUBLIC_H_
 
-#include "starboard/raspi/2/thread_types_public.h"
+#include "starboard/atomic.h"
+#include "starboard/shared/gcc/atomic_gcc_public.h"
 
-#endif  // STARBOARD_RASPI_2_MOZJS_THREAD_TYPES_PUBLIC_H_
+#endif  // STARBOARD_EVERGREEN_X64_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/sbversion/6/__init__.py b/src/starboard/evergreen/x64/cobalt/__init__.py
similarity index 100%
copy from src/starboard/linux/x64x11/sbversion/6/__init__.py
copy to src/starboard/evergreen/x64/cobalt/__init__.py
diff --git a/src/starboard/linux/x64x11/mozjs/starboard_platform_tests.gyp b/src/starboard/evergreen/x64/cobalt/configuration.gypi
similarity index 75%
copy from src/starboard/linux/x64x11/mozjs/starboard_platform_tests.gyp
copy to src/starboard/evergreen/x64/cobalt/configuration.gypi
index 0ea8622..5fcbf9f 100644
--- a/src/starboard/linux/x64x11/mozjs/starboard_platform_tests.gyp
+++ b/src/starboard/evergreen/x64/cobalt/configuration.gypi
@@ -1,4 +1,4 @@
-# Copyright 2018 The Cobalt Authors. All Rights Reserved.
+# 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.
@@ -11,8 +11,11 @@
 # 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.
+
+# Cobalt Evergreen x86_64 specific configuration.
+
 {
-  'includes': [
-    '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
-  ],
+  'variables': {
+    'in_app_dial%': 1,
+  }, # end of variables
 }
diff --git a/src/starboard/evergreen/x64/cobalt/configuration.py b/src/starboard/evergreen/x64/cobalt/configuration.py
new file mode 100644
index 0000000..9d0947d
--- /dev/null
+++ b/src/starboard/evergreen/x64/cobalt/configuration.py
@@ -0,0 +1,70 @@
+# 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.
+"""Starboard Cobalt Evergreen x64 configuration."""
+
+import os
+
+from cobalt.build import cobalt_configuration
+from starboard.tools.testing import test_filter
+
+
+class CobaltX64Configuration(cobalt_configuration.CobaltConfiguration):
+  """Starboard Cobalt Evergreen x64 configuration."""
+
+  def __init__(self, platform_configuration, application_name,
+               application_directory):
+    super(CobaltX64Configuration,
+          self).__init__(platform_configuration, application_name,
+                         application_directory)
+
+  def GetPostIncludes(self):
+    # If there isn't a configuration.gypi found in the usual place, we'll
+    # supplement with our shared implementation.
+    includes = super(CobaltX64Configuration, self).GetPostIncludes()
+    for include in includes:
+      if os.path.basename(include) == 'configuration.gypi':
+        return includes
+
+    shared_gypi_path = os.path.join(
+        os.path.dirname(__file__), 'configuration.gypi')
+    if os.path.isfile(shared_gypi_path):
+      includes.append(shared_gypi_path)
+    return includes
+
+  def WebdriverBenchmarksEnabled(self):
+    return True
+
+  def GetTestFilters(self):
+    filters = super(CobaltX64Configuration, self).GetTestFilters()
+    for target, tests in self.__FILTERED_TESTS.iteritems():
+      filters.extend(test_filter.TestFilter(target, test) for test in tests)
+    return filters
+
+  def GetTestEnvVariables(self):
+    return {
+        'base_unittests': {
+            'ASAN_OPTIONS': 'detect_leaks=0'
+        },
+        'crypto_unittests': {
+            'ASAN_OPTIONS': 'detect_leaks=0'
+        },
+        'net_unittests': {
+            'ASAN_OPTIONS': 'detect_leaks=0'
+        }
+    }
+
+  __FILTERED_TESTS = {
+      'base_unittests': [test_filter.FILTER_ALL],
+      'net_unittests': [test_filter.FILTER_ALL],
+  }
diff --git a/src/starboard/evergreen/x64/configuration_public.h b/src/starboard/evergreen/x64/configuration_public.h
new file mode 100644
index 0000000..fe9c06d
--- /dev/null
+++ b/src/starboard/evergreen/x64/configuration_public.h
@@ -0,0 +1,210 @@
+// 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.
+
+// The Starboard configuration for x64. Other devices will have
+// specific Starboard implementations.
+
+// Other source files should never include this header directly, but should
+// include the generic "starboard/configuration.h" instead.
+
+#ifndef STARBOARD_EVERGREEN_X64_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_EVERGREEN_X64_CONFIGURATION_PUBLIC_H_
+
+// Configuration parameters that allow the application to make some general
+// compile-time decisions with respect to the the number of cores likely to be
+// available on this platform. For a definitive measure, the application should
+// still call SbSystemGetNumberOfProcessors at runtime.
+
+// Whether the current platform's thread scheduler will automatically balance
+// threads between cores, as opposed to systems where threads will only ever run
+// on the specifically pinned core.
+#define SB_HAS_CROSS_CORE_SCHEDULER 1
+
+// --- System Header Configuration -------------------------------------------
+
+// Any system headers listed here that are not provided by the platform will be
+// emulated in starboard/types.h.
+
+// Whether the current platform provides the standard header stdarg.h.
+#define SB_HAS_STDARG_H 1
+
+// Whether the current platform provides the standard header stdbool.h.
+#define SB_HAS_STDBOOL_H 1
+
+// Whether the current platform provides the standard header stddef.h.
+#define SB_HAS_STDDEF_H 1
+
+// Whether the current platform provides the standard header stdint.h.
+#define SB_HAS_STDINT_H 1
+
+// Whether the current platform provides the standard header inttypes.h.
+#define SB_HAS_INTTYPES_H 1
+
+// Whether the current platform provides the standard header sys/types.h.
+#define SB_HAS_SYS_TYPES_H 0
+
+// Whether the current platform provides the standard header limits.h.
+#define SB_HAS_LIMITS_H 1
+
+// Whether the current platform provides the standard header float.h.
+#define SB_HAS_FLOAT_H 1
+
+// Whether the current platform provides ssize_t.
+#define SB_HAS_SSIZE_T 0
+
+// Type detection for wchar_t.
+#if defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
+#define SB_IS_WCHAR_T_UTF32 1
+#elif defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff)
+#define SB_IS_WCHAR_T_UTF16 1
+#endif
+
+// Chrome only defines these two if ARMEL or MIPSEL are defined.
+#if defined(__ARMEL__)
+// Chrome has an exclusion for iOS here, we should too when we support iOS.
+#define SB_IS_WCHAR_T_UNSIGNED 1
+#elif defined(__MIPSEL__)
+#define SB_IS_WCHAR_T_SIGNED 1
+#endif
+
+// This quirk is used to fix an issue caused by the rewriting of memset to
+// SbMemorySet in third_party/protobuf/src/google/protobuf/stubs/port.h.
+#define SB_HAS_QUIRK_MEMSET_IN_SYSTEM_HEADERS 1
+
+// --- Compiler Configuration ------------------------------------------------
+
+// The platform's annotation for forcing a C function to be inlined.
+#define SB_C_FORCE_INLINE __inline__ __attribute__((always_inline))
+
+// The platform's annotation for marking a C function as suggested to be
+// inlined.
+#define SB_C_INLINE inline
+
+// The platform's annotation for marking a C function as forcibly not
+// inlined.
+#define SB_C_NOINLINE __attribute__((noinline))
+
+// The platform's annotation for marking a symbol as exported outside of the
+// current shared library.
+#define SB_EXPORT_PLATFORM __attribute__((visibility("default")))
+
+// The platform's annotation for marking a symbol as imported from outside of
+// the current linking unit.
+#define SB_IMPORT_PLATFORM
+
+// --- Extensions Configuration ----------------------------------------------
+
+// Do not use <unordered_map> and <unordered_set> for the hash table types.
+#define SB_HAS_STD_UNORDERED_HASH 0
+
+// GCC/Clang doesn't define a long long hash function, except for Android and
+// Game consoles.
+#define SB_HAS_LONG_LONG_HASH 0
+
+// GCC/Clang doesn't define a string hash function, except for Game Consoles.
+#define SB_HAS_STRING_HASH 0
+
+// Desktop Linux needs a using statement for the hash functions.
+#define SB_HAS_HASH_USING 0
+
+// Set this to 1 if hash functions for custom types can be defined as a
+// hash_value() function. Otherwise, they need to be placed inside a
+// partially-specified hash struct template with an operator().
+#define SB_HAS_HASH_VALUE 0
+
+// Set this to 1 if use of hash_map or hash_set causes a deprecation warning
+// (which then breaks the build).
+#define SB_HAS_HASH_WARNING 1
+
+// The location to include hash_map on this platform.
+#define SB_HASH_MAP_INCLUDE <ext/hash_map>
+
+// C++'s hash_map and hash_set are often found in different namespaces depending
+// on the compiler.
+#define SB_HASH_NAMESPACE __gnu_cxx
+
+// The location to include hash_set on this platform.
+#define SB_HASH_SET_INCLUDE <ext/hash_set>
+
+// Define this to how this platform copies varargs blocks.
+#define SB_VA_COPY(dest, source) va_copy(dest, source)
+
+// --- Graphics Configuration ------------------------------------------------
+
+// Specifies whether this platform supports a performant accelerated blitter
+// API. The basic requirement is a scaled, clipped, alpha-blended blit.
+#define SB_HAS_BLITTER 0
+
+// Indicates whether or not the given platform supports bilinear filtering.
+// This can be checked to enable/disable renderer tests that verify that this is
+// working properly.
+#define SB_HAS_BILINEAR_FILTERING_SUPPORT 1
+
+// Indicates whether or not the given platform supports rendering of NV12
+// textures. These textures typically originate from video decoders.
+#define SB_HAS_NV12_TEXTURE_SUPPORT 0
+
+// --- I/O Configuration -----------------------------------------------------
+
+// Whether the current platform implements the on screen keyboard interface.
+#define SB_HAS_ON_SCREEN_KEYBOARD 0
+
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 0
+
+// Whether the current platform has speech synthesis.
+#define SB_HAS_SPEECH_SYNTHESIS 0
+
+// --- Decoder-only Params ---
+
+// --- Memory Configuration --------------------------------------------------
+
+// Whether this platform has and should use an MMAP function to map physical
+// memory to the virtual address space.
+#if SB_API_VERSION < SB_MMAP_REQUIRED_VERSION
+#define SB_HAS_MMAP 1
+#endif
+
+// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
+// required for platforms that want to JIT.
+#define SB_CAN_MAP_EXECUTABLE_MEMORY 1
+// --- Network Configuration -------------------------------------------------
+
+// Specifies whether this platform supports IPV6.
+#define SB_HAS_IPV6 1
+
+// Specifies whether this platform supports pipe.
+#define SB_HAS_PIPE 1
+
+// --- Thread Configuration --------------------------------------------------
+
+// --- Timing API ------------------------------------------------------------
+
+// Whether this platform has an API to retrieve how long the current thread
+// has spent in the executing state.
+#define SB_HAS_TIME_THREAD_NOW 1
+
+// --- Tuneable Parameters ---------------------------------------------------
+
+// --- User Configuration ----------------------------------------------------
+
+// --- Platform Specific Audits ----------------------------------------------
+
+#if !defined(__GNUC__)
+#error "Evergreen-x64 builds need a GCC-like compiler (for the moment)."
+#endif
+
+#endif  // STARBOARD_EVERGREEN_X64_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/evergreen/x64/gyp_configuration.gypi b/src/starboard/evergreen/x64/gyp_configuration.gypi
new file mode 100644
index 0000000..dcb689e
--- /dev/null
+++ b/src/starboard/evergreen/x64/gyp_configuration.gypi
@@ -0,0 +1,58 @@
+# 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.
+
+# This file was initially generated by starboard/tools/create_derived_build.py,
+# though it may have been modified since its creation.
+
+{
+  'variables': {
+    'yasm_exists': 1,
+    'sb_target_platform': 'evergreen-x64',
+
+    'compiler_flags': [
+      '-isystem<(cobalt_repo_root)/third_party/musl/arch/x86_64',
+    ],
+  },
+
+  'target_defaults': {
+    'defines' : [
+      # Ensure that the Starboardized __external_threading file is included.
+      '_LIBCPP_HAS_THREAD_API_EXTERNAL',
+      # Ensure that only the forward declarations and type definitions are included
+      # in __external_threading.
+      '_LIBCPP_HAS_THREAD_LIBRARY_EXTERNAL',
+      # Enable GNU extensions to get prototypes like ffsl.
+      '_GNU_SOURCE=1',
+    ],
+    'default_configuration': 'evergreen-x64_debug',
+    'configurations': {
+      'evergreen-x64_debug': {
+        'inherit_from': ['debug_base'],
+      },
+      'evergreen-x64_devel': {
+        'inherit_from': ['devel_base'],
+      },
+      'evergreen-x64_qa': {
+        'inherit_from': ['qa_base'],
+      },
+      'evergreen-x64_gold': {
+        'inherit_from': ['gold_base'],
+      },
+    }, # end of configurations
+  },
+  'includes': [
+    '<(DEPTH)/starboard/evergreen/shared/gyp_configuration.gypi',
+    '<(DEPTH)/starboard/evergreen/shared/compiler_flags.gypi',
+  ],
+}
diff --git a/src/starboard/evergreen/x64/gyp_configuration.py b/src/starboard/evergreen/x64/gyp_configuration.py
new file mode 100644
index 0000000..9c572b0
--- /dev/null
+++ b/src/starboard/evergreen/x64/gyp_configuration.py
@@ -0,0 +1,110 @@
+# 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.
+"""Starboard evergreen-x64 platform configuration for gyp_cobalt."""
+
+import os.path
+
+from starboard.build import clang as clang_build
+from starboard.evergreen.shared import gyp_configuration as shared_configuration
+from starboard.tools import build
+from starboard.tools import paths
+from starboard.tools.toolchain import ar
+from starboard.tools.toolchain import bash
+from starboard.tools.toolchain import clang
+from starboard.tools.toolchain import clangxx
+from starboard.tools.toolchain import cp
+from starboard.tools.toolchain import evergreen_linker
+from starboard.tools.toolchain import touch
+
+
+class EvergreenX64Configuration(shared_configuration.EvergreenConfiguration):
+  """Starboard Evergreen x64 platform configuration."""
+
+  def __init__(self,
+               platform_name='evergreen-x64',
+               asan_enabled_by_default=False,
+               goma_supports_compiler=True,
+               sabi_json_path=None):
+    # pylint: disable=useless-super-delegation
+    super(EvergreenX64Configuration,
+          self).__init__(platform_name, asan_enabled_by_default,
+                         goma_supports_compiler, sabi_json_path)
+    self.AppendApplicationConfigurationPath(os.path.dirname(__file__))
+    self._host_toolchain = None
+
+  def GetTargetToolchain(self):
+    return self.GetHostToolchain()
+
+  def GetHostToolchain(self):
+    if not self._host_toolchain:
+      if not hasattr(self, 'host_compiler_environment'):
+        self.host_compiler_environment = build.GetHostCompilerEnvironment(
+            clang_build.GetClangSpecification(), False)
+      cc_path = self.host_compiler_environment['CC_host']
+      cxx_path = self.host_compiler_environment['CXX_host']
+
+      # Takes the provided value of CXX_HOST with a prepended 'gomacc' and an
+      # appended 'bin/clang++' and strips them off, leaving us with an absolute
+      # path to the root directory of our toolchain.
+      begin_path_index = cxx_path.find('/')
+      end_path_index = cxx_path.rfind('/', 0, cxx_path.rfind('/')) + 1
+
+      cxx_path_root = cxx_path[begin_path_index:end_path_index]
+
+      self._host_toolchain = [
+          clang.CCompiler(path=cc_path),
+          clang.CxxCompiler(path=cxx_path),
+          clang.AssemblerWithCPreprocessor(path=cc_path),
+          ar.StaticThinLinker(),
+          ar.StaticLinker(),
+          clangxx.ExecutableLinker(path=cxx_path),
+          evergreen_linker.SharedLibraryLinker(
+              path=cxx_path_root, extra_flags=['-m elf_x86_64']),
+          cp.Copy(),
+          touch.Stamp(),
+          bash.Shell(),
+      ]
+    return self._host_toolchain
+
+  def GetTestFilters(self):
+    filters = super(EvergreenX64Configuration, self).GetTestFilters()
+    # Remove the exclusion filter on SbDrmTest.AnySupportedKeySystems.
+    # Generally, children of linux/shared do not support widevine, but children
+    # of linux/x64x11 do, if the content decryption module is present.
+
+    has_cdm = os.path.isfile(
+        os.path.join(paths.REPOSITORY_ROOT, 'third_party', 'cdm', 'cdm',
+                     'include', 'content_decryption_module.h'))
+
+    if not has_cdm:
+      return filters
+
+    for test_filter in filters:
+      if (test_filter.target_name == 'nplb' and
+          test_filter.test_name == 'SbDrmTest.AnySupportedKeySystems'):
+        filters.remove(test_filter)
+    return filters
+
+  def GetVariables(self, configuration):
+    variables = super(EvergreenX64Configuration, self).GetVariables(configuration)
+    variables.update({
+        'include_path_platform_deploy_gypi':
+            'starboard/evergreen/x64/platform_deploy.gypi',
+    })
+    return variables
+
+
+def CreatePlatformConfig():
+  return EvergreenX64Configuration(
+      sabi_json_path='starboard/sabi/x64/sysv/sabi.json')
diff --git a/src/starboard/evergreen/x64/platform_deploy.gypi b/src/starboard/evergreen/x64/platform_deploy.gypi
new file mode 100644
index 0000000..f2524b5
--- /dev/null
+++ b/src/starboard/evergreen/x64/platform_deploy.gypi
@@ -0,0 +1,37 @@
+# Copyright 2020 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.
+
+{
+  'variables': {
+    'deploy_executable_file': '<(target_deploy_dir)/lib/lib<(executable_name).so',
+    'executable_file': '<(PRODUCT_DIR)/lib/lib<(executable_name).so',
+  },
+  'includes': [ '<(DEPTH)/starboard/build/collect_deploy_content.gypi' ],
+  'actions': [
+    {
+      'action_name': 'deploy_executable',
+      'message': 'Strip executable: <(deploy_executable_file)',
+      'inputs': [
+        '<(executable_file)',
+        '<(content_deploy_stamp_file)',
+      ],
+      'outputs': [ '<(deploy_executable_file)' ],
+      'action': [
+        'strip',
+        '-o', '<(deploy_executable_file)',
+        '<(executable_file)',
+      ],
+    },
+  ],
+}
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform.gyp b/src/starboard/evergreen/x64/starboard_platform.gyp
similarity index 78%
copy from src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform.gyp
copy to src/starboard/evergreen/x64/starboard_platform.gyp
index 8163408..8fa33ef 100644
--- a/src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform.gyp
+++ b/src/starboard/evergreen/x64/starboard_platform.gyp
@@ -12,13 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# This file was initially generated by starboard/tools/create_derived_build.py,
-# though it may have been modified since its creation.
-
 {
   'includes': [
     # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file.  The idea
     # is that we just want this file to *be* the parent gyp file.
-    '<(DEPTH)/starboard/linux/x64x11/blittergles/starboard_platform.gyp',
+    '<(DEPTH)/starboard/evergreen/shared/starboard_platform.gyp',
   ],
 }
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/6/atomic_public.h b/src/starboard/evergreen/x64/thread_types_public.h
similarity index 72%
copy from src/starboard/linux/x64x11/blittergles/sbversion/6/atomic_public.h
copy to src/starboard/evergreen/x64/thread_types_public.h
index d2414dd..e420a58 100644
--- a/src/starboard/linux/x64x11/blittergles/sbversion/6/atomic_public.h
+++ b/src/starboard/evergreen/x64/thread_types_public.h
@@ -15,9 +15,9 @@
 // This file was initially generated by starboard/tools/create_derived_build.py,
 // though it may have been modified since its creation.
 
-#ifndef STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_ATOMIC_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_ATOMIC_PUBLIC_H_
+#ifndef STARBOARD_EVERGREEN_X64_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_EVERGREEN_X64_THREAD_TYPES_PUBLIC_H_
 
-#include "starboard/linux/x64x11/blittergles/atomic_public.h"
+#include "starboard/shared/pthread/types_public.h"
 
-#endif  // STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_ATOMIC_PUBLIC_H_
+#endif  // STARBOARD_EVERGREEN_X64_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/sbversion/6/__init__.py b/src/starboard/evergreen/x86/__init__.py
similarity index 100%
rename from src/starboard/linux/x64x11/sbversion/6/__init__.py
rename to src/starboard/evergreen/x86/__init__.py
diff --git a/src/starboard/raspi/2/mozjs/thread_types_public.h b/src/starboard/evergreen/x86/atomic_public.h
similarity index 65%
copy from src/starboard/raspi/2/mozjs/thread_types_public.h
copy to src/starboard/evergreen/x86/atomic_public.h
index 96c08d1..9b310a7 100644
--- a/src/starboard/raspi/2/mozjs/thread_types_public.h
+++ b/src/starboard/evergreen/x86/atomic_public.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Cobalt Authors. All Rights Reserved.
+// 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.
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_RASPI_2_MOZJS_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_RASPI_2_MOZJS_THREAD_TYPES_PUBLIC_H_
+#ifndef STARBOARD_EVERGREEN_X86_ATOMIC_PUBLIC_H_
+#define STARBOARD_EVERGREEN_X86_ATOMIC_PUBLIC_H_
 
-#include "starboard/raspi/2/thread_types_public.h"
+#include "starboard/atomic.h"
+#include "starboard/shared/gcc/atomic_gcc_public.h"
 
-#endif  // STARBOARD_RASPI_2_MOZJS_THREAD_TYPES_PUBLIC_H_
+#endif  // STARBOARD_EVERGREEN_X86_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/evergreen/x86/configuration_public.h b/src/starboard/evergreen/x86/configuration_public.h
new file mode 100644
index 0000000..56b5909
--- /dev/null
+++ b/src/starboard/evergreen/x86/configuration_public.h
@@ -0,0 +1,181 @@
+// 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.
+
+// The Starboard configuration for x86. Other devices will have
+// specific Starboard implementations.
+
+// Other source files should never include this header directly, but should
+// include the generic "starboard/configuration.h" instead.
+
+#ifndef STARBOARD_EVERGREEN_X86_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_EVERGREEN_X86_CONFIGURATION_PUBLIC_H_
+
+// Configuration parameters that allow the application to make some general
+// compile-time decisions with respect to the the number of cores likely to be
+// available on this platform. For a definitive measure, the application should
+// still call SbSystemGetNumberOfProcessors at runtime.
+
+// Whether the current platform's thread scheduler will automatically balance
+// threads between cores, as opposed to systems where threads will only ever run
+// on the specifically pinned core.
+#define SB_HAS_CROSS_CORE_SCHEDULER 1
+
+// --- System Header Configuration -------------------------------------------
+
+// Any system headers listed here that are not provided by the platform will be
+// emulated in starboard/types.h.
+
+// Whether the current platform provides the standard header stdarg.h.
+#define SB_HAS_STDARG_H 1
+
+// Whether the current platform provides the standard header stdbool.h.
+#define SB_HAS_STDBOOL_H 1
+
+// Whether the current platform provides the standard header stddef.h.
+#define SB_HAS_STDDEF_H 1
+
+// Whether the current platform provides the standard header stdint.h.
+#define SB_HAS_STDINT_H 1
+
+// Whether the current platform provides the standard header inttypes.h.
+#define SB_HAS_INTTYPES_H 1
+
+// Whether the current platform provides the standard header sys/types.h.
+#define SB_HAS_SYS_TYPES_H 0
+
+// Whether the current platform provides the standard header limits.h.
+#define SB_HAS_LIMITS_H 1
+
+// Whether the current platform provides the standard header float.h.
+#define SB_HAS_FLOAT_H 1
+
+// Whether the current platform provides ssize_t.
+#define SB_HAS_SSIZE_T 0
+
+// Type detection for wchar_t.
+#if defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
+#define SB_IS_WCHAR_T_UTF32 1
+#elif defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff)
+#define SB_IS_WCHAR_T_UTF16 1
+#endif
+
+// Chrome only defines these two if ARMEL or MIPSEL are defined.
+#if defined(__ARMEL__)
+// Chrome has an exclusion for iOS here, we should too when we support iOS.
+#define SB_IS_WCHAR_T_UNSIGNED 1
+#elif defined(__MIPSEL__)
+#define SB_IS_WCHAR_T_SIGNED 1
+#endif
+
+// --- Compiler Configuration ------------------------------------------------
+
+// The platform's annotation for forcing a C function to be inlined.
+#define SB_C_FORCE_INLINE __inline__ __attribute__((always_inline))
+
+// The platform's annotation for marking a C function as suggested to be
+// inlined.
+#define SB_C_INLINE inline
+
+// The platform's annotation for marking a C function as forcibly not
+// inlined.
+#define SB_C_NOINLINE __attribute__((noinline))
+
+// The platform's annotation for marking a symbol as exported outside of the
+// current shared library.
+#define SB_EXPORT_PLATFORM __attribute__((visibility("default")))
+
+// The platform's annotation for marking a symbol as imported from outside of
+// the current linking unit.
+#define SB_IMPORT_PLATFORM
+
+// --- Extensions Configuration ----------------------------------------------
+
+// Do not use <unordered_map> and <unordered_set> for the hash table types.
+#define SB_HAS_STD_UNORDERED_HASH 0
+
+// GCC/Clang doesn't define a long long hash function, except for Android and
+// Game consoles.
+#define SB_HAS_LONG_LONG_HASH 0
+
+// GCC/Clang doesn't define a string hash function, except for Game Consoles.
+#define SB_HAS_STRING_HASH 0
+
+// Desktop Linux needs a using statement for the hash functions.
+#define SB_HAS_HASH_USING 0
+
+// Set this to 1 if hash functions for custom types can be defined as a
+// hash_value() function. Otherwise, they need to be placed inside a
+// partially-specified hash struct template with an operator().
+#define SB_HAS_HASH_VALUE 0
+
+// Set this to 1 if use of hash_map or hash_set causes a deprecation warning
+// (which then breaks the build).
+#define SB_HAS_HASH_WARNING 1
+
+// The location to include hash_map on this platform.
+#define SB_HASH_MAP_INCLUDE <ext/hash_map>
+
+// C++'s hash_map and hash_set are often found in different namespaces depending
+// on the compiler.
+#define SB_HASH_NAMESPACE __gnu_cxx
+
+// The location to include hash_set on this platform.
+#define SB_HASH_SET_INCLUDE <ext/hash_set>
+
+// Define this to how this platform copies varargs blocks.
+#define SB_VA_COPY(dest, source) va_copy(dest, source)
+
+// --- Graphics Configuration ------------------------------------------------
+
+// Specifies whether this platform supports a performant accelerated blitter
+// API. The basic requirement is a scaled, clipped, alpha-blended blit.
+#define SB_HAS_BLITTER 0
+
+// Indicates whether or not the given platform supports bilinear filtering.
+// This can be checked to enable/disable renderer tests that verify that this is
+// working properly.
+#define SB_HAS_BILINEAR_FILTERING_SUPPORT 1
+
+// Indicates whether or not the given platform supports rendering of NV12
+// textures. These textures typically originate from video decoders.
+#define SB_HAS_NV12_TEXTURE_SUPPORT 1
+
+// --- Decoder-only Params ---
+
+// --- Memory Configuration --------------------------------------------------
+
+// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
+// required for platforms that want to JIT.
+#define SB_CAN_MAP_EXECUTABLE_MEMORY 1
+
+// --- Network Configuration -------------------------------------------------
+
+// Specifies whether this platform supports pipe.
+#define SB_HAS_PIPE 1
+
+// --- Thread Configuration --------------------------------------------------
+
+// --- Tuneable Parameters ---------------------------------------------------
+
+// --- User Configuration ----------------------------------------------------
+
+// --- Platform Specific Audits ----------------------------------------------
+
+#if !defined(__GNUC__)
+#error "evergreen-x86 builds need a GCC-like compiler (for the moment)."
+#endif
+
+#endif  // STARBOARD_EVERGREEN_X86_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/evergreen/x86/gyp_configuration.gypi b/src/starboard/evergreen/x86/gyp_configuration.gypi
new file mode 100644
index 0000000..f8a868f
--- /dev/null
+++ b/src/starboard/evergreen/x86/gyp_configuration.gypi
@@ -0,0 +1,58 @@
+# 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.
+
+# This file was initially generated by starboard/tools/create_derived_build.py,
+# though it may have been modified since its creation.
+
+{
+  'variables': {
+    'yasm_exists': 1,
+    'sb_target_platform': 'evergreen-x86',
+
+    'compiler_flags': [
+      '-isystem<(cobalt_repo_root)/third_party/musl/arch/i386',
+    ],
+  },
+
+  'target_defaults': {
+    'defines' : [
+      # Ensure that the Starboardized __external_threading file is included.
+      '_LIBCPP_HAS_THREAD_API_EXTERNAL',
+      # Ensure that only the forward declarations and type definitions are included
+      # in __external_threading.
+      '_LIBCPP_HAS_THREAD_LIBRARY_EXTERNAL',
+      # Enable GNU extensions to get prototypes like ffsl.
+      '_GNU_SOURCE=1',
+    ],
+    'default_configuration': 'evergreen-x86_debug',
+    'configurations': {
+      'evergreen-x86_debug': {
+        'inherit_from': ['debug_base'],
+      },
+      'evergreen-x86_devel': {
+        'inherit_from': ['devel_base'],
+      },
+      'evergreen-x86_qa': {
+        'inherit_from': ['qa_base'],
+      },
+      'evergreen-x86_gold': {
+        'inherit_from': ['gold_base'],
+      },
+    }, # end of configurations
+  },
+  'includes': [
+    '<(DEPTH)/starboard/evergreen/shared/gyp_configuration.gypi',
+    '<(DEPTH)/starboard/evergreen/shared/compiler_flags.gypi',
+  ],
+}
diff --git a/src/starboard/evergreen/x86/gyp_configuration.py b/src/starboard/evergreen/x86/gyp_configuration.py
new file mode 100644
index 0000000..a67484c
--- /dev/null
+++ b/src/starboard/evergreen/x86/gyp_configuration.py
@@ -0,0 +1,101 @@
+# 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.
+"""Starboard evergreen-x86 platform configuration for gyp_cobalt."""
+
+import os.path
+
+from starboard.build import clang as clang_build
+from starboard.evergreen.shared import gyp_configuration as shared_configuration
+from starboard.tools import build
+from starboard.tools import paths
+from starboard.tools.toolchain import ar
+from starboard.tools.toolchain import bash
+from starboard.tools.toolchain import clang
+from starboard.tools.toolchain import clangxx
+from starboard.tools.toolchain import cp
+from starboard.tools.toolchain import evergreen_linker
+from starboard.tools.toolchain import touch
+
+
+class EvergreenX86Configuration(shared_configuration.EvergreenConfiguration):
+  """Starboard Evergreen x86 platform configuration."""
+
+  def __init__(self,
+               platform_name='evergreen-x86',
+               asan_enabled_by_default=False,
+               goma_supports_compiler=True,
+               sabi_json_path=None):
+    # pylint: disable=useless-super-delegation
+    super(EvergreenX86Configuration,
+          self).__init__(platform_name, asan_enabled_by_default,
+                         goma_supports_compiler, sabi_json_path)
+    self._host_toolchain = None
+
+  def GetTargetToolchain(self):
+    return self.GetHostToolchain()
+
+  def GetHostToolchain(self):
+    if not self._host_toolchain:
+      if not hasattr(self, 'host_compiler_environment'):
+        self.host_compiler_environment = build.GetHostCompilerEnvironment(
+            clang_build.GetClangSpecification(), False)
+      cc_path = self.host_compiler_environment['CC_host']
+      cxx_path = self.host_compiler_environment['CXX_host']
+
+      # Takes the provided value of CXX_HOST with a prepended 'gomacc' and an
+      # appended 'bin/clang++' and strips them off, leaving us with an absolute
+      # path to the root directory of our toolchain.
+      begin_path_index = cxx_path.find('/')
+      end_path_index = cxx_path.rfind('/', 0, cxx_path.rfind('/')) + 1
+
+      cxx_path_root = cxx_path[begin_path_index:end_path_index]
+
+      self._host_toolchain = [
+          clang.CCompiler(path=cc_path),
+          clang.CxxCompiler(path=cxx_path),
+          clang.AssemblerWithCPreprocessor(path=cc_path),
+          ar.StaticThinLinker(),
+          ar.StaticLinker(),
+          clangxx.ExecutableLinker(path=cxx_path),
+          evergreen_linker.SharedLibraryLinker(
+              path=cxx_path_root, extra_flags=['-m elf_i686']),
+          cp.Copy(),
+          touch.Stamp(),
+          bash.Shell(),
+      ]
+    return self._host_toolchain
+
+  def GetTestFilters(self):
+    filters = super(EvergreenX86Configuration, self).GetTestFilters()
+    # Remove the exclusion filter on SbDrmTest.AnySupportedKeySystems.
+    # Generally, children of linux/shared do not support widevine, but children
+    # of linux/x64x11 do, if the content decryption module is present.
+
+    has_cdm = os.path.isfile(
+        os.path.join(paths.REPOSITORY_ROOT, 'third_party', 'cdm', 'cdm',
+                     'include', 'content_decryption_module.h'))
+
+    if not has_cdm:
+      return filters
+
+    for test_filter in filters:
+      if (test_filter.target_name == 'nplb' and
+          test_filter.test_name == 'SbDrmTest.AnySupportedKeySystems'):
+        filters.remove(test_filter)
+    return filters
+
+
+def CreatePlatformConfig():
+  return EvergreenX86Configuration(
+      sabi_json_path='starboard/sabi/x86/sabi.json')
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform.gyp b/src/starboard/evergreen/x86/starboard_platform.gyp
similarity index 78%
copy from src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform.gyp
copy to src/starboard/evergreen/x86/starboard_platform.gyp
index 8163408..8fa33ef 100644
--- a/src/starboard/linux/x64x11/blittergles/sbversion/6/starboard_platform.gyp
+++ b/src/starboard/evergreen/x86/starboard_platform.gyp
@@ -12,13 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# This file was initially generated by starboard/tools/create_derived_build.py,
-# though it may have been modified since its creation.
-
 {
   'includes': [
     # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file.  The idea
     # is that we just want this file to *be* the parent gyp file.
-    '<(DEPTH)/starboard/linux/x64x11/blittergles/starboard_platform.gyp',
+    '<(DEPTH)/starboard/evergreen/shared/starboard_platform.gyp',
   ],
 }
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/6/atomic_public.h b/src/starboard/evergreen/x86/thread_types_public.h
similarity index 72%
copy from src/starboard/linux/x64x11/blittergles/sbversion/6/atomic_public.h
copy to src/starboard/evergreen/x86/thread_types_public.h
index d2414dd..261f06a 100644
--- a/src/starboard/linux/x64x11/blittergles/sbversion/6/atomic_public.h
+++ b/src/starboard/evergreen/x86/thread_types_public.h
@@ -15,9 +15,9 @@
 // This file was initially generated by starboard/tools/create_derived_build.py,
 // though it may have been modified since its creation.
 
-#ifndef STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_ATOMIC_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_ATOMIC_PUBLIC_H_
+#ifndef STARBOARD_EVERGREEN_X86_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_EVERGREEN_X86_THREAD_TYPES_PUBLIC_H_
 
-#include "starboard/linux/x64x11/blittergles/atomic_public.h"
+#include "starboard/shared/pthread/types_public.h"
 
-#endif  // STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_ATOMIC_PUBLIC_H_
+#endif  // STARBOARD_EVERGREEN_X86_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/examples/glclear/main.cc b/src/starboard/examples/glclear/main.cc
index 2a12c7a..d74d90f 100644
--- a/src/starboard/examples/glclear/main.cc
+++ b/src/starboard/examples/glclear/main.cc
@@ -172,7 +172,7 @@
   SbEglInt32 context_attrib_list[] = {
       SB_EGL_CONTEXT_CLIENT_VERSION, 3, SB_EGL_NONE,
   };
-#if defined(GLES3_SUPPORTED)
+#if SB_API_VERSION < SB_GLES3_DEPRECATED_VERSION && defined(GLES3_SUPPORTED)
   // Attempt to create an OpenGL ES 3.0 context.
   context_ = EGL_CALL_SIMPLE(eglCreateContext(
       display_, config, SB_EGL_NO_CONTEXT, context_attrib_list));
diff --git a/src/starboard/input.h b/src/starboard/input.h
index 3014ce6..3c608bf 100644
--- a/src/starboard/input.h
+++ b/src/starboard/input.h
@@ -150,14 +150,12 @@
 
 // Event data for |kSbEventTypeInput| events.
 typedef struct SbInputData {
-#if SB_API_VERSION >= 10
   // The time that should be reported for this event. This is intended to
   // facilitate calculation of time-sensitive information (e.g. velocity for
   // kSbInputEventTypeMove). This may be set to 0 to have the relevant systems
   // automatically set the timestamp. However, this may happen at a later time,
   // so the relative time between events may not be preserved.
   SbTimeMonotonic timestamp;
-#endif
 
   // The window in which the input was generated.
   SbWindow window;
diff --git a/src/starboard/key.h b/src/starboard/key.h
index e61a74c..8626ffb 100644
--- a/src/starboard/key.h
+++ b/src/starboard/key.h
@@ -241,10 +241,8 @@
   // A button that will switch between different available audio tracks.
   kSbKeyMediaAudioTrack = 0x3001,
 
-#if SB_API_VERSION >= 10
   // A button that will trigger voice input.
   kSbKeyMicrophone = 0x3002,
-#endif  // SB_API_VERSION >= 10
 
   // Mouse buttons, starting with the left mouse button.
   kSbKeyMouse1 = 0x7000,
diff --git a/src/starboard/linux/shared/BUILD.gn b/src/starboard/linux/shared/BUILD.gn
index 7121e20..8e843ac 100644
--- a/src/starboard/linux/shared/BUILD.gn
+++ b/src/starboard/linux/shared/BUILD.gn
@@ -501,8 +501,9 @@
     "//starboard/shared/starboard/player/filter/audio_decoder_internal.h",
     "//starboard/shared/starboard/player/filter/audio_frame_tracker.cc",
     "//starboard/shared/starboard/player/filter/audio_frame_tracker.h",
-    "//starboard/shared/starboard/player/filter/audio_renderer_internal.cc",
     "//starboard/shared/starboard/player/filter/audio_renderer_internal.h",
+    "//starboard/shared/starboard/player/filter/audio_renderer_internal_impl.cc",
+    "//starboard/shared/starboard/player/filter/audio_renderer_internal_impl.h",
     "//starboard/shared/starboard/player/filter/audio_renderer_sink.h",
     "//starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc",
     "//starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h",
@@ -523,8 +524,9 @@
     "//starboard/shared/starboard/player/filter/punchout_video_renderer_sink.h",
     "//starboard/shared/starboard/player/filter/video_decoder_internal.h",
     "//starboard/shared/starboard/player/filter/video_frame_internal.h",
-    "//starboard/shared/starboard/player/filter/video_renderer_internal.cc",
     "//starboard/shared/starboard/player/filter/video_renderer_internal.h",
+    "//starboard/shared/starboard/player/filter/video_renderer_internal_impl.cc",
+    "//starboard/shared/starboard/player/filter/video_renderer_internal_impl.h",
     "//starboard/shared/starboard/player/filter/wsola_internal.cc",
     "//starboard/shared/starboard/player/filter/wsola_internal.h",
     "//starboard/shared/starboard/player/input_buffer_internal.cc",
@@ -536,18 +538,15 @@
     "//starboard/shared/starboard/player/player_create.cc",
     "//starboard/shared/starboard/player/player_destroy.cc",
     "//starboard/shared/starboard/player/player_get_current_frame.cc",
-    "//starboard/shared/starboard/player/player_get_info.cc",
     "//starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc",
     "//starboard/shared/starboard/player/player_internal.cc",
     "//starboard/shared/starboard/player/player_internal.h",
-    "//starboard/shared/starboard/player/player_seek.cc",
     "//starboard/shared/starboard/player/player_set_bounds.cc",
     "//starboard/shared/starboard/player/player_set_playback_rate.cc",
     "//starboard/shared/starboard/player/player_set_volume.cc",
     "//starboard/shared/starboard/player/player_worker.cc",
     "//starboard/shared/starboard/player/player_worker.h",
     "//starboard/shared/starboard/player/player_write_end_of_stream.cc",
-    "//starboard/shared/starboard/player/player_write_sample.cc",
     "//starboard/shared/starboard/queue_application.cc",
     "//starboard/shared/starboard/string_concat.cc",
     "//starboard/shared/starboard/string_concat_wide.cc",
diff --git a/src/starboard/linux/shared/configuration.cc b/src/starboard/linux/shared/configuration.cc
new file mode 100644
index 0000000..bc5a3b1
--- /dev/null
+++ b/src/starboard/linux/shared/configuration.cc
@@ -0,0 +1,68 @@
+// Copyright 2020 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/linux/shared/configuration.h"
+
+#include "cobalt/extension/configuration.h"
+#include "starboard/common/configuration_defaults.h"
+
+// Omit namespace linux due to symbol name conflict.
+namespace starboard {
+namespace shared {
+
+namespace {
+
+int CobaltEglSwapInterval() {
+  return 0;
+}
+
+bool CobaltEnableJit() {
+  return true;
+}
+
+const CobaltExtensionConfigurationApi kConfigurationApi = {
+    kCobaltExtensionConfigurationName,
+    1,
+    &common::CobaltUserOnExitStrategyDefault,
+    &common::CobaltRenderDirtyRegionOnlyDefault,
+    &CobaltEglSwapInterval,
+    &common::CobaltFallbackSplashScreenUrlDefault,
+    &common::CobaltEnableQuicDefault,
+    &common::CobaltSkiaCacheSizeInBytesDefault,
+    &common::CobaltOffscreenTargetCacheSizeInBytesDefault,
+    &common::CobaltEncodedImageCacheSizeInBytesDefault,
+    &common::CobaltImageCacheSizeInBytesDefault,
+    &common::CobaltLocalTypefaceCacheSizeInBytesDefault,
+    &common::CobaltRemoteTypefaceCacheSizeInBytesDefault,
+    &common::CobaltMeshCacheSizeInBytesDefault,
+    &common::CobaltSoftwareSurfaceCacheSizeInBytesDefault,
+    &common::CobaltImageCacheCapacityMultiplierWhenPlayingVideoDefault,
+    &common::CobaltSkiaGlyphAtlasWidthDefault,
+    &common::CobaltSkiaGlyphAtlasHeightDefault,
+    &common::CobaltJsGarbageCollectionThresholdInBytesDefault,
+    &common::CobaltReduceCpuMemoryByDefault,
+    &common::CobaltReduceGpuMemoryByDefault,
+    &common::CobaltGcZealDefault,
+    &common::CobaltRasterizerTypeDefault,
+    &CobaltEnableJit,
+};
+
+}  // namespace
+
+const void* GetConfigurationApi() {
+  return &kConfigurationApi;
+}
+
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/linux/shared/configuration.gni b/src/starboard/linux/shared/configuration.gni
index a997d81..c4562c3 100644
--- a/src/starboard/linux/shared/configuration.gni
+++ b/src/starboard/linux/shared/configuration.gni
@@ -18,7 +18,8 @@
 # The source of EGL and GLES headers and libraries.
 # Valid values (case and everything sensitive!):
 #   "none"   - No EGL + GLES implementation is available on this platform.
-#   "system_gles3" - Use the system implementation of EGL + GLES3. The
+#   "system_gles3" - Deprecated. Use system_gles2 instead.
+#                    Use the system implementation of EGL + GLES3. The
 #                    headers and libraries must be on the system include and
 #                    link paths.
 #   "system_gles2" - Use the system implementation of EGL + GLES2. The
@@ -30,7 +31,7 @@
 #              ANGLE implementation for the platform.
 # Choosing an unsupported value will result in a GYP error:
 #   "cobalt/renderer/egl_and_gles/egl_and_gles_<gl_type>.gyp not found"
-gl_type = "system_gles3"
+gl_type = "system_gles2"
 
 # Use media source extension implementation that is conformed to the
 # Candidate Recommendation of July 5th 2016.
diff --git a/src/starboard/linux/shared/configuration.h b/src/starboard/linux/shared/configuration.h
new file mode 100644
index 0000000..95861de
--- /dev/null
+++ b/src/starboard/linux/shared/configuration.h
@@ -0,0 +1,27 @@
+// Copyright 2020 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 STARBOARD_LINUX_SHARED_CONFIGURATION_H_
+#define STARBOARD_LINUX_SHARED_CONFIGURATION_H_
+
+// Omit namespace linux due to symbol name conflict.
+namespace starboard {
+namespace shared {
+
+const void* GetConfigurationApi();
+
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_LINUX_SHARED_CONFIGURATION_H_
diff --git a/src/starboard/linux/shared/configuration_constants.cc b/src/starboard/linux/shared/configuration_constants.cc
index eec85fc..f95a9f3 100644
--- a/src/starboard/linux/shared/configuration_constants.cc
+++ b/src/starboard/linux/shared/configuration_constants.cc
@@ -57,9 +57,6 @@
 // Specifies whether this platform updates audio frames asynchronously.
 const bool kSbHasAsyncAudioFramesReporting = false;
 
-// Allow playing audioless video.
-const bool kSbHasAudiolessVideo = true;
-
 // Specifies whether this platform has webm/vp9 support.  This should be set to
 // non-zero on platforms with webm/vp9 support.
 const bool kSbHasMediaWebmVp9Support = false;
@@ -142,6 +139,11 @@
 // The string form of SB_PATH_SEP_CHAR.
 const char* kSbPathSepString = ":";
 
+// Specifies the preferred byte order of color channels in a pixel. Refer to
+// starboard/configuration.h for the possible values. EGL/GLES platforms should
+// generally prefer a byte order of RGBA, regardless of endianness.
+const int kSbPreferredRgbaByteOrder = SB_PREFERRED_RGBA_BYTE_ORDER_RGBA;
+
 // The maximum number of users that can be signed in at the same time.
 const uint32_t kSbUserMaxSignedIn = 1;
 
diff --git a/src/starboard/linux/shared/configuration_public.h b/src/starboard/linux/shared/configuration_public.h
index 9c32fa1..f7f15e7 100644
--- a/src/starboard/linux/shared/configuration_public.h
+++ b/src/starboard/linux/shared/configuration_public.h
@@ -195,10 +195,12 @@
 // API. The basic requirement is a scaled, clipped, alpha-blended blit.
 #define SB_HAS_BLITTER 0
 
+#if SB_API_VERSION < SB_FEATURE_RUNTIME_CONFIGS_VERSION
 // Specifies the preferred byte order of color channels in a pixel. Refer to
 // starboard/configuration.h for the possible values. EGL/GLES platforms should
 // generally prefer a byte order of RGBA, regardless of endianness.
 #define SB_PREFERRED_RGBA_BYTE_ORDER SB_PREFERRED_RGBA_BYTE_ORDER_RGBA
+#endif  // SB_API_VERSION < SB_FEATURE_RUNTIME_CONFIGS_VERSION
 
 // Indicates whether or not the given platform supports bilinear filtering.
 // This can be checked to enable/disable renderer tests that verify that this is
@@ -259,11 +261,6 @@
 #endif  // SB_API_VERSION < SB_FEATURE_RUNTIME_CONFIGS_VERSION
 
 #if SB_API_VERSION < SB_FEATURE_RUNTIME_CONFIGS_VERSION
-// Allow playing audioless video.
-#define SB_HAS_AUDIOLESS_VIDEO 1
-#endif  // SB_API_VERSION < SB_FEATURE_RUNTIME_CONFIGS_VERSION
-
-#if SB_API_VERSION < SB_FEATURE_RUNTIME_CONFIGS_VERSION
 // Allow ac3 and ec3 support
 #define SB_HAS_AC3_AUDIO 1
 #endif  // SB_API_VERSION < SB_FEATURE_RUNTIME_CONFIGS_VERSION
diff --git a/src/starboard/linux/shared/drm_create_system.cc b/src/starboard/linux/shared/drm_create_system.cc
index cde9aa9..43d517e 100644
--- a/src/starboard/linux/shared/drm_create_system.cc
+++ b/src/starboard/linux/shared/drm_create_system.cc
@@ -23,13 +23,9 @@
     void* context,
     SbDrmSessionUpdateRequestFunc update_request_callback,
     SbDrmSessionUpdatedFunc session_updated_callback,
-    SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback
-#if SB_API_VERSION >= 10
-    ,
+    SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback,
     SbDrmServerCertificateUpdatedFunc server_certificate_updated_callback,
-    SbDrmSessionClosedFunc session_closed_callback
-#endif  // SB_API_VERSION >= 10
-    ) {
+    SbDrmSessionClosedFunc session_closed_callback) {
   using starboard::shared::widevine::DrmSystemWidevine;
   if (!update_request_callback || !session_updated_callback) {
     return kSbDrmSystemInvalid;
@@ -37,26 +33,15 @@
   if (!key_statuses_changed_callback) {
     return kSbDrmSystemInvalid;
   }
-#if SB_API_VERSION >= 10
   if (!server_certificate_updated_callback || !session_closed_callback) {
     return kSbDrmSystemInvalid;
   }
-#endif  // SB_API_VERSION >= 10
   if (!DrmSystemWidevine::IsKeySystemSupported(key_system)) {
     SB_DLOG(WARNING) << "Invalid key system " << key_system;
     return kSbDrmSystemInvalid;
   }
-  return new DrmSystemWidevine(context, update_request_callback,
-                               session_updated_callback,
-                               key_statuses_changed_callback
-#if SB_API_VERSION >= 10
-                               ,
-                               server_certificate_updated_callback
-#endif  // SB_API_VERSION >= 10
-#if SB_HAS(DRM_SESSION_CLOSED)
-                               ,
-                               session_closed_callback
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
-                               ,
-                               "Linux", "Linux");
+  return new DrmSystemWidevine(
+      context, update_request_callback, session_updated_callback,
+      key_statuses_changed_callback, server_certificate_updated_callback,
+      session_closed_callback, "Linux", "Linux");
 }
diff --git a/src/starboard/linux/shared/gyp_configuration.gypi b/src/starboard/linux/shared/gyp_configuration.gypi
index b565543..cf8dc21 100644
--- a/src/starboard/linux/shared/gyp_configuration.gypi
+++ b/src/starboard/linux/shared/gyp_configuration.gypi
@@ -21,7 +21,6 @@
     # TODO: Remove when omitted for all platforms in base_configuration.gypi.
     'sb_static_contents_output_data_dir': '<(PRODUCT_DIR)/content',
 
-    'target_arch%': 'x64',
     'target_os': 'linux',
     'yasm_exists': 1,
     'sb_widevine_platform' : 'linux',
diff --git a/src/starboard/linux/shared/libraries.gypi b/src/starboard/linux/shared/libraries.gypi
index 505de3d..45fccd0 100644
--- a/src/starboard/linux/shared/libraries.gypi
+++ b/src/starboard/linux/shared/libraries.gypi
@@ -25,11 +25,6 @@
       '-Wl,--wrap=eglSwapBuffers',
     ],
 
-    'cobalt_platform_dependencies': [
-      # GL Linux makes some GL calls within decode_target_internal.cc.
-      '<(DEPTH)/starboard/egl_and_gles/egl_and_gles.gyp:egl_and_gles',
-    ],
-
     'platform_libraries': [
       '-lX11',
       '-lXcomposite',
diff --git a/src/starboard/linux/shared/media_is_audio_supported.cc b/src/starboard/linux/shared/media_is_audio_supported.cc
index bea80a7..5128fb0 100644
--- a/src/starboard/linux/shared/media_is_audio_supported.cc
+++ b/src/starboard/linux/shared/media_is_audio_supported.cc
@@ -14,11 +14,23 @@
 
 #include "starboard/shared/starboard/media/media_support_internal.h"
 
+#include "starboard/common/log.h"
 #include "starboard/configuration.h"
 #include "starboard/configuration_constants.h"
 #include "starboard/media.h"
 
-bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec, int64_t bitrate) {
+bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                             const char* content_type,
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                             int64_t bitrate) {
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+  if (!content_type) {
+    SB_LOG(WARNING) << "|content_type| cannot be nullptr.";
+    return false;
+  }
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+
   if (audio_codec == kSbMediaAudioCodecAac) {
     return bitrate <= kSbMediaMaxAudioBitrateInBitsPerSecond;
   }
diff --git a/src/starboard/linux/shared/media_is_video_supported.cc b/src/starboard/linux/shared/media_is_video_supported.cc
index 488abba..a323a8c 100644
--- a/src/starboard/linux/shared/media_is_video_supported.cc
+++ b/src/starboard/linux/shared/media_is_video_supported.cc
@@ -14,6 +14,7 @@
 
 #include "starboard/shared/starboard/media/media_support_internal.h"
 
+#include "starboard/common/log.h"
 #include "starboard/configuration.h"
 #include "starboard/configuration_constants.h"
 #if SB_API_VERSION >= 11
@@ -31,6 +32,9 @@
 using starboard::shared::vpx::is_vpx_supported;
 
 bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                             const char* content_type,
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
 #if SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
                              int profile,
                              int level,
@@ -42,12 +46,8 @@
                              int frame_width,
                              int frame_height,
                              int64_t bitrate,
-                             int fps
-#if SB_API_VERSION >= 10
-                             ,
-                             bool decode_to_texture_required
-#endif  // SB_API_VERSION >= 10
-                             ) {
+                             int fps,
+                             bool decode_to_texture_required) {
 #if SB_API_VERSION < 11
   const auto kSbMediaVideoCodecAv1 = kSbMediaVideoCodecVp10;
 #endif  // SB_API_VERSION < 11
@@ -56,6 +56,13 @@
   SB_UNREFERENCED_PARAMETER(profile);
   SB_UNREFERENCED_PARAMETER(level);
 
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+  if (!content_type) {
+    SB_LOG(WARNING) << "|content_type| cannot be nullptr.";
+    return false;
+  }
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+
   if (!IsSDRVideo(bit_depth, primary_id, transfer_id, matrix_id)) {
     if (bit_depth != 10 && bit_depth != 12) {
       return false;
@@ -67,7 +74,6 @@
   }
 #endif  // SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
 
-#if SB_API_VERSION >= 10
   if (decode_to_texture_required) {
     bool has_gles_support = false;
 
@@ -83,7 +89,6 @@
     // Assume that all GLES2 Linux platforms can play decode-to-texture video
     // just as well as normal video.
   }
-#endif  // SB_API_VERSION >= 10
 
   return ((video_codec == kSbMediaVideoCodecAv1 && is_aom_supported()) ||
           video_codec == kSbMediaVideoCodecH264 ||
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi
index 028dcb6..fdebe88 100644
--- a/src/starboard/linux/shared/starboard_platform.gypi
+++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -26,6 +26,8 @@
       '<@(filter_based_player_sources)',
       '<(DEPTH)/starboard/linux/shared/atomic_public.h',
       '<(DEPTH)/starboard/linux/shared/audio_sink_type_dispatcher.cc',
+      '<(DEPTH)/starboard/linux/shared/configuration.cc',
+      '<(DEPTH)/starboard/linux/shared/configuration.h',
       '<(DEPTH)/starboard/linux/shared/configuration_public.h',
       '<(DEPTH)/starboard/linux/shared/configuration_constants.cc',
       '<(DEPTH)/starboard/linux/shared/decode_target_get_info.cc',
@@ -37,6 +39,7 @@
       '<(DEPTH)/starboard/linux/shared/player_components_factory.cc',
       '<(DEPTH)/starboard/linux/shared/system_get_connection_type.cc',
       '<(DEPTH)/starboard/linux/shared/system_get_device_type.cc',
+      '<(DEPTH)/starboard/linux/shared/system_get_extensions.cc',
       '<(DEPTH)/starboard/linux/shared/system_get_path.cc',
       '<(DEPTH)/starboard/linux/shared/system_has_capability.cc',
       '<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.cc',
@@ -294,14 +297,12 @@
       '<(DEPTH)/starboard/shared/starboard/player/player_create.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_get_current_frame.cc',
-      '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_get_info2.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_get_maximum_number_of_samples_per_write.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_internal.h',
       '<(DEPTH)/starboard/shared/starboard/player/player_output_mode_supported.cc',
-      '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_seek2.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_set_playback_rate.cc',
@@ -309,7 +310,6 @@
       '<(DEPTH)/starboard/shared/starboard/player/player_worker.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_worker.h',
       '<(DEPTH)/starboard/shared/starboard/player/player_write_end_of_stream.cc',
-      '<(DEPTH)/starboard/shared/starboard/player/player_write_sample.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_write_sample2.cc',
       '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
       '<(DEPTH)/starboard/shared/starboard/string_concat.cc',
@@ -334,7 +334,6 @@
       '<(DEPTH)/starboard/shared/stub/cryptography_set_authenticated_data.cc',
       '<(DEPTH)/starboard/shared/stub/cryptography_set_initialization_vector.cc',
       '<(DEPTH)/starboard/shared/stub/cryptography_transform.cc',
-      '<(DEPTH)/starboard/shared/stub/system_get_extensions.cc',
       '<(DEPTH)/starboard/shared/stub/image_decode.cc',
       '<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc',
       '<(DEPTH)/starboard/shared/stub/media_set_audio_write_duration.cc',
@@ -454,6 +453,7 @@
       }],
       ['sb_evergreen_compatible == 1', {
         'starboard_platform_dependencies': [
+          '<(DEPTH)/starboard/elf_loader/evergreen_config.gyp:evergreen_config',
           '<(DEPTH)/third_party/llvm-project/libunwind/libunwind.gyp:unwind_starboard',
        ]},
       ],
diff --git a/src/starboard/linux/shared/system_get_extensions.cc b/src/starboard/linux/shared/system_get_extensions.cc
new file mode 100644
index 0000000..bb7cb7a
--- /dev/null
+++ b/src/starboard/linux/shared/system_get_extensions.cc
@@ -0,0 +1,40 @@
+// Copyright 2020 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/system.h"
+
+#include "cobalt/extension/configuration.h"
+#include "starboard/common/string.h"
+#if SB_IS(EVERGREEN_COMPATIBLE)
+#include "starboard/elf_loader/evergreen_config.h"
+#endif
+#include "starboard/linux/shared/configuration.h"
+
+const void* SbSystemGetExtension(const char* name) {
+#if SB_IS(EVERGREEN_COMPATIBLE)
+  const starboard::elf_loader::EvergreenConfig* evergreen_config =
+      starboard::elf_loader::EvergreenConfig::GetInstance();
+  if (evergreen_config != NULL &&
+      evergreen_config->custom_get_extension_ != NULL) {
+    const void* ext = evergreen_config->custom_get_extension_(name);
+    if (ext != NULL) {
+      return ext;
+    }
+  }
+#endif
+  if (SbStringCompareAll(name, kCobaltExtensionConfigurationName) == 0) {
+    return starboard::shared::GetConfigurationApi();
+  }
+  return NULL;
+}
diff --git a/src/starboard/linux/shared/system_get_path.cc b/src/starboard/linux/shared/system_get_path.cc
index ee5d301..06abade 100644
--- a/src/starboard/linux/shared/system_get_path.cc
+++ b/src/starboard/linux/shared/system_get_path.cc
@@ -26,6 +26,9 @@
 #include "starboard/common/string.h"
 #include "starboard/configuration_constants.h"
 #include "starboard/directory.h"
+#if SB_IS(EVERGREEN_COMPATIBLE)
+#include "starboard/elf_loader/evergreen_config.h"
+#endif
 #include "starboard/user.h"
 
 namespace {
@@ -87,6 +90,29 @@
   return true;
 }
 
+#if SB_IS(EVERGREEN_COMPATIBLE)
+// May override the content path if there is EvergreenConfig published.
+// The override allows for switching to different content paths based
+// on the Evergreen binary executed.
+// Returns false if it failed.
+bool GetEvergreenContentPathOverride(char* out_path, int path_size) {
+  const starboard::elf_loader::EvergreenConfig* evergreen_config =
+      starboard::elf_loader::EvergreenConfig::GetInstance();
+  if (!evergreen_config) {
+    return true;
+  }
+  if (evergreen_config->content_path_.empty()) {
+    return true;
+  }
+
+  if (SbStringCopy(out_path, evergreen_config->content_path_.c_str(),
+                   path_size) >= path_size) {
+    return false;
+  }
+  return true;
+}
+#endif
+
 // Places up to |path_size| - 1 characters of the path to the directory
 // containing the current executable in |out_path|, ensuring it is
 // NULL-terminated. Returns success status. The result being greater than
@@ -156,6 +182,12 @@
       if (SbStringConcat(path.data(), "/content", kPathSize) >= kPathSize) {
         return false;
       }
+#if SB_IS(EVERGREEN_COMPATIBLE)
+      if (!GetEvergreenContentPathOverride(path.data(), kPathSize)) {
+        return false;
+      }
+
+#endif
       break;
 
     case kSbSystemPathCacheDirectory:
diff --git a/src/starboard/linux/shared/system_has_capability.cc b/src/starboard/linux/shared/system_has_capability.cc
index 401e712..7473825 100644
--- a/src/starboard/linux/shared/system_has_capability.cc
+++ b/src/starboard/linux/shared/system_has_capability.cc
@@ -22,10 +22,8 @@
       return false;
     case kSbSystemCapabilityCanQueryGPUMemoryStats:
       return false;
-#if SB_API_VERSION >= 10
     case kSbSystemCapabilitySetsInputTimestamp:
       return false;
-#endif
   }
 
   SB_DLOG(WARNING) << "Unrecognized capability: " << capability_id;
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/10/configuration_public.h b/src/starboard/linux/x64x11/blittergles/sbversion/10/configuration_public.h
index 5b3ac1e..3356def 100644
--- a/src/starboard/linux/x64x11/blittergles/sbversion/10/configuration_public.h
+++ b/src/starboard/linux/x64x11/blittergles/sbversion/10/configuration_public.h
@@ -71,6 +71,10 @@
 // textures. These textures typically originate from video decoders.
 #define SB_HAS_NV12_TEXTURE_SUPPORT 1
 
+#ifndef COBALT_FORCE_DIRECT_GLES_RASTERIZER
+#define COBALT_FORCE_DIRECT_GLES_RASTERIZER 1
+#endif
+
 // --- Shared Configuration and Overrides ------------------------------------
 
 // Include the Linux configuration that's common between all Desktop Linuxes.
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/6/configuration_public.h b/src/starboard/linux/x64x11/blittergles/sbversion/6/configuration_public.h
deleted file mode 100644
index beb7a18..0000000
--- a/src/starboard/linux/x64x11/blittergles/sbversion/6/configuration_public.h
+++ /dev/null
@@ -1,91 +0,0 @@
-// 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.
-
-// This file was initially generated by starboard/tools/create_derived_build.py,
-// though it may have been modified since its creation.
-
-#ifndef STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_CONFIGURATION_PUBLIC_H_
-
-#undef SB_API_VERSION
-#define SB_API_VERSION 6
-
-// --- Architecture Configuration --------------------------------------------
-
-// Whether the current platform is big endian. SB_IS_LITTLE_ENDIAN will be
-// automatically set based on this.
-#define SB_IS_BIG_ENDIAN 0
-
-// Whether the current platform is an ARM architecture.
-#define SB_IS_ARCH_ARM 0
-
-// Whether the current platform is a MIPS architecture.
-#define SB_IS_ARCH_MIPS 0
-
-// Whether the current platform is a PPC architecture.
-#define SB_IS_ARCH_PPC 0
-
-// Whether the current platform is an x86 architecture.
-#define SB_IS_ARCH_X86 1
-
-// Whether the current platform is a 32-bit architecture.
-#define SB_IS_32_BIT 0
-
-// Whether the current platform is a 64-bit architecture.
-#define SB_IS_64_BIT 1
-
-// Whether the current platform's pointers are 32-bit.
-// Whether the current platform's longs are 32-bit.
-#define SB_HAS_32_BIT_POINTERS 0
-#define SB_HAS_32_BIT_LONG 0
-
-// Whether the current platform's pointers are 64-bit.
-// Whether the current platform's longs are 64-bit.
-#define SB_HAS_64_BIT_POINTERS 1
-#define SB_HAS_64_BIT_LONG 1
-
-// Configuration parameters that allow the application to make some general
-// compile-time decisions with respect to the the number of cores likely to be
-// available on this platform. For a definitive measure, the application should
-// still call SbSystemGetNumberOfProcessors at runtime.
-
-// Whether the current platform's thread scheduler will automatically balance
-// threads between cores, as opposed to systems where threads will only ever run
-// on the specifically pinned core.
-#define SB_HAS_CROSS_CORE_SCHEDULER 1
-
-// --- Graphics Configuration ------------------------------------------------
-
-// Indicates whether or not the given platform supports rendering of NV12
-// textures. These textures typically originate from video decoders.
-#define SB_HAS_NV12_TEXTURE_SUPPORT 1
-
-// --- Shared Configuration and Overrides ------------------------------------
-
-// Include the Linux configuration that's common between all Desktop Linuxes.
-#include "starboard/linux/shared/configuration_public.h"
-
-// Include the Blitter - GLES configuration that's common between all Blitter -
-// GLES configurations.
-#include "starboard/linux/x64x11/blittergles/shared/configuration_public.h"
-
-// Starboard API versions 11 and earlier must define this variable, and have
-// microphone supported.
-#define SB_HAS_MICROPHONE 1
-
-// Whether the current platform has speech synthesis.
-#undef SB_HAS_SPEECH_SYNTHESIS
-#define SB_HAS_SPEECH_SYNTHESIS 0
-
-#endif  // STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/6/gyp_configuration.py b/src/starboard/linux/x64x11/blittergles/sbversion/6/gyp_configuration.py
deleted file mode 100644
index 228c1ba..0000000
--- a/src/starboard/linux/x64x11/blittergles/sbversion/6/gyp_configuration.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# 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.
-
-# This file was initially generated by starboard/tools/create_derived_build.py,
-# though it may have been modified since its creation.
-"""Starboard Linux X64X11 Blittergles Sbversion 6 platform configuration."""
-
-from starboard.linux.x64x11 import gyp_configuration as parent_configuration
-
-
-class LinuxX64X11BlitterglesSbversion6Configuration(
-    parent_configuration.LinuxX64X11Configuration):
-
-  def GetVariables(self, config_name):
-    variables = super(LinuxX64X11BlitterglesSbversion6Configuration,
-                      self).GetVariables(config_name)
-    # V8 requires new Starboard features so we must use SpiderMonkey in older
-    # versions of Starboard.
-    variables.update({
-        'javascript_engine': 'mozjs-45',
-        'cobalt_enable_jit': 0,
-    })
-    return variables
-
-
-def CreatePlatformConfig():
-  return LinuxX64X11BlitterglesSbversion6Configuration(
-      'linux-x64x11-blittergles-sbversion-6')
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/6/thread_types_public.h b/src/starboard/linux/x64x11/blittergles/sbversion/6/thread_types_public.h
deleted file mode 100644
index 6e977ae..0000000
--- a/src/starboard/linux/x64x11/blittergles/sbversion/6/thread_types_public.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// 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.
-
-// This file was initially generated by starboard/tools/create_derived_build.py,
-// though it may have been modified since its creation.
-
-#ifndef STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_THREAD_TYPES_PUBLIC_H_
-
-#include "starboard/linux/x64x11/blittergles/thread_types_public.h"
-
-#endif  // STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_6_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/blittergles/starboard_platform.gyp b/src/starboard/linux/x64x11/blittergles/starboard_platform.gyp
index 67b3b7e..b7cc9ca 100644
--- a/src/starboard/linux/x64x11/blittergles/starboard_platform.gyp
+++ b/src/starboard/linux/x64x11/blittergles/starboard_platform.gyp
@@ -82,10 +82,10 @@
         '<(DEPTH)/starboard/shared/blittergles/shader_program.h',
         '<(DEPTH)/starboard/shared/starboard/blitter_blit_rect_to_rect_tiled.cc',
         '<(DEPTH)/starboard/shared/starboard/blitter_blit_rects_to_rects.cc',
-	      '<(DEPTH)/starboard/shared/stub/system_gles.cc',
+        '<(DEPTH)/starboard/shared/stub/system_gles.cc',
       ],
       'sources!': [
-	      '<(DEPTH)/starboard/shared/gles/system_gles2.cc',
+        '<(DEPTH)/starboard/shared/gles/system_gles2.cc',
       ],
       'defines': [
         # This must be defined when building Starboard, and must not when
diff --git a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
index 1c3847b..7baf5ea 100644
--- a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
@@ -19,6 +19,12 @@
 
 from starboard.linux.shared import gyp_configuration as shared_configuration
 from starboard.tools import build
+from starboard.tools.toolchain import ar
+from starboard.tools.toolchain import bash
+from starboard.tools.toolchain import clang
+from starboard.tools.toolchain import clangxx
+from starboard.tools.toolchain import cp
+from starboard.tools.toolchain import touch
 
 
 class LinuxX64X11Clang36Configuration(shared_configuration.LinuxConfiguration):
@@ -66,6 +72,42 @@
     })
     return variables
 
+  def GetTargetToolchain(self):
+    environment_variables = self.GetEnvironmentVariables()
+    cc_path = environment_variables['CC']
+    cxx_path = environment_variables['CXX']
+
+    return [
+        clang.CCompiler(path=cc_path),
+        clang.CxxCompiler(path=cxx_path),
+        clang.AssemblerWithCPreprocessor(path=cc_path),
+        ar.StaticThinLinker(),
+        ar.StaticLinker(),
+        clangxx.ExecutableLinker(path=cxx_path, write_group=True),
+        clangxx.SharedLibraryLinker(path=cxx_path),
+        cp.Copy(),
+        touch.Stamp(),
+        bash.Shell(),
+    ]
+
+  def GetHostToolchain(self):
+    environment_variables = self.GetEnvironmentVariables()
+    cc_path = environment_variables['CC_host']
+    cxx_path = environment_variables['CXX_host']
+
+    return [
+        clang.CCompiler(path=cc_path),
+        clang.CxxCompiler(path=cxx_path),
+        clang.AssemblerWithCPreprocessor(path=cc_path),
+        ar.StaticThinLinker(),
+        ar.StaticLinker(),
+        clangxx.ExecutableLinker(path=cxx_path, write_group=True),
+        clangxx.SharedLibraryLinker(path=cxx_path),
+        cp.Copy(),
+        touch.Stamp(),
+        bash.Shell(),
+    ]
+
 
 def CreatePlatformConfig():
   try:
diff --git a/src/starboard/linux/x64x11/gczeal/configuration.cc b/src/starboard/linux/x64x11/gczeal/configuration.cc
new file mode 100644
index 0000000..d692d79
--- /dev/null
+++ b/src/starboard/linux/x64x11/gczeal/configuration.cc
@@ -0,0 +1,74 @@
+// Copyright 2020 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/linux/x64x11/gczeal/configuration.h"
+
+#include "cobalt/extension/configuration.h"
+#include "starboard/common/configuration_defaults.h"
+
+// Omit namespace linux due to symbol name conflict.
+namespace starboard {
+namespace x64x11 {
+namespace gczeal {
+
+namespace {
+
+int CobaltEglSwapInterval() {
+  return 0;
+}
+
+bool CobaltGcZeal() {
+  return true;
+}
+
+bool CobaltEnableJit() {
+  return true;
+}
+
+const CobaltExtensionConfigurationApi kConfigurationApi = {
+    kCobaltExtensionConfigurationName,
+    1,
+    &common::CobaltUserOnExitStrategyDefault,
+    &common::CobaltRenderDirtyRegionOnlyDefault,
+    &CobaltEglSwapInterval,
+    &common::CobaltFallbackSplashScreenUrlDefault,
+    &common::CobaltEnableQuicDefault,
+    &common::CobaltSkiaCacheSizeInBytesDefault,
+    &common::CobaltOffscreenTargetCacheSizeInBytesDefault,
+    &common::CobaltEncodedImageCacheSizeInBytesDefault,
+    &common::CobaltImageCacheSizeInBytesDefault,
+    &common::CobaltLocalTypefaceCacheSizeInBytesDefault,
+    &common::CobaltRemoteTypefaceCacheSizeInBytesDefault,
+    &common::CobaltMeshCacheSizeInBytesDefault,
+    &common::CobaltSoftwareSurfaceCacheSizeInBytesDefault,
+    &common::CobaltImageCacheCapacityMultiplierWhenPlayingVideoDefault,
+    &common::CobaltSkiaGlyphAtlasWidthDefault,
+    &common::CobaltSkiaGlyphAtlasHeightDefault,
+    &common::CobaltJsGarbageCollectionThresholdInBytesDefault,
+    &common::CobaltReduceCpuMemoryByDefault,
+    &common::CobaltReduceGpuMemoryByDefault,
+    &CobaltGcZeal,
+    &common::CobaltRasterizerTypeDefault,
+    &CobaltEnableJit,
+};
+
+}  // namespace
+
+const void* GetConfigurationApi() {
+  return &kConfigurationApi;
+}
+
+}  // namespace gczeal
+}  // namespace x64x11
+}  // namespace starboard
diff --git a/src/starboard/linux/x64x11/gczeal/configuration.h b/src/starboard/linux/x64x11/gczeal/configuration.h
new file mode 100644
index 0000000..5248102
--- /dev/null
+++ b/src/starboard/linux/x64x11/gczeal/configuration.h
@@ -0,0 +1,29 @@
+// Copyright 2020 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 STARBOARD_LINUX_X64X11_GCZEAL_CONFIGURATION_H_
+#define STARBOARD_LINUX_X64X11_GCZEAL_CONFIGURATION_H_
+
+// Omit namespace linux due to symbol name conflict.
+namespace starboard {
+namespace x64x11 {
+namespace gczeal {
+
+const void* GetConfigurationApi();
+
+}  // namespace gczeal
+}  // namespace x64x11
+}  // namespace starboard
+
+#endif  // STARBOARD_LINUX_X64X11_GCZEAL_CONFIGURATION_H_
diff --git a/src/starboard/linux/x64x11/gczeal/starboard_platform.gyp b/src/starboard/linux/x64x11/gczeal/starboard_platform.gyp
index 82c4436..029148d 100644
--- a/src/starboard/linux/x64x11/gczeal/starboard_platform.gyp
+++ b/src/starboard/linux/x64x11/gczeal/starboard_platform.gyp
@@ -12,6 +12,16 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 {
+  'variables': {
+    'sources': [
+      '<(DEPTH)/starboard/linux/x64x11/gczeal/configuration.cc',
+      '<(DEPTH)/starboard/linux/x64x11/gczeal/configuration.h'
+      '<(DEPTH)/starboard/linux/x64x11/gczeal/system_get_extensions.cc',
+    ],
+    'sources!': [
+      '<(DEPTH)/starboard/linux/shared/system_get_extensions.cc',
+    ],
+  },
   'includes': [
     '../shared/starboard_platform_target.gypi',
   ],
diff --git a/src/starboard/linux/x64x11/gczeal/system_get_extensions.cc b/src/starboard/linux/x64x11/gczeal/system_get_extensions.cc
new file mode 100644
index 0000000..9d6382a
--- /dev/null
+++ b/src/starboard/linux/x64x11/gczeal/system_get_extensions.cc
@@ -0,0 +1,26 @@
+// Copyright 2020 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/system.h"
+
+#include "cobalt/extension/configuration.h"
+#include "starboard/common/string.h"
+#include "starboard/linux/x64x11/gczeal/configuration.h"
+
+const void* SbSystemGetExtension(const char* name) {
+  if (SbStringCompareAll(name, kCobaltExtensionConfigurationName) == 0) {
+    return starboard::shared::GetConfigurationApi();
+  }
+  return NULL;
+}
diff --git a/src/starboard/linux/x64x11/gyp_configuration.gypi b/src/starboard/linux/x64x11/gyp_configuration.gypi
index baea958..7571506 100644
--- a/src/starboard/linux/x64x11/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/gyp_configuration.gypi
@@ -15,7 +15,7 @@
 {
   'variables': {
     'variables': {
-      'sb_evergreen_compatible': '<!(python <(DEPTH)/build/file_exists.py <(DEPTH)/starboard/elf_loader/evergreen_info.gyp)',
+      'sb_evergreen_compatible': 1,
     }
   },
   'target_defaults': {
diff --git a/src/starboard/linux/x64x11/main.cc b/src/starboard/linux/x64x11/main.cc
index 954077b..cdb21c5 100644
--- a/src/starboard/linux/x64x11/main.cc
+++ b/src/starboard/linux/x64x11/main.cc
@@ -24,6 +24,13 @@
   tzset();
   starboard::shared::signal::InstallCrashSignalHandlers();
   starboard::shared::signal::InstallSuspendSignalHandlers();
+
+#if SB_HAS_QUIRK(BACKTRACE_DLOPEN_BUG)
+  // Call backtrace() once to work around potential
+  // crash bugs in glibc, in dlopen()
+  SbLogRawDumpStack(3);
+#endif
+
   starboard::shared::x11::ApplicationX11 application;
   int result = 0;
   {
diff --git a/src/starboard/linux/x64x11/mozjs/configuration_public.h b/src/starboard/linux/x64x11/mozjs/configuration_public.h
deleted file mode 100644
index d342943..0000000
--- a/src/starboard/linux/x64x11/mozjs/configuration_public.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2018 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 STARBOARD_LINUX_X64X11_MOZJS_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_MOZJS_CONFIGURATION_PUBLIC_H_
-
-// Include the X64X11 Linux configuration.
-#include "starboard/linux/x64x11/configuration_public.h"
-
-#endif  // STARBOARD_LINUX_X64X11_MOZJS_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/mozjs/gyp_configuration.gypi b/src/starboard/linux/x64x11/mozjs/gyp_configuration.gypi
deleted file mode 100644
index 24749cf..0000000
--- a/src/starboard/linux/x64x11/mozjs/gyp_configuration.gypi
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2018 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.
-
-{
-  'target_defaults': {
-    'default_configuration': 'linux-x64x11-mozjs_debug',
-    'configurations': {
-      'linux-x64x11-mozjs_debug': {
-        'inherit_from': ['debug_base'],
-      },
-      'linux-x64x11-mozjs_devel': {
-        'inherit_from': ['devel_base'],
-      },
-      'linux-x64x11-mozjs_qa': {
-        'inherit_from': ['qa_base'],
-      },
-      'linux-x64x11-mozjs_gold': {
-        'inherit_from': ['gold_base'],
-      },
-    }, # end of configurations
-  },
-
-  'includes': [
-    '../gyp_configuration.gypi',
-  ],
-}
diff --git a/src/starboard/linux/x64x11/mozjs/gyp_configuration.py b/src/starboard/linux/x64x11/mozjs/gyp_configuration.py
deleted file mode 100644
index e8bba52..0000000
--- a/src/starboard/linux/x64x11/mozjs/gyp_configuration.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright 2018 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.
-"""Starboard Linux X64 X11 mozjs platform configuration."""
-
-from starboard.linux.x64x11 import gyp_configuration as linux_configuration
-
-
-class LinuxX64X11MozjsConfiguration(linux_configuration.LinuxX64X11Configuration
-                                   ):
-  """Starboard Linux X64 X11 mozjs platform configuration."""
-
-  def GetVariables(self, config_name):
-    variables = super(LinuxX64X11MozjsConfiguration,
-                      self).GetVariables(config_name)
-    variables.update({
-        'javascript_engine': 'mozjs-45',
-        'cobalt_enable_jit': 0,
-    })
-    return variables
-
-
-def CreatePlatformConfig():
-  return LinuxX64X11MozjsConfiguration(
-      'linux-x64x11-mozjs', sabi_json_path='starboard/sabi/x64/sysv/sabi.json')
diff --git a/src/starboard/linux/x64x11/mozjs/starboard_platform.gyp b/src/starboard/linux/x64x11/mozjs/starboard_platform.gyp
deleted file mode 100644
index 9ef3bfc..0000000
--- a/src/starboard/linux/x64x11/mozjs/starboard_platform.gyp
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright 2018 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.
-{
-  'includes': [
-    '../shared/starboard_platform_target.gypi',
-  ],
-}
diff --git a/src/starboard/linux/x64x11/sbversion/10/configuration_public.h b/src/starboard/linux/x64x11/sbversion/10/configuration_public.h
index 61d198d..4b7de4e 100644
--- a/src/starboard/linux/x64x11/sbversion/10/configuration_public.h
+++ b/src/starboard/linux/x64x11/sbversion/10/configuration_public.h
@@ -71,6 +71,10 @@
 // textures. These textures typically originate from video decoders.
 #define SB_HAS_NV12_TEXTURE_SUPPORT 1
 
+#ifndef COBALT_FORCE_DIRECT_GLES_RASTERIZER
+#define COBALT_FORCE_DIRECT_GLES_RASTERIZER 1
+#endif
+
 // --- Shared Configuration and Overrides ------------------------------------
 
 // Include the Linux configuration that's common between all Desktop Linuxes.
diff --git a/src/starboard/linux/x64x11/sbversion/6/atomic_public.h b/src/starboard/linux/x64x11/sbversion/6/atomic_public.h
deleted file mode 100644
index 14cedc9..0000000
--- a/src/starboard/linux/x64x11/sbversion/6/atomic_public.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2018 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.
-
-// This file was initially generated by create_derived_build.py,
-// though it may have been modified since its creation.
-
-#ifndef STARBOARD_LINUX_X64X11_SBVERSION_6_ATOMIC_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_SBVERSION_6_ATOMIC_PUBLIC_H_
-
-#include "starboard/linux/x64x11/atomic_public.h"
-
-#endif  // STARBOARD_LINUX_X64X11_SBVERSION_6_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/sbversion/6/configuration_public.h b/src/starboard/linux/x64x11/sbversion/6/configuration_public.h
deleted file mode 100644
index ff0ff15..0000000
--- a/src/starboard/linux/x64x11/sbversion/6/configuration_public.h
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2018 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.
-
-// This file was initially generated by create_derived_build.py,
-// though it may have been modified since its creation.
-
-#ifndef STARBOARD_LINUX_X64X11_SBVERSION_6_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_SBVERSION_6_CONFIGURATION_PUBLIC_H_
-
-#undef SB_API_VERSION
-#define SB_API_VERSION 6
-
-// --- Architecture Configuration --------------------------------------------
-
-// Whether the current platform is big endian. SB_IS_LITTLE_ENDIAN will be
-// automatically set based on this.
-#define SB_IS_BIG_ENDIAN 0
-
-// Whether the current platform is an ARM architecture.
-#define SB_IS_ARCH_ARM 0
-
-// Whether the current platform is a MIPS architecture.
-#define SB_IS_ARCH_MIPS 0
-
-// Whether the current platform is a PPC architecture.
-#define SB_IS_ARCH_PPC 0
-
-// Whether the current platform is an x86 architecture.
-#define SB_IS_ARCH_X86 1
-
-// Whether the current platform is a 32-bit architecture.
-#define SB_IS_32_BIT 0
-
-// Whether the current platform is a 64-bit architecture.
-#define SB_IS_64_BIT 1
-
-// Whether the current platform's pointers are 32-bit.
-// Whether the current platform's longs are 32-bit.
-#define SB_HAS_32_BIT_POINTERS 0
-#define SB_HAS_32_BIT_LONG 0
-
-// Whether the current platform's pointers are 64-bit.
-// Whether the current platform's longs are 64-bit.
-#define SB_HAS_64_BIT_POINTERS 1
-#define SB_HAS_64_BIT_LONG 1
-
-// Configuration parameters that allow the application to make some general
-// compile-time decisions with respect to the the number of cores likely to be
-// available on this platform. For a definitive measure, the application should
-// still call SbSystemGetNumberOfProcessors at runtime.
-
-// Whether the current platform's thread scheduler will automatically balance
-// threads between cores, as opposed to systems where threads will only ever run
-// on the specifically pinned core.
-#define SB_HAS_CROSS_CORE_SCHEDULER 1
-
-// --- Graphics Configuration ------------------------------------------------
-
-// Indicates whether or not the given platform supports rendering of NV12
-// textures. These textures typically originate from video decoders.
-#define SB_HAS_NV12_TEXTURE_SUPPORT 1
-
-// --- Shared Configuration and Overrides ------------------------------------
-
-// Include the Linux configuration that's common between all Desktop Linuxes.
-#include "starboard/linux/shared/configuration_public.h"
-
-// Starboard API versions 11 and earlier must define this variable, and have
-// microphone supported.
-#define SB_HAS_MICROPHONE 1
-
-// Whether the current platform has speech synthesis.
-#undef SB_HAS_SPEECH_SYNTHESIS
-#define SB_HAS_SPEECH_SYNTHESIS 0
-
-#endif  // STARBOARD_LINUX_X64X11_SBVERSION_6_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/sbversion/6/gyp_configuration.gypi b/src/starboard/linux/x64x11/sbversion/6/gyp_configuration.gypi
deleted file mode 100644
index 9b8d915..0000000
--- a/src/starboard/linux/x64x11/sbversion/6/gyp_configuration.gypi
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2018 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.
-
-# This file was initially generated by create_derived_build.py,
-# though it may have been modified since its creation.
-
-{
-  'target_defaults': {
-    'default_configuration': 'linux-x64x11-sbversion-6_debug',
-    'configurations': {
-      'linux-x64x11-sbversion-6_debug': {
-        'inherit_from': ['debug_base'],
-      },
-      'linux-x64x11-sbversion-6_devel': {
-        'inherit_from': ['devel_base'],
-      },
-      'linux-x64x11-sbversion-6_qa': {
-        'inherit_from': ['qa_base'],
-      },
-      'linux-x64x11-sbversion-6_gold': {
-        'inherit_from': ['gold_base'],
-      },
-    }, # end of configurations
-  },
-
-  'variables': {
-    'enable_map_to_mesh': 1,
-  },
-
-  'includes': [
-    '<(DEPTH)/starboard/linux/x64x11/shared/gyp_configuration.gypi',
-  ],
-}
diff --git a/src/starboard/linux/x64x11/sbversion/6/gyp_configuration.py b/src/starboard/linux/x64x11/sbversion/6/gyp_configuration.py
deleted file mode 100644
index a60fc25..0000000
--- a/src/starboard/linux/x64x11/sbversion/6/gyp_configuration.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2018 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.
-
-# This file was initially generated by create_derived_build.py,
-# though it may have been modified since its creation.
-
-from starboard.linux.x64x11 import gyp_configuration as parent_configuration
-
-
-class LinuxX64X11Sbversion6Configuration(
-    parent_configuration.LinuxX64X11Configuration):
-
-  def GetVariables(self, config_name):
-    variables = super(LinuxX64X11Sbversion6Configuration,
-                      self).GetVariables(config_name)
-    # V8 requires new Starboard features so we must use SpiderMonkey in older
-    # versions of Starboard.
-    variables.update({
-        'javascript_engine': 'mozjs-45',
-        'cobalt_enable_jit': 0,
-    })
-    return variables
-
-
-def CreatePlatformConfig():
-  return LinuxX64X11Sbversion6Configuration('linux-x64x11-sbversion-6')
diff --git a/src/starboard/linux/x64x11/sbversion/6/starboard_platform.gyp b/src/starboard/linux/x64x11/sbversion/6/starboard_platform.gyp
deleted file mode 100644
index 95cd6ef..0000000
--- a/src/starboard/linux/x64x11/sbversion/6/starboard_platform.gyp
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright 2018 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.
-
-# This file was initially generated by create_derived_build.py,
-# though it may have been modified since its creation.
-
-{
-  'includes': [
-    # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file.  The idea
-    # is that we just want this file to *be* the parent gyp file.
-    '<(DEPTH)/starboard/linux/x64x11/starboard_platform.gyp',
-  ],
-}
diff --git a/src/starboard/linux/x64x11/sbversion/6/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/sbversion/6/starboard_platform_tests.gyp
deleted file mode 100644
index 6eb123d..0000000
--- a/src/starboard/linux/x64x11/sbversion/6/starboard_platform_tests.gyp
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright 2018 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.
-
-# This file was initially generated by create_derived_build.py,
-# though it may have been modified since its creation.
-
-{
-  'includes': [
-    # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file.  The idea
-    # is that we just want this file to *be* the parent gyp file.
-    '<(DEPTH)/starboard/linux/x64x11/starboard_platform_tests.gyp',
-  ],
-}
diff --git a/src/starboard/linux/x64x11/sbversion/6/thread_types_public.h b/src/starboard/linux/x64x11/sbversion/6/thread_types_public.h
deleted file mode 100644
index 8c70c99..0000000
--- a/src/starboard/linux/x64x11/sbversion/6/thread_types_public.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2018 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.
-
-// This file was initially generated by create_derived_build.py,
-// though it may have been modified since its creation.
-
-#ifndef STARBOARD_LINUX_X64X11_SBVERSION_6_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_SBVERSION_6_THREAD_TYPES_PUBLIC_H_
-
-#include "starboard/linux/x64x11/thread_types_public.h"
-
-#endif  // STARBOARD_LINUX_X64X11_SBVERSION_6_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/skia/configuration.cc b/src/starboard/linux/x64x11/skia/configuration.cc
new file mode 100644
index 0000000..cc918a5
--- /dev/null
+++ b/src/starboard/linux/x64x11/skia/configuration.cc
@@ -0,0 +1,70 @@
+// Copyright 2020 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/linux/x64x11/skia/configuration.h"
+
+#include "cobalt/extension/configuration.h"
+#include "starboard/common/configuration_defaults.h"
+
+// Omit namespace linux due to symbol name conflict.
+namespace starboard {
+namespace x64x11 {
+namespace skia {
+
+namespace {
+
+int CobaltEglSwapInterval() {
+  return 0;
+}
+
+const char* CobaltRasterizerType() {
+  return "hardware";
+}
+
+const CobaltExtensionConfigurationApi kConfigurationApi = {
+    kCobaltExtensionConfigurationName,
+    1,
+    &common::CobaltUserOnExitStrategyDefault,
+    &common::CobaltRenderDirtyRegionOnlyDefault,
+    &CobaltEglSwapInterval,
+    &common::CobaltFallbackSplashScreenUrlDefault,
+    &common::CobaltEnableQuicDefault,
+    &common::CobaltSkiaCacheSizeInBytesDefault,
+    &common::CobaltOffscreenTargetCacheSizeInBytesDefault,
+    &common::CobaltEncodedImageCacheSizeInBytesDefault,
+    &common::CobaltImageCacheSizeInBytesDefault,
+    &common::CobaltLocalTypefaceCacheSizeInBytesDefault,
+    &common::CobaltRemoteTypefaceCacheSizeInBytesDefault,
+    &common::CobaltMeshCacheSizeInBytesDefault,
+    &common::CobaltSoftwareSurfaceCacheSizeInBytesDefault,
+    &common::CobaltImageCacheCapacityMultiplierWhenPlayingVideoDefault,
+    &common::CobaltSkiaGlyphAtlasWidthDefault,
+    &common::CobaltSkiaGlyphAtlasHeightDefault,
+    &common::CobaltJsGarbageCollectionThresholdInBytesDefault,
+    &common::CobaltReduceCpuMemoryByDefault,
+    &common::CobaltReduceGpuMemoryByDefault,
+    &common::CobaltGcZealDefault,
+    &CobaltRasterizerType,
+    &common::CobaltEnableJitDefault,
+};
+
+}  // namespace
+
+const void* GetConfigurationApi() {
+  return &kConfigurationApi;
+}
+
+}  // namespace skia
+}  // namespace x64x11
+}  // namespace starboard
diff --git a/src/starboard/linux/x64x11/skia/configuration.h b/src/starboard/linux/x64x11/skia/configuration.h
new file mode 100644
index 0000000..54b1205
--- /dev/null
+++ b/src/starboard/linux/x64x11/skia/configuration.h
@@ -0,0 +1,29 @@
+// Copyright 2020 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 STARBOARD_LINUX_X64X11_SKIA_CONFIGURATION_H_
+#define STARBOARD_LINUX_X64X11_SKIA_CONFIGURATION_H_
+
+// Omit namespace linux due to symbol name conflict.
+namespace starboard {
+namespace x64x11 {
+namespace skia {
+
+const void* GetConfigurationApi();
+
+}  // namespace skia
+}  // namespace x64x11
+}  // namespace starboard
+
+#endif  // STARBOARD_LINUX_X64X11_SKIA_CONFIGURATION_H_
diff --git a/src/starboard/linux/x64x11/skia/starboard_platform.gyp b/src/starboard/linux/x64x11/skia/starboard_platform.gyp
index 665c74c..a026091 100644
--- a/src/starboard/linux/x64x11/skia/starboard_platform.gyp
+++ b/src/starboard/linux/x64x11/skia/starboard_platform.gyp
@@ -15,4 +15,16 @@
   'includes': [
     '../shared/starboard_platform_target.gypi',
   ],
+  'target_defaults': {
+    'sources': [
+      '<(DEPTH)/starboard/linux/x64x11/skia/configuration.cc',
+      '<(DEPTH)/starboard/linux/x64x11/skia/configuration.h',
+      '<(DEPTH)/starboard/linux/x64x11/skia/system_get_extensions.cc',
+    ],
+    'sources!': [
+      '<(DEPTH)/starboard/linux/shared/configuration.cc',
+      '<(DEPTH)/starboard/linux/shared/configuration.h',
+      '<(DEPTH)/starboard/linux/shared/system_get_extensions.cc',
+    ],
+  },
 }
diff --git a/src/starboard/linux/x64x11/skia/system_get_extensions.cc b/src/starboard/linux/x64x11/skia/system_get_extensions.cc
new file mode 100644
index 0000000..4743dab
--- /dev/null
+++ b/src/starboard/linux/x64x11/skia/system_get_extensions.cc
@@ -0,0 +1,26 @@
+// Copyright 2020 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/system.h"
+
+#include "cobalt/extension/configuration.h"
+#include "starboard/common/string.h"
+#include "starboard/linux/x64x11/skia/configuration.h"
+
+const void* SbSystemGetExtension(const char* name) {
+  if (SbStringCompareAll(name, kCobaltExtensionConfigurationName) == 0) {
+    return starboard::x64x11::skia::GetConfigurationApi();
+  }
+  return NULL;
+}
diff --git a/src/starboard/linux/x64x11/system_get_property_impl.cc b/src/starboard/linux/x64x11/system_get_property_impl.cc
index 3b11fea..c503210 100644
--- a/src/starboard/linux/x64x11/system_get_property_impl.cc
+++ b/src/starboard/linux/x64x11/system_get_property_impl.cc
@@ -28,52 +28,6 @@
 const char* kFriendlyName = "Linux Desktop";
 const char* kPlatformName = "X11; Linux x86_64";
 
-#if SB_API_VERSION < 10
-
-bool GetPlatformUuid(char* out_value, int value_length) {
-  struct ifreq interface;
-  struct ifconf config;
-  char buf[1024];
-
-  int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
-  if (fd == -1) {
-    return false;
-  }
-  config.ifc_len = sizeof(buf);
-  config.ifc_buf = buf;
-  int result = ioctl(fd, SIOCGIFCONF, &config);
-  if (result == -1) {
-    return false;
-  }
-
-  struct ifreq* cur_interface = config.ifc_req;
-  const struct ifreq* const end = cur_interface +
-      (config.ifc_len / sizeof(struct ifreq));
-
-  for (; cur_interface != end; ++cur_interface) {
-    SbStringCopy(interface.ifr_name,
-        cur_interface->ifr_name,
-        sizeof(cur_interface->ifr_name));
-    if (ioctl(fd, SIOCGIFFLAGS, &interface) == -1) {
-      continue;
-    }
-    if (interface.ifr_flags & IFF_LOOPBACK) {
-      continue;
-    }
-    if (ioctl(fd, SIOCGIFHWADDR, &interface) == -1) {
-      continue;
-    }
-    SbStringFormatF(out_value, value_length, "%x:%x:%x:%x:%x:%x",
-        interface.ifr_addr.sa_data[0], interface.ifr_addr.sa_data[1],
-        interface.ifr_addr.sa_data[2], interface.ifr_addr.sa_data[3],
-        interface.ifr_addr.sa_data[4], interface.ifr_addr.sa_data[5]);
-    return true;
-  }
-  return false;
-}
-
-#endif  // SB_API_VERSION < 10
-
 }  // namespace
 
 // Omit namespace linux due to symbol name conflict.
@@ -109,18 +63,14 @@
 #endif
     case kSbSystemPropertySpeechApiKey:
       return false;
-
+    case kSbSystemPropertyUserAgentAuxField:
+      return false;
     case kSbSystemPropertyFriendlyName:
       return CopyStringAndTestIfSuccess(out_value, value_length, kFriendlyName);
 
     case kSbSystemPropertyPlatformName:
       return CopyStringAndTestIfSuccess(out_value, value_length, kPlatformName);
 
-#if SB_API_VERSION < 10
-    case kSbSystemPropertyPlatformUuid:
-      return GetPlatformUuid(out_value, value_length);
-#endif  // SB_API_VERSION < 10
-
 #if SB_API_VERSION >= 11
     case kSbSystemPropertyCertificationScope:
       if (kCertificationScope[0] == '\0')
diff --git a/src/starboard/loader_app/installation_manager.cc b/src/starboard/loader_app/installation_manager.cc
new file mode 100644
index 0000000..b463471
--- /dev/null
+++ b/src/starboard/loader_app/installation_manager.cc
@@ -0,0 +1,707 @@
+// 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 "starboard/loader_app/installation_manager.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "starboard/common/log.h"
+#include "starboard/common/mutex.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/configuration_constants.h"
+#include "starboard/directory.h"
+#include "starboard/file.h"
+#include "starboard/loader_app/installation_store.pb.h"
+#include "starboard/once.h"
+#include "starboard/string.h"
+
+namespace starboard {
+namespace loader_app {
+namespace installation_manager {
+
+class InstallationManager {
+ public:
+  explicit InstallationManager(int max_num_installations);
+
+  int Initialize();
+
+  int GetInstallationStatus(int installation_index);
+  int GetInstallationNumTriesLeft(int installation_index);
+  int RollForwardIfNeeded();
+  int DecrementInstallationNumTries(int installation_index);
+
+  int RevertToSuccessfulInstallation();
+  int GetInstallationPath(int installation_index, char* path, int path_length);
+  int GetCurrentInstallationIndex();
+  int MarkInstallationSuccessful(int installation_index);
+  int SelectNewInstallationIndex();
+  int RequestRollForwardToInstallation(int installation_index);
+
+ private:
+  bool IsValidIndex(int i);
+  void ValidatePriorities();
+  int FindCurrentInstallationIndex();
+  void CreateInstallationStore();
+  std::string DumpInstallationSlots();
+  void LogLastSystemError(const char* msg);
+  void ShiftPrioritiesInRange(int high_priority,
+                              int low_priority,
+                              int shift_amount);
+  bool InitInstallationStorePath();
+  bool LoadInstallationStore();
+  bool SaveInstallationStore();
+  bool CreateInstallationDirs();
+  bool GetInstallationPathInternal(int installation_index,
+                                   char* path,
+                                   int path_length);
+
+  cobalt::loader::InstallationStore installation_store_;
+  bool initialized_;
+  int current_installation_;
+  std::string store_path_;
+  std::string storage_dir_;
+  std::string content_dir_;
+  const int max_num_installations_;
+  const int lowest_priority_;
+  const int highest_priority_;
+};
+
+InstallationManager::InstallationManager(int max_num_installations)
+    : initialized_(false),
+      current_installation_(-1),
+      max_num_installations_(max_num_installations),
+      lowest_priority_(max_num_installations_ - 1),
+      highest_priority_(0) {
+  SB_CHECK(max_num_installations_ >= 2);
+}
+
+int InstallationManager::Initialize() {
+  if (initialized_) {
+    return IM_ERROR;
+  }
+  if (!InitInstallationStorePath()) {
+    SB_LOG(ERROR) << "Initialize: failed to init paths";
+    return IM_ERROR;
+  }
+
+  // If there is no existing store, create one.
+  if (!LoadInstallationStore()) {
+    CreateInstallationStore();
+    current_installation_ = FindCurrentInstallationIndex();
+    if (!IsValidIndex(current_installation_)) {
+      SB_LOG(ERROR) << "Initialize: Unable to find current installation"
+                    << current_installation_;
+      return IM_ERROR;
+    }
+    if (!CreateInstallationDirs()) {
+      SB_LOG(ERROR) << "Initialize: Unable to create installations dirs";
+      return IM_ERROR;
+    }
+    initialized_ = SaveInstallationStore();
+  } else {
+    current_installation_ = FindCurrentInstallationIndex();
+    if (!IsValidIndex(current_installation_)) {
+      SB_LOG(ERROR) << "Initialize: Unable to find current installation"
+                    << current_installation_;
+      return IM_ERROR;
+    }
+    initialized_ = true;
+  }
+  if (!initialized_) {
+    SB_LOG(ERROR) << "Initialize: failed";
+  }
+  SB_DLOG(INFO) << DumpInstallationSlots();
+  return initialized_ ? IM_SUCCESS : IM_ERROR;
+}
+
+void InstallationManager::CreateInstallationStore() {
+  int priority = highest_priority_;
+  for (int i = 0; i < max_num_installations_; i++) {
+    installation_store_.add_installations();
+    installation_store_.mutable_installations(i)->set_is_successful(false);
+    installation_store_.mutable_installations(i)->set_num_tries_left(
+        IM_MAX_NUM_TRIES);
+    installation_store_.mutable_installations(i)->set_priority(priority++);
+  }
+  installation_store_.set_roll_forward_to_installation(-1);
+}
+
+std::string InstallationManager::DumpInstallationSlots() {
+  std::ostringstream out;
+  out << "size=";
+  const int kBufSize = 50;
+  char buf_num[kBufSize];
+  SbStringFormatF(buf_num, kBufSize, "%d",
+                  installation_store_.installations_size());
+  out << buf_num;
+
+  out << " roll_forward_to_installation=";
+  SbStringFormatF(buf_num, kBufSize, "%d",
+                  installation_store_.roll_forward_to_installation());
+  out << buf_num;
+  out << ";";
+  for (int i = 0; i < installation_store_.installations_size(); i++) {
+    out << " installation_";
+    SbStringFormatF(buf_num, kBufSize, "%d", i);
+    out << buf_num;
+    out << " is_successful=";
+    if (installation_store_.installations(i).is_successful()) {
+      out << "true";
+    } else {
+      out << "false";
+    }
+
+    out << " num_tries_left=";
+    SbStringFormatF(buf_num, kBufSize, "%d",
+                    installation_store_.installations(i).num_tries_left());
+    out << buf_num;
+
+    out << " priority=";
+    SbStringFormatF(buf_num, kBufSize, "%d",
+                    installation_store_.installations(i).priority());
+    out << buf_num;
+    out << ";";
+  }
+  return out.str();
+}
+
+int InstallationManager::GetInstallationStatus(int installation_index) {
+  if (!initialized_) {
+    SB_LOG(ERROR) << "GetInstallationStatus: not initialized";
+    return IM_INSTALLATION_STATUS_ERROR;
+  }
+  if (!IsValidIndex(installation_index)) {
+    SB_LOG(ERROR) << "GetInstallationStatus: invalid index: "
+                  << installation_index;
+    return IM_INSTALLATION_STATUS_ERROR;
+  }
+  return installation_store_.installations(installation_index).is_successful();
+}
+
+int InstallationManager::GetInstallationNumTriesLeft(int installation_index) {
+  if (!initialized_) {
+    SB_LOG(ERROR) << "GetInstallationNumTriesLeft: not initialized";
+    return IM_ERROR;
+  }
+
+  if (!IsValidIndex(installation_index)) {
+    SB_LOG(ERROR) << "GetInstallationNumTriesLeft: invalid index: "
+                  << installation_index;
+    return IM_ERROR;
+  }
+  return installation_store_.installations(installation_index).num_tries_left();
+}
+
+int InstallationManager::DecrementInstallationNumTries(int installation_index) {
+  if (!initialized_) {
+    SB_LOG(ERROR) << "DecrementInstallationNumTries: not initialized";
+    return IM_ERROR;
+  }
+  if (!IsValidIndex(installation_index)) {
+    SB_LOG(ERROR) << "DecrementInstallationNumTries: invalid index: "
+                  << installation_index;
+    return IM_ERROR;
+  }
+
+  int num_tries_left =
+      installation_store_.installations(installation_index).num_tries_left();
+  if (num_tries_left <= 0) {
+    return IM_ERROR;
+  }
+  installation_store_.mutable_installations(installation_index)
+      ->set_num_tries_left(--num_tries_left);
+
+  if (!SaveInstallationStore()) {
+    SB_LOG(ERROR) << "DecrementInstallationNumTries: failed to save store";
+    return IM_ERROR;
+  }
+  return IM_SUCCESS;
+}
+
+//
+// Revert to a previous successful installation and make it
+// the highest priority. The current installation becomes the
+// lowest priority.
+//
+//     high [-]    [x]
+//          [ ]    [ ]
+//          [x] => [ ]
+//     low  [ ]    [-]
+//
+int InstallationManager::RevertToSuccessfulInstallation() {
+  if (!initialized_) {
+    SB_LOG(ERROR) << "RevertToSuccessfulInstallation: not initialized";
+    return IM_ERROR;
+  }
+  int fallback_priority = lowest_priority_;
+  int fallback_installation = -1;
+
+  SB_DLOG(INFO) << "RevertToSuccessfulInstallation: Start "
+                << DumpInstallationSlots();
+  // Find the highest priority successful installation for fallback.
+  for (int i = 0; i < installation_store_.installations().size(); i++) {
+    int priority = installation_store_.installations(i).priority();
+    if (installation_store_.installations(i).is_successful() &&
+        priority > highest_priority_ && priority <= fallback_priority) {
+      fallback_priority = priority;
+      fallback_installation = i;
+      SB_DLOG(INFO) << "fallback_installation= " << fallback_installation;
+    }
+  }
+
+  if (fallback_installation == -1) {
+    SB_LOG(ERROR) << "RevertToSuccessfulInstallation: Unable to find fallback "
+                     "installation";
+    return IM_ERROR;
+  }
+
+  // Shift up all the priorities below the fallback installation up.
+  ShiftPrioritiesInRange(fallback_priority, lowest_priority_,
+                         -1 /* shift up -1 */);
+
+  // Disable current installation.
+  installation_store_.mutable_installations(current_installation_)
+      ->set_priority(lowest_priority_);
+
+  // Move the current installation at the bottom.
+  installation_store_.mutable_installations(current_installation_)
+      ->set_is_successful(false);
+
+  // Change current installation.
+  installation_store_.mutable_installations(fallback_installation)
+      ->set_priority(highest_priority_);
+  current_installation_ = fallback_installation;
+
+  SB_DLOG(INFO) << "RevertToSuccessfulInstallation: End "
+                << DumpInstallationSlots();
+
+  if (SaveInstallationStore()) {
+    return fallback_installation;
+  }
+  return IM_ERROR;
+}
+
+//
+// Roll forward to a new installation and make it
+// the new highest priority installation.
+//
+//     high [ ]    [x]
+//          [ ]    [ ]
+//          [x] => [ ]
+//     low  [ ]    [ ]
+//
+int InstallationManager::RollForwardIfNeeded() {
+  if (!initialized_) {
+    SB_LOG(ERROR) << "RollForwardIfNeeded: not initialized";
+    return IM_ERROR;
+  }
+  //  Check if we need to roll_forward to new slot.
+  int new_installation = installation_store_.roll_forward_to_installation();
+  if (new_installation == -1) {
+    // No need to roll forward.
+    return IM_SUCCESS;
+  }
+  if (!IsValidIndex(new_installation)) {
+    SB_LOG(ERROR) << "RollForwardIfNeeded: invalid new_installation="
+                  << new_installation;
+    return IM_ERROR;
+  }
+  SB_DLOG(INFO) << "RollForwardIfNeeded: new_installation=" << new_installation;
+
+  // Save old priority.
+  int new_installation_old_prority =
+      installation_store_.installations(new_installation).priority();
+
+  SB_DLOG(INFO) << "RollForwardIfNeeded: new_installation_old_priority="
+                << new_installation_old_prority;
+
+  // Lower priorities of all jumped over installations.
+  ShiftPrioritiesInRange(highest_priority_, new_installation_old_prority,
+                         1 /* shift down +1 */);
+
+  // The new installation will be set to the highest priority.
+  installation_store_.mutable_installations(new_installation)
+      ->set_priority(highest_priority_);
+
+  // Reset the roll forward index.
+  installation_store_.set_roll_forward_to_installation(-1);
+  current_installation_ = new_installation;
+
+  SB_DLOG(INFO) << "RollForwardIfNeeded: " << DumpInstallationSlots();
+  return SaveInstallationStore() ? IM_SUCCESS : IM_ERROR;
+}
+
+// Shift the priority in the inclusive range either up or down based
+// on the |direction_up" flag.
+void InstallationManager::ShiftPrioritiesInRange(int high_priority,
+                                                 int low_priority,
+                                                 int shift_amount) {
+  for (int i = 0; i < installation_store_.installations().size(); i++) {
+    int priority = installation_store_.installations(i).priority();
+    if (priority >= high_priority && priority <= low_priority) {
+      installation_store_.mutable_installations(i)->set_priority(priority +
+                                                                 shift_amount);
+      SB_DLOG(INFO) << "ShiftPrioritiesInRange i=" << i << " priority_new"
+                    << priority;
+    }
+  }
+}
+
+int InstallationManager::GetInstallationPath(int installation_index,
+                                             char* path,
+                                             int path_length) {
+  if (!initialized_) {
+    SB_LOG(ERROR) << "GetInstallationPath: not initialized";
+    return IM_ERROR;
+  }
+  return GetInstallationPathInternal(installation_index, path, path_length)
+             ? IM_SUCCESS
+             : IM_ERROR;
+}
+
+int InstallationManager::GetCurrentInstallationIndex() {
+  if (!initialized_) {
+    SB_LOG(ERROR) << "GetCurrentInstallationIndex: not initialized";
+    return IM_ERROR;
+  }
+  return current_installation_;
+}
+
+void InstallationManager::LogLastSystemError(const char* msg) {
+  const int kErrorMessageBufferSize = 256;
+  char msgbuf[kErrorMessageBufferSize];
+  SbSystemError error_code = SbSystemGetLastError();
+  if (SbSystemGetErrorString(error_code, msgbuf, kErrorMessageBufferSize) > 0) {
+    SB_LOG(ERROR) << msg << ": " << msgbuf;
+  }
+}
+
+int InstallationManager::FindCurrentInstallationIndex() {
+  int highest_priority_index = -1;
+  for (int i = 0; i < installation_store_.installations_size(); i++) {
+    if (highest_priority_ == installation_store_.installations(i).priority()) {
+      highest_priority_index = i;
+      break;
+    }
+  }
+
+  return highest_priority_index;
+}
+
+int InstallationManager::MarkInstallationSuccessful(int installation_index) {
+  if (!initialized_) {
+    SB_LOG(ERROR) << "MarkInstallationSuccessful: not initialized";
+    return IM_ERROR;
+  }
+  if (!IsValidIndex(installation_index)) {
+    SB_LOG(ERROR) << "MarkInstallationSuccessful: invalid index"
+                  << installation_index;
+    return IM_ERROR;
+  }
+  if (!installation_store_.installations(installation_index).is_successful()) {
+    installation_store_.mutable_installations(installation_index)
+        ->set_is_successful(true);
+    return SaveInstallationStore() ? IM_SUCCESS : IM_ERROR;
+  }
+  return IM_SUCCESS;
+}
+
+int InstallationManager::SelectNewInstallationIndex() {
+  if (!initialized_) {
+    SB_LOG(ERROR) << "SelectNewInstallationIndex: not initialized";
+    return IM_ERROR;
+  }
+
+  int priority = highest_priority_;
+  int new_installation_index = -1;
+
+  // If we have more than 2 slots the 0 index slot is always the system image.
+  // We would ignore the 0 slot in that case.
+  int start = max_num_installations_ > 2 ? 1 : 0;
+
+  // Find the lowest priority installation that we can use.
+  for (int i = start; i < installation_store_.installations().size(); i++) {
+    if (priority < installation_store_.installations(i).priority()) {
+      new_installation_index = i;
+      priority = installation_store_.installations(i).priority();
+      SB_DLOG(INFO) << "SelectNewInstallationIndex: lowest_priority_index= "
+                    << new_installation_index;
+    }
+  }
+
+  if (new_installation_index != -1) {
+    installation_store_.mutable_installations(new_installation_index)
+        ->set_is_successful(false);
+    installation_store_.mutable_installations(new_installation_index)
+        ->set_num_tries_left(IM_MAX_NUM_TRIES);
+
+    if (!SaveInstallationStore()) {
+      SB_LOG(ERROR) << "SelectNewInstallationIndex: failed to store";
+      return IM_ERROR;
+    }
+
+    SB_DLOG(INFO) << "SelectNewInstallationIndex: " << DumpInstallationSlots();
+    return new_installation_index;
+  }
+  return IM_ERROR;
+}
+
+int InstallationManager::RequestRollForwardToInstallation(
+    int installation_index) {
+  if (!initialized_) {
+    SB_LOG(ERROR) << "RequestRollForwardToInstallation: not initialized";
+    return IM_ERROR;
+  }
+  if (!IsValidIndex(installation_index)) {
+    SB_LOG(ERROR) << "RequestRollForwardToInstallation: invalid index"
+                  << installation_index;
+    return IM_ERROR;
+  }
+  installation_store_.set_roll_forward_to_installation(installation_index);
+  return SaveInstallationStore() ? IM_SUCCESS : IM_ERROR;
+}
+
+bool InstallationManager::SaveInstallationStore() {
+  ValidatePriorities();
+  char buf[IM_MAX_INSTALLATION_STORE_SIZE];
+  if (IM_MAX_INSTALLATION_STORE_SIZE < installation_store_.ByteSize()) {
+    SB_LOG(ERROR) << "SaveInstallationStore: Data too large"
+                  << installation_store_.ByteSize();
+    return false;
+  }
+  installation_store_.SerializeToArray(buf, installation_store_.ByteSize());
+
+#if SB_API_VERSION >= SB_FILE_ATOMIC_REPLACE_VERSION
+  if (!SbFileAtomicReplace(store_path_.c_str(), buf,
+                           installation_store_.ByteSize())) {
+    SB_LOG(ERROR)
+        << "SaveInstallationStore: Failed to store installation store: "
+        << store_path_;
+    return false;
+  }
+  SB_DLOG(INFO) << "SaveInstallationStore successful";
+  return true;
+
+#else
+  SB_NOTREACHED()
+      << "SbFileAtomicReplace is not available before starboard version "
+      << SB_FILE_ATOMIC_REPLACE_VERSION;
+  return false;
+#endif
+}
+
+bool InstallationManager::InitInstallationStorePath() {
+  std::vector<char> storage_dir(kSbFileMaxPath);
+#if SB_API_VERSION >= SB_STORAGE_PATH_VERSION
+  if (!SbSystemGetPath(kSbSystemPathStorageDirectory, storage_dir.data(),
+                       kSbFileMaxPath)) {
+    SB_LOG(ERROR) << "InitInstallationStorePath: Failed to get "
+                     "kSbSystemPathStorageDirectory";
+    return false;
+  }
+#else
+  SB_NOTREACHED() << "InitInstallationStorePath: kSbSystemPathStorageDirectory "
+                     "is not available before "
+                     "starboard version "
+                  << SB_STORAGE_PATH_VERSION;
+  return false;
+
+#endif
+  storage_dir_ = storage_dir.data();
+  store_path_ = storage_dir.data();
+  store_path_ += kSbFileSepString;
+  store_path_ += IM_STORE_FILE_NAME;
+
+  if (max_num_installations_ > 2) {
+    std::vector<char> content_dir(kSbFileMaxPath);
+    if (!SbSystemGetPath(kSbSystemPathContentDirectory, content_dir.data(),
+                         kSbFileMaxPath)) {
+      return false;
+    }
+    content_dir_ = content_dir.data();
+  }
+  return true;
+}
+
+bool InstallationManager::IsValidIndex(int index) {
+  return index >= 0 && index < max_num_installations_;
+}
+
+void InstallationManager::ValidatePriorities() {
+  std::set<int> priorities;
+  for (int i = 0; i < max_num_installations_; i++) {
+    priorities.insert(installation_store_.installations(i).priority());
+  }
+  for (int i = 0; i < max_num_installations_; i++) {
+    SB_DCHECK(priorities.find(i) != priorities.end());
+  }
+  SB_DCHECK(priorities.size() == max_num_installations_);
+}
+
+bool InstallationManager::LoadInstallationStore() {
+  SbFile file;
+
+  file = SbFileOpen(store_path_.c_str(), kSbFileOpenOnly | kSbFileRead, NULL,
+                    NULL);
+  if (!file) {
+    SB_LOG(WARNING) << "Failed to open file: " << store_path_;
+    return false;
+  }
+
+  char buf[IM_MAX_INSTALLATION_STORE_SIZE];
+  int count = SbFileReadAll(file, buf, IM_MAX_INSTALLATION_STORE_SIZE);
+  SB_DLOG(INFO) << "SbFileReadAll: count=" << count;
+  if (count == -1) {
+    LogLastSystemError("SbFileReadAll failed");
+    return false;
+  }
+  if (!installation_store_.ParseFromArray(buf, count)) {
+    SB_LOG(ERROR) << "LoadInstallationStore: Unable to parse storage";
+    return false;
+  }
+  SbFileClose(file);
+  return true;
+}
+
+bool InstallationManager::GetInstallationPathInternal(int installation_index,
+                                                      char* path,
+                                                      int path_length) {
+  if (!IsValidIndex(installation_index)) {
+    SB_LOG(ERROR) << "GetInstallationPath: invalid index" << installation_index;
+    return false;
+  }
+  if (!path) {
+    SB_LOG(ERROR) << "GetInstallationPath: path is null";
+    return false;
+  }
+  // When more than 2 slots are availabe the installation 0 slot is
+  // located under the content directory.
+  if (installation_index == 0 && max_num_installations_ > 2) {
+    SbStringFormatF(path, path_length, "%s%s%s%s%s", content_dir_.c_str(),
+                    kSbFileSepString, "app", kSbFileSepString, "cobalt");
+  } else {
+    SbStringFormatF(path, path_length, "%s%s%s%d", storage_dir_.c_str(),
+                    kSbFileSepString, "installation_", installation_index);
+  }
+
+  return true;
+}
+
+bool InstallationManager::CreateInstallationDirs() {
+  std::vector<char> path(kSbFileMaxPath);
+  for (int i = 0; i < max_num_installations_; i++) {
+    // The index 0 slot when more than 2 slots are available is
+    // under the content directory.
+    if (i == 0 && max_num_installations_ > 2) {
+      continue;
+    }
+    if (!GetInstallationPathInternal(i, path.data(), kSbFileMaxPath)) {
+      return false;
+    }
+    if (!SbDirectoryCreate(path.data())) {
+      return false;
+    }
+  }
+  return true;
+}
+
+}  // namespace installation_manager
+}  // namespace loader_app
+}  // namespace starboard
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+starboard::scoped_ptr<
+    starboard::loader_app::installation_manager::InstallationManager>
+    g_istallation_manager_;
+
+// Global Installation Manager Mutex.
+SB_ONCE_INITIALIZE_FUNCTION(starboard::Mutex, GetImMutex);
+
+int ImInitialize(int max_num_installations) {
+  starboard::ScopedLock lock(*GetImMutex());
+  if (g_istallation_manager_.get() == NULL) {
+    g_istallation_manager_.reset(
+        new starboard::loader_app::installation_manager::InstallationManager(
+            max_num_installations));
+  }
+  return g_istallation_manager_->Initialize();
+}
+
+void ImUninitialize() {
+  starboard::ScopedLock lock(*GetImMutex());
+  g_istallation_manager_.reset(NULL);
+}
+
+int ImGetInstallationStatus(int installation_index) {
+  starboard::ScopedLock lock(*GetImMutex());
+  return g_istallation_manager_->GetInstallationStatus(installation_index);
+}
+
+int ImGetInstallationNumTriesLeft(int installation_index) {
+  starboard::ScopedLock lock(*GetImMutex());
+  return g_istallation_manager_->GetInstallationNumTriesLeft(
+      installation_index);
+}
+
+int ImDecrementInstallationNumTries(int installation_index) {
+  starboard::ScopedLock lock(*GetImMutex());
+  return g_istallation_manager_->DecrementInstallationNumTries(
+      installation_index);
+}
+
+int ImGetCurrentInstallationIndex() {
+  starboard::ScopedLock lock(*GetImMutex());
+  return g_istallation_manager_->GetCurrentInstallationIndex();
+}
+
+int ImSelectNewInstallationIndex() {
+  starboard::ScopedLock lock(*GetImMutex());
+  return g_istallation_manager_->SelectNewInstallationIndex();
+}
+
+int ImGetInstallationPath(int installation_index, char* path, int path_length) {
+  starboard::ScopedLock lock(*GetImMutex());
+  return g_istallation_manager_->GetInstallationPath(installation_index, path,
+                                                     path_length);
+}
+
+int ImMarkInstallationSuccessful(int installation_index) {
+  starboard::ScopedLock lock(*GetImMutex());
+  return g_istallation_manager_->MarkInstallationSuccessful(installation_index);
+}
+
+int ImRollForwardIfNeeded() {
+  starboard::ScopedLock lock(*GetImMutex());
+  return g_istallation_manager_->RollForwardIfNeeded();
+}
+
+int ImRevertToSuccessfulInstallation() {
+  starboard::ScopedLock lock(*GetImMutex());
+  return g_istallation_manager_->RevertToSuccessfulInstallation();
+}
+
+int ImRequestRollForwardToInstallation(int installation_index) {
+  starboard::ScopedLock lock(*GetImMutex());
+  return g_istallation_manager_->RequestRollForwardToInstallation(
+      installation_index);
+}
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
diff --git a/src/starboard/loader_app/installation_manager.gyp b/src/starboard/loader_app/installation_manager.gyp
new file mode 100644
index 0000000..bff8131
--- /dev/null
+++ b/src/starboard/loader_app/installation_manager.gyp
@@ -0,0 +1,71 @@
+# 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.
+
+# This files contains all targets that should be created by gyp_cobalt by
+# default.
+{
+  'targets': [
+    {
+      'target_name': 'installation_store_proto',
+      'type': 'static_library',
+      'sources': [
+        'installation_store.pb.cc',
+        'installation_store_store.pb.h',
+      ],
+      'dependencies': [
+        '<(DEPTH)/third_party/protobuf/protobuf.gyp:protobuf_lite',
+      ],
+    },
+    {
+      'target_name': 'installation_manager',
+      'type': 'static_library',
+      'sources': [
+        'installation_manager.cc',
+        'installation_manager.h',
+      ],
+      'dependencies': [
+        ':installation_store_proto',
+        '<(DEPTH)/starboard/starboard.gyp:starboard',
+      ],
+      'include_dirs': [
+        # Get protobuf headers from the chromium tree.
+        '<(DEPTH)/third_party/protobuf/src',
+      ],
+    },
+    {
+      'target_name': 'installation_manager_test',
+      'type': '<(gtest_target_type)',
+      'sources': [
+        'installation_manager_test.cc',
+        '<(DEPTH)/starboard/common/test_main.cc',
+      ],
+      'dependencies': [
+         ':installation_manager',
+         '<(DEPTH)/testing/gmock.gyp:gmock',
+         '<(DEPTH)/testing/gtest.gyp:gtest',
+      ],
+    },
+    {
+      'target_name': 'installation_manager_test_deploy',
+      'type': 'none',
+      'dependencies': [
+        'installation_manager_test',
+      ],
+      'variables': {
+        'executable_name': 'installation_manager_test',
+      },
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+    },
+  ],
+}
diff --git a/src/starboard/loader_app/installation_manager.h b/src/starboard/loader_app/installation_manager.h
new file mode 100644
index 0000000..30a27fb
--- /dev/null
+++ b/src/starboard/loader_app/installation_manager.h
@@ -0,0 +1,111 @@
+// 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.
+
+#ifndef STARBOARD_LOADER_APP_INSTALLATION_MANAGER_H_
+#define STARBOARD_LOADER_APP_INSTALLATION_MANAGER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// The installation is not successful.
+#define IM_INSTALLATION_STATUS_NOT_SUCCESS 0
+
+// The installation is successful.
+#define IM_INSTALLATION_STATUS_SUCCESS 1
+
+// An error occurd and the status of the installation is unknown.
+#define IM_INSTALLATION_STATUS_ERROR -1
+
+#define IM_SUCCESS 0
+#define IM_ERROR -1
+
+// The filename for the Installation Manager store file.
+#define IM_STORE_FILE_NAME "installation_store.pb"
+
+// The max size in bytes of the store file.
+#define IM_MAX_INSTALLATION_STORE_SIZE 1024 * 1024
+
+// The max number a tries per installation before it
+// is discarded if not successful.
+#define IM_MAX_NUM_TRIES 5
+
+// The Installation Manager API is thread safe and
+// can be used from any thread. Most calls would
+// trigger I/O operation unless the information is
+// already cached in memory.
+
+// Initializes the Installation Manager with the
+// max number of installations.
+// If the store doesn't exist an initial store would be
+// created. A subsequent call to |ImUninitialize| without
+// corresponding |ImUninitialize| will fail.
+// Returns IM_SUCCESS on success and IM_ERROR on error.
+int ImInitialize(int max_num_installations);
+
+// Uninitialize the Installation Manager.
+void ImUninitialize();
+
+// Gets the status of the |installation_index| installation.
+// Returns |IM_INSTALLATION_STATUS_SUCCESS| if the installation is successful.
+// Returns |IM_INSTALLATION_STATUS_NOT_SUCCESS| if the installation is not
+// successful. Returns |IM_INSTALLATION_STATUS_ERROR| on error.
+int ImGetInstallationStatus(int installation_index);
+
+// Returns the number of tries left for the |installation_index| installation.
+// Returns |IM_ERROR| on error.
+int ImGetInstallationNumTriesLeft(int installation_index);
+
+// Decrements the number of tries left for installation |installation_index|.
+// Returns IM_SUCCESS on success and IM_ERROR on error.
+int ImDecrementInstallationNumTries(int installation_index);
+
+// Retrieves the current installation index.
+// Returns |IM_ERROR| on error.
+int ImGetCurrentInstallationIndex();
+
+// Selects a new installation index and prepares the installation
+// slot for new installation use.
+// Returns |IM_ERROR| on error.
+int ImSelectNewInstallationIndex();
+
+// Retrieves the absolute installation path for the installation
+// |installation_index| in the buffer |path| with length |path_length|.
+// Returns IM_SUCCESS on success and IM_ERROR on error.
+int ImGetInstallationPath(int installation_index, char* path, int path_length);
+
+// Mark the installation |installation_index| as successful.
+// Returns IM_SUCCESS on success and IM_ERROR on error.
+int ImMarkInstallationSuccessful(int installation_index);
+
+// Rolls forward the installation slot requested to be used.
+// The operation makes it current installation.
+// Returns IM_SUCCESS on success and IM_ERROR on error.
+int ImRollForwardIfNeeded();
+
+// Revert to a previous successful installation.
+// Returns the installation to which it was reverted.
+// Returns |IM_ERROR| on error.
+int ImRevertToSuccessfulInstallation();
+
+// Request the installation at |installation_index| to be rolled
+// forward next time the Loader App tries to load the app.
+// Returns IM_SUCCESS on success and IM_ERROR on error.
+int ImRequestRollForwardToInstallation(int installation_index);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // STARBOARD_LOADER_APP_INSTALLATION_MANAGER_H_
diff --git a/src/starboard/loader_app/installation_manager_test.cc b/src/starboard/loader_app/installation_manager_test.cc
new file mode 100644
index 0000000..0d5f358
--- /dev/null
+++ b/src/starboard/loader_app/installation_manager_test.cc
@@ -0,0 +1,555 @@
+// 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 "starboard/loader_app/installation_manager.h"
+
+#include <string>
+#include <vector>
+
+#include "starboard/configuration_constants.h"
+#include "starboard/loader_app/installation_store.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_API_VERSION >= SB_STORAGE_PATH_VERSION && \
+    SB_API_VERSION >= SB_FILE_ATOMIC_REPLACE_VERSION
+
+#define NUMBER_INSTALLS_PARAMS ::testing::Values(2, 3, 4, 5, 6)
+
+namespace starboard {
+namespace loader_app {
+namespace installation_manager {
+
+namespace {
+
+class InstallationManagerTest : public ::testing::TestWithParam<int> {
+ protected:
+  virtual void SetUp() {
+    std::vector<char> buf(kSbFileMaxPath);
+    storage_path_implemented_ = SbSystemGetPath(kSbSystemPathStorageDirectory,
+                                                buf.data(), kSbFileMaxPath);
+    // Short-circuit the tests if the kSbSystemPathStorageDirectory is not
+    // implemented.
+    if (!storage_path_implemented_) {
+      return;
+    }
+    storage_path_ = buf.data();
+    ASSERT_TRUE(!storage_path_.empty());
+    SbDirectoryCreate(storage_path_.c_str());
+
+    installation_store_path_ = storage_path_;
+    installation_store_path_ += kSbFileSepString;
+    installation_store_path_ += IM_STORE_FILE_NAME;
+  }
+
+  void SaveStorageState(
+      const cobalt::loader::InstallationStore& installation_store) {
+    char buf[IM_MAX_INSTALLATION_STORE_SIZE];
+    ASSERT_GT(IM_MAX_INSTALLATION_STORE_SIZE, installation_store.ByteSize());
+    installation_store.SerializeToArray(buf, installation_store.ByteSize());
+
+    ASSERT_TRUE(SbFileAtomicReplace(installation_store_path_.c_str(), buf,
+                                    installation_store.ByteSize()));
+  }
+
+  void ReadStorageState(cobalt::loader::InstallationStore* installation_store) {
+    SbFile file;
+
+    file = SbFileOpen(installation_store_path_.c_str(),
+                      kSbFileOpenOnly | kSbFileRead, NULL, NULL);
+    ASSERT_TRUE(file);
+
+    char buf[IM_MAX_INSTALLATION_STORE_SIZE];
+    int count = SbFileReadAll(file, buf, IM_MAX_INSTALLATION_STORE_SIZE);
+    SB_DLOG(INFO) << "SbFileReadAll: count=" << count;
+    ASSERT_NE(-1, count);
+    ASSERT_TRUE(installation_store->ParseFromArray(buf, count));
+    SbFileClose(file);
+  }
+
+  // Roll forward to |index| installation in a |max_num_installations|
+  // configuration. The priorities at each corresponding index are provided in
+  // |setup_priorities|. After the transition the new priorities at each
+  // corresponding index should match |expected_priorities|.
+  void RollForwardHelper(int index,
+                         int max_num_installations,
+                         int* setup_priorities,
+                         int* expected_priorities) {
+    cobalt::loader::InstallationStore installation_store;
+    int priority = 0;
+    for (int i = 0; i < max_num_installations; i++) {
+      installation_store.add_installations();
+      installation_store.mutable_installations(i)->set_is_successful(false);
+      installation_store.mutable_installations(i)->set_num_tries_left(
+          IM_MAX_NUM_TRIES);
+      installation_store.mutable_installations(i)->set_priority(
+          setup_priorities[i]);
+    }
+    installation_store.set_roll_forward_to_installation(-1);
+
+    SaveStorageState(installation_store);
+
+    ASSERT_EQ(IM_SUCCESS, ImInitialize(max_num_installations));
+    ASSERT_EQ(IM_SUCCESS, ImRequestRollForwardToInstallation(index));
+    ASSERT_EQ(IM_SUCCESS, ImRollForwardIfNeeded());
+    ASSERT_EQ(index, ImGetCurrentInstallationIndex());
+    ImUninitialize();
+
+    ReadStorageState(&installation_store);
+    for (int i = 0; i < max_num_installations; i++) {
+      ASSERT_EQ(expected_priorities[i],
+                installation_store.installations(i).priority());
+    }
+  }
+
+  // Revert to a previous successful installation in a |max_num_installations|
+  // configuration. After the installation is reverted the priorities at each
+  // corresponding index should match |expected_priorities|.
+  // The |expected_succeed| indicate whether the revert is expected to succeed.
+  // For example if there is no successful installation it is impossible to
+  // revert. The |priority_success_pairs| have initialization values for
+  // priority and is_success states for each corresponding installation.
+  template <size_t N>
+  void RevertHelper(int max_num_installations,
+                    int (*priority_success_pairs)[N],
+                    int* expected_priorities,
+                    bool expected_succeed) {
+    cobalt::loader::InstallationStore installation_store;
+    int priority = 0;
+    for (int i = 0; i < max_num_installations; i++) {
+      installation_store.add_installations();
+      installation_store.mutable_installations(i)->set_priority(
+          priority_success_pairs[i][0]);
+      installation_store.mutable_installations(i)->set_is_successful(
+          priority_success_pairs[i][1]);
+      installation_store.mutable_installations(i)->set_num_tries_left(
+          IM_MAX_NUM_TRIES);
+    }
+    installation_store.set_roll_forward_to_installation(-1);
+
+    SaveStorageState(installation_store);
+
+    ASSERT_EQ(IM_SUCCESS, ImInitialize(max_num_installations));
+    int result = ImRevertToSuccessfulInstallation();
+    if (!expected_succeed) {
+      ASSERT_EQ(IM_ERROR, result);
+    }
+
+    if (expected_succeed) {
+      int index = ImGetCurrentInstallationIndex();
+      ASSERT_EQ(IM_INSTALLATION_STATUS_SUCCESS, ImGetInstallationStatus(index));
+    }
+
+    ImUninitialize();
+
+    if (expected_succeed) {
+      ReadStorageState(&installation_store);
+      for (int i = 0; i < max_num_installations; i++) {
+        ASSERT_EQ(expected_priorities[i],
+                  installation_store.installations(i).priority());
+      }
+    }
+  }
+
+  virtual void TearDown() {
+    if (!storage_path_implemented_) {
+      return;
+    }
+    ImUninitialize();
+    SbDirectory dir = SbDirectoryOpen(storage_path_.c_str(), NULL);
+    std::vector<std::string> dir_;
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+    std::vector<char> dir_entry(kSbFileMaxName);
+
+    while (SbDirectoryGetNext(dir, dir_entry.data(), dir_entry.size())) {
+      std::string full_path = storage_path_;
+      full_path += kSbFileSepString;
+      full_path += dir_entry.data();
+      SbFileDelete(full_path.c_str());
+    }
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+    SbDirectoryEntry dir_entry;
+
+    while (SbDirectoryGetNext(dir, &dir_entry)) {
+      std::string full_path = storage_path_;
+      full_path += kSbFileSepString;
+      full_path += dir_entry.name;
+      SbFileDelete(full_path.c_str());
+    }
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+    SbDirectoryClose(dir);
+    SbFileDelete(storage_path_.c_str());
+  }
+
+ protected:
+  std::string storage_path_;
+  std::string installation_store_path_;
+  bool storage_path_implemented_;
+};
+
+}  // namespace
+
+TEST_P(InstallationManagerTest, InitializeMultiple) {
+  if (!storage_path_implemented_) {
+    return;
+  }
+  ASSERT_EQ(IM_SUCCESS, ImInitialize(GetParam()));
+  ImUninitialize();
+  ASSERT_EQ(IM_SUCCESS, ImInitialize(GetParam()));
+}
+
+TEST_P(InstallationManagerTest, DefaultInstallationSuccessful) {
+  if (!storage_path_implemented_) {
+    return;
+  }
+  ASSERT_EQ(IM_SUCCESS, ImInitialize(GetParam()));
+  ASSERT_EQ(IM_INSTALLATION_STATUS_NOT_SUCCESS, ImGetInstallationStatus(0));
+}
+
+TEST_P(InstallationManagerTest, MarkInstallationSuccessful) {
+  if (!storage_path_implemented_) {
+    return;
+  }
+  ASSERT_EQ(IM_SUCCESS, ImInitialize(GetParam()));
+  ASSERT_EQ(IM_INSTALLATION_STATUS_NOT_SUCCESS, ImGetInstallationStatus(0));
+  ASSERT_EQ(IM_SUCCESS, ImMarkInstallationSuccessful(0));
+  ASSERT_EQ(IM_INSTALLATION_STATUS_SUCCESS, ImGetInstallationStatus(0));
+}
+
+TEST_P(InstallationManagerTest, DecrementInstallationNumTries) {
+  if (!storage_path_implemented_) {
+    return;
+  }
+  ASSERT_EQ(IM_SUCCESS, ImInitialize(GetParam()));
+  int max_num_tries = ImGetInstallationNumTriesLeft(0);
+  ASSERT_EQ(IM_SUCCESS, ImDecrementInstallationNumTries(0));
+  ASSERT_EQ(max_num_tries - 1, ImGetInstallationNumTriesLeft(0));
+  int num_tries = max_num_tries - 1;
+  while (num_tries--) {
+    ASSERT_EQ(IM_SUCCESS, ImDecrementInstallationNumTries(0));
+    ASSERT_EQ(num_tries, ImGetInstallationNumTriesLeft(0));
+  }
+  ASSERT_EQ(IM_ERROR, ImDecrementInstallationNumTries(0));
+}
+
+TEST_P(InstallationManagerTest, GetCurrentInstallationIndex) {
+  if (!storage_path_implemented_) {
+    return;
+  }
+  ASSERT_EQ(IM_SUCCESS, ImInitialize(GetParam()));
+  ASSERT_EQ(0, ImGetCurrentInstallationIndex());
+}
+
+TEST_P(InstallationManagerTest, SelectNewInstallationIndex) {
+  if (!storage_path_implemented_) {
+    return;
+  }
+  ASSERT_EQ(IM_SUCCESS, ImInitialize(GetParam()));
+  int index = ImSelectNewInstallationIndex();
+  if (GetParam() > 2) {
+    for (int i = 0; i < 10; i++) {
+      ASSERT_EQ(GetParam() > 2 ? GetParam() - 1 : 1, index);
+    }
+  }
+}
+
+TEST_P(InstallationManagerTest, GetInstallationPath) {
+  if (!storage_path_implemented_) {
+    return;
+  }
+  ASSERT_EQ(IM_SUCCESS, ImInitialize(GetParam()));
+  std::vector<char> buf0(kSbFileMaxPath);
+  ASSERT_EQ(IM_SUCCESS, ImGetInstallationPath(0, buf0.data(), kSbFileMaxPath));
+  // For 3 or more slots the 0 index one is under the content directory,
+  // which will not have the correct file path for a Cobalt binary when running
+  // a test.
+  if (GetParam() < 3) {
+    ASSERT_TRUE(SbFileExists(buf0.data()));
+  }
+  std::vector<char> buf1(kSbFileMaxPath);
+  ASSERT_EQ(IM_SUCCESS, ImGetInstallationPath(1, buf1.data(), kSbFileMaxPath));
+  ASSERT_TRUE(SbFileExists(buf1.data()));
+}
+
+TEST_P(InstallationManagerTest, RollForwardIfNeeded) {
+  if (!storage_path_implemented_) {
+    return;
+  }
+  ASSERT_EQ(IM_SUCCESS, ImInitialize(GetParam()));
+  ASSERT_EQ(IM_SUCCESS, ImRollForwardIfNeeded());
+  ASSERT_EQ(0, ImGetCurrentInstallationIndex());
+  ASSERT_EQ(IM_SUCCESS, ImRequestRollForwardToInstallation(1));
+  ASSERT_EQ(0, ImGetCurrentInstallationIndex());
+  ASSERT_EQ(IM_SUCCESS, ImRollForwardIfNeeded());
+  ASSERT_EQ(1, ImGetCurrentInstallationIndex());
+  if (GetParam() > 2) {
+    for (int i = 2; i < GetParam() - 1; i++) {
+      ASSERT_EQ(IM_SUCCESS, ImRequestRollForwardToInstallation(i));
+      ASSERT_EQ(i - 1, ImGetCurrentInstallationIndex());
+      ASSERT_EQ(IM_SUCCESS, ImRollForwardIfNeeded());
+      ASSERT_EQ(i, ImGetCurrentInstallationIndex());
+    }
+  }
+  for (int i = 0; i < 10; i++) {
+    int new_index = i % GetParam();
+    ASSERT_EQ(IM_SUCCESS, ImRequestRollForwardToInstallation(new_index));
+    ASSERT_EQ(IM_SUCCESS, ImRollForwardIfNeeded());
+    ASSERT_EQ(new_index, ImGetCurrentInstallationIndex());
+  }
+}
+
+TEST_P(InstallationManagerTest, RevertToSuccessfulInstallation) {
+  if (!storage_path_implemented_) {
+    return;
+  }
+  ASSERT_EQ(IM_SUCCESS, ImInitialize(GetParam()));
+  ASSERT_EQ(0, ImGetCurrentInstallationIndex());
+  ASSERT_EQ(IM_SUCCESS, ImMarkInstallationSuccessful(0));
+  ASSERT_EQ(IM_SUCCESS, ImRequestRollForwardToInstallation(1));
+  ASSERT_EQ(0, ImGetCurrentInstallationIndex());
+  ASSERT_EQ(IM_SUCCESS, ImRollForwardIfNeeded());
+  ASSERT_EQ(1, ImGetCurrentInstallationIndex());
+  ASSERT_EQ(IM_SUCCESS, ImMarkInstallationSuccessful(1));
+  ASSERT_EQ(1, ImGetCurrentInstallationIndex());
+  ASSERT_EQ(0, ImRevertToSuccessfulInstallation());
+  ASSERT_EQ(0, ImGetCurrentInstallationIndex());
+}
+
+TEST_F(InstallationManagerTest, InvalidInput) {
+  if (!storage_path_implemented_) {
+    return;
+  }
+  ASSERT_EQ(IM_SUCCESS, ImInitialize(3));
+  ASSERT_EQ(IM_INSTALLATION_STATUS_ERROR, ImGetInstallationStatus(10));
+  ASSERT_EQ(IM_SUCCESS, ImMarkInstallationSuccessful(0));
+  ASSERT_EQ(IM_INSTALLATION_STATUS_ERROR, ImGetInstallationStatus(-2));
+  ASSERT_EQ(IM_ERROR, ImRevertToSuccessfulInstallation());
+  ASSERT_EQ(IM_ERROR, ImMarkInstallationSuccessful(10));
+  ASSERT_EQ(IM_ERROR, ImMarkInstallationSuccessful(-2));
+  ASSERT_EQ(IM_ERROR, ImDecrementInstallationNumTries(10));
+  ASSERT_EQ(IM_ERROR, ImDecrementInstallationNumTries(-2));
+
+  std::vector<char> buf(kSbFileMaxPath);
+  ASSERT_EQ(IM_ERROR, ImGetInstallationPath(10, buf.data(), kSbFileMaxPath));
+  ASSERT_EQ(IM_ERROR, ImGetInstallationPath(-2, buf.data(), kSbFileMaxPath));
+  ASSERT_EQ(IM_ERROR, ImGetInstallationPath(-2, NULL, kSbFileMaxPath));
+}
+
+TEST_F(InstallationManagerTest, RollForwardMatrix20) {
+  if (!storage_path_implemented_) {
+    return;
+  }
+  int max_num_installations = 2;
+  int setup_priorities[2][2] = {
+      {0, 1}, {1, 0},
+  };
+
+  int expected_priorities[2][2] = {
+      {0, 1}, {0, 1},
+  };
+
+  for (int i = 0; i < 2; i++) {
+    SB_LOG(INFO) << "Testing Maxtrix20 expected_priorities at index: " << i;
+    RollForwardHelper(0, 2, setup_priorities[i], expected_priorities[i]);
+  }
+}
+
+TEST_F(InstallationManagerTest, RollForwardMatrix21) {
+  if (!storage_path_implemented_) {
+    return;
+  }
+  int max_num_installations = 2;
+  int setup_priorities[2][2] = {
+      {0, 1}, {1, 0},
+  };
+
+  int expected_priorities[2][2] = {
+      {1, 0}, {1, 0},
+  };
+
+  for (int i = 0; i < 2; i++) {
+    SB_LOG(INFO) << "Testing Maxtrix21 expected_priorities at index: " << i;
+    RollForwardHelper(1, 2, setup_priorities[i], expected_priorities[i]);
+  }
+}
+
+TEST_F(InstallationManagerTest, RollForwardMatrix30) {
+  if (!storage_path_implemented_) {
+    return;
+  }
+  int max_num_installations = 3;
+  int setup_priorities[6][3] = {
+      {0, 1, 2}, {0, 2, 1}, {1, 2, 0}, {1, 0, 2}, {2, 0, 1}, {2, 1, 0},
+  };
+
+  int expected_priorities[6][3] = {
+      {0, 1, 2}, {0, 2, 1}, {0, 2, 1}, {0, 1, 2}, {0, 1, 2}, {0, 2, 1},
+  };
+
+  for (int i = 0; i < 6; i++) {
+    SB_LOG(INFO) << "Testing Maxtrix30 expected_priorities at index: " << i;
+    RollForwardHelper(0, 3, setup_priorities[i], expected_priorities[i]);
+  }
+}
+
+TEST_F(InstallationManagerTest, RollForwardMatrix31) {
+  if (!storage_path_implemented_) {
+    return;
+  }
+  int max_num_installations = 3;
+  int setup_priorities[6][3] = {
+      {0, 1, 2}, {0, 2, 1}, {1, 2, 0}, {1, 0, 2}, {2, 0, 1}, {2, 1, 0},
+  };
+
+  int expected_priorities[6][3] = {
+      {1, 0, 2}, {1, 0, 2}, {2, 0, 1}, {1, 0, 2}, {2, 0, 1}, {2, 0, 1},
+  };
+
+  for (int i = 0; i < 6; i++) {
+    SB_LOG(INFO) << "Testing Maxtrix31 expected_priorities at index: " << i;
+    RollForwardHelper(1, 3, setup_priorities[i], expected_priorities[i]);
+  }
+}
+
+TEST_F(InstallationManagerTest, RollForwardMatrix32) {
+  if (!storage_path_implemented_) {
+    return;
+  }
+  int max_num_installations = 3;
+  int setup_priorities[6][3] = {
+      {0, 1, 2}, {0, 2, 1}, {1, 2, 0}, {1, 0, 2}, {2, 0, 1}, {2, 1, 0},
+  };
+
+  int expected_priorities[6][3] = {
+      {1, 2, 0}, {1, 2, 0}, {1, 2, 0}, {2, 1, 0}, {2, 1, 0}, {2, 1, 0},
+  };
+  for (int i = 0; i < 6; i++) {
+    SB_LOG(INFO) << "Testing Maxtrix32 expected_priorities at index: " << i;
+    RollForwardHelper(2, 3, setup_priorities[i], expected_priorities[i]);
+  }
+}
+
+TEST_F(InstallationManagerTest, RevertMatrix2) {
+  if (!storage_path_implemented_) {
+    return;
+  }
+  int max_num_installations = 2;
+  // List of 2 index pairs of (priority, is_success) for which the revert would
+  // fail.
+  int priority_success_pairs1[4][2][2] = {
+      {{0, 1}, {1, 0}}, {{0, 0}, {1, 0}}, {{1, 0}, {0, 1}}, {{1, 0}, {0, 0}},
+  };
+
+  // List of 2 index pairs of (priority, is_success) for which the revert would
+  // succeed.
+  int priority_success_pairs2[4][2][2] = {
+      {{0, 0}, {1, 1}}, {{1, 1}, {0, 0}}, {{0, 1}, {1, 1}}, {{1, 1}, {0, 1}},
+  };
+
+  int expected_priorities[4][2] = {
+      {1, 0}, {0, 1}, {1, 0}, {0, 1},
+  };
+
+  for (int i = 0; i < 4; i++) {
+    SB_LOG(INFO) << "Testing RevertMaxtrix2 expected_priorities at index: "
+                 << i;
+    RevertHelper(2, priority_success_pairs1[i], NULL, false);
+  }
+  for (int i = 0; i < 4; i++) {
+    SB_LOG(INFO) << "Testing RevertMaxtrix2 expected_priorities at index: "
+                 << i;
+    RevertHelper(2, priority_success_pairs2[i], expected_priorities[i], true);
+  }
+}
+
+TEST_F(InstallationManagerTest, RevertMatrix3) {
+  if (!storage_path_implemented_) {
+    return;
+  }
+  int max_num_installations = 3;
+
+  // List of 3 index pairs of (priority, is_success) for which the revert would
+  // fail.
+  int priority_success_pairs1[12][3][2] = {
+      {{0, 0}, {1, 0}, {2, 0}}, {{0, 1}, {1, 0}, {2, 0}},
+
+      {{0, 0}, {2, 0}, {1, 0}}, {{0, 1}, {2, 0}, {1, 0}},
+
+      {{1, 0}, {2, 0}, {0, 0}}, {{1, 0}, {2, 0}, {0, 1}},
+
+      {{1, 0}, {0, 0}, {2, 0}}, {{1, 0}, {0, 1}, {2, 0}},
+
+      {{2, 0}, {0, 0}, {1, 0}}, {{2, 0}, {0, 1}, {1, 0}},
+
+      {{2, 0}, {1, 0}, {0, 0}}, {{2, 0}, {1, 0}, {0, 1}},
+  };
+
+  // List of 3 index pairs of (priority, is_success) for which the revert would
+  // succeed.
+  int priority_success_pairs2[36][3][2] = {
+      {{0, 0}, {1, 0}, {2, 1}}, {{0, 0}, {1, 1}, {2, 0}},
+      {{0, 0}, {1, 1}, {2, 1}}, {{0, 1}, {1, 1}, {2, 0}},
+      {{0, 1}, {1, 1}, {2, 1}}, {{0, 1}, {1, 0}, {2, 1}},
+
+      {{0, 0}, {2, 0}, {1, 1}}, {{0, 0}, {2, 1}, {1, 0}},
+      {{0, 0}, {2, 1}, {1, 1}}, {{0, 1}, {2, 1}, {1, 0}},
+      {{0, 1}, {2, 0}, {1, 1}}, {{0, 1}, {2, 1}, {1, 1}},
+
+      {{1, 0}, {2, 1}, {0, 0}}, {{1, 1}, {2, 0}, {0, 0}},
+      {{1, 0}, {2, 1}, {0, 1}}, {{1, 1}, {2, 1}, {0, 0}},
+      {{1, 1}, {2, 0}, {0, 1}}, {{1, 1}, {2, 1}, {0, 1}},
+
+      {{1, 0}, {0, 0}, {2, 1}}, {{1, 1}, {0, 0}, {2, 0}},
+      {{1, 0}, {0, 1}, {2, 1}}, {{1, 1}, {0, 1}, {2, 0}},
+      {{1, 1}, {0, 0}, {2, 1}}, {{1, 1}, {0, 1}, {2, 1}},
+
+      {{2, 0}, {0, 0}, {1, 1}}, {{2, 1}, {0, 0}, {1, 0}},
+      {{2, 0}, {0, 1}, {1, 1}}, {{2, 1}, {0, 1}, {1, 0}},
+      {{2, 1}, {0, 0}, {1, 1}}, {{2, 1}, {0, 1}, {1, 1}},
+
+      {{2, 0}, {1, 1}, {0, 0}}, {{2, 1}, {1, 0}, {0, 0}},
+      {{2, 0}, {1, 1}, {0, 1}}, {{2, 1}, {1, 1}, {0, 0}},
+      {{2, 1}, {1, 0}, {0, 1}}, {{2, 1}, {1, 1}, {0, 1}},
+  };
+
+  int expected_priorities[36][3] = {
+      {2, 1, 0}, {2, 0, 1}, {2, 0, 1}, {2, 0, 1}, {2, 0, 1}, {2, 1, 0},
+      {2, 1, 0}, {2, 0, 1}, {2, 1, 0}, {2, 0, 1}, {2, 1, 0}, {2, 1, 0},
+      {1, 0, 2}, {0, 1, 2}, {1, 0, 2}, {0, 1, 2}, {0, 1, 2}, {0, 1, 2},
+      {1, 2, 0}, {0, 2, 1}, {1, 2, 0}, {0, 2, 1}, {0, 2, 1}, {0, 2, 1},
+      {1, 2, 0}, {0, 2, 1}, {1, 2, 0}, {0, 2, 1}, {1, 2, 0}, {1, 2, 0},
+      {1, 0, 2}, {0, 1, 2}, {1, 0, 2}, {1, 0, 2}, {0, 1, 2}, {1, 0, 2},
+  };
+
+  for (int i = 0; i < 12; i++) {
+    SB_LOG(INFO) << "Testing RevertMaxtrix3 expected_priorities at index: "
+                 << i;
+    RevertHelper(3, priority_success_pairs1[i], NULL, false);
+  }
+
+  for (int i = 0; i < 36; i++) {
+    SB_LOG(INFO) << "Testing RevertMaxtrix3 expected_priorities at index: "
+                 << i;
+    RevertHelper(3, priority_success_pairs2[i], expected_priorities[i], true);
+  }
+}
+INSTANTIATE_TEST_CASE_P(NumberOfMaxInstallations,
+                        InstallationManagerTest,
+                        NUMBER_INSTALLS_PARAMS);
+
+}  // namespace installation_manager
+}  // namespace loader_app
+}  // namespace starboard
+
+#endif  // SB_API_VERSION >= SB_STORAGE_PATH_VERSION && SB_API_VERSION >=
+        // SB_FILE_ATOMIC_REPLACE_VERSION
diff --git a/src/starboard/loader_app/installation_store.pb.cc b/src/starboard/loader_app/installation_store.pb.cc
new file mode 100644
index 0000000..7d6e3ae
--- /dev/null
+++ b/src/starboard/loader_app/installation_store.pb.cc
@@ -0,0 +1,660 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: installation_store.proto
+
+#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
+#include "installation_store.pb.h"
+
+#include <algorithm>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/port.h>
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/stubs/starboard_poem.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/wire_format_lite_inl.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+// @@protoc_insertion_point(includes)
+
+namespace cobalt {
+namespace loader {
+
+void protobuf_ShutdownFile_installation_5fstore_2eproto() {
+  delete Installation::default_instance_;
+  delete InstallationStore::default_instance_;
+}
+
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+void protobuf_AddDesc_installation_5fstore_2eproto_impl() {
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+#else
+void protobuf_AddDesc_installation_5fstore_2eproto() {
+  static bool already_here = false;
+  if (already_here) return;
+  already_here = true;
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+#endif
+  Installation::default_instance_ = new Installation();
+  InstallationStore::default_instance_ = new InstallationStore();
+  Installation::default_instance_->InitAsDefaultInstance();
+  InstallationStore::default_instance_->InitAsDefaultInstance();
+  ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_installation_5fstore_2eproto);
+}
+
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AddDesc_installation_5fstore_2eproto_once_);
+void protobuf_AddDesc_installation_5fstore_2eproto() {
+  ::google::protobuf::GoogleOnceInit(&protobuf_AddDesc_installation_5fstore_2eproto_once_,
+                 &protobuf_AddDesc_installation_5fstore_2eproto_impl);
+}
+#else
+// Force AddDescriptors() to be called at static initialization time.
+struct StaticDescriptorInitializer_installation_5fstore_2eproto {
+  StaticDescriptorInitializer_installation_5fstore_2eproto() {
+    protobuf_AddDesc_installation_5fstore_2eproto();
+  }
+} static_descriptor_initializer_installation_5fstore_2eproto_;
+#endif
+
+namespace {
+
+static void MergeFromFail(int line) GOOGLE_ATTRIBUTE_COLD;
+GOOGLE_ATTRIBUTE_NOINLINE static void MergeFromFail(int line) {
+  GOOGLE_CHECK(false) << __FILE__ << ":" << line;
+}
+
+}  // namespace
+
+
+// ===================================================================
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
+const int Installation::kIsSuccessfulFieldNumber;
+const int Installation::kNumTriesLeftFieldNumber;
+const int Installation::kPriorityFieldNumber;
+#endif  // !defined(_MSC_VER) || _MSC_VER >= 1900
+
+Installation::Installation()
+  : ::google::protobuf::MessageLite(), _arena_ptr_(NULL) {
+  SharedCtor();
+  // @@protoc_insertion_point(constructor:cobalt.loader.Installation)
+}
+
+void Installation::InitAsDefaultInstance() {
+  _is_default_instance_ = true;
+}
+
+Installation::Installation(const Installation& from)
+  : ::google::protobuf::MessageLite(),
+    _arena_ptr_(NULL) {
+  SharedCtor();
+  MergeFrom(from);
+  // @@protoc_insertion_point(copy_constructor:cobalt.loader.Installation)
+}
+
+void Installation::SharedCtor() {
+    _is_default_instance_ = false;
+  _cached_size_ = 0;
+  is_successful_ = false;
+  num_tries_left_ = 0;
+  priority_ = 0;
+}
+
+Installation::~Installation() {
+  // @@protoc_insertion_point(destructor:cobalt.loader.Installation)
+  SharedDtor();
+}
+
+void Installation::SharedDtor() {
+  #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+  if (this != &default_instance()) {
+  #else
+  if (this != default_instance_) {
+  #endif
+  }
+}
+
+void Installation::SetCachedSize(int size) const {
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const Installation& Installation::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+  protobuf_AddDesc_installation_5fstore_2eproto();
+#else
+  if (default_instance_ == NULL) protobuf_AddDesc_installation_5fstore_2eproto();
+#endif
+  return *default_instance_;
+}
+
+Installation* Installation::default_instance_ = NULL;
+
+Installation* Installation::New(::google::protobuf::Arena* arena) const {
+  Installation* n = new Installation;
+  if (arena != NULL) {
+    arena->Own(n);
+  }
+  return n;
+}
+
+void Installation::Clear() {
+// @@protoc_insertion_point(message_clear_start:cobalt.loader.Installation)
+#if defined(__clang__)
+#define ZR_HELPER_(f) \
+  _Pragma("clang diagnostic push") \
+  _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \
+  __builtin_offsetof(Installation, f) \
+  _Pragma("clang diagnostic pop")
+#else
+#define ZR_HELPER_(f) reinterpret_cast<char*>(\
+  &reinterpret_cast<Installation*>(16)->f)
+#endif
+
+#define ZR_(first, last) do {\
+  ::memset(&first, 0,\
+           ZR_HELPER_(last) - ZR_HELPER_(first) + sizeof(last));\
+} while (0)
+
+  ZR_(is_successful_, priority_);
+
+#undef ZR_HELPER_
+#undef ZR_
+
+}
+
+bool Installation::MergePartialFromCodedStream(
+    ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure
+  ::google::protobuf::uint32 tag;
+  // @@protoc_insertion_point(parse_start:cobalt.loader.Installation)
+  for (;;) {
+    ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+    tag = p.first;
+    if (!p.second) goto handle_unusual;
+    switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+      // optional bool is_successful = 1;
+      case 1: {
+        if (tag == 8) {
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+                 input, &is_successful_)));
+
+        } else {
+          goto handle_unusual;
+        }
+        if (input->ExpectTag(16)) goto parse_num_tries_left;
+        break;
+      }
+
+      // optional int32 num_tries_left = 2;
+      case 2: {
+        if (tag == 16) {
+         parse_num_tries_left:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+                 input, &num_tries_left_)));
+
+        } else {
+          goto handle_unusual;
+        }
+        if (input->ExpectTag(24)) goto parse_priority;
+        break;
+      }
+
+      // optional int32 priority = 3;
+      case 3: {
+        if (tag == 24) {
+         parse_priority:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+                 input, &priority_)));
+
+        } else {
+          goto handle_unusual;
+        }
+        if (input->ExpectAtEnd()) goto success;
+        break;
+      }
+
+      default: {
+      handle_unusual:
+        if (tag == 0 ||
+            ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+          goto success;
+        }
+        DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag));
+        break;
+      }
+    }
+  }
+success:
+  // @@protoc_insertion_point(parse_success:cobalt.loader.Installation)
+  return true;
+failure:
+  // @@protoc_insertion_point(parse_failure:cobalt.loader.Installation)
+  return false;
+#undef DO_
+}
+
+void Installation::SerializeWithCachedSizes(
+    ::google::protobuf::io::CodedOutputStream* output) const {
+  // @@protoc_insertion_point(serialize_start:cobalt.loader.Installation)
+  // optional bool is_successful = 1;
+  if (this->is_successful() != 0) {
+    ::google::protobuf::internal::WireFormatLite::WriteBool(1, this->is_successful(), output);
+  }
+
+  // optional int32 num_tries_left = 2;
+  if (this->num_tries_left() != 0) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->num_tries_left(), output);
+  }
+
+  // optional int32 priority = 3;
+  if (this->priority() != 0) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt32(3, this->priority(), output);
+  }
+
+  // @@protoc_insertion_point(serialize_end:cobalt.loader.Installation)
+}
+
+int Installation::ByteSize() const {
+// @@protoc_insertion_point(message_byte_size_start:cobalt.loader.Installation)
+  int total_size = 0;
+
+  // optional bool is_successful = 1;
+  if (this->is_successful() != 0) {
+    total_size += 1 + 1;
+  }
+
+  // optional int32 num_tries_left = 2;
+  if (this->num_tries_left() != 0) {
+    total_size += 1 +
+      ::google::protobuf::internal::WireFormatLite::Int32Size(
+        this->num_tries_left());
+  }
+
+  // optional int32 priority = 3;
+  if (this->priority() != 0) {
+    total_size += 1 +
+      ::google::protobuf::internal::WireFormatLite::Int32Size(
+        this->priority());
+  }
+
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = total_size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+  return total_size;
+}
+
+void Installation::CheckTypeAndMergeFrom(
+    const ::google::protobuf::MessageLite& from) {
+  MergeFrom(*::google::protobuf::down_cast<const Installation*>(&from));
+}
+
+void Installation::MergeFrom(const Installation& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:cobalt.loader.Installation)
+  if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
+  if (from.is_successful() != 0) {
+    set_is_successful(from.is_successful());
+  }
+  if (from.num_tries_left() != 0) {
+    set_num_tries_left(from.num_tries_left());
+  }
+  if (from.priority() != 0) {
+    set_priority(from.priority());
+  }
+}
+
+void Installation::CopyFrom(const Installation& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:cobalt.loader.Installation)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Installation::IsInitialized() const {
+
+  return true;
+}
+
+void Installation::Swap(Installation* other) {
+  if (other == this) return;
+  InternalSwap(other);
+}
+void Installation::InternalSwap(Installation* other) {
+  std::swap(is_successful_, other->is_successful_);
+  std::swap(num_tries_left_, other->num_tries_left_);
+  std::swap(priority_, other->priority_);
+  _unknown_fields_.Swap(&other->_unknown_fields_);
+  std::swap(_cached_size_, other->_cached_size_);
+}
+
+::std::string Installation::GetTypeName() const {
+  return "cobalt.loader.Installation";
+}
+
+#if PROTOBUF_INLINE_NOT_IN_HEADERS
+// Installation
+
+// optional bool is_successful = 1;
+void Installation::clear_is_successful() {
+  is_successful_ = false;
+}
+ bool Installation::is_successful() const {
+  // @@protoc_insertion_point(field_get:cobalt.loader.Installation.is_successful)
+  return is_successful_;
+}
+ void Installation::set_is_successful(bool value) {
+  
+  is_successful_ = value;
+  // @@protoc_insertion_point(field_set:cobalt.loader.Installation.is_successful)
+}
+
+// optional int32 num_tries_left = 2;
+void Installation::clear_num_tries_left() {
+  num_tries_left_ = 0;
+}
+ ::google::protobuf::int32 Installation::num_tries_left() const {
+  // @@protoc_insertion_point(field_get:cobalt.loader.Installation.num_tries_left)
+  return num_tries_left_;
+}
+ void Installation::set_num_tries_left(::google::protobuf::int32 value) {
+  
+  num_tries_left_ = value;
+  // @@protoc_insertion_point(field_set:cobalt.loader.Installation.num_tries_left)
+}
+
+// optional int32 priority = 3;
+void Installation::clear_priority() {
+  priority_ = 0;
+}
+ ::google::protobuf::int32 Installation::priority() const {
+  // @@protoc_insertion_point(field_get:cobalt.loader.Installation.priority)
+  return priority_;
+}
+ void Installation::set_priority(::google::protobuf::int32 value) {
+  
+  priority_ = value;
+  // @@protoc_insertion_point(field_set:cobalt.loader.Installation.priority)
+}
+
+#endif  // PROTOBUF_INLINE_NOT_IN_HEADERS
+
+// ===================================================================
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
+const int InstallationStore::kInstallationsFieldNumber;
+const int InstallationStore::kRollForwardToInstallationFieldNumber;
+#endif  // !defined(_MSC_VER) || _MSC_VER >= 1900
+
+InstallationStore::InstallationStore()
+  : ::google::protobuf::MessageLite(), _arena_ptr_(NULL) {
+  SharedCtor();
+  // @@protoc_insertion_point(constructor:cobalt.loader.InstallationStore)
+}
+
+void InstallationStore::InitAsDefaultInstance() {
+  _is_default_instance_ = true;
+}
+
+InstallationStore::InstallationStore(const InstallationStore& from)
+  : ::google::protobuf::MessageLite(),
+    _arena_ptr_(NULL) {
+  SharedCtor();
+  MergeFrom(from);
+  // @@protoc_insertion_point(copy_constructor:cobalt.loader.InstallationStore)
+}
+
+void InstallationStore::SharedCtor() {
+    _is_default_instance_ = false;
+  _cached_size_ = 0;
+  roll_forward_to_installation_ = 0;
+}
+
+InstallationStore::~InstallationStore() {
+  // @@protoc_insertion_point(destructor:cobalt.loader.InstallationStore)
+  SharedDtor();
+}
+
+void InstallationStore::SharedDtor() {
+  #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+  if (this != &default_instance()) {
+  #else
+  if (this != default_instance_) {
+  #endif
+  }
+}
+
+void InstallationStore::SetCachedSize(int size) const {
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const InstallationStore& InstallationStore::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+  protobuf_AddDesc_installation_5fstore_2eproto();
+#else
+  if (default_instance_ == NULL) protobuf_AddDesc_installation_5fstore_2eproto();
+#endif
+  return *default_instance_;
+}
+
+InstallationStore* InstallationStore::default_instance_ = NULL;
+
+InstallationStore* InstallationStore::New(::google::protobuf::Arena* arena) const {
+  InstallationStore* n = new InstallationStore;
+  if (arena != NULL) {
+    arena->Own(n);
+  }
+  return n;
+}
+
+void InstallationStore::Clear() {
+// @@protoc_insertion_point(message_clear_start:cobalt.loader.InstallationStore)
+  roll_forward_to_installation_ = 0;
+  installations_.Clear();
+}
+
+bool InstallationStore::MergePartialFromCodedStream(
+    ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure
+  ::google::protobuf::uint32 tag;
+  // @@protoc_insertion_point(parse_start:cobalt.loader.InstallationStore)
+  for (;;) {
+    ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+    tag = p.first;
+    if (!p.second) goto handle_unusual;
+    switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+      // repeated .cobalt.loader.Installation installations = 1;
+      case 1: {
+        if (tag == 10) {
+          DO_(input->IncrementRecursionDepth());
+         parse_loop_installations:
+          DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth(
+                input, add_installations()));
+        } else {
+          goto handle_unusual;
+        }
+        if (input->ExpectTag(10)) goto parse_loop_installations;
+        input->UnsafeDecrementRecursionDepth();
+        if (input->ExpectTag(16)) goto parse_roll_forward_to_installation;
+        break;
+      }
+
+      // optional int32 roll_forward_to_installation = 2;
+      case 2: {
+        if (tag == 16) {
+         parse_roll_forward_to_installation:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+                 input, &roll_forward_to_installation_)));
+
+        } else {
+          goto handle_unusual;
+        }
+        if (input->ExpectAtEnd()) goto success;
+        break;
+      }
+
+      default: {
+      handle_unusual:
+        if (tag == 0 ||
+            ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+          goto success;
+        }
+        DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag));
+        break;
+      }
+    }
+  }
+success:
+  // @@protoc_insertion_point(parse_success:cobalt.loader.InstallationStore)
+  return true;
+failure:
+  // @@protoc_insertion_point(parse_failure:cobalt.loader.InstallationStore)
+  return false;
+#undef DO_
+}
+
+void InstallationStore::SerializeWithCachedSizes(
+    ::google::protobuf::io::CodedOutputStream* output) const {
+  // @@protoc_insertion_point(serialize_start:cobalt.loader.InstallationStore)
+  // repeated .cobalt.loader.Installation installations = 1;
+  for (unsigned int i = 0, n = this->installations_size(); i < n; i++) {
+    ::google::protobuf::internal::WireFormatLite::WriteMessage(
+      1, this->installations(i), output);
+  }
+
+  // optional int32 roll_forward_to_installation = 2;
+  if (this->roll_forward_to_installation() != 0) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->roll_forward_to_installation(), output);
+  }
+
+  // @@protoc_insertion_point(serialize_end:cobalt.loader.InstallationStore)
+}
+
+int InstallationStore::ByteSize() const {
+// @@protoc_insertion_point(message_byte_size_start:cobalt.loader.InstallationStore)
+  int total_size = 0;
+
+  // optional int32 roll_forward_to_installation = 2;
+  if (this->roll_forward_to_installation() != 0) {
+    total_size += 1 +
+      ::google::protobuf::internal::WireFormatLite::Int32Size(
+        this->roll_forward_to_installation());
+  }
+
+  // repeated .cobalt.loader.Installation installations = 1;
+  total_size += 1 * this->installations_size();
+  for (int i = 0; i < this->installations_size(); i++) {
+    total_size +=
+      ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+        this->installations(i));
+  }
+
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = total_size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+  return total_size;
+}
+
+void InstallationStore::CheckTypeAndMergeFrom(
+    const ::google::protobuf::MessageLite& from) {
+  MergeFrom(*::google::protobuf::down_cast<const InstallationStore*>(&from));
+}
+
+void InstallationStore::MergeFrom(const InstallationStore& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:cobalt.loader.InstallationStore)
+  if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
+  installations_.MergeFrom(from.installations_);
+  if (from.roll_forward_to_installation() != 0) {
+    set_roll_forward_to_installation(from.roll_forward_to_installation());
+  }
+}
+
+void InstallationStore::CopyFrom(const InstallationStore& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:cobalt.loader.InstallationStore)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool InstallationStore::IsInitialized() const {
+
+  return true;
+}
+
+void InstallationStore::Swap(InstallationStore* other) {
+  if (other == this) return;
+  InternalSwap(other);
+}
+void InstallationStore::InternalSwap(InstallationStore* other) {
+  installations_.UnsafeArenaSwap(&other->installations_);
+  std::swap(roll_forward_to_installation_, other->roll_forward_to_installation_);
+  _unknown_fields_.Swap(&other->_unknown_fields_);
+  std::swap(_cached_size_, other->_cached_size_);
+}
+
+::std::string InstallationStore::GetTypeName() const {
+  return "cobalt.loader.InstallationStore";
+}
+
+#if PROTOBUF_INLINE_NOT_IN_HEADERS
+// InstallationStore
+
+// repeated .cobalt.loader.Installation installations = 1;
+int InstallationStore::installations_size() const {
+  return installations_.size();
+}
+void InstallationStore::clear_installations() {
+  installations_.Clear();
+}
+const ::cobalt::loader::Installation& InstallationStore::installations(int index) const {
+  // @@protoc_insertion_point(field_get:cobalt.loader.InstallationStore.installations)
+  return installations_.Get(index);
+}
+::cobalt::loader::Installation* InstallationStore::mutable_installations(int index) {
+  // @@protoc_insertion_point(field_mutable:cobalt.loader.InstallationStore.installations)
+  return installations_.Mutable(index);
+}
+::cobalt::loader::Installation* InstallationStore::add_installations() {
+  // @@protoc_insertion_point(field_add:cobalt.loader.InstallationStore.installations)
+  return installations_.Add();
+}
+::google::protobuf::RepeatedPtrField< ::cobalt::loader::Installation >*
+InstallationStore::mutable_installations() {
+  // @@protoc_insertion_point(field_mutable_list:cobalt.loader.InstallationStore.installations)
+  return &installations_;
+}
+const ::google::protobuf::RepeatedPtrField< ::cobalt::loader::Installation >&
+InstallationStore::installations() const {
+  // @@protoc_insertion_point(field_list:cobalt.loader.InstallationStore.installations)
+  return installations_;
+}
+
+// optional int32 roll_forward_to_installation = 2;
+void InstallationStore::clear_roll_forward_to_installation() {
+  roll_forward_to_installation_ = 0;
+}
+ ::google::protobuf::int32 InstallationStore::roll_forward_to_installation() const {
+  // @@protoc_insertion_point(field_get:cobalt.loader.InstallationStore.roll_forward_to_installation)
+  return roll_forward_to_installation_;
+}
+ void InstallationStore::set_roll_forward_to_installation(::google::protobuf::int32 value) {
+  
+  roll_forward_to_installation_ = value;
+  // @@protoc_insertion_point(field_set:cobalt.loader.InstallationStore.roll_forward_to_installation)
+}
+
+#endif  // PROTOBUF_INLINE_NOT_IN_HEADERS
+
+// @@protoc_insertion_point(namespace_scope)
+
+}  // namespace loader
+}  // namespace cobalt
+
+// @@protoc_insertion_point(global_scope)
diff --git a/src/starboard/loader_app/installation_store.pb.h b/src/starboard/loader_app/installation_store.pb.h
new file mode 100644
index 0000000..a3ad5c9
--- /dev/null
+++ b/src/starboard/loader_app/installation_store.pb.h
@@ -0,0 +1,361 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: installation_store.proto
+
+#ifndef PROTOBUF_installation_5fstore_2eproto__INCLUDED
+#define PROTOBUF_installation_5fstore_2eproto__INCLUDED
+
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+
+#if GOOGLE_PROTOBUF_VERSION < 3000000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers.  Please update
+#error your headers.
+#endif
+#if 3000000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers.  Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/extension_set.h>
+// @@protoc_insertion_point(includes)
+
+namespace cobalt {
+namespace loader {
+
+// Internal implementation detail -- do not call these.
+void protobuf_AddDesc_installation_5fstore_2eproto();
+void protobuf_AssignDesc_installation_5fstore_2eproto();
+void protobuf_ShutdownFile_installation_5fstore_2eproto();
+
+class Installation;
+class InstallationStore;
+
+// ===================================================================
+
+class Installation : public ::google::protobuf::MessageLite {
+ public:
+  Installation();
+  virtual ~Installation();
+
+  Installation(const Installation& from);
+
+  inline Installation& operator=(const Installation& from) {
+    CopyFrom(from);
+    return *this;
+  }
+
+  static const Installation& default_instance();
+
+  #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+  // Returns the internal default instance pointer. This function can
+  // return NULL thus should not be used by the user. This is intended
+  // for Protobuf internal code. Please use default_instance() declared
+  // above instead.
+  static inline const Installation* internal_default_instance() {
+    return default_instance_;
+  }
+  #endif
+
+  GOOGLE_ATTRIBUTE_NOINLINE void Swap(Installation* other);
+
+  // implements Message ----------------------------------------------
+
+  inline Installation* New() const { return New(NULL); }
+
+  Installation* New(::google::protobuf::Arena* arena) const;
+  void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+  void CopyFrom(const Installation& from);
+  void MergeFrom(const Installation& from);
+  void Clear();
+  bool IsInitialized() const;
+
+  int ByteSize() const;
+  bool MergePartialFromCodedStream(
+      ::google::protobuf::io::CodedInputStream* input);
+  void SerializeWithCachedSizes(
+      ::google::protobuf::io::CodedOutputStream* output) const;
+  void DiscardUnknownFields();
+  int GetCachedSize() const { return _cached_size_; }
+  private:
+  void SharedCtor();
+  void SharedDtor();
+  void SetCachedSize(int size) const;
+  void InternalSwap(Installation* other);
+  private:
+  inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
+    return _arena_ptr_;
+  }
+  inline ::google::protobuf::Arena* MaybeArenaPtr() const {
+    return _arena_ptr_;
+  }
+  public:
+
+  ::std::string GetTypeName() const;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  // optional bool is_successful = 1;
+  void clear_is_successful();
+  static const int kIsSuccessfulFieldNumber = 1;
+  bool is_successful() const;
+  void set_is_successful(bool value);
+
+  // optional int32 num_tries_left = 2;
+  void clear_num_tries_left();
+  static const int kNumTriesLeftFieldNumber = 2;
+  ::google::protobuf::int32 num_tries_left() const;
+  void set_num_tries_left(::google::protobuf::int32 value);
+
+  // optional int32 priority = 3;
+  void clear_priority();
+  static const int kPriorityFieldNumber = 3;
+  ::google::protobuf::int32 priority() const;
+  void set_priority(::google::protobuf::int32 value);
+
+  // @@protoc_insertion_point(class_scope:cobalt.loader.Installation)
+ private:
+
+  ::google::protobuf::internal::ArenaStringPtr _unknown_fields_;
+  ::google::protobuf::Arena* _arena_ptr_;
+
+  bool _is_default_instance_;
+  bool is_successful_;
+  ::google::protobuf::int32 num_tries_left_;
+  ::google::protobuf::int32 priority_;
+  mutable int _cached_size_;
+  #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+  friend void  protobuf_AddDesc_installation_5fstore_2eproto_impl();
+  #else
+  friend void  protobuf_AddDesc_installation_5fstore_2eproto();
+  #endif
+  friend void protobuf_AssignDesc_installation_5fstore_2eproto();
+  friend void protobuf_ShutdownFile_installation_5fstore_2eproto();
+
+  void InitAsDefaultInstance();
+  static Installation* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class InstallationStore : public ::google::protobuf::MessageLite {
+ public:
+  InstallationStore();
+  virtual ~InstallationStore();
+
+  InstallationStore(const InstallationStore& from);
+
+  inline InstallationStore& operator=(const InstallationStore& from) {
+    CopyFrom(from);
+    return *this;
+  }
+
+  static const InstallationStore& default_instance();
+
+  #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+  // Returns the internal default instance pointer. This function can
+  // return NULL thus should not be used by the user. This is intended
+  // for Protobuf internal code. Please use default_instance() declared
+  // above instead.
+  static inline const InstallationStore* internal_default_instance() {
+    return default_instance_;
+  }
+  #endif
+
+  GOOGLE_ATTRIBUTE_NOINLINE void Swap(InstallationStore* other);
+
+  // implements Message ----------------------------------------------
+
+  inline InstallationStore* New() const { return New(NULL); }
+
+  InstallationStore* New(::google::protobuf::Arena* arena) const;
+  void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+  void CopyFrom(const InstallationStore& from);
+  void MergeFrom(const InstallationStore& from);
+  void Clear();
+  bool IsInitialized() const;
+
+  int ByteSize() const;
+  bool MergePartialFromCodedStream(
+      ::google::protobuf::io::CodedInputStream* input);
+  void SerializeWithCachedSizes(
+      ::google::protobuf::io::CodedOutputStream* output) const;
+  void DiscardUnknownFields();
+  int GetCachedSize() const { return _cached_size_; }
+  private:
+  void SharedCtor();
+  void SharedDtor();
+  void SetCachedSize(int size) const;
+  void InternalSwap(InstallationStore* other);
+  private:
+  inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
+    return _arena_ptr_;
+  }
+  inline ::google::protobuf::Arena* MaybeArenaPtr() const {
+    return _arena_ptr_;
+  }
+  public:
+
+  ::std::string GetTypeName() const;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  // repeated .cobalt.loader.Installation installations = 1;
+  int installations_size() const;
+  void clear_installations();
+  static const int kInstallationsFieldNumber = 1;
+  const ::cobalt::loader::Installation& installations(int index) const;
+  ::cobalt::loader::Installation* mutable_installations(int index);
+  ::cobalt::loader::Installation* add_installations();
+  ::google::protobuf::RepeatedPtrField< ::cobalt::loader::Installation >*
+      mutable_installations();
+  const ::google::protobuf::RepeatedPtrField< ::cobalt::loader::Installation >&
+      installations() const;
+
+  // optional int32 roll_forward_to_installation = 2;
+  void clear_roll_forward_to_installation();
+  static const int kRollForwardToInstallationFieldNumber = 2;
+  ::google::protobuf::int32 roll_forward_to_installation() const;
+  void set_roll_forward_to_installation(::google::protobuf::int32 value);
+
+  // @@protoc_insertion_point(class_scope:cobalt.loader.InstallationStore)
+ private:
+
+  ::google::protobuf::internal::ArenaStringPtr _unknown_fields_;
+  ::google::protobuf::Arena* _arena_ptr_;
+
+  bool _is_default_instance_;
+  ::google::protobuf::RepeatedPtrField< ::cobalt::loader::Installation > installations_;
+  ::google::protobuf::int32 roll_forward_to_installation_;
+  mutable int _cached_size_;
+  #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+  friend void  protobuf_AddDesc_installation_5fstore_2eproto_impl();
+  #else
+  friend void  protobuf_AddDesc_installation_5fstore_2eproto();
+  #endif
+  friend void protobuf_AssignDesc_installation_5fstore_2eproto();
+  friend void protobuf_ShutdownFile_installation_5fstore_2eproto();
+
+  void InitAsDefaultInstance();
+  static InstallationStore* default_instance_;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#if !PROTOBUF_INLINE_NOT_IN_HEADERS
+// Installation
+
+// optional bool is_successful = 1;
+inline void Installation::clear_is_successful() {
+  is_successful_ = false;
+}
+inline bool Installation::is_successful() const {
+  // @@protoc_insertion_point(field_get:cobalt.loader.Installation.is_successful)
+  return is_successful_;
+}
+inline void Installation::set_is_successful(bool value) {
+  
+  is_successful_ = value;
+  // @@protoc_insertion_point(field_set:cobalt.loader.Installation.is_successful)
+}
+
+// optional int32 num_tries_left = 2;
+inline void Installation::clear_num_tries_left() {
+  num_tries_left_ = 0;
+}
+inline ::google::protobuf::int32 Installation::num_tries_left() const {
+  // @@protoc_insertion_point(field_get:cobalt.loader.Installation.num_tries_left)
+  return num_tries_left_;
+}
+inline void Installation::set_num_tries_left(::google::protobuf::int32 value) {
+  
+  num_tries_left_ = value;
+  // @@protoc_insertion_point(field_set:cobalt.loader.Installation.num_tries_left)
+}
+
+// optional int32 priority = 3;
+inline void Installation::clear_priority() {
+  priority_ = 0;
+}
+inline ::google::protobuf::int32 Installation::priority() const {
+  // @@protoc_insertion_point(field_get:cobalt.loader.Installation.priority)
+  return priority_;
+}
+inline void Installation::set_priority(::google::protobuf::int32 value) {
+  
+  priority_ = value;
+  // @@protoc_insertion_point(field_set:cobalt.loader.Installation.priority)
+}
+
+// -------------------------------------------------------------------
+
+// InstallationStore
+
+// repeated .cobalt.loader.Installation installations = 1;
+inline int InstallationStore::installations_size() const {
+  return installations_.size();
+}
+inline void InstallationStore::clear_installations() {
+  installations_.Clear();
+}
+inline const ::cobalt::loader::Installation& InstallationStore::installations(int index) const {
+  // @@protoc_insertion_point(field_get:cobalt.loader.InstallationStore.installations)
+  return installations_.Get(index);
+}
+inline ::cobalt::loader::Installation* InstallationStore::mutable_installations(int index) {
+  // @@protoc_insertion_point(field_mutable:cobalt.loader.InstallationStore.installations)
+  return installations_.Mutable(index);
+}
+inline ::cobalt::loader::Installation* InstallationStore::add_installations() {
+  // @@protoc_insertion_point(field_add:cobalt.loader.InstallationStore.installations)
+  return installations_.Add();
+}
+inline ::google::protobuf::RepeatedPtrField< ::cobalt::loader::Installation >*
+InstallationStore::mutable_installations() {
+  // @@protoc_insertion_point(field_mutable_list:cobalt.loader.InstallationStore.installations)
+  return &installations_;
+}
+inline const ::google::protobuf::RepeatedPtrField< ::cobalt::loader::Installation >&
+InstallationStore::installations() const {
+  // @@protoc_insertion_point(field_list:cobalt.loader.InstallationStore.installations)
+  return installations_;
+}
+
+// optional int32 roll_forward_to_installation = 2;
+inline void InstallationStore::clear_roll_forward_to_installation() {
+  roll_forward_to_installation_ = 0;
+}
+inline ::google::protobuf::int32 InstallationStore::roll_forward_to_installation() const {
+  // @@protoc_insertion_point(field_get:cobalt.loader.InstallationStore.roll_forward_to_installation)
+  return roll_forward_to_installation_;
+}
+inline void InstallationStore::set_roll_forward_to_installation(::google::protobuf::int32 value) {
+  
+  roll_forward_to_installation_ = value;
+  // @@protoc_insertion_point(field_set:cobalt.loader.InstallationStore.roll_forward_to_installation)
+}
+
+#endif  // !PROTOBUF_INLINE_NOT_IN_HEADERS
+// -------------------------------------------------------------------
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+}  // namespace loader
+}  // namespace cobalt
+
+// @@protoc_insertion_point(global_scope)
+
+#endif  // PROTOBUF_installation_5fstore_2eproto__INCLUDED
diff --git a/src/starboard/loader_app/installation_store.proto b/src/starboard/loader_app/installation_store.proto
new file mode 100644
index 0000000..905ecac
--- /dev/null
+++ b/src/starboard/loader_app/installation_store.proto
@@ -0,0 +1,44 @@
+// 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.
+
+syntax = "proto3";
+
+option optimize_for = LITE_RUNTIME;
+
+package cobalt.loader;
+
+// Single installation slot.
+message Installation {
+  // Is the installation successful.
+  bool is_successful  = 1;
+
+  // Number of tries left for this installation.
+  int32 num_tries_left = 2;
+
+  // Priority of the installation.
+  // Valid values are '0' to 'num_installations - 1',
+  // with '0' being the highest priority.
+  int32 priority = 3;
+}
+
+// Installation store for all installations.
+message InstallationStore {
+  // All the installation slots.
+  repeated Installation installations = 1;
+
+  // To which installation to roll forward to.
+  // The value of -1 means no change is needed.
+  // The updater sets the value and the loader performs the action.
+  int32 roll_forward_to_installation = 2;
+}
diff --git a/src/starboard/loader_app/loader_app.cc b/src/starboard/loader_app/loader_app.cc
new file mode 100644
index 0000000..7091fb9
--- /dev/null
+++ b/src/starboard/loader_app/loader_app.cc
@@ -0,0 +1,167 @@
+// 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 <vector>
+
+#include "starboard/common/log.h"
+#include "starboard/configuration.h"
+#include "starboard/configuration_constants.h"
+#include "starboard/elf_loader/elf_loader.h"
+#include "starboard/event.h"
+#include "starboard/loader_app/installation_manager.h"
+#include "starboard/loader_app/system_get_extension_shim.h"
+#include "starboard/mutex.h"
+#include "starboard/string.h"
+#include "starboard/thread_types.h"
+
+// TODO: Try to merge with the implementation in starboard/elf_loader/sandbox.cc
+
+namespace {
+// The max number of installations slots.
+const int kMaxNumInstallations = 3;
+
+// Relative path for the Cobalt so file.
+const char kCobaltLibraryPath[] = "lib";
+
+// Filename for the Cobalt binary.
+const char kCobaltLibraryName[] = "libcobalt.so";
+
+// Relative path for the content directory of
+// the Cobalt installation.
+const char kCobaltContentPath[] = "content";
+
+// Portable ELF loader.
+starboard::elf_loader::ElfLoader g_elf_loader;
+
+// Pointer to the |SbEventHandle| function in the
+// Cobalt binary.
+void (*g_sb_event_func)(const SbEvent*) = NULL;
+
+void LoadLibraryAndInitialize() {
+  // Initialize the Installation Manager.
+  SB_CHECK(ImInitialize(kMaxNumInstallations) == IM_SUCCESS)
+      << "Abort. Failed to initialize Installation Manager";
+
+  // Roll forward if needed.
+  if (ImRollForwardIfNeeded() == IM_ERROR) {
+    SB_LOG(WARNING) << "Failed to roll forward";
+  }
+
+  // Loop by priority.
+  int current_installation = ImGetCurrentInstallationIndex();
+  while (current_installation != IM_ERROR) {
+    // if not successful and num_tries_left > 0 decrement and try to
+    // load the library.
+    if (ImGetInstallationStatus(current_installation) !=
+        IM_INSTALLATION_STATUS_SUCCESS) {
+      int num_tries_left = ImGetInstallationNumTriesLeft(current_installation);
+      if (num_tries_left == IM_ERROR || num_tries_left <= 0 ||
+          ImDecrementInstallationNumTries(current_installation) == IM_ERROR) {
+        // If no more tries are left or if we have hard failure,
+        // discard the image and auto rollback, but only if
+        // the current image is not the system image.
+        if (current_installation != 0) {
+          current_installation = ImRevertToSuccessfulInstallation();
+        }
+      }
+    }
+
+    SB_LOG(INFO) << "Try to load the Cobalt binary";
+    SB_LOG(INFO) << "current_installation=" << current_installation;
+
+    //  Try to load the image. Failures here discard the image.
+    std::vector<char> installation_path(kSbFileMaxPath);
+    if (ImGetInstallationPath(current_installation, installation_path.data(),
+                              kSbFileMaxPath) == IM_ERROR) {
+      SB_LOG(ERROR) << "Failed to find library file";
+
+      // Hard failure. Discard the image and auto rollback, but only if
+      // the current image is not the system image.
+      if (current_installation != 0) {
+        current_installation = ImRevertToSuccessfulInstallation();
+        continue;
+      } else {
+        // The system image at index 0 failed.
+        break;
+      }
+    }
+
+    SB_DLOG(INFO) << "installation_path=" << installation_path.data();
+
+    // installation_n/lib/libcobalt.so
+    std::vector<char> lib_path(kSbFileMaxPath);
+    SbStringFormatF(lib_path.data(), kSbFileMaxPath, "%s%s%s%s%s",
+                    installation_path.data(), kSbFileSepString,
+                    kCobaltLibraryPath, kSbFileSepString, kCobaltLibraryName);
+    SB_LOG(INFO) << "lib_path=" << lib_path.data();
+
+    // installation_n/content
+    std::vector<char> content_path(kSbFileMaxPath);
+    SbStringFormatF(content_path.data(), kSbFileMaxPath, "%s%s%s",
+                    installation_path.data(), kSbFileSepString,
+                    kCobaltContentPath);
+    SB_LOG(INFO) << "content_path=" << content_path.data();
+
+    if (!g_elf_loader.Load(lib_path.data(), content_path.data(), false,
+                           &starboard::loader_app::SbSystemGetExtensionShim)) {
+      SB_LOG(WARNING) << "Failed to load Cobalt!";
+
+      // Hard failure. Discard the image and auto rollback, but only if
+      // the current image is not the system image.
+      if (current_installation != 0) {
+        current_installation = ImRevertToSuccessfulInstallation();
+        continue;
+      } else {
+        // The system image at index 0 failed.
+        break;
+      }
+    }
+
+    SB_DLOG(INFO) << "Successfully loaded Cobalt!\n";
+    void* p = g_elf_loader.LookupSymbol("SbEventHandle");
+    if (p != NULL) {
+      SB_DLOG(INFO) << "Symbol Lookup succeeded address: " << p;
+      g_sb_event_func = (void (*)(const SbEvent*))p;
+      break;
+    } else {
+      SB_LOG(ERROR) << "Symbol Lookup failed\n";
+
+      // Hard failure. Discard the image and auto rollback, but only if
+      // the current image is not the system image.
+      if (current_installation != 0) {
+        current_installation = ImRevertToSuccessfulInstallation();
+        continue;
+      } else {
+        // The system image at index 0 failed.
+        break;
+      }
+    }
+  }
+}
+}  // namespace
+
+void SbEventHandle(const SbEvent* event) {
+  static SbMutex mutex = SB_MUTEX_INITIALIZER;
+
+  SB_CHECK(SbMutexAcquire(&mutex) == kSbMutexAcquired);
+
+  if (!g_sb_event_func) {
+    LoadLibraryAndInitialize();
+    SB_CHECK(g_sb_event_func);
+  }
+
+  g_sb_event_func(event);
+
+  SB_CHECK(SbMutexRelease(&mutex) == true);
+}
diff --git a/src/starboard/loader_app/loader_app.gyp b/src/starboard/loader_app/loader_app.gyp
new file mode 100644
index 0000000..097b5fa
--- /dev/null
+++ b/src/starboard/loader_app/loader_app.gyp
@@ -0,0 +1,91 @@
+# 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.
+
+# This files contains all targets that should be created by gyp_cobalt by
+# default.
+{
+  'variables': {
+    'common_loader_app_sources': [
+        'loader_app.cc',
+        'system_get_extension_shim.h',
+        'system_get_extension_shim.cc',
+    ],
+    'common_loader_app_dependencies': [
+        '<(DEPTH)/starboard/loader_app/installation_manager.gyp:installation_manager',
+        '<(DEPTH)/starboard/starboard.gyp:starboard',
+    ],
+  },
+  'targets': [
+    {
+      'target_name': 'loader_app',
+      'type': '<(final_executable_type)',
+      'conditions': [
+        ['target_arch in ["x86", "x64", "arm", "arm64"] ', {
+          'sources': [
+            '<@(common_loader_app_sources)',
+          ],
+          'dependencies': [
+            '<(DEPTH)/starboard/elf_loader/elf_loader.gyp:elf_loader',
+            '<@(common_loader_app_dependencies)',
+            # TODO: Remove this dependency once MediaSession is migrated to use CobaltExtensions.
+            '<@(cobalt_platform_dependencies)',
+          ],
+        }],
+      ],
+    },
+    {
+      'target_name': 'loader_app_deploy',
+      'type': 'none',
+      'dependencies': [
+        'loader_app',
+      ],
+      'variables': {
+        'executable_name': 'loader_app',
+      },
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+    },
+    {
+      'target_name': 'loader_app_sys',
+      'type': '<(final_executable_type)',
+      'conditions': [
+        ['target_arch in ["x86", "x64", "arm", "arm64"] ', {
+          'sources': [
+            '<@(common_loader_app_sources)',
+          ],
+          'dependencies': [
+            '<(DEPTH)/starboard/elf_loader/elf_loader.gyp:elf_loader_sys',
+            '<@(common_loader_app_dependencies)',
+            # TODO: Remove this dependency once MediaSession is migrated to use CobaltExtensions.
+            '<@(cobalt_platform_dependencies)',
+          ],
+          'ldflags': [
+            '-Wl,--dynamic-list=<(DEPTH)/starboard/starboard.syms',
+            '-ldl' ,
+          ],
+        }],
+      ],
+    },
+    {
+      'target_name': 'loader_app_sys_deploy',
+      'type': 'none',
+      'dependencies': [
+        'loader_app',
+      ],
+      'variables': {
+        'executable_name': 'loader_app_sys',
+      },
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+    },
+  ],
+}
diff --git a/src/starboard/loader_app/system_get_extension_shim.cc b/src/starboard/loader_app/system_get_extension_shim.cc
new file mode 100644
index 0000000..8017159
--- /dev/null
+++ b/src/starboard/loader_app/system_get_extension_shim.cc
@@ -0,0 +1,46 @@
+// 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 "starboard/loader_app/system_get_extension_shim.h"
+
+#include <string>
+
+#include "cobalt/extension/installation_manager.h"
+#include "starboard/common/log.h"
+#include "starboard/loader_app/installation_manager.h"
+#include "starboard/string.h"
+#include "starboard/system.h"
+
+namespace {
+const CobaltExtensionInstallationManagerApi kInstallationManagerApi = {
+    kCobaltExtensionInstallationManagerName,
+    1,  // API version that's implemented.
+    &ImGetCurrentInstallationIndex,
+    &ImMarkInstallationSuccessful,
+    &ImRequestRollForwardToInstallation,
+    &ImGetInstallationPath,
+    &ImSelectNewInstallationIndex,
+};
+}  // namespace
+namespace starboard {
+namespace loader_app {
+
+const void* SbSystemGetExtensionShim(const char* name) {
+  if (SbStringCompareAll(name, kCobaltExtensionInstallationManagerName) == 0) {
+    return &kInstallationManagerApi;
+  }
+  return NULL;
+}
+}  // namespace loader_app
+}  // namespace starboard
diff --git a/src/starboard/loader_app/system_get_extension_shim.h b/src/starboard/loader_app/system_get_extension_shim.h
new file mode 100644
index 0000000..befaf9c
--- /dev/null
+++ b/src/starboard/loader_app/system_get_extension_shim.h
@@ -0,0 +1,39 @@
+// 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.
+
+// ELF Loader shim functions are intended to be patched into the Cobalt shared
+// library when the library is loaded. Cobalt will then invoke the shim function
+// where it would otherwise invoke the original function, allowing us to
+// partially or entirely change the behavior within that function call.
+
+#ifndef STARBOARD_LOADER_APP_SYSTEM_GET_EXTENSION_SHIM_H_
+#define STARBOARD_LOADER_APP_SYSTEM_GET_EXTENSION_SHIM_H_
+
+#include "starboard/system.h"
+
+namespace starboard {
+namespace loader_app {
+
+// Adds the |kCobaltExtensionInstallationManagerName| to the list of extensions
+// supported by SbSystemGetExtension() by wrapping the call to
+// SbSystemGetExtension.
+//
+// Please reference //starboard/system.h for documentation on
+// SbSystemGetExtension().
+const void* SbSystemGetExtensionShim(const char* name);
+
+}  // namespace loader_app
+}  // namespace starboard
+
+#endif  // STARBOARD_LOADER_APP_SYSTEM_GET_EXTENSION_SHIM_H_
diff --git a/src/starboard/media.h b/src/starboard/media.h
index c554c58..bfc5864 100644
--- a/src/starboard/media.h
+++ b/src/starboard/media.h
@@ -31,20 +31,6 @@
 
 // --- Types -----------------------------------------------------------------
 
-#if SB_API_VERSION < 10
-// Time represented in 90KHz ticks.
-typedef int64_t SbMediaTime;
-
-#define SB_MEDIA_TIME_TO_SB_TIME(media) \
-  (((media == SB_PLAYER_NO_DURATION)    \
-        ? SB_PLAYER_NO_DURATION         \
-        : ((media == kSbInt64Max) ? kSbInt64Max : (media * 100 / 9))))
-#define SB_TIME_TO_SB_MEDIA_TIME(time) \
-  (((time == SB_PLAYER_NO_DURATION)    \
-        ? SB_PLAYER_NO_DURATION        \
-        : ((time == kSbInt64Max) ? kSbInt64Max : (time * 9 / 100))))
-#endif  // SB_API_VERSION < 10
-
 // Types of media component streams.
 typedef enum SbMediaType {
   // Value used for audio streams.
@@ -531,14 +517,6 @@
 typedef SbMediaAudioSampleInfo SbMediaAudioHeader;
 #endif  // SB_API_VERSION < 11
 
-#if SB_API_VERSION < 10
-// --- Constants -------------------------------------------------------------
-
-// TODO: remove entirely.
-// One second in SbMediaTime (90KHz ticks).
-#define kSbMediaTimeSecond ((SbMediaTime)(90000))
-#endif  // SB_API_VERSION < 10
-
 // --- Functions -------------------------------------------------------------
 
 // Indicates whether this platform supports decoding |video_codec| and
@@ -609,7 +587,6 @@
 //   disabled.
 SB_EXPORT bool SbMediaSetOutputProtection(bool enabled);
 
-#if SB_API_VERSION >= 10
 // Value used when a video's resolution is not known.
 #define kSbMediaVideoResolutionDimensionInvalid 0
 // Value used when a video's bits per pixel is not known.
@@ -732,7 +709,6 @@
                                           int resolution_width,
                                           int resolution_height,
                                           int bits_per_pixel);
-#endif  // SB_API_VERSION >= 10
 
 #if SB_API_VERSION >= 11
 // Communicate to the platform how far past |current_playback_position| the app
diff --git a/src/starboard/memory.h b/src/starboard/memory.h
index c844b8d..24d2b61 100644
--- a/src/starboard/memory.h
+++ b/src/starboard/memory.h
@@ -56,9 +56,7 @@
 typedef enum SbMemoryMapFlags {
 // No flags set: Reserves virtual address space. SbMemoryProtect() can later
 // make it accessible.
-#if SB_API_VERSION >= 10
   kSbMemoryMapProtectReserved = 0,
-#endif
   kSbMemoryMapProtectRead = 1 << 0,   // Mapped memory can be read.
   kSbMemoryMapProtectWrite = 1 << 1,  // Mapped memory can be written to.
 #if SB_CAN(MAP_EXECUTABLE_MEMORY)
@@ -221,13 +219,11 @@
 // |SbMemoryUnmap(0xA000, 0x2000)| should free both regions.
 SB_EXPORT bool SbMemoryUnmap(void* virtual_address, int64_t size_bytes);
 
-#if SB_API_VERSION >= 10
 // Change the protection of |size_bytes| of memory regions, starting from
 // |virtual_address|, to |flags|, returning |true| on success.
 SB_EXPORT bool SbMemoryProtect(void* virtual_address,
                                int64_t size_bytes,
                                int flags);
-#endif
 
 #if SB_CAN(MAP_EXECUTABLE_MEMORY)
 // Flushes any data in the given virtual address range that is cached locally in
diff --git a/src/starboard/mutex.h b/src/starboard/mutex.h
index 4192c42..790087f 100644
--- a/src/starboard/mutex.h
+++ b/src/starboard/mutex.h
@@ -32,7 +32,7 @@
 #if SB_API_VERSION >= SB_PORTABLE_THREAD_TYPES_VERSION
 
 // Max size of the SbMutex type.
-#define SB_MUTEX_MAX_SIZE 64
+#define SB_MUTEX_MAX_SIZE 80
 
 // An opaque handle to a mutex type with reserved memory
 // buffer of size SB_MUTEX_MAX_SIZE and aligned at void
diff --git a/src/starboard/nplb/atomic_test.cc b/src/starboard/nplb/atomic_test.cc
index 9adf0e7..16fd789 100644
--- a/src/starboard/nplb/atomic_test.cc
+++ b/src/starboard/nplb/atomic_test.cc
@@ -46,9 +46,7 @@
 };
 
 typedef testing::Types<
-#if SB_API_VERSION >= 10
     SbAtomic8,
-#endif
     SbAtomic32,
 #if SB_HAS(64_BIT_ATOMICS)
     SbAtomic64,
@@ -61,11 +59,7 @@
 #endif
                        SbAtomicPtr> AdvancedSbAtomicTestTypes;
 
-#if SB_API_VERSION >= 10
 TYPED_TEST_CASE(BasicSbAtomicTest, BasicSbAtomicTestTypes);
-#else
-TYPED_TEST_CASE(BasicSbAtomicTest, AdvancedSbAtomicTestTypes);
-#endif
 
 TYPED_TEST_CASE(AdvancedSbAtomicTest, AdvancedSbAtomicTestTypes);
 
diff --git a/src/starboard/nplb/audio_sink_create_test.cc b/src/starboard/nplb/audio_sink_create_test.cc
index eba658d..194b27f 100644
--- a/src/starboard/nplb/audio_sink_create_test.cc
+++ b/src/starboard/nplb/audio_sink_create_test.cc
@@ -62,7 +62,6 @@
   SbAudioSinkDestroy(audio_sink);
 }
 
-#if SB_API_VERSION >= 10
 TEST(SbAudioSinkCreateTest, MultiSink) {
   ASSERT_GE(SbAudioSinkGetMaxChannels(), 1);
 
@@ -88,7 +87,6 @@
     SbAudioSinkDestroy(sink);
   }
 }
-#endif  // SB_API_VERSION >= 10
 
 TEST(SbAudioSinkCreateTest, SunnyDayAllCombinations) {
   std::vector<SbMediaAudioSampleType> sample_types;
diff --git a/src/starboard/nplb/audio_sink_get_max_channels_test.cc b/src/starboard/nplb/audio_sink_get_max_channels_test.cc
index ba94c54..e50cf18 100644
--- a/src/starboard/nplb/audio_sink_get_max_channels_test.cc
+++ b/src/starboard/nplb/audio_sink_get_max_channels_test.cc
@@ -19,10 +19,13 @@
 namespace nplb {
 
 TEST(SbAudioSinkGetMaxChannelsTest, SunnyDay) {
-  int max_channels = SbAudioSinkGetMaxChannels();
-  EXPECT_GT(max_channels, 0);
-  // See if it reports anything ridiculous.
-  EXPECT_LT(max_channels, 99);
+  // Call the function a few times to ensure that it works consistently
+  for (int i = 0; i < 4; ++i) {
+    int max_channels = SbAudioSinkGetMaxChannels();
+    EXPECT_GT(max_channels, 0);
+    // See if it reports anything ridiculous.
+    EXPECT_LT(max_channels, 99);
+  }
 }
 
 }  // namespace nplb
diff --git a/src/starboard/nplb/audio_sink_get_min_buffer_size_in_frames_test.cc b/src/starboard/nplb/audio_sink_get_min_buffer_size_in_frames_test.cc
index a07a8c8..cac5917 100644
--- a/src/starboard/nplb/audio_sink_get_min_buffer_size_in_frames_test.cc
+++ b/src/starboard/nplb/audio_sink_get_min_buffer_size_in_frames_test.cc
@@ -19,6 +19,9 @@
 namespace nplb {
 
 #if SB_API_VERSION >= 11
+
+const int kMaxAllowedMinRequiredFrames = 16 * 1024;
+
 TEST(SbAudioSinkGetMinBufferSizeInFramesTest, SunnyDay) {
   SbMediaAudioSampleType sample_type = kSbMediaAudioSampleTypeFloat32;
   if (!SbAudioSinkIsAudioSampleTypeSupported(sample_type)) {
@@ -31,7 +34,7 @@
       SbAudioSinkGetNearestSupportedSampleFrequency(kDefaultSampleFrequency));
 
   EXPECT_GT(min_required_frames, 0);
-  EXPECT_LE(min_required_frames, 10 * 1024);
+  EXPECT_LE(min_required_frames, kMaxAllowedMinRequiredFrames);
 
   if (max_channels > 1) {
     min_required_frames = SbAudioSinkGetMinBufferSizeInFrames(
@@ -39,7 +42,7 @@
         SbAudioSinkGetNearestSupportedSampleFrequency(kDefaultSampleFrequency));
 
     EXPECT_GT(min_required_frames, 0);
-    EXPECT_LE(min_required_frames, 10 * 1024);
+    EXPECT_LE(min_required_frames, kMaxAllowedMinRequiredFrames);
   }
   if (max_channels > 2) {
     min_required_frames = SbAudioSinkGetMinBufferSizeInFrames(
@@ -47,7 +50,7 @@
         SbAudioSinkGetNearestSupportedSampleFrequency(kDefaultSampleFrequency));
 
     EXPECT_GT(min_required_frames, 0);
-    EXPECT_LE(min_required_frames, 10 * 1024);
+    EXPECT_LE(min_required_frames, kMaxAllowedMinRequiredFrames);
   }
 }
 #endif  // SB_API_VERSION >= 11
diff --git a/src/starboard/nplb/blitter_pixel_tests/tests.cc b/src/starboard/nplb/blitter_pixel_tests/tests.cc
index a936d6f..b349476 100644
--- a/src/starboard/nplb/blitter_pixel_tests/tests.cc
+++ b/src/starboard/nplb/blitter_pixel_tests/tests.cc
@@ -32,6 +32,7 @@
 // including image diffs between the actual results and expected results.
 
 #include "starboard/blitter.h"
+#include "starboard/configuration_constants.h"
 #include "starboard/nplb/blitter_pixel_tests/fixture.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -262,15 +263,16 @@
                                                  int height) {
   SbBlitterPixelDataFormat pixel_data_format;
 
-#if SB_PREFERRED_RGBA_BYTE_ORDER == SB_PREFERRED_RGBA_BYTE_ORDER_RGBA
-  pixel_data_format = kSbBlitterPixelDataFormatRGBA8;
-#elif SB_PREFERRED_RGBA_BYTE_ORDER == SB_PREFERRED_RGBA_BYTE_ORDER_BGRA
-  pixel_data_format = kSbBlitterPixelDataFormatBGRA8;
-#elif SB_PREFERRED_RGBA_BYTE_ORDER == SB_PREFERRED_RGBA_BYTE_ORDER_ARGB
-  pixel_data_format = kSbBlitterPixelDataFormatARGB8;
-#else
-#error "Platform's preferred RGBA byte order is not yet supported."
-#endif
+  if (kSbPreferredRgbaByteOrder == SB_PREFERRED_RGBA_BYTE_ORDER_RGBA) {
+    pixel_data_format = kSbBlitterPixelDataFormatRGBA8;
+  } else if (kSbPreferredRgbaByteOrder == SB_PREFERRED_RGBA_BYTE_ORDER_BGRA) {
+    pixel_data_format = kSbBlitterPixelDataFormatBGRA8;
+  } else if (kSbPreferredRgbaByteOrder == SB_PREFERRED_RGBA_BYTE_ORDER_ARGB) {
+    pixel_data_format = kSbBlitterPixelDataFormatARGB8;
+  } else {
+    SB_CHECK(false)
+        << "Platform's preferred RGBA byte order is not yet supported.";
+  }
 
   // RGBA byte-offsets into each pixel.
   int r, g, b, a;
diff --git a/src/starboard/nplb/cpu_features_get_test.cc b/src/starboard/nplb/cpu_features_get_test.cc
index 53469cc..c72a84e 100644
--- a/src/starboard/nplb/cpu_features_get_test.cc
+++ b/src/starboard/nplb/cpu_features_get_test.cc
@@ -67,6 +67,9 @@
   EXPECT_EQ(false, features.x86.has_sse2);
   EXPECT_EQ(false, features.x86.has_tsc);
   EXPECT_EQ(false, features.x86.has_sse3);
+#if defined(SB_CPU_FEATURE_PCLMULQDQ)
+  EXPECT_EQ(false, features.x86.has_pclmulqdq);
+#endif  // defined(SB_CPU_FEATURE_PCLMULQDQ)
   EXPECT_EQ(false, features.x86.has_ssse3);
   EXPECT_EQ(false, features.x86.has_sse41);
   EXPECT_EQ(false, features.x86.has_sse42);
@@ -121,24 +124,14 @@
     ExpectArmInvalid(features);
 #endif  // SB_IS(ARCH_ARM) || SB_IS(ARCH_ARM64)
 
-#if SB_IS(ARCH_MIPS)
-    EXPECT_TRUE(features.architecture == kSbCPUFeaturesArchitectureMips ||
-                features.architecture == kSbCPUFeaturesArchitectureMips64);
-#else   // !SB_IS(ARCH_MIPS)
-    ExpectMipsInvalid(features);
-#endif  // SB_IS(ARCH_MIPS)
-
-#if SB_IS(ARCH_PPC)
-    EXPECT_TRUE(features.architecture == kSbCPUFeaturesArchitecturePpc ||
-                features.architecture == kSbCPUFeaturesArchitecturePpc64);
-#endif  // SB_IS(ARCH_PPC)
-
 #if SB_IS(ARCH_X86) || SB_IS(ARCH_X64)
     EXPECT_TRUE(features.architecture == kSbCPUFeaturesArchitectureX86 ||
                 features.architecture == kSbCPUFeaturesArchitectureX86_64);
 #else   // !SB_IS(ARCH_X86) && !SB_IS(ARCH_X64)
     ExpectX86Invalid(features);
 #endif  // SB_IS(ARCH_X86) || SB_IS(ARCH_X64)
+
+    ExpectMipsInvalid(features);
   }
 }
 
diff --git a/src/starboard/nplb/cryptography_create_transformer_test.cc b/src/starboard/nplb/cryptography_create_transformer_test.cc
index 7a1c1c6..1dcf280 100644
--- a/src/starboard/nplb/cryptography_create_transformer_test.cc
+++ b/src/starboard/nplb/cryptography_create_transformer_test.cc
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "starboard/configuration.h"
+
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #include "starboard/cryptography.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
@@ -46,3 +50,5 @@
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
+
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
diff --git a/src/starboard/nplb/cryptography_helpers.cc b/src/starboard/nplb/cryptography_helpers.cc
index 6b189d8..c92f422 100644
--- a/src/starboard/nplb/cryptography_helpers.cc
+++ b/src/starboard/nplb/cryptography_helpers.cc
@@ -60,6 +60,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "starboard/configuration.h"
+
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #include "starboard/nplb/cryptography_helpers.h"
 
 #include <string>
@@ -143,3 +147,5 @@
 
 }  // namespace nplb
 }  // namespace starboard
+
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
diff --git a/src/starboard/nplb/cryptography_transform_gcm_test.cc b/src/starboard/nplb/cryptography_transform_gcm_test.cc
index 69a59e1..081067f 100644
--- a/src/starboard/nplb/cryptography_transform_gcm_test.cc
+++ b/src/starboard/nplb/cryptography_transform_gcm_test.cc
@@ -62,6 +62,10 @@
 
 // This test is adapted from BoringSSL's crypto/fipsmodule/modes/gcm_test.cc
 
+#include "starboard/configuration.h"
+
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #include "starboard/cryptography.h"
 
 #include "starboard/common/log.h"
@@ -443,3 +447,5 @@
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
+
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
diff --git a/src/starboard/nplb/cryptography_transform_test.cc b/src/starboard/nplb/cryptography_transform_test.cc
index 0ffc4c9..98feac6 100644
--- a/src/starboard/nplb/cryptography_transform_test.cc
+++ b/src/starboard/nplb/cryptography_transform_test.cc
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "starboard/configuration.h"
+
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #include "starboard/cryptography.h"
 
 #include "starboard/common/log.h"
@@ -223,3 +227,5 @@
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
+
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
diff --git a/src/starboard/nplb/drm_create_system_test.cc b/src/starboard/nplb/drm_create_system_test.cc
index 9cd4b7a..4082580 100644
--- a/src/starboard/nplb/drm_create_system_test.cc
+++ b/src/starboard/nplb/drm_create_system_test.cc
@@ -40,7 +40,6 @@
   EXPECT_TRUE(any_supported_key_systems) << " no DRM key systems supported";
 }
 
-#if SB_API_VERSION >= 10
 TEST(SbDrmTest, NullCallbacks) {
   for (int i = 0; i < SB_ARRAY_SIZE_INT(kKeySystems); ++i) {
     const char* key_system = kKeySystems[i];
@@ -111,7 +110,6 @@
     SbDrmDestroySystem(drm_system);
   }
 }
-#endif  // SB_API_VERSION >= 10
 
 }  // namespace
 }  // namespace nplb
diff --git a/src/starboard/nplb/drm_helpers.cc b/src/starboard/nplb/drm_helpers.cc
index 930fb82..cac39fa 100644
--- a/src/starboard/nplb/drm_helpers.cc
+++ b/src/starboard/nplb/drm_helpers.cc
@@ -19,8 +19,6 @@
 namespace starboard {
 namespace nplb {
 
-#if SB_API_VERSION >= 10
-
 void DummySessionUpdateRequestFunc(SbDrmSystem drm_system,
                                    void* context,
                                    int ticket,
@@ -47,26 +45,6 @@
                                        SbDrmStatus status,
                                        const char* error_message) {}
 
-#else  // SB_API_VERSION >= 10
-
-void DummySessionUpdateRequestFunc(SbDrmSystem drm_system,
-                                   void* context,
-                                   int ticket,
-                                   const void* session_id,
-                                   int session_id_size,
-                                   const void* content,
-                                   int content_size,
-                                   const char* url) {}
-
-void DummySessionUpdatedFunc(SbDrmSystem drm_system,
-                             void* context,
-                             int ticket,
-                             const void* session_id,
-                             int session_id_size,
-                             bool succeeded) {}
-
-#endif  // SB_API_VERSION >= 10
-
 void DummySessionKeyStatusesChangedFunc(SbDrmSystem drm_system,
                                         void* context,
                                         const void* session_id,
@@ -81,21 +59,10 @@
                             int session_id_size) {}
 
 SbDrmSystem CreateDummyDrmSystem(const char* key_system) {
-#if SB_API_VERSION >= 10
   return SbDrmCreateSystem(
       key_system, NULL /* context */, DummySessionUpdateRequestFunc,
       DummySessionUpdatedFunc, DummySessionKeyStatusesChangedFunc,
       DummyServerCertificateUpdatedFunc, DummySessionClosedFunc);
-#elif SB_HAS(DRM_SESSION_CLOSED)
-  return SbDrmCreateSystem(
-      key_system, NULL /* context */, DummySessionUpdateRequestFunc,
-      DummySessionUpdatedFunc, DummySessionKeyStatusesChangedFunc,
-      DummySessionClosedFunc);
-#else   // SB_HAS(DRM_SESSION_CLOSED)
-  return SbDrmCreateSystem(
-      key_system, NULL /* context */, DummySessionUpdateRequestFunc,
-      DummySessionUpdatedFunc, DummySessionKeyStatusesChangedFunc);
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
 }
 
 }  // namespace nplb
diff --git a/src/starboard/nplb/drm_helpers.h b/src/starboard/nplb/drm_helpers.h
index f6d012d..c24b696 100644
--- a/src/starboard/nplb/drm_helpers.h
+++ b/src/starboard/nplb/drm_helpers.h
@@ -20,8 +20,6 @@
 namespace starboard {
 namespace nplb {
 
-#if SB_API_VERSION >= 10
-
 void DummySessionUpdateRequestFunc(SbDrmSystem drm_system,
                                    void* context,
                                    int ticket,
@@ -48,26 +46,6 @@
                                        SbDrmStatus status,
                                        const char* error_message);
 
-#else  // SB_API_VERSION >= 10
-
-void DummySessionUpdateRequestFunc(SbDrmSystem drm_system,
-                                   void* context,
-                                   int ticket,
-                                   const void* session_id,
-                                   int session_id_size,
-                                   const void* content,
-                                   int content_size,
-                                   const char* url);
-
-void DummySessionUpdatedFunc(SbDrmSystem drm_system,
-                             void* context,
-                             int ticket,
-                             const void* session_id,
-                             int session_id_size,
-                             bool succeeded);
-
-#endif  // SB_API_VERSION >= 10
-
 void DummySessionKeyStatusesChangedFunc(SbDrmSystem drm_system,
                                         void* context,
                                         const void* session_id,
diff --git a/src/starboard/nplb/drm_is_server_certificate_updatable_test.cc b/src/starboard/nplb/drm_is_server_certificate_updatable_test.cc
index dff3ea2..94ea7dd 100644
--- a/src/starboard/nplb/drm_is_server_certificate_updatable_test.cc
+++ b/src/starboard/nplb/drm_is_server_certificate_updatable_test.cc
@@ -21,8 +21,6 @@
 namespace nplb {
 namespace {
 
-#if SB_API_VERSION >= 10
-
 TEST(SbDrmIsServerCertificateUpdatableTest, SunnyDay) {
   // Ensure that |SbDrmIsServerCertificateUpdatable| can be called over all key
   // systems.
@@ -50,8 +48,6 @@
   }
 }
 
-#endif  // SB_API_VERSION >= 10
-
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/drm_update_server_certificate_test.cc b/src/starboard/nplb/drm_update_server_certificate_test.cc
index 2646312..9d9e9c7 100644
--- a/src/starboard/nplb/drm_update_server_certificate_test.cc
+++ b/src/starboard/nplb/drm_update_server_certificate_test.cc
@@ -22,8 +22,6 @@
 namespace nplb {
 namespace {
 
-#if SB_API_VERSION >= 10
-
 TEST(SbDrmUpdateServerCertificateTest, SunnyDay) {
   // Ensure that |SbDrmUpdateServerCertificate| can be called over all key
   // systems.
@@ -42,8 +40,6 @@
   }
 }
 
-#endif  // SB_API_VERSION >= 10
-
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/extern_c_test.cc b/src/starboard/nplb/extern_c_test.cc
index d209d1e..63a56b1 100644
--- a/src/starboard/nplb/extern_c_test.cc
+++ b/src/starboard/nplb/extern_c_test.cc
@@ -24,7 +24,9 @@
 #include "starboard/condition_variable.h"
 #include "starboard/configuration.h"
 #include "starboard/cpu_features.h"
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
 #include "starboard/cryptography.h"
+#endif
 #include "starboard/decode_target.h"
 #include "starboard/directory.h"
 #include "starboard/double.h"
diff --git a/src/starboard/nplb/include_all.c b/src/starboard/nplb/include_all.c
index b4c6892..39216fb 100644
--- a/src/starboard/nplb/include_all.c
+++ b/src/starboard/nplb/include_all.c
@@ -23,7 +23,9 @@
 #include "starboard/condition_variable.h"
 #include "starboard/configuration.h"
 #include "starboard/cpu_features.h"
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
 #include "starboard/cryptography.h"
+#endif
 #include "starboard/decode_target.h"
 #include "starboard/directory.h"
 #include "starboard/double.h"
diff --git a/src/starboard/nplb/media_buffer_test.cc b/src/starboard/nplb/media_buffer_test.cc
index baa7811..6b0116b 100644
--- a/src/starboard/nplb/media_buffer_test.cc
+++ b/src/starboard/nplb/media_buffer_test.cc
@@ -16,7 +16,6 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if SB_API_VERSION >= 10
 namespace starboard {
 namespace nplb {
 namespace {
@@ -198,4 +197,3 @@
 }
 }  // namespace nplb
 }  // namespace starboard
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/nplb/media_can_play_mime_and_key_system_test.cc b/src/starboard/nplb/media_can_play_mime_and_key_system_test.cc
new file mode 100644
index 0000000..2e38c86
--- /dev/null
+++ b/src/starboard/nplb/media_can_play_mime_and_key_system_test.cc
@@ -0,0 +1,102 @@
+// Copyright 2020 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/media.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+namespace {
+
+TEST(SbMediaCanPlayMimeAndKeySystem, SunnyDay) {
+  // Vp9
+  SbMediaCanPlayMimeAndKeySystem(
+      "video/webm; codecs=\"vp9\"; width=3840; height=2160; framerate=30; "
+      "bitrate=21823784; eotf=bt709",
+      "");
+  // Avc
+  SbMediaSupportType result = SbMediaCanPlayMimeAndKeySystem(
+      "video/mp4; codecs=\"avc1.4d4015\"; width=640; "
+      "height=360; framerate=30;",
+      "");
+  ASSERT_EQ(result, kSbMediaSupportTypeProbably);
+  // Hdr bt709
+  SbMediaCanPlayMimeAndKeySystem(
+      "video/webm; codecs=\"vp09.02.10.10\";eotf=bt709;width=640;height=360",
+      "");
+  // Aac
+  result = SbMediaCanPlayMimeAndKeySystem(
+      "audio/mp4; codecs=\"mp4a.40.2\"; channels=2", "");
+  ASSERT_EQ(result, kSbMediaSupportTypeProbably);
+  // Opus
+  SbMediaCanPlayMimeAndKeySystem("audio/webm; codecs=\"opus\"; channels=2", "");
+}
+
+TEST(SbMediaCanPlayMimeAndKeySystem, Invalid) {
+  // Invalid codec
+  SbMediaSupportType result =
+      SbMediaCanPlayMimeAndKeySystem("video/webm; codecs=\"abc\";", "");
+  ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+
+  result = SbMediaCanPlayMimeAndKeySystem(
+      "video/webm; codecs=\"vp09.00.01.00.22\";", "");
+  ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+
+  // Invalid container
+  result =
+      SbMediaCanPlayMimeAndKeySystem("video/abc; codecs=\"avc1.4d4015\";", "");
+  ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+
+  // Invalid size
+  result = SbMediaCanPlayMimeAndKeySystem(
+      "video/mp4; codecs=\"avc1.4d4015\"; width=99999; height=1080;", "");
+  ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+
+  result = SbMediaCanPlayMimeAndKeySystem(
+      "video/mp4; codecs=\"avc1.4d4015\"; width=1920; height=99999;", "");
+  ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+
+  // Invalid bitrate
+  result = SbMediaCanPlayMimeAndKeySystem(
+      "video/mp4; codecs=\"avc1.4d4015\"; width=1920; height=1080; "
+      "bitrate=999999999;",
+      "");
+  ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+
+  // Invalid eotf
+  result = SbMediaCanPlayMimeAndKeySystem(
+      "video/webm; codecs=\"vp09.02.10.10\"; width=1920; height=1080; "
+      "eotf=abc",
+      "");
+  ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+
+  // Invalid channels
+  result = SbMediaCanPlayMimeAndKeySystem(
+      "audio/mp4; codecs=\"mp4a.40.2\"; channels=99", "");
+  ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+
+  // Invalid keysystem
+  result = SbMediaCanPlayMimeAndKeySystem(
+      "video/mp4; codecs=\"avc1.4d4015\"; width=1920; height=1080;", "abc");
+  ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+}
+
+}  // namespace
+}  // namespace media
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/nplb/media_set_audio_write_duration_test.cc b/src/starboard/nplb/media_set_audio_write_duration_test.cc
index 73fc90d..77563b1 100644
--- a/src/starboard/nplb/media_set_audio_write_duration_test.cc
+++ b/src/starboard/nplb/media_set_audio_write_duration_test.cc
@@ -93,7 +93,7 @@
     }
 
     SbPlayerSampleInfo player_sample_info =
-        dmp_reader_.GetPlayerSampleInfo(kSbMediaTypeAudio, ++index_);
+        dmp_reader_.GetPlayerSampleInfo(kSbMediaTypeAudio, index_++);
 
     SbPlayerSampleInfo sample_info = {};
     sample_info.buffer = player_sample_info.buffer;
@@ -249,10 +249,18 @@
 
   WaitForPlayerState(kSbPlayerStatePresenting);
 
-  // Check that the playback time is > 0.
-  SbPlayerInfo2 info;
-  SbPlayerGetInfo2(player, &info);
-  ASSERT_GT(info.current_media_timestamp, 0);
+  // Wait until the playback time is > 0.
+  const SbTime kMaxWaitTime = 5 * kSbTimeSecond;
+  SbTime start_of_wait = SbTimeGetMonotonicNow();
+  SbPlayerInfo2 info = {};
+
+  while (SbTimeGetMonotonicNow() - start_of_wait < kMaxWaitTime &&
+         info.current_media_timestamp == 0) {
+    SbThreadSleep(kSbTimeMillisecond * 500);
+    SbPlayerGetInfo2(player, &info);
+  }
+
+  EXPECT_GT(info.current_media_timestamp, 0);
 
   SbPlayerDestroy(player);
 }
@@ -305,6 +313,9 @@
     VideoDmpReader dmp_reader(ResolveTestFileName(filename).c_str());
     SB_DCHECK(dmp_reader.number_of_audio_buffers() > 0);
     if (SbMediaIsAudioSupported(dmp_reader.audio_codec(),
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                                "",  // content_type
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
                                 dmp_reader.audio_bitrate())) {
       test_params.push_back(filename);
     }
diff --git a/src/starboard/nplb/memory_map_test.cc b/src/starboard/nplb/memory_map_test.cc
index c1e2137..c24bb36 100644
--- a/src/starboard/nplb/memory_map_test.cc
+++ b/src/starboard/nplb/memory_map_test.cc
@@ -201,7 +201,6 @@
                          original_function);
 }
 
-#if SB_API_VERSION >= 10
 // Cobalt can not map executable memory. If executable memory is needed, map
 // non-executable memory first and use SbMemoryProtect to change memory accesss
 // to executable.
@@ -219,10 +218,8 @@
     EXPECT_FALSE(SbMemoryUnmap(memory, 0));
   }
 }
-#endif  // 10
 #endif  // SB_CAN(MAP_EXECUTABLE_MEMORY)
 
-#if SB_API_VERSION >= 10
 TEST(SbMemoryMapTest, CanChangeMemoryProtection) {
   SbMemoryMapFlags all_from_flags[] = {
     SbMemoryMapFlags(kSbMemoryMapProtectReserved),
@@ -292,7 +289,6 @@
     }
   }
 }
-#endif  // SB_API_VERSION >= 10
 
 #endif  // SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP)
 
diff --git a/src/starboard/nplb/mutex_create_test.cc b/src/starboard/nplb/mutex_create_test.cc
index 62f0c74..1d072c9 100644
--- a/src/starboard/nplb/mutex_create_test.cc
+++ b/src/starboard/nplb/mutex_create_test.cc
@@ -32,13 +32,6 @@
   SbMutex mutex;
   EXPECT_TRUE(SbMutexCreate(&mutex));
   EXPECT_TRUE(SbMutexDestroy(&mutex));
-  // TODO: Remove those logs once we get the measurements.
-  SB_LOG(INFO) << "SB_THREAD_TYPES: sizeof(SbMutex): " << sizeof(SbMutex);
-  SB_LOG(INFO) << "SB_THREAD_TYPES: sizeof(SbThread): " << sizeof(SbThread);
-  SB_LOG(INFO) << "SB_THREAD_TYPES: sizeof(SbOnceControl): "
-               << sizeof(SbOnceControl);
-  SB_LOG(INFO) << "SB_THREAD_TYPES: sizeof(SbConditionVariable): "
-               << sizeof(SbConditionVariable);
 }
 
 TEST(SbMutexCreateTest, SunnyDayAutoInit) {
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index 37f0c7c..0f100ce 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -152,6 +152,7 @@
         # TODO: Separate functions tested by media buffer test into multiple
         # files.
         'media_buffer_test.cc',
+        'media_can_play_mime_and_key_system_test.cc',
         'memory_align_to_page_size_test.cc',
         'memory_allocate_aligned_test.cc',
         'memory_allocate_test.cc',
diff --git a/src/starboard/nplb/nplb_evergreen_compat_tests/checks.h b/src/starboard/nplb/nplb_evergreen_compat_tests/checks.h
new file mode 100644
index 0000000..0dbff4e
--- /dev/null
+++ b/src/starboard/nplb/nplb_evergreen_compat_tests/checks.h
@@ -0,0 +1,41 @@
+// Copyright 2020 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 STARBOARD_NPLB_NPLB_EVERGREEN_COMPAT_TESTS_CHECKS_H_
+#define STARBOARD_NPLB_NPLB_EVERGREEN_COMPAT_TESTS_CHECKS_H_
+
+#include "starboard/configuration.h"
+
+#if SB_IS(EVERGREEN_COMPATIBLE)
+
+#if SB_API_VERSION < 12
+#error "Evergreen requires starboard version 12 or higher!"
+#endif
+
+#if SB_API_VERSION < SB_STORAGE_PATH_VERSION
+#error \
+    "Evergreen requires support for the kSbSystemPathStorageDirectory" \
+    " system property!"
+#endif
+
+#if SB_API_VERSION < SB_MMAP_REQUIRED_VERSION
+#error "Evergreen requires memory mapping!"
+#endif
+
+#if !SB_CAN(MAP_EXECUTABLE_MEMORY)
+#error "Evergreen requires executable memory support!"
+#endif
+
+#endif  // SB_IS(EVERGREEN_COMPATIBLE)
+#endif  // STARBOARD_NPLB_NPLB_EVERGREEN_COMPAT_TESTS_CHECKS_H_
diff --git a/src/starboard/nplb/nplb_evergreen_compat_tests/executable_memory_test.cc b/src/starboard/nplb/nplb_evergreen_compat_tests/executable_memory_test.cc
new file mode 100644
index 0000000..ece5aa3
--- /dev/null
+++ b/src/starboard/nplb/nplb_evergreen_compat_tests/executable_memory_test.cc
@@ -0,0 +1,52 @@
+// Copyright 2020 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/nplb/nplb_evergreen_compat_tests/checks.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_IS(EVERGREEN_COMPATIBLE)
+
+namespace starboard {
+namespace nplb {
+namespace nplb_evergreen_compat_tests {
+
+namespace {
+
+const size_t kSize = 30 * 1024 * 1024;
+const size_t kSmallerSize = 15 * 1024 * 1024;
+
+class ExecutableMemoryTest : public ::testing::Test {
+ protected:
+  ExecutableMemoryTest() {}
+  ~ExecutableMemoryTest() {}
+};
+
+TEST_F(ExecutableMemoryTest, VerifyMemoryProtection) {
+  void* memory =
+      SbMemoryMap(kSize, kSbMemoryMapProtectWrite, "evergreen_buffer");
+  ASSERT_NE(SB_MEMORY_MAP_FAILED, memory);
+  SbMemorySet(memory, 0, kSize);
+  ASSERT_TRUE(SbMemoryProtect(
+      memory, kSmallerSize, kSbMemoryMapProtectRead | kSbMemoryMapProtectExec));
+  SbMemoryUnmap(memory, kSize);
+  EXPECT_TRUE(true);
+}
+
+}  // namespace
+}  // namespace nplb_evergreen_compat_tests
+}  // namespace nplb
+}  // namespace starboard
+
+#endif  // SB_IS(EVERGREEN_COMPATIBLE)
diff --git a/src/starboard/nplb/nplb_evergreen_compat_tests/nplb_evergreen_compat_tests.gyp b/src/starboard/nplb/nplb_evergreen_compat_tests/nplb_evergreen_compat_tests.gyp
new file mode 100644
index 0000000..bcca8be
--- /dev/null
+++ b/src/starboard/nplb/nplb_evergreen_compat_tests/nplb_evergreen_compat_tests.gyp
@@ -0,0 +1,45 @@
+# Copyright 2020 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.
+
+{
+  'targets': [
+    {
+      'target_name': 'nplb_evergreen_compat_tests',
+      'type': '<(gtest_target_type)',
+      'sources': [
+        'checks.h',
+        'executable_memory_test.cc',
+        'sabi_test.cc',
+        'storage_test.cc',
+        '<(DEPTH)/starboard/common/test_main.cc',
+      ],
+      'dependencies': [
+        '<(DEPTH)/starboard/starboard.gyp:starboard',
+        '<(DEPTH)/testing/gmock.gyp:gmock',
+        '<(DEPTH)/testing/gtest.gyp:gtest',
+      ],
+    },
+    {
+      'target_name': 'nplb_evergreen_compat_tests_deploy',
+      'type': 'none',
+      'dependencies': [
+        'nplb_evergreen_compat_tests',
+      ],
+      'variables': {
+        'executable_name': 'nplb_evergreen_compat_tests',
+      },
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+    },
+  ],
+}
diff --git a/src/starboard/nplb/nplb_evergreen_compat_tests/sabi_test.cc b/src/starboard/nplb/nplb_evergreen_compat_tests/sabi_test.cc
new file mode 100644
index 0000000..08560cb
--- /dev/null
+++ b/src/starboard/nplb/nplb_evergreen_compat_tests/sabi_test.cc
@@ -0,0 +1,120 @@
+// Copyright 2020 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 <iostream>
+#include <set>
+#include <string>
+
+#include "starboard/configuration.h"
+#include "starboard/nplb/nplb_evergreen_compat_tests/checks.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_IS(EVERGREEN_COMPATIBLE)
+
+namespace starboard {
+namespace nplb {
+namespace nplb_evergreen_compat_tests {
+
+namespace {
+
+const char* kSabiJsonIdArmHardfp =
+    "{\"alignment_char\":1,\"alignment_double\":8,\"alignment_float\":4,"
+    "\"alignment_int\":4,\"alignment_llong\":8,\"alignment_long\":4,"
+    "\"alignment_pointer\":4,\"alignment_short\":2,\"calling_convention\":"
+    "\"eabi\",\"endianness\":\"little\",\"floating_point_abi\":\"hard\","
+    "\"floating_point_fpu\":\"vfpv3\",\"sb_api_version\":12,\"signedness_of_"
+    "char\":\"signed\",\"signedness_of_enum\":\"signed\",\"size_of_char\":1,"
+    "\"size_of_double\":8,\"size_of_enum\":4,\"size_of_float\":4,\"size_of_"
+    "int\":4,\"size_of_llong\":8,\"size_of_long\":4,\"size_of_pointer\":4,"
+    "\"size_of_short\":2,\"target_arch\":\"arm\",\"target_arch_sub\":\"v7a\","
+    "\"word_size\":32}";
+
+const char* kSabiJsonIdArmSoftfp =
+    "{\"alignment_char\":1,\"alignment_double\":8,\"alignment_float\":4,"
+    "\"alignment_int\":4,\"alignment_llong\":8,\"alignment_long\":4,"
+    "\"alignment_pointer\":4,\"alignment_short\":2,\"calling_convention\":"
+    "\"eabi\",\"endianness\":\"little\",\"floating_point_abi\":\"softfp\","
+    "\"floating_point_fpu\":\"vfpv3\",\"sb_api_version\":12,\"signedness_of_"
+    "char\":\"signed\",\"signedness_of_enum\":\"signed\",\"size_of_char\":1,"
+    "\"size_of_double\":8,\"size_of_enum\":4,\"size_of_float\":4,\"size_of_"
+    "int\":4,\"size_of_llong\":8,\"size_of_long\":4,\"size_of_pointer\":4,"
+    "\"size_of_short\":2,\"target_arch\":\"arm\",\"target_arch_sub\":\"v7a\","
+    "\"word_size\":32}";
+
+const char* kSabiJsonIdArm64 =
+    "{\"alignment_char\":1,\"alignment_double\":8,\"alignment_float\":4,"
+    "\"alignment_int\":4,\"alignment_llong\":8,\"alignment_long\":8,"
+    "\"alignment_pointer\":8,\"alignment_short\":2,\"calling_convention\":"
+    "\"aarch64\",\"endianness\":\"little\",\"floating_point_abi\":\"\","
+    "\"floating_point_fpu\":\"\",\"sb_api_version\":12,\"signedness_of_char\":"
+    "\"signed\",\"signedness_of_enum\":\"signed\",\"size_of_char\":1,\"size_of_"
+    "double\":8,\"size_of_enum\":4,\"size_of_float\":4,\"size_of_int\":4,"
+    "\"size_of_llong\":8,\"size_of_long\":8,\"size_of_pointer\":8,\"size_of_"
+    "short\":2,\"target_arch\":\"arm64\",\"target_arch_sub\":\"v8a\",\"word_"
+    "size\":64}";
+
+const char* kSabiJsonIdX86 =
+    "{\"alignment_char\":1,\"alignment_double\":8,\"alignment_float\":4,"
+    "\"alignment_int\":4,\"alignment_llong\":8,\"alignment_long\":4,"
+    "\"alignment_pointer\":4,\"alignment_short\":2,\"calling_convention\":"
+    "\"sysv\",\"endianness\":\"little\",\"floating_point_abi\":\"\",\"floating_"
+    "point_fpu\":\"\",\"sb_api_version\":12,\"signedness_of_char\":\"signed\","
+    "\"signedness_of_enum\":\"signed\",\"size_of_char\":1,\"size_of_double\":8,"
+    "\"size_of_enum\":4,\"size_of_float\":4,\"size_of_int\":4,\"size_of_"
+    "llong\":8,\"size_of_long\":4,\"size_of_pointer\":4,\"size_of_short\":2,"
+    "\"target_arch\":\"x86\",\"target_arch_sub\":\"\",\"word_size\":32}";
+
+const char* kSabiJsonIdX64Sysv =
+    "{\"alignment_char\":1,\"alignment_double\":8,\"alignment_float\":4,"
+    "\"alignment_int\":4,\"alignment_llong\":8,\"alignment_long\":8,"
+    "\"alignment_pointer\":8,\"alignment_short\":2,\"calling_convention\":"
+    "\"sysv\",\"endianness\":\"little\",\"floating_point_abi\":\"\",\"floating_"
+    "point_fpu\":\"\",\"sb_api_version\":12,\"signedness_of_char\":\"signed\","
+    "\"signedness_of_enum\":\"signed\",\"size_of_char\":1,\"size_of_double\":8,"
+    "\"size_of_enum\":4,\"size_of_float\":4,\"size_of_int\":4,\"size_of_"
+    "llong\":8,\"size_of_long\":8,\"size_of_pointer\":8,\"size_of_short\":2,"
+    "\"target_arch\":\"x64\",\"target_arch_sub\":\"\",\"word_size\":64}";
+
+class SabiTest : public ::testing::Test {
+ protected:
+  SabiTest() {}
+  ~SabiTest() {}
+};
+
+TEST_F(SabiTest, VerifySABI) {
+  std::set<std::string> sabi_set;
+  sabi_set.insert(kSabiJsonIdArmHardfp);
+  sabi_set.insert(kSabiJsonIdArmSoftfp);
+  sabi_set.insert(kSabiJsonIdArm64);
+  sabi_set.insert(kSabiJsonIdX86);
+  sabi_set.insert(kSabiJsonIdX64Sysv);
+
+  ASSERT_NE(sabi_set.find(SB_SABI_JSON_ID), sabi_set.end())
+      << "Unsupported SABI configuration. " << std::endl
+      << "The platform should use one of the predefined SABI json files!"
+      << std::endl
+      << "Currently supported are: " << std::endl
+      << "  starboard/sabi/arm/hardfp/sabi.json" << std::endl
+      << "  starboard/sabi/arm/softfp/sabi.json" << std::endl
+      << "  starboard/sabi/arm64/sabi.json" << std::endl
+      << "  starboard/sabi/x86/sabi.json" << std::endl
+      << "  starboard/sabi/x64/sysv/sabi.json" << std::endl;
+}
+
+}  // namespace
+}  // namespace nplb_evergreen_compat_tests
+}  // namespace nplb
+}  // namespace starboard
+
+#endif  // SB_IS(EVERGREEN_COMPATIBLE)
diff --git a/src/starboard/nplb/nplb_evergreen_compat_tests/storage_test.cc b/src/starboard/nplb/nplb_evergreen_compat_tests/storage_test.cc
new file mode 100644
index 0000000..8ac05ac
--- /dev/null
+++ b/src/starboard/nplb/nplb_evergreen_compat_tests/storage_test.cc
@@ -0,0 +1,93 @@
+// Copyright 2020 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 <string>
+#include <vector>
+
+#include "starboard/common/log.h"
+#include "starboard/configuration.h"
+#include "starboard/file.h"
+#include "starboard/memory.h"
+#include "starboard/nplb/nplb_evergreen_compat_tests/checks.h"
+#include "starboard/system.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_IS(EVERGREEN_COMPATIBLE)
+
+namespace starboard {
+namespace nplb {
+namespace nplb_evergreen_compat_tests {
+
+namespace {
+
+const char kFileName[] = "test_file.data";
+const size_t kBufSize = 128 * 1024 * 1024;  // 128 MB
+
+class StorageTest : public ::testing::Test {
+ protected:
+  StorageTest() {}
+  ~StorageTest() {}
+};
+
+void WriteBuffer(const char* file_path,
+                 const char* buffer,
+                 size_t buffer_size) {
+  SbFileError error;
+  ScopedFile file(file_path, kSbFileOpenAlways | kSbFileWrite, nullptr, &error);
+  ASSERT_EQ(kSbFileOk, error) << "Failed to open file for writing";
+  int bytes_written = file.WriteAll(buffer, buffer_size);
+  ASSERT_EQ(kBufSize, bytes_written);
+}
+
+void ReadBuffer(const char* file_path, char* buffer, size_t buffer_size) {
+  SbFileError error;
+  ScopedFile file(file_path, kSbFileOpenOnly | kSbFileRead, nullptr, &error);
+  ASSERT_EQ(kSbFileOk, error) << "Failed to open file for reading";
+  int count = file.ReadAll(buffer, buffer_size);
+  ASSERT_EQ(kBufSize, count);
+}
+
+TEST_F(StorageTest, VerifyStorageDirectory) {
+  std::vector<char> storage_dir(kSbFileMaxPath);
+  ASSERT_TRUE(SbSystemGetPath(kSbSystemPathStorageDirectory, storage_dir.data(),
+                              kSbFileMaxPath));
+
+  std::string file_path = storage_dir.data();
+  file_path += kSbFileSepString;
+  file_path += kFileName;
+  SB_LOG(INFO) << "file: " << file_path;
+
+  std::vector<char> buf(kBufSize);
+  SbMemorySet(buf.data(), 'A', kBufSize);
+
+  WriteBuffer(file_path.c_str(), buf.data(), kBufSize);
+
+  SbMemorySet(buf.data(), 0, kBufSize);
+
+  ReadBuffer(file_path.c_str(), buf.data(), kBufSize);
+
+  for (int i = 0; i < kBufSize; i++) {
+    ASSERT_EQ('A', buf[i]);
+  }
+
+  ASSERT_TRUE(SbFileDelete(file_path.data()));
+  ASSERT_FALSE(SbFileExists(file_path.data()));
+}
+
+}  // namespace
+}  // namespace nplb_evergreen_compat_tests
+}  // namespace nplb
+}  // namespace starboard
+
+#endif  // SB_IS(EVERGREEN_COMPATIBLE)
diff --git a/src/starboard/nplb/player_create_test.cc b/src/starboard/nplb/player_create_test.cc
index 5c4e6a2..294db6a 100644
--- a/src/starboard/nplb/player_create_test.cc
+++ b/src/starboard/nplb/player_create_test.cc
@@ -48,12 +48,10 @@
                      SbPlayerState state,
                      int ticket) {}
 
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
 void DummyErrorFunc(SbPlayer player,
                     void* context,
                     SbPlayerError error,
                     const char* message) {}
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 
 SbPlayer CallSbPlayerCreate(
     SbWindow window,
@@ -93,18 +91,13 @@
 #else  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 
   return SbPlayerCreate(window, video_codec, audio_codec,
-#if SB_API_VERSION < 10
-                        SB_PLAYER_NO_DURATION,
-#endif  // SB_API_VERSION < 10
                         kSbDrmSystemInvalid, audio_sample_info,
 #if SB_API_VERSION >= 11
                         max_video_capabilities,
 #endif  // SB_API_VERSION >= 11
                         sample_deallocate_func, decoder_status_func,
                         player_status_func,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
                         DummyErrorFunc,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
                         context, output_mode, context_provider);
 
 #endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
@@ -152,7 +145,6 @@
   }
 }
 
-#if SB_API_VERSION >= 10
 TEST_F(SbPlayerTest, NullCallbacks) {
   SbMediaAudioSampleInfo audio_sample_info =
       CreateAudioSampleInfo(kSbMediaAudioCodecAac);
@@ -206,8 +198,6 @@
       SbPlayerDestroy(player);
     }
 
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
-
 #if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 
     {
@@ -243,19 +233,10 @@
     }
 
 #endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
-
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
   }
 }
-#endif  // SB_API_VERSION >= 10
 
-#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
-    defined(SB_HAS_AUDIOLESS_VIDEO)
 TEST_F(SbPlayerTest, Audioless) {
-  if (!kSbHasAudiolessVideo) {
-    return;
-  }
-
   SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
 
   SbPlayerOutputMode output_modes[] = {kSbPlayerOutputModeDecodeToTexture,
@@ -283,10 +264,7 @@
     SbPlayerDestroy(player);
   }
 }
-#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
-        // defined(SB_HAS_AUDIOLESS_VIDEO)
 
-#if SB_API_VERSION >= 10
 TEST_F(SbPlayerTest, AudioOnly) {
   SbMediaAudioSampleInfo audio_sample_info =
       CreateAudioSampleInfo(kSbMediaAudioCodecAac);
@@ -419,7 +397,6 @@
     SbPlayerDestroy(player);
   }
 }
-#endif  // SB_API_VERSION >= 10
 
 }  // namespace
 }  // namespace nplb
diff --git a/src/starboard/nplb/system_get_property_test.cc b/src/starboard/nplb/system_get_property_test.cc
index 46716ee..1fe8f97 100644
--- a/src/starboard/nplb/system_get_property_test.cc
+++ b/src/starboard/nplb/system_get_property_test.cc
@@ -133,9 +133,6 @@
   UnmodifiedOnFailureTest(kSbSystemPropertyNetworkOperatorName, __LINE__);
 #endif
   UnmodifiedOnFailureTest(kSbSystemPropertyPlatformName, __LINE__);
-#if SB_API_VERSION < 10
-  UnmodifiedOnFailureTest(kSbSystemPropertyPlatformUuid, __LINE__);
-#endif  // SB_API_VERSION < 10
   UnmodifiedOnFailureTest(kSbSystemPropertySpeechApiKey, __LINE__);
 #if SB_API_VERSION >= 5
   UnmodifiedOnFailureTest(kSbSystemPropertyUserAgentAuxField, __LINE__);
diff --git a/src/starboard/once.h b/src/starboard/once.h
index 076e3c7..98e8763 100644
--- a/src/starboard/once.h
+++ b/src/starboard/once.h
@@ -31,7 +31,7 @@
 #if SB_API_VERSION >= SB_PORTABLE_THREAD_TYPES_VERSION
 
 // Max size of the SbOnceControl type.
-#define SB_ONCE_MAX_SIZE 16
+#define SB_ONCE_MAX_SIZE 64
 
 // An opaque handle to a once control type with
 // reserved memory buffer of size SB_ONCE_MAX_SIZE and
diff --git a/src/starboard/player.h b/src/starboard/player.h
index 2ea9de1..c830893 100644
--- a/src/starboard/player.h
+++ b/src/starboard/player.h
@@ -70,31 +70,21 @@
 
   // The player has been destroyed, and will send no more callbacks.
   kSbPlayerStateDestroyed,
-#if !SB_HAS(PLAYER_ERROR_MESSAGE)
-  // The player encountered an error. It expects an SbPlayerDestroy() call
-  // to tear down the player. Calls to other functions may be ignored and
-  // callbacks may not be triggered.
-  kSbPlayerStateError,
-#endif  // !SB_HAS(PLAYER_ERROR_MESSAGE)
 } SbPlayerState;
 
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
 typedef enum SbPlayerError {
   kSbPlayerErrorDecode,
-#if SB_API_VERSION >= 10
   // The playback capability of the player has changed, likely because of a
   // change of the system environment.  For example, the system may support vp9
   // decoding with an external GPU.  When the external GPU is detached, this
   // error code can signal the app to retry the playback, possibly with h264.
   kSbPlayerErrorCapabilityChanged,
-#endif  // SB_API_VERSION >= 10
 #if SB_API_VERSION >= 11
   // The max value of SbPlayer error type. It should always at the bottom
   // of SbPlayerError and never be used.
   kSbPlayerErrorMax,
 #endif  // SB_API_VERSION >= 11
 } SbPlayerError;
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 
 typedef enum SbPlayerOutputMode {
   // Requests for SbPlayer to produce an OpenGL texture that the client must
@@ -209,56 +199,6 @@
   const SbDrmSampleInfo* drm_info;
 } SbPlayerSampleInfo;
 
-#if SB_API_VERSION < 10
-// Information about the current media playback state.
-typedef struct SbPlayerInfo {
-  // The position of the playback head, as precisely as possible, in 90KHz ticks
-  // (PTS).
-  SbMediaTime current_media_pts;
-
-  // The known duration of the currently playing media stream, in 90KHz ticks
-  // (PTS).
-  SbMediaTime duration_pts;
-
-  // The result of getStartDate for the currently playing media stream, in
-  // microseconds since the epoch of January 1, 1601 UTC.
-  SbTime start_date;
-
-  // The width of the currently displayed frame, in pixels, or 0 if not provided
-  // by this player.
-  int frame_width;
-
-  // The height of the currently displayed frame, in pixels, or 0 if not
-  // provided by this player.
-  int frame_height;
-
-  // Whether playback is currently paused.
-  bool is_paused;
-
-  // The current player volume in [0, 1].
-  double volume;
-
-  // The number of video frames sent to the player since the creation of the
-  // player.
-  int total_video_frames;
-
-  // The number of video frames decoded but not displayed since the creation of
-  // the player.
-  int dropped_video_frames;
-
-  // The number of video frames that failed to be decoded since the creation of
-  // the player.
-  int corrupted_video_frames;
-
-  // The rate of playback.  The video is played back in a speed that is
-  // proportional to this.  By default it is 1.0 which indicates that the
-  // playback is at normal speed.  When it is greater than one, the video is
-  // played in a faster than normal speed.  When it is less than one, the video
-  // is played in a slower than normal speed.  Negative speeds are not
-  // supported.
-  double playback_rate;
-} SbPlayerInfo;
-#else  // SB_API_VERSION < 10
 // Information about the current media playback state.
 typedef struct SbPlayerInfo2 {
   // The position of the playback head, as precisely as possible, in
@@ -306,7 +246,6 @@
   // supported.
   double playback_rate;
 } SbPlayerInfo2;
-#endif  // SB_API_VERSION < 10
 
 // An opaque handle to an implementation-private structure representing a
 // player.
@@ -338,7 +277,6 @@
                                    SbPlayerState state,
                                    int ticket);
 
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
 // Callback for player errors, that may set a |message|.
 // |error|: indicates the error code.
 // |message|: provides specific informative diagnostic message about the error
@@ -348,7 +286,6 @@
                                   void* context,
                                   SbPlayerError error,
                                   const char* message);
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 
 // Callback to free the given sample buffer data.  When more than one buffer
 // are sent in SbPlayerWriteSample(), the implementation only has to call this
@@ -402,27 +339,13 @@
 // |video_codec|: The video codec used for the player. If |video_codec| is
 //   |kSbMediaVideoCodecNone|, the player is an audio-only player. If
 //   |video_codec| is any other value, the player is an audio/video decoder.
-#if SB_API_VERSION >= 10
 //   This can be set to |kSbMediaVideoCodecNone| to play a video with only an
 //   audio track.
-#endif  // SB_API_VERSION >= 10
 //
-// |audio_codec|: The audio codec used for the player. The value should never
-//   be |kSbMediaAudioCodecNone|. In addition, the caller must provide a
-//   populated |audio_sample_info| if the audio codec is
-//   |kSbMediaAudioCodecAac|.
-#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
-    SB_HAS(AUDIOLESS_VIDEO)
-//   If |kSbHasAudiolessVideo| is |true| or SB_HAS(AUDIOLESS_VIDEO), this can
-//   be set to |kSbMediaAudioCodecNone| to play a video without any audio
+// |audio_codec|: The audio codec used for the player. The caller must provide a
+//   populated |audio_sample_info| if audio codec is |kSbMediaAudioCodecAac|.
+//   Can be set to |kSbMediaAudioCodecNone| to play a video without any audio
 //   track.  In such case |audio_sample_info| must be NULL.
-#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
-        // SB_HAS(AUDIOLESS_VIDEO)
-//
-#if SB_API_VERSION < 10
-// |duration_pts|: The expected media duration in 90KHz ticks (PTS). It may be
-//   set to |SB_PLAYER_NO_DURATION| for live streams.
-#endif  // SB_API_VERSION < 10
 //
 // |drm_system|: If the media stream has encrypted portions, then this
 //   parameter provides an appropriate DRM system, created with
@@ -430,18 +353,26 @@
 //   then |drm_system| may be |kSbDrmSystemInvalid|.
 //
 #if SB_API_VERSION < 11
+//
 // |audio_header|: |audio_header| is same as |audio_sample_info| in old
-// starboard version.
+//   starboard version. When |audio_codec| is |kSbMediaAudioCodecNone|, this
+//   must be set to NULL.
+//
 #else   // SB_API_VERSION < 11
+//
 // |audio_sample_info|: Note that the caller must provide a populated
 //   |audio_sample_info| if the audio codec is |kSbMediaAudioCodecAac|.
 //   Otherwise, |audio_sample_info| can be NULL. See media.h for the format of
 //   the |SbMediaAudioSampleInfo| struct.
+//
 #endif  // SB_API_VERSION < 11
+//
 //   Note that |audio_specific_config| is a pointer and the content it points to
 //   is no longer valid after this function returns.  The implementation has to
 //   make a copy of the content if it is needed after the function returns.
+//
 #if SB_API_VERSION >= 11
+//
 // |max_video_capabilities|: This string communicates the max video capabilities
 //   required to the platform. The web app will not provide a video stream
 //   exceeding the maximums described by this parameter. Allows the platform to
@@ -453,12 +384,7 @@
 //   second higher than 15 fps. When the maximums are unknown, this will be set
 //   to NULL.
 #endif  // SB_API_VERSION >= 11
-#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
-    SB_HAS(AUDIOLESS_VIDEO)
-//   If |kSbHasAudiolessVideo| is |true| or SB_HAS(AUDIOLESS_VIDEO), when
-//   |audio_codec| is |kSbMediaAudioCodecNone|, this must be set to NULL.
-#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
-        // SB_HAS(AUDIOLESS_VIDEO)
+
 //
 // |sample_deallocator_func|: If not |NULL|, the player calls this function
 //   on an internal thread to free the sample buffers passed into
@@ -474,11 +400,9 @@
 //   should be done on this thread. Rather, it should just signal the client
 //   thread interacting with the decoder.
 //
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
 // |player_error_func|: If not |NULL|, the player calls this function on an
 //   internal thread to provide an update on the error status. This callback is
 //   responsible for setting the media error message.
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 //
 // |context|: This is passed to all callbacks and is generally used to point
 //   at a class or struct that contains state associated with the player.
@@ -497,11 +421,9 @@
 //   the provider is not given, the player will fail by returning
 //   |kSbPlayerInvalid|.
 //
-#if SB_API_VERSION >= 10
 // If |NULL| is passed to any of the callbacks (|sample_deallocator_func|,
 // |decoder_status_func|, |player_status_func|, or |player_error_func| if it
 // applies), then |kSbPlayerInvalid| must be returned.
-#endif  // SB_API_VERSION >= 10
 
 #if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 
@@ -521,9 +443,6 @@
 SbPlayerCreate(SbWindow window,
                SbMediaVideoCodec video_codec,
                SbMediaAudioCodec audio_codec,
-#if SB_API_VERSION < 10
-               SbMediaTime duration_pts,
-#endif  // SB_API_VERSION < 10
                SbDrmSystem drm_system,
 #if SB_API_VERSION < 11
                const SbMediaAudioHeader* audio_header,
@@ -536,9 +455,7 @@
                SbPlayerDeallocateSampleFunc sample_deallocate_func,
                SbPlayerDecoderStatusFunc decoder_status_func,
                SbPlayerStatusFunc player_status_func,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
                SbPlayerErrorFunc player_error_func,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
                void* context,
                SbPlayerOutputMode output_mode,
                SbDecodeTargetGraphicsContextProvider* context_provider);
@@ -586,39 +503,6 @@
 // |player|: The player to be destroyed.
 SB_EXPORT void SbPlayerDestroy(SbPlayer player);
 
-#if SB_API_VERSION < 10
-// Tells the player to freeze playback (if playback has already started),
-// reset or flush the decoder pipeline, and go back to the Prerolling state.
-// The player should restart playback once it can display the frame at
-// |seek_to_pts|, or the closest it can get. (Some players can only seek to
-// I-Frames, for example.)
-//
-// - Seek must be called before samples are sent when starting playback for
-//   the first time, or the client never receives the
-//   |kSbPlayerDecoderStateNeedsData| signal.
-// - A call to seek may interrupt another seek.
-// - After this function is called, the client should not send any more audio
-//   or video samples until |SbPlayerDecoderStatusFunc| is called back with
-//   |kSbPlayerDecoderStateNeedsData| for each required media type.
-//   |SbPlayerDecoderStatusFunc| is the |decoder_status_func| callback function
-//   that was specified when the player was created (SbPlayerCreate).
-//
-// |player|: The SbPlayer in which the seek operation is being performed.
-// |seek_to_pts|: The frame at which playback should begin.
-// |ticket|: A user-supplied unique ID that is be passed to all subsequent
-//   |SbPlayerDecoderStatusFunc| calls. (That is the |decoder_status_func|
-//   callback function specified when calling SbPlayerCreate.)
-//
-//   The |ticket| value is used to filter calls that may have been in flight
-//   when SbPlayerSeek was called. To be very specific, once SbPlayerSeek has
-//   been called with ticket X, a client should ignore all
-//   |SbPlayerDecoderStatusFunc| calls that do not pass in ticket X.
-
-// presubmit: allow sb_export mismatch
-SB_EXPORT void SbPlayerSeek(SbPlayer player,
-                            SbMediaTime seek_to_pts,
-                            int ticket);
-#else   // SB_API_VERSION < 10
 // SbPlayerSeek2 is like the deprecated SbPlayerSeek, but accepts SbTime
 // |seek_to_timestamp| instead of SbMediaTime |seek_to_pts|.
 
@@ -652,7 +536,6 @@
 SB_EXPORT void SbPlayerSeek2(SbPlayer player,
                              SbTime seek_to_timestamp,
                              int ticket);
-#endif  // SB_API_VERSION < 10
 
 // Writes a single sample of the given media type to |player|'s input stream.
 // Its data may be passed in via more than one buffers.  The lifetime of
@@ -688,18 +571,7 @@
 //   |NULL|.
 // |sample_drm_info|: The DRM system related info for the media sample. This
 //   value is required for encrypted samples. Otherwise, it must be |NULL|.
-#if SB_API_VERSION < 10
-// presubmit: allow sb_export mismatch
-SB_EXPORT void SbPlayerWriteSample(
-    SbPlayer player,
-    SbMediaType sample_type,
-    const void* const* sample_buffers,
-    const int* sample_buffer_sizes,
-    int number_of_sample_buffers,
-    SbMediaTime sample_pts,
-    const SbMediaVideoSampleInfo* video_sample_info,
-    const SbDrmSampleInfo* sample_drm_info);
-#else  // SB_API_VERSION < 10
+
 // SbPlayerWriteSample2 is like the deprecated SbPlayerWriteSample, but accepts
 // SbTime |sample_timestamp| instead of SbMediaTime |sample_pts|, and also
 // allows writing of multiple samples in one SbPlayerWriteSample2() call.
@@ -744,8 +616,6 @@
     SbPlayer player,
     SbMediaType sample_type);
 
-#endif  // SB_API_VERSION < 10
-
 // Writes a marker to |player|'s input stream of |stream_type| indicating that
 // there are no more samples for that media type for the remainder of this
 // media stream. This marker is invalidated, along with the rest of the stream's
@@ -803,17 +673,6 @@
 //   value of |1.0| means that it should be played at full volume.
 SB_EXPORT void SbPlayerSetVolume(SbPlayer player, double volume);
 
-#if SB_API_VERSION < 10
-// Gets a snapshot of the current player state and writes it to
-// |out_player_info|. This function may be called very frequently and is
-// expected to be inexpensive.
-//
-// |player|: The player about which information is being retrieved.
-// |out_player_info|: The information retrieved for the player.
-
-// presubmit: allow sb_export mismatch
-SB_EXPORT void SbPlayerGetInfo(SbPlayer player, SbPlayerInfo* out_player_info);
-#else   // SB_API_VERSION < 10
 // SbPlayerGetInfo2 is like the deprecated SbPlayerGetInfo, but accepts
 // SbPlayerInfo2* |out_player_info2| instead of SbPlayerInfo |out_player_info|.
 
@@ -825,7 +684,6 @@
 // |out_player_info|: The information retrieved for the player.
 SB_EXPORT void SbPlayerGetInfo2(SbPlayer player,
                                 SbPlayerInfo2* out_player_info2);
-#endif  // SB_API_VERSION < 10
 
 // Given a player created with the kSbPlayerOutputModeDecodeToTexture
 // output mode, it will return a SbDecodeTarget representing the current frame
diff --git a/src/starboard/raspi/2/gyp_configuration.gypi b/src/starboard/raspi/2/gyp_configuration.gypi
index a5617d4..86a20c1 100644
--- a/src/starboard/raspi/2/gyp_configuration.gypi
+++ b/src/starboard/raspi/2/gyp_configuration.gypi
@@ -13,6 +13,12 @@
 # limitations under the License.
 
 {
+  'variables': {
+    'variables': {
+      'sb_evergreen_compatible': 1,
+    },
+  },
+
   'target_defaults': {
     'default_configuration': 'raspi-2_debug',
     'configurations': {
diff --git a/src/starboard/raspi/2/mozjs/atomic_public.h b/src/starboard/raspi/2/mozjs/atomic_public.h
deleted file mode 100644
index 5ad1ce6..0000000
--- a/src/starboard/raspi/2/mozjs/atomic_public.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2018 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 STARBOARD_RASPI_2_MOZJS_ATOMIC_PUBLIC_H_
-#define STARBOARD_RASPI_2_MOZJS_ATOMIC_PUBLIC_H_
-
-#include "starboard/raspi/2/atomic_public.h"
-
-#endif  // STARBOARD_RASPI_2_MOZJS_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/raspi/2/mozjs/configuration_public.h b/src/starboard/raspi/2/mozjs/configuration_public.h
deleted file mode 100644
index 3b4427e..0000000
--- a/src/starboard/raspi/2/mozjs/configuration_public.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2018 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.
-
-// The Starboard configuration for Raspberry PI 2 Raspbian.
-
-// Other source files should never include this header directly, but should
-// include the generic "starboard/configuration.h" instead.
-
-#ifndef STARBOARD_RASPI_2_MOZJS_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_RASPI_2_MOZJS_CONFIGURATION_PUBLIC_H_
-
-#include "starboard/raspi/2/configuration_public.h"
-
-#endif  // STARBOARD_RASPI_2_MOZJS_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/raspi/2/mozjs/gyp_configuration.gypi b/src/starboard/raspi/2/mozjs/gyp_configuration.gypi
deleted file mode 100644
index 4cc3212..0000000
--- a/src/starboard/raspi/2/mozjs/gyp_configuration.gypi
+++ /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.
-
-{
-  'target_defaults': {
-    'default_configuration': 'raspi-2-mozjs_debug',
-    'configurations': {
-      'raspi-2-mozjs_debug': {
-        'inherit_from': ['debug_base'],
-      },
-      'raspi-2-mozjs_devel': {
-        'inherit_from': ['devel_base'],
-      },
-      'raspi-2-mozjs_qa': {
-        'inherit_from': ['qa_base'],
-      },
-      'raspi-2-mozjs_gold': {
-        'inherit_from': ['gold_base'],
-      },
-    }, # end of configurations
-  },
-
-  'includes': [
-    '../architecture.gypi',
-    '../../shared/gyp_configuration.gypi',
-    '<(DEPTH)/starboard/sabi/sabi.gypi',
-  ],
-}
diff --git a/src/starboard/raspi/2/mozjs/gyp_configuration.py b/src/starboard/raspi/2/mozjs/gyp_configuration.py
deleted file mode 100644
index decc64f..0000000
--- a/src/starboard/raspi/2/mozjs/gyp_configuration.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright 2018 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.
-"""Starboard Raspberry Pi 2 platform configuration for gyp_cobalt."""
-
-import importlib
-
-# pylint: disable=invalid-name
-Raspi2PlatformConfig = importlib.import_module(
-    'starboard.raspi.2.gyp_configuration').Raspi2PlatformConfig
-
-
-class Raspi2MozjsPlatformConfig(Raspi2PlatformConfig):
-
-  def __init__(self, platform, sabi_json_path=None):
-    super(Raspi2MozjsPlatformConfig, self).__init__(
-        platform, sabi_json_path=sabi_json_path)
-
-  def GetVariables(self, config_name):
-    variables = super(Raspi2MozjsPlatformConfig, self).GetVariables(config_name)
-    variables.update({
-        'javascript_engine': 'mozjs-45',
-        'cobalt_enable_jit': 0,
-    })
-    return variables
-
-
-def CreatePlatformConfig():
-  return Raspi2MozjsPlatformConfig(
-      'raspi-2-mozjs', sabi_json_path='starboard/sabi/arm/hardfp/sabi.json')
diff --git a/src/starboard/raspi/2/skia/configuration.cc b/src/starboard/raspi/2/skia/configuration.cc
new file mode 100644
index 0000000..0ffdf45
--- /dev/null
+++ b/src/starboard/raspi/2/skia/configuration.cc
@@ -0,0 +1,73 @@
+// Copyright 2020 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/raspi/2/skia/configuration.h"
+
+#include "cobalt/extension/configuration.h"
+#include "starboard/common/configuration_defaults.h"
+
+namespace starboard {
+namespace raspi {
+namespace skia {
+
+namespace {
+
+int CobaltSkiaGlyphAtlasWidth() {
+  return 2048;
+}
+
+int CobaltSkiaGlyphAtlasHeight() {
+  return 2048;
+}
+
+const char* CobaltRasterizerType() {
+  return "hardware";
+}
+
+const CobaltExtensionConfigurationApi kConfigurationApi = {
+    kCobaltExtensionConfigurationName,
+    1,
+    &common::CobaltUserOnExitStrategyDefault,
+    &common::CobaltRenderDirtyRegionOnlyDefault,
+    &common::CobaltEglSwapIntervalDefault,
+    &common::CobaltFallbackSplashScreenUrlDefault,
+    &common::CobaltEnableQuicDefault,
+    &common::CobaltSkiaCacheSizeInBytesDefault,
+    &common::CobaltOffscreenTargetCacheSizeInBytesDefault,
+    &common::CobaltEncodedImageCacheSizeInBytesDefault,
+    &common::CobaltImageCacheSizeInBytesDefault,
+    &common::CobaltLocalTypefaceCacheSizeInBytesDefault,
+    &common::CobaltRemoteTypefaceCacheSizeInBytesDefault,
+    &common::CobaltMeshCacheSizeInBytesDefault,
+    &common::CobaltSoftwareSurfaceCacheSizeInBytesDefault,
+    &common::CobaltImageCacheCapacityMultiplierWhenPlayingVideoDefault,
+    &CobaltSkiaGlyphAtlasWidth,
+    &CobaltSkiaGlyphAtlasHeight,
+    &common::CobaltJsGarbageCollectionThresholdInBytesDefault,
+    &common::CobaltReduceCpuMemoryByDefault,
+    &common::CobaltReduceGpuMemoryByDefault,
+    &common::CobaltGcZealDefault,
+    &CobaltRasterizerType,
+    &common::CobaltEnableJitDefault,
+};
+
+}  // namespace
+
+const void* GetConfigurationApi() {
+  return &kConfigurationApi;
+}
+
+}  // namespace skia
+}  // namespace raspi
+}  // namespace starboard
diff --git a/src/starboard/raspi/2/skia/configuration.h b/src/starboard/raspi/2/skia/configuration.h
new file mode 100644
index 0000000..4ce8394
--- /dev/null
+++ b/src/starboard/raspi/2/skia/configuration.h
@@ -0,0 +1,28 @@
+// Copyright 2020 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 STARBOARD_RASPI_2_SKIA_CONFIGURATION_H_
+#define STARBOARD_RASPI_2_SKIA_CONFIGURATION_H_
+
+namespace starboard {
+namespace raspi {
+namespace skia {
+
+const void* GetConfigurationApi();
+
+}  // namespace skia
+}  // namespace raspi
+}  // namespace starboard
+
+#endif  // STARBOARD_RASPI_2_SKIA_CONFIGURATION_H_
diff --git a/src/starboard/raspi/2/skia/starboard_platform.gyp b/src/starboard/raspi/2/skia/starboard_platform.gyp
index 1a1111d..8057cd1 100644
--- a/src/starboard/raspi/2/skia/starboard_platform.gyp
+++ b/src/starboard/raspi/2/skia/starboard_platform.gyp
@@ -15,4 +15,16 @@
   'includes': [
     '../../shared/starboard_platform.gypi',
   ],
+  'target_defaults': {
+    'sources': [
+      '<(DEPTH)/starboard/raspi/2/skia/configuration.cc',
+      '<(DEPTH)/starboard/raspi/2/skia/configuration.h',
+      '<(DEPTH)/starboard/raspi/2/skia/system_get_extensions.cc',
+    ],
+    'sources!': [
+      '<(DEPTH)/starboard/raspi/shared/configuration.cc',
+      '<(DEPTH)/starboard/raspi/shared/configuration.h',
+      '<(DEPTH)/starboard/raspi/shared/system_get_extensions.cc',
+    ],
+  },
 }
diff --git a/src/starboard/raspi/2/skia/system_get_extensions.cc b/src/starboard/raspi/2/skia/system_get_extensions.cc
new file mode 100644
index 0000000..01e50ba
--- /dev/null
+++ b/src/starboard/raspi/2/skia/system_get_extensions.cc
@@ -0,0 +1,26 @@
+// Copyright 2020 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/system.h"
+
+#include "cobalt/extension/configuration.h"
+#include "starboard/common/string.h"
+#include "starboard/raspi/2/skia/configuration.h"
+
+const void* SbSystemGetExtension(const char* name) {
+  if (SbStringCompareAll(name, kCobaltExtensionConfigurationName) == 0) {
+    return starboard::raspi::skia::GetConfigurationApi();
+  }
+  return NULL;
+}
diff --git a/src/starboard/raspi/shared/cobalt/configuration.py b/src/starboard/raspi/shared/cobalt/configuration.py
index 49d9ade..7c7d912 100644
--- a/src/starboard/raspi/shared/cobalt/configuration.py
+++ b/src/starboard/raspi/shared/cobalt/configuration.py
@@ -61,6 +61,7 @@
     filters += [
         ('csp/WebPlatformTest.Run/'
          'content_security_policy_media_src_media_src_allowed_html'),
+        ('websockets/WebPlatformTest.Run/websockets_*'),
     ]
     return filters
 
diff --git a/src/starboard/raspi/shared/configuration.cc b/src/starboard/raspi/shared/configuration.cc
new file mode 100644
index 0000000..5dc4c10
--- /dev/null
+++ b/src/starboard/raspi/shared/configuration.cc
@@ -0,0 +1,73 @@
+// Copyright 2020 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/raspi/shared/configuration.h"
+
+#include "cobalt/extension/configuration.h"
+#include "starboard/common/configuration_defaults.h"
+
+namespace starboard {
+namespace raspi {
+namespace shared {
+
+namespace {
+
+int CobaltSkiaGlyphAtlasWidth() {
+  return 2048;
+}
+
+int CobaltSkiaGlyphAtlasHeight() {
+  return 2048;
+}
+
+bool CobaltEnableJit() {
+  return true;
+}
+
+const CobaltExtensionConfigurationApi kConfigurationApi = {
+    kCobaltExtensionConfigurationName,
+    1,
+    &common::CobaltUserOnExitStrategyDefault,
+    &common::CobaltRenderDirtyRegionOnlyDefault,
+    &common::CobaltEglSwapIntervalDefault,
+    &common::CobaltFallbackSplashScreenUrlDefault,
+    &common::CobaltEnableQuicDefault,
+    &common::CobaltSkiaCacheSizeInBytesDefault,
+    &common::CobaltOffscreenTargetCacheSizeInBytesDefault,
+    &common::CobaltEncodedImageCacheSizeInBytesDefault,
+    &common::CobaltImageCacheSizeInBytesDefault,
+    &common::CobaltLocalTypefaceCacheSizeInBytesDefault,
+    &common::CobaltRemoteTypefaceCacheSizeInBytesDefault,
+    &common::CobaltMeshCacheSizeInBytesDefault,
+    &common::CobaltSoftwareSurfaceCacheSizeInBytesDefault,
+    &common::CobaltImageCacheCapacityMultiplierWhenPlayingVideoDefault,
+    &CobaltSkiaGlyphAtlasWidth,
+    &CobaltSkiaGlyphAtlasHeight,
+    &common::CobaltJsGarbageCollectionThresholdInBytesDefault,
+    &common::CobaltReduceCpuMemoryByDefault,
+    &common::CobaltReduceGpuMemoryByDefault,
+    &common::CobaltGcZealDefault,
+    &common::CobaltRasterizerTypeDefault,
+    &CobaltEnableJit,
+};
+
+}  // namespace
+
+const void* GetConfigurationApi() {
+  return &kConfigurationApi;
+}
+
+}  // namespace shared
+}  // namespace raspi
+}  // namespace starboard
diff --git a/src/starboard/raspi/shared/configuration.h b/src/starboard/raspi/shared/configuration.h
new file mode 100644
index 0000000..cee3db6
--- /dev/null
+++ b/src/starboard/raspi/shared/configuration.h
@@ -0,0 +1,28 @@
+// Copyright 2020 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 STARBOARD_RASPI_SHARED_CONFIGURATION_H_
+#define STARBOARD_RASPI_SHARED_CONFIGURATION_H_
+
+namespace starboard {
+namespace raspi {
+namespace shared {
+
+const void* GetConfigurationApi();
+
+}  // namespace shared
+}  // namespace raspi
+}  // namespace starboard
+
+#endif  // STARBOARD_RASPI_SHARED_CONFIGURATION_H_
diff --git a/src/starboard/raspi/shared/configuration_constants.cc b/src/starboard/raspi/shared/configuration_constants.cc
index 459b15e..b81883f 100644
--- a/src/starboard/raspi/shared/configuration_constants.cc
+++ b/src/starboard/raspi/shared/configuration_constants.cc
@@ -55,9 +55,6 @@
 // Specifies whether this platform updates audio frames asynchronously.
 const bool kSbHasAsyncAudioFramesReporting = false;
 
-// Allow playing audioless video.
-const bool kSbHasAudiolessVideo = true;
-
 // Specifies whether this platform has webm/vp9 support.  This should be set to
 // non-zero on platforms with webm/vp9 support.
 const bool kSbHasMediaWebmVp9Support = false;
@@ -142,5 +139,10 @@
 // The string form of SB_PATH_SEP_CHAR.
 const char* kSbPathSepString = ":";
 
+// Specifies the preferred byte order of color channels in a pixel. Refer to
+// starboard/configuration.h for the possible values. EGL/GLES platforms should
+// generally prefer a byte order of RGBA, regardless of endianness.
+const int kSbPreferredRgbaByteOrder = SB_PREFERRED_RGBA_BYTE_ORDER_RGBA;
+
 // The maximum number of users that can be signed in at the same time.
 const uint32_t kSbUserMaxSignedIn = 1;
diff --git a/src/starboard/raspi/shared/configuration_public.h b/src/starboard/raspi/shared/configuration_public.h
index 374a37f..8623dcd 100644
--- a/src/starboard/raspi/shared/configuration_public.h
+++ b/src/starboard/raspi/shared/configuration_public.h
@@ -143,11 +143,6 @@
 // API. The basic requirement is a scaled, clipped, alpha-blended blit.
 #define SB_HAS_BLITTER 0
 
-// Specifies the preferred byte order of color channels in a pixel. Refer to
-// starboard/configuration.h for the possible values. EGL/GLES platforms should
-// generally prefer a byte order of RGBA, regardless of endianness.
-#define SB_PREFERRED_RGBA_BYTE_ORDER SB_PREFERRED_RGBA_BYTE_ORDER_RGBA
-
 // Indicates whether or not the given platform supports bilinear filtering.
 // This can be checked to enable/disable renderer tests that verify that this is
 // working properly.
diff --git a/src/starboard/raspi/shared/gyp_configuration.gypi b/src/starboard/raspi/shared/gyp_configuration.gypi
index da08556..a404e2b 100644
--- a/src/starboard/raspi/shared/gyp_configuration.gypi
+++ b/src/starboard/raspi/shared/gyp_configuration.gypi
@@ -14,15 +14,10 @@
 
 {
   'variables': {
-    'variables': {
-      'sb_evergreen_compatible': '<!(python <(DEPTH)/build/file_exists.py <(DEPTH)/starboard/elf_loader/evergreen_info.gyp)',
-    },
-
     # Override that omits the "data" subdirectory.
     # TODO: Remove when omitted for all platforms in base_configuration.gypi.
     'sb_static_contents_output_data_dir': '<(PRODUCT_DIR)/content',
 
-    'target_arch': 'arm',
     'target_os': 'linux',
 
     'sysroot%': '/',
diff --git a/src/starboard/raspi/shared/gyp_configuration.py b/src/starboard/raspi/shared/gyp_configuration.py
index 7b6a213..5abd0c8 100644
--- a/src/starboard/raspi/shared/gyp_configuration.py
+++ b/src/starboard/raspi/shared/gyp_configuration.py
@@ -15,10 +15,16 @@
 
 import os
 
-from starboard.build import clang
+from starboard.build import clang as clang_specification
 from starboard.build import platform_configuration
 from starboard.tools import build
 from starboard.tools.testing import test_filter
+from starboard.tools.toolchain import ar
+from starboard.tools.toolchain import bash
+from starboard.tools.toolchain import clang
+from starboard.tools.toolchain import clangxx
+from starboard.tools.toolchain import cp
+from starboard.tools.toolchain import touch
 
 # Use a bogus path instead of None so that anything based on $RASPI_HOME won't
 # inadvertently end up pointing to something in the root directory, and this
@@ -60,7 +66,7 @@
 
   def GetEnvironmentVariables(self):
     env_variables = build.GetHostCompilerEnvironment(
-        clang.GetClangSpecification(), False)
+        clang_specification.GetClangSpecification(), False)
     toolchain = os.path.realpath(
         os.path.join(
             self.raspi_home,
@@ -82,6 +88,42 @@
       raise RuntimeError('RasPi builds require $RASPI_HOME/sysroot '
                          'to be a valid directory.')
 
+  def GetTargetToolchain(self):
+    environment_variables = self.GetEnvironmentVariables()
+    cc_path = environment_variables['CC']
+    cxx_path = environment_variables['CXX']
+
+    return [
+        clang.CCompiler(path=cc_path),
+        clang.CxxCompiler(path=cxx_path),
+        clang.AssemblerWithCPreprocessor(path=cc_path),
+        ar.StaticThinLinker(),
+        ar.StaticLinker(),
+        clangxx.ExecutableLinker(path=cxx_path, write_group=True),
+        clangxx.SharedLibraryLinker(path=cxx_path),
+        cp.Copy(),
+        touch.Stamp(),
+        bash.Shell(),
+    ]
+
+  def GetHostToolchain(self):
+    environment_variables = self.GetEnvironmentVariables()
+    cc_path = environment_variables['CC_host']
+    cxx_path = environment_variables['CXX_host']
+
+    return [
+        clang.CCompiler(path=cc_path),
+        clang.CxxCompiler(path=cxx_path),
+        clang.AssemblerWithCPreprocessor(path=cc_path),
+        ar.StaticThinLinker(),
+        ar.StaticLinker(),
+        clangxx.ExecutableLinker(path=cxx_path, write_group=True),
+        clangxx.SharedLibraryLinker(path=cxx_path),
+        cp.Copy(),
+        touch.Stamp(),
+        bash.Shell(),
+    ]
+
   def GetLauncherPath(self):
     """Gets the path to the launcher module for this platform."""
     return os.path.dirname(__file__)
diff --git a/src/starboard/raspi/shared/media_is_video_supported.cc b/src/starboard/raspi/shared/media_is_video_supported.cc
index e7e7d2e..04ef357 100644
--- a/src/starboard/raspi/shared/media_is_video_supported.cc
+++ b/src/starboard/raspi/shared/media_is_video_supported.cc
@@ -20,6 +20,7 @@
 #include "starboard/shared/starboard/media/media_util.h"
 
 bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
+                             const char* content_type,
                              int profile,
                              int level,
                              int bit_depth,
@@ -31,6 +32,7 @@
                              int64_t bitrate,
                              int fps,
                              bool decode_to_texture_required) {
+  SB_UNREFERENCED_PARAMETER(content_type);
   SB_UNREFERENCED_PARAMETER(profile);
   SB_UNREFERENCED_PARAMETER(level);
 
diff --git a/src/starboard/raspi/shared/open_max/open_max_component_base.cc b/src/starboard/raspi/shared/open_max/open_max_component_base.cc
index 1ed246a..3af019a 100644
--- a/src/starboard/raspi/shared/open_max/open_max_component_base.cc
+++ b/src/starboard/raspi/shared/open_max/open_max_component_base.cc
@@ -36,7 +36,8 @@
 
 void DoInitializeOpenMax() {
   OMX_ERRORTYPE error = OMX_Init();
-  SB_DCHECK(error == OMX_ErrorNone);
+  SB_DCHECK(error == OMX_ErrorNone)
+      << "OMX_Init() failed with error code: 0x" << std::hex << error << ".";
 }
 
 void InitializeOpenMax() {
diff --git a/src/starboard/raspi/shared/starboard_platform.gypi b/src/starboard/raspi/shared/starboard_platform.gypi
index c596afd..63f2be3 100644
--- a/src/starboard/raspi/shared/starboard_platform.gypi
+++ b/src/starboard/raspi/shared/starboard_platform.gypi
@@ -47,6 +47,8 @@
         '<(DEPTH)/starboard/linux/shared/system_has_capability.cc',
         '<(DEPTH)/starboard/raspi/shared/application_dispmanx.cc',
         '<(DEPTH)/starboard/raspi/shared/audio_sink_type_dispatcher.cc',
+        '<(DEPTH)/starboard/raspi/shared/configuration.cc',
+        '<(DEPTH)/starboard/raspi/shared/configuration.h',
         '<(DEPTH)/starboard/raspi/shared/dispmanx_util.cc',
         '<(DEPTH)/starboard/raspi/shared/dispmanx_util.h',
         '<(DEPTH)/starboard/raspi/shared/main.cc',
@@ -74,6 +76,7 @@
         '<(DEPTH)/starboard/raspi/shared/open_max/video_decoder.h',
         '<(DEPTH)/starboard/raspi/shared/player_components_factory.cc',
         '<(DEPTH)/starboard/raspi/shared/system_get_device_type.cc',
+        '<(DEPTH)/starboard/raspi/shared/system_get_extensions.cc',
         '<(DEPTH)/starboard/raspi/shared/system_get_property.cc',
         '<(DEPTH)/starboard/raspi/shared/system_gles2.cc',
         '<(DEPTH)/starboard/raspi/shared/thread_create_priority.cc',
@@ -326,13 +329,11 @@
         '<(DEPTH)/starboard/shared/starboard/player/player_create.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_get_current_frame.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_get_info2.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_get_maximum_number_of_samples_per_write.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_seek2.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_set_playback_rate.cc',
@@ -340,7 +341,6 @@
         '<(DEPTH)/starboard/shared/starboard/player/player_worker.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_worker.h',
         '<(DEPTH)/starboard/shared/starboard/player/player_write_end_of_stream.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_write_sample.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_write_sample2.cc',
         '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
         '<(DEPTH)/starboard/shared/starboard/string_concat.cc',
@@ -390,7 +390,6 @@
         '<(DEPTH)/starboard/shared/stub/speech_synthesis_cancel.cc',
         '<(DEPTH)/starboard/shared/stub/speech_synthesis_is_supported.cc',
         '<(DEPTH)/starboard/shared/stub/speech_synthesis_speak.cc',
-        '<(DEPTH)/starboard/shared/stub/system_get_extensions.cc',
         '<(DEPTH)/starboard/shared/stub/system_get_total_gpu_memory.cc',
         '<(DEPTH)/starboard/shared/stub/system_get_used_gpu_memory.cc',
         '<(DEPTH)/starboard/shared/stub/system_hide_splash_screen.cc',
@@ -425,6 +424,7 @@
       'conditions': [
         ['sb_evergreen_compatible == 1', {
           'dependencies': [
+            '<(DEPTH)/starboard/elf_loader/evergreen_config.gyp:evergreen_config',
             '<(DEPTH)/third_party/llvm-project/libunwind/libunwind.gyp:unwind_starboard',
           ],},
         ],
diff --git a/src/starboard/raspi/shared/system_get_extensions.cc b/src/starboard/raspi/shared/system_get_extensions.cc
new file mode 100644
index 0000000..8ac669a
--- /dev/null
+++ b/src/starboard/raspi/shared/system_get_extensions.cc
@@ -0,0 +1,42 @@
+// Copyright 2020 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/system.h"
+
+#include "cobalt/extension/configuration.h"
+#include "starboard/common/string.h"
+#if SB_IS(EVERGREEN_COMPATIBLE)
+#include "starboard/elf_loader/evergreen_config.h"
+#endif
+
+#include "starboard/raspi/shared/configuration.h"
+
+const void* SbSystemGetExtension(const char* name) {
+#if SB_IS(EVERGREEN_COMPATIBLE)
+  const starboard::elf_loader::EvergreenConfig* evergreen_config =
+      starboard::elf_loader::EvergreenConfig::GetInstance();
+  if (evergreen_config != NULL &&
+      evergreen_config->custom_get_extension_ != NULL) {
+    const void* ext = evergreen_config->custom_get_extension_(name);
+    if (ext != NULL) {
+      return ext;
+    }
+  }
+#endif
+
+  if (SbStringCompareAll(name, kCobaltExtensionConfigurationName) == 0) {
+    return starboard::raspi::shared::GetConfigurationApi();
+  }
+  return NULL;
+}
diff --git a/src/starboard/shared/alsa/alsa_util.cc b/src/starboard/shared/alsa/alsa_util.cc
index 3d43c7e..8413a6d 100644
--- a/src/starboard/shared/alsa/alsa_util.cc
+++ b/src/starboard/shared/alsa/alsa_util.cc
@@ -29,6 +29,19 @@
     }                                                         \
   } while (false)
 
+#if defined(ADDRESS_SANITIZER)
+// By default, Leak Sanitizer and Address Sanitizer is expected to exist
+// together. However, this is not true for all platforms.
+// HAS_LEAK_SANTIZER=0 explicitly removes the Leak Sanitizer from code.
+#ifndef HAS_LEAK_SANITIZER
+#define HAS_LEAK_SANITIZER 1
+#endif  // HAS_LEAK_SANITIZER
+#endif  // defined(ADDRESS_SANITIZER)
+
+#if HAS_LEAK_SANITIZER
+#include <sanitizer/lsan_interface.h>
+#endif  // HAS_LEAK_SANITIZER
+
 namespace starboard {
 namespace shared {
 namespace alsa {
@@ -105,8 +118,14 @@
             sample_type == SND_PCM_FORMAT_S16);
 
   PcmHandle playback_handle;
+#if HAS_LEAK_SANITIZER
+  __lsan_disable();
+#endif  // HAS_LEAK_SANITIZER
   int error =
       snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
+#if HAS_LEAK_SANITIZER
+  __lsan_enable();
+#endif  // HAS_LEAK_SANITIZER
   ALSA_CHECK(error, snd_pcm_open, NULL);
   playback_handle.set_valid();
 
diff --git a/src/starboard/shared/directfb/application_directfb.cc b/src/starboard/shared/directfb/application_directfb.cc
index 0a89a08..c8c7a83 100644
--- a/src/starboard/shared/directfb/application_directfb.cc
+++ b/src/starboard/shared/directfb/application_directfb.cc
@@ -524,9 +524,7 @@
 
     SbInputData* data = new SbInputData();
     SbMemorySet(data, 0, sizeof(*data));
-#if SB_API_VERSION >= 10
     data->timestamp = SbTimeGetMonotonicNow();
-#endif  // SB_API_VERSION >= 10
     data->window = window_;
     SB_DCHECK(SbWindowIsValid(data->window));
     data->type = (event.type == DIET_KEYPRESS ? kSbInputEventTypePress
diff --git a/src/starboard/shared/dlmalloc/memory_protect.cc b/src/starboard/shared/dlmalloc/memory_protect.cc
index 8ab3a22..91a4c59 100644
--- a/src/starboard/shared/dlmalloc/memory_protect.cc
+++ b/src/starboard/shared/dlmalloc/memory_protect.cc
@@ -16,8 +16,6 @@
 #include "starboard/memory.h"
 #include "starboard/shared/dlmalloc/page_internal.h"
 
-#if SB_API_VERSION >= 10
 bool SbMemoryProtect(void* virtual_address, int64_t size_bytes, int flags) {
   return SbPageProtect(virtual_address, size_bytes, flags);
 }
-#endif
diff --git a/src/starboard/shared/dlmalloc/page_internal.h b/src/starboard/shared/dlmalloc/page_internal.h
index 040e362..6b1d5ca 100644
--- a/src/starboard/shared/dlmalloc/page_internal.h
+++ b/src/starboard/shared/dlmalloc/page_internal.h
@@ -129,11 +129,9 @@
 // allocated via MapUntracked().
 bool SbPageUnmapUntracked(void* virtual_address, size_t size_bytes);
 
-#if SB_API_VERSION >= 10
 // Change the protection of |size_bytes| of physical pages, starting from
 // |virtual_address|, to |flags|, returning |true| on success.
 bool SbPageProtect(void* virtual_address, int64_t size_bytes, int flags);
-#endif
 #endif  // SB_API_VERSION >= SB_MMAP_REQUIRED_VERSION || SB_HAS(MMAP)
 
 // Returns the total amount, in bytes, of physical memory available. Should
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc
index fec161c..ae422cb 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc
@@ -137,13 +137,9 @@
     SB_DLOG(WARNING) << "avcodec_decode_audio4() failed with result: " << result
                      << " with input buffer size: " << input_buffer->size()
                      << " and frame decoded: " << frame_decoded;
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     error_cb_(
         kSbPlayerErrorDecode,
         FormatString("avcodec_decode_audio4() failed with result %d.", result));
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-    error_cb_();
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
     return;
   }
 
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc
index fac3b9b..0509caa 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc
@@ -257,13 +257,9 @@
   if (decode_result < 0) {
     SB_DLOG(ERROR) << "avcodec_decode_video2() failed with result "
                    << decode_result;
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     error_cb_(kSbPlayerErrorDecode,
               FormatString("avcodec_decode_video2() failed with result %d.",
                            decode_result));
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-    error_cb_();
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
     error_occured_ = true;
     return false;
   }
@@ -273,12 +269,8 @@
 
   if (av_frame_->opaque == NULL) {
     SB_DLOG(ERROR) << "Video frame was produced yet has invalid frame data.";
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     error_cb_(kSbPlayerErrorDecode,
               "Video frame was produced yet has invalid frame data.");
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-    error_cb_();
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
     error_occured_ = true;
     return false;
   }
diff --git a/src/starboard/shared/gcc/atomic_gcc_public.h b/src/starboard/shared/gcc/atomic_gcc_public.h
index 6e6e645..bf1eded 100644
--- a/src/starboard/shared/gcc/atomic_gcc_public.h
+++ b/src/starboard/shared/gcc/atomic_gcc_public.h
@@ -101,7 +101,6 @@
 }
 
 // 8-bit atomic operations.
-#if SB_API_VERSION >= 10
 static SB_C_FORCE_INLINE SbAtomic8
 SbAtomicRelease_CompareAndSwap8(volatile SbAtomic8* ptr,
                                SbAtomic8 old_value,
@@ -121,7 +120,6 @@
 SbAtomicNoBarrier_Load8(volatile const SbAtomic8* ptr) {
   return __atomic_load_n(ptr, __ATOMIC_RELAXED);
 }
-#endif  // SB_API_VERSION >= 10
 
 // 64-bit atomic operations (only available on 64-bit processors).
 #if SB_HAS(64_BIT_ATOMICS)
diff --git a/src/starboard/shared/libaom/aom_video_decoder.cc b/src/starboard/shared/libaom/aom_video_decoder.cc
index f2caac4..711ea24 100644
--- a/src/starboard/shared/libaom/aom_video_decoder.cc
+++ b/src/starboard/shared/libaom/aom_video_decoder.cc
@@ -141,11 +141,7 @@
   SB_DCHECK(decoder_thread_->job_queue()->BelongsToCurrentThread());
 
   error_occured_ = true;
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
   Schedule(std::bind(error_cb_, kSbPlayerErrorDecode, error_message));
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-  Schedule(error_cb_);
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 }
 
 void VideoDecoder::InitializeCodec() {
diff --git a/src/starboard/shared/libdav1d/dav1d_video_decoder.cc b/src/starboard/shared/libdav1d/dav1d_video_decoder.cc
index 143df5c..d063310 100644
--- a/src/starboard/shared/libdav1d/dav1d_video_decoder.cc
+++ b/src/starboard/shared/libdav1d/dav1d_video_decoder.cc
@@ -199,11 +199,7 @@
 
 void VideoDecoder::ReportError(const std::string& error_message) {
   SB_LOG(ERROR) << error_message;
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
   Schedule(std::bind(error_cb_, kSbPlayerErrorDecode, error_message));
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-  Schedule(error_cb_);
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 }
 
 void VideoDecoder::InitializeCodec() {
diff --git a/src/starboard/shared/libde265/de265_video_decoder.cc b/src/starboard/shared/libde265/de265_video_decoder.cc
index 619f962..931e5ab 100644
--- a/src/starboard/shared/libde265/de265_video_decoder.cc
+++ b/src/starboard/shared/libde265/de265_video_decoder.cc
@@ -132,11 +132,7 @@
   SB_DCHECK(decoder_thread_->job_queue()->BelongsToCurrentThread());
 
   error_occured_ = true;
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
   Schedule(std::bind(error_cb_, kSbPlayerErrorDecode, error_message));
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-  Schedule(error_cb_);
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 }
 
 void VideoDecoder::InitializeCodec() {
diff --git a/src/starboard/shared/libjpeg/jpeg_image_decoder.cc b/src/starboard/shared/libjpeg/jpeg_image_decoder.cc
index 30e3991..c0be8f8 100644
--- a/src/starboard/shared/libjpeg/jpeg_image_decoder.cc
+++ b/src/starboard/shared/libjpeg/jpeg_image_decoder.cc
@@ -165,9 +165,7 @@
         break;
       case kSbDecodeTargetFormat2PlaneYUVNV12:
       case kSbDecodeTargetFormat3PlaneYUVI420:
-#if SB_API_VERSION >= 10
       case kSbDecodeTargetFormat3Plane10BitYUVI420:
-#endif  // SB_API_VERSION >= 10
       case kSbDecodeTargetFormat1PlaneUYVY:
       case kSbDecodeTargetFormatInvalid:
         SB_NOTREACHED();
diff --git a/src/starboard/shared/libvpx/vpx_video_decoder.cc b/src/starboard/shared/libvpx/vpx_video_decoder.cc
index 9cb5253..6bd1488 100644
--- a/src/starboard/shared/libvpx/vpx_video_decoder.cc
+++ b/src/starboard/shared/libvpx/vpx_video_decoder.cc
@@ -137,11 +137,7 @@
   SB_DCHECK(decoder_thread_->job_queue()->BelongsToCurrentThread());
 
   error_occured_ = true;
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
   Schedule(std::bind(error_cb_, kSbPlayerErrorDecode, error_message));
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-  Schedule(error_cb_);
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 }
 
 void VideoDecoder::InitializeCodec() {
diff --git a/src/starboard/shared/linux/cpu_features_get.cc b/src/starboard/shared/linux/cpu_features_get.cc
index 2eead19..8dface2 100644
--- a/src/starboard/shared/linux/cpu_features_get.cc
+++ b/src/starboard/shared/linux/cpu_features_get.cc
@@ -656,6 +656,9 @@
     features->x86.has_tsc = (cpuid_info[3] & (1 << 4)) != 0;
 
     features->x86.has_sse3 = (cpuid_info[2] & (1 << 0)) != 0;
+#if defined(SB_CPU_FEATURE_PCLMULQDQ)
+    features->x86.has_pclmulqdq = (cpuid_info[2] & (1 << 1)) != 0;
+#endif  // defined(SB_CPU_FEATURE_PCLMULQDQ)
     features->x86.has_ssse3 = (cpuid_info[2] & (1 << 9)) != 0;
     features->x86.has_sse41 = (cpuid_info[2] & (1 << 19)) != 0;
     features->x86.has_sse42 = (cpuid_info[2] & (1 << 20)) != 0;
diff --git a/src/starboard/shared/linux/dev_input/dev_input.cc b/src/starboard/shared/linux/dev_input/dev_input.cc
index bd5a4d3..a834764 100644
--- a/src/starboard/shared/linux/dev_input/dev_input.cc
+++ b/src/starboard/shared/linux/dev_input/dev_input.cc
@@ -1051,9 +1051,7 @@
 
   SbInputData* data = new SbInputData();
   SbMemorySet(data, 0, sizeof(*data));
-#if SB_API_VERSION >= 10
   data->timestamp = SbTimeGetMonotonicNow();
-#endif  // SB_API_VERSION >= 10
   data->window = window;
   data->type = type;
   data->device_type = kSbInputDeviceTypeGamepad;
@@ -1074,9 +1072,7 @@
   SbInputData* data = new SbInputData();
   SbMemorySet(data, 0, sizeof(*data));
 
-#if SB_API_VERSION >= 10
   data->timestamp = SbTimeGetMonotonicNow();
-#endif  // SB_API_VERSION >= 10
   data->window = window;
   data->type = kSbInputEventTypeMove;
   data->device_type = kSbInputDeviceTypeGamepad;
@@ -1103,9 +1099,7 @@
   SbInputData* data = new SbInputData();
   SbMemorySet(data, 0, sizeof(*data));
 
-#if SB_API_VERSION >= 10
   data->timestamp = SbTimeGetMonotonicNow();
-#endif  // SB_API_VERSION >= 10
   data->window = window;
   data->type = type;
   data->device_type = kSbInputDeviceTypeTouchPad;
@@ -1295,9 +1289,7 @@
 
   SbInputData* data = new SbInputData();
   SbMemorySet(data, 0, sizeof(*data));
-#if SB_API_VERSION >= 10
   data->timestamp = SbTimeGetMonotonicNow();
-#endif  // SB_API_VERSION >= 10
   data->window = window_;
   data->type =
       (event.value == 0 ? kSbInputEventTypeUnpress : kSbInputEventTypePress);
diff --git a/src/starboard/shared/linux/page_internal.cc b/src/starboard/shared/linux/page_internal.cc
index 8ee3dd2..b9cdbaf 100644
--- a/src/starboard/shared/linux/page_internal.cc
+++ b/src/starboard/shared/linux/page_internal.cc
@@ -37,11 +37,9 @@
 int SbMemoryMapFlagsToMmapProtect(int sb_flags) {
   bool flag_set = false;
   int mmap_protect = 0;
-#if SB_API_VERSION >= 10
   if (sb_flags == kSbMemoryMapProtectReserved) {
     return PROT_NONE;
   }
-#endif
   if (sb_flags & kSbMemoryMapProtectRead) {
     mmap_protect |= PROT_READ;
     flag_set = true;
@@ -76,7 +74,7 @@
 void* SbPageMapUntracked(size_t size_bytes,
                          int flags,
                          const char* /*unused_name*/) {
-#if SB_CAN(MAP_EXECUTABLE_MEMORY) && SB_API_VERSION >= 10
+#if SB_CAN(MAP_EXECUTABLE_MEMORY)
   if (flags & kSbMemoryMapProtectExec) {
     // Cobalt does not allow mapping executable memory directly.
     return SB_MEMORY_MAP_FAILED;
@@ -96,12 +94,10 @@
   return munmap(ptr, size_bytes) == 0;
 }
 
-#if SB_API_VERSION >= 10
 bool SbPageProtect(void* virtual_address, int64_t size_bytes, int flags) {
   int mmap_protect = SbMemoryMapFlagsToMmapProtect(flags);
   return mprotect(virtual_address, size_bytes, mmap_protect) == 0;
 }
-#endif
 
 size_t SbPageGetTotalPhysicalMemoryBytes() {
   // Limit ourselves to remain similar to more constrained platforms.
diff --git a/src/starboard/shared/linux/thread_set_name.cc b/src/starboard/shared/linux/thread_set_name.cc
index be97f15..825379a 100644
--- a/src/starboard/shared/linux/thread_set_name.cc
+++ b/src/starboard/shared/linux/thread_set_name.cc
@@ -34,8 +34,6 @@
 
   if (SbStringGetLength(name) >= SB_ARRAY_SIZE_INT(buffer)) {
     SbStringCopy(buffer, name, SB_ARRAY_SIZE_INT(buffer));
-    SB_DLOG(WARNING) << "Thread name \"" << name << "\" was truncated to \""
-                     << buffer << "\"";
     name = buffer;
   }
 
diff --git a/src/starboard/shared/opus/opus_audio_decoder.cc b/src/starboard/shared/opus/opus_audio_decoder.cc
index 5ca3d94..8555023 100644
--- a/src/starboard/shared/opus/opus_audio_decoder.cc
+++ b/src/starboard/shared/opus/opus_audio_decoder.cc
@@ -137,13 +137,9 @@
     // TODO: Consider fill it with silence.
     SB_LOG(ERROR) << kDecodeFunctionName
                   << "() failed with error code: " << decoded_frames;
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     error_cb_(kSbPlayerErrorDecode,
               FormatString("%s() failed with error code: %d",
                            kDecodeFunctionName, decoded_frames));
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-    error_cb_();
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
     return;
   }
 
diff --git a/src/starboard/shared/posix/memory_flush.cc b/src/starboard/shared/posix/memory_flush.cc
index 81ead5c..61c1b70 100644
--- a/src/starboard/shared/posix/memory_flush.cc
+++ b/src/starboard/shared/posix/memory_flush.cc
@@ -18,10 +18,6 @@
 
 #include <iomanip>
 
-#if SB_IS(ARCH_MIPS)
-#include <sys/cachectl.h>
-#endif
-
 #include "starboard/common/log.h"
 
 #if !SB_CAN(MAP_EXECUTABLE_MEMORY)
@@ -31,17 +27,12 @@
 
 void SbMemoryFlush(void* virtual_address, int64_t size_bytes) {
   char* memory = reinterpret_cast<char*>(virtual_address);
-#if !SB_IS(ARCH_ARM) && !SB_IS(ARCH_ARM64) && !SB_IS(ARCH_MIPS)
+#if !SB_IS(ARCH_ARM) && !SB_IS(ARCH_ARM64)
   int result = msync(memory, size_bytes, MS_SYNC);
   SB_DCHECK(result == 0) << "msync failed: 0x" << std::hex << result << " ("
                          << std::dec << result << "d)";
 #endif
 
-#if SB_IS(ARCH_MIPS)
-  _flush_cache(reinterpret_cast<char*>(memory), (size_t)size_bytes, BCACHE);
-  return;
-#endif
-
 #if !defined(__has_builtin)
 #define __has_builtin(a) (0)
 #endif
diff --git a/src/starboard/shared/pthread/mutex_acquire.cc b/src/starboard/shared/pthread/mutex_acquire.cc
index d247727..796d99f 100644
--- a/src/starboard/shared/pthread/mutex_acquire.cc
+++ b/src/starboard/shared/pthread/mutex_acquire.cc
@@ -19,12 +19,22 @@
 #include "starboard/common/experimental/concurrency_debug.h"
 #include "starboard/shared/pthread/is_success.h"
 #include "starboard/shared/pthread/types_internal.h"
+#include "starboard/shared/starboard/lazy_initialization_internal.h"
+
+using starboard::shared::starboard::EnsureInitialized;
+using starboard::shared::starboard::SetInitialized;
 
 SbMutexResult SbMutexAcquire(SbMutex* mutex) {
   if (!mutex) {
     return kSbMutexDestroyed;
   }
 
+#if SB_API_VERSION >= SB_PORTABLE_THREAD_TYPES_VERSION
+  if (!EnsureInitialized(&(SB_INTERNAL_MUTEX(mutex)->initialized_state))) {
+    *SB_PTHREAD_INTERNAL_MUTEX(mutex) = PTHREAD_MUTEX_INITIALIZER;
+    SetInitialized(&(SB_INTERNAL_MUTEX(mutex)->initialized_state));
+  }
+#endif
 #if SB_ENABLE_CONCURRENTY_DEBUG
   starboard::experimental::ScopedMutexWaitTracker tracker(mutex);
   if (tracker.acquired()) {
diff --git a/src/starboard/shared/pthread/mutex_acquire_try.cc b/src/starboard/shared/pthread/mutex_acquire_try.cc
index d7a55c5..5ec2db7 100644
--- a/src/starboard/shared/pthread/mutex_acquire_try.cc
+++ b/src/starboard/shared/pthread/mutex_acquire_try.cc
@@ -18,11 +18,23 @@
 
 #include "starboard/shared/pthread/is_success.h"
 #include "starboard/shared/pthread/types_internal.h"
+#include "starboard/shared/starboard/lazy_initialization_internal.h"
+
+using starboard::shared::starboard::EnsureInitialized;
+using starboard::shared::starboard::SetInitialized;
 
 SbMutexResult SbMutexAcquireTry(SbMutex* mutex) {
   if (!mutex) {
     return kSbMutexDestroyed;
   }
+
+#if SB_API_VERSION >= SB_PORTABLE_THREAD_TYPES_VERSION
+  if (!EnsureInitialized(&(SB_INTERNAL_MUTEX(mutex)->initialized_state))) {
+    *SB_PTHREAD_INTERNAL_MUTEX(mutex) = PTHREAD_MUTEX_INITIALIZER;
+    SetInitialized(&(SB_INTERNAL_MUTEX(mutex)->initialized_state));
+  }
+#endif
+
   int result = pthread_mutex_trylock(SB_PTHREAD_INTERNAL_MUTEX(mutex));
   if (IsSuccess(result)) {
     return kSbMutexAcquired;
diff --git a/src/starboard/shared/pthread/mutex_create.cc b/src/starboard/shared/pthread/mutex_create.cc
index a37ff65..fb6f65a 100644
--- a/src/starboard/shared/pthread/mutex_create.cc
+++ b/src/starboard/shared/pthread/mutex_create.cc
@@ -19,13 +19,21 @@
 #include "starboard/common/log.h"
 #include "starboard/shared/pthread/is_success.h"
 #include "starboard/shared/pthread/types_internal.h"
+#include "starboard/shared/starboard/lazy_initialization_internal.h"
+
+using starboard::shared::starboard::SetInitialized;
 
 bool SbMutexCreate(SbMutex* mutex) {
-  SB_COMPILE_ASSERT(sizeof(SbMutex) >= sizeof(pthread_mutex_t),
-                    pthread_mutex_t_larger_than_sb_mutex);
+#if SB_API_VERSION >= SB_PORTABLE_THREAD_TYPES_VERSION
+  SB_COMPILE_ASSERT(sizeof(SbMutex) >= sizeof(SbMutexPrivate),
+                    sb_mutex_private_larger_than_sb_mutex);
+#endif
   if (!mutex) {
     return false;
   }
 
+#if SB_API_VERSION >= SB_PORTABLE_THREAD_TYPES_VERSION
+  SetInitialized(&(SB_INTERNAL_MUTEX(mutex)->initialized_state));
+#endif
   return IsSuccess(pthread_mutex_init(SB_PTHREAD_INTERNAL_MUTEX(mutex), NULL));
 }
diff --git a/src/starboard/shared/pthread/mutex_destroy.cc b/src/starboard/shared/pthread/mutex_destroy.cc
index 7b58e31..df01a41 100644
--- a/src/starboard/shared/pthread/mutex_destroy.cc
+++ b/src/starboard/shared/pthread/mutex_destroy.cc
@@ -20,12 +20,22 @@
 #include "starboard/configuration.h"
 #include "starboard/shared/pthread/is_success.h"
 #include "starboard/shared/pthread/types_internal.h"
+#include "starboard/shared/starboard/lazy_initialization_internal.h"
+
+using starboard::shared::starboard::IsInitialized;
 
 bool SbMutexDestroy(SbMutex* mutex) {
   if (!mutex) {
     return false;
   }
 
+#if SB_API_VERSION >= SB_PORTABLE_THREAD_TYPES_VERSION
+  if (!IsInitialized(&(SB_INTERNAL_MUTEX(mutex)->initialized_state))) {
+    // If the mutex is not initialized there is nothing to destroy.
+    return true;
+  }
+#endif
+
 #if SB_API_VERSION >= SB_MUTEX_ACQUIRE_TRY_API_CHANGE_VERSION
   // Both trying to recursively acquire a mutex that is locked by the calling
   // thread, as well as deleting a locked mutex, result in undefined behavior.
diff --git a/src/starboard/shared/pthread/mutex_release.cc b/src/starboard/shared/pthread/mutex_release.cc
index e55603c..2e9bc41 100644
--- a/src/starboard/shared/pthread/mutex_release.cc
+++ b/src/starboard/shared/pthread/mutex_release.cc
@@ -18,11 +18,21 @@
 
 #include "starboard/shared/pthread/is_success.h"
 #include "starboard/shared/pthread/types_internal.h"
+#include "starboard/shared/starboard/lazy_initialization_internal.h"
+
+using starboard::shared::starboard::IsInitialized;
 
 bool SbMutexRelease(SbMutex* mutex) {
   if (!mutex) {
     return false;
   }
 
+#if SB_API_VERSION >= SB_PORTABLE_THREAD_TYPES_VERSION
+  if (!IsInitialized(&(SB_INTERNAL_MUTEX(mutex)->initialized_state))) {
+    // If the mutex is not initialized there is nothing to release.
+    return true;
+  }
+#endif
+
   return IsSuccess(pthread_mutex_unlock(SB_PTHREAD_INTERNAL_MUTEX(mutex)));
 }
diff --git a/src/starboard/shared/pthread/once.cc b/src/starboard/shared/pthread/once.cc
index 56ba9a8..c8c937f 100644
--- a/src/starboard/shared/pthread/once.cc
+++ b/src/starboard/shared/pthread/once.cc
@@ -18,16 +18,29 @@
 
 #include "starboard/common/log.h"
 #include "starboard/shared/pthread/types_internal.h"
+#include "starboard/shared/starboard/lazy_initialization_internal.h"
+
+using starboard::shared::starboard::EnsureInitialized;
+using starboard::shared::starboard::SetInitialized;
 
 bool SbOnce(SbOnceControl* once_control, SbOnceInitRoutine init_routine) {
-  SB_COMPILE_ASSERT(sizeof(SbOnceControl) >= sizeof(pthread_once_t),
-                    pthread_once_t_larger_than_sb_once_control);
+#if SB_API_VERSION >= SB_PORTABLE_THREAD_TYPES_VERSION
+  SB_COMPILE_ASSERT(sizeof(SbOnceControl) >= sizeof(SbOnceControlPrivate),
+                    sb_once_control_private_larger_than_sb_once_control);
+#endif
   if (once_control == NULL) {
     return false;
   }
   if (init_routine == NULL) {
     return false;
   }
+#if SB_API_VERSION >= SB_PORTABLE_THREAD_TYPES_VERSION
+  if (!EnsureInitialized(
+          &(SB_INTERNAL_ONCE(once_control)->initialized_state))) {
+    *SB_PTHREAD_INTERNAL_ONCE(once_control) = PTHREAD_ONCE_INIT;
+    SetInitialized(&(SB_INTERNAL_ONCE(once_control)->initialized_state));
+  }
+#endif
 
   return pthread_once(SB_PTHREAD_INTERNAL_ONCE(once_control), init_routine) ==
          0;
diff --git a/src/starboard/shared/pthread/types_internal.h b/src/starboard/shared/pthread/types_internal.h
index e55ec05..53100c54 100644
--- a/src/starboard/shared/pthread/types_internal.h
+++ b/src/starboard/shared/pthread/types_internal.h
@@ -18,10 +18,14 @@
 #define STARBOARD_SHARED_PTHREAD_TYPES_INTERNAL_H_
 
 #if SB_API_VERSION >= SB_PORTABLE_THREAD_TYPES_VERSION
-#define SB_PTHREAD_INTERNAL_MUTEX(mutex_ptr) \
-  reinterpret_cast<pthread_mutex_t*>((mutex_ptr)->mutex_buffer)
-#define SB_PTHREAD_INTERNAL_ONCE(once_ptr) \
-  reinterpret_cast<pthread_once_t*>((once_ptr)->once_buffer)
+#define SB_INTERNAL_MUTEX(mutex_var) \
+  reinterpret_cast<SbMutexPrivate*>((mutex_var)->mutex_buffer)
+#define SB_PTHREAD_INTERNAL_MUTEX(mutex_var) \
+  &(reinterpret_cast<SbMutexPrivate*>((mutex_var)->mutex_buffer)->mutex)
+#define SB_INTERNAL_ONCE(once_var) \
+  reinterpret_cast<SbOnceControlPrivate*>((once_var)->once_buffer)
+#define SB_PTHREAD_INTERNAL_ONCE(once_var) \
+  &(reinterpret_cast<SbOnceControlPrivate*>((once_var)->once_buffer)->once)
 #define SB_PTHREAD_INTERNAL_THREAD(thread) reinterpret_cast<pthread_t>(thread)
 #define SB_PTHREAD_INTERNAL_THREAD_PTR(thread) \
   reinterpret_cast<pthread_t*>(&(thread))
@@ -30,8 +34,8 @@
   reinterpret_cast<SbConditionVariablePrivate*>(     \
       (condition_var)->condition_buffer)
 #else
-#define SB_PTHREAD_INTERNAL_MUTEX(mutex_ptr) (mutex_ptr)
-#define SB_PTHREAD_INTERNAL_ONCE(once_ptr) (once_ptr)
+#define SB_PTHREAD_INTERNAL_MUTEX(mutex_var) (mutex_var)
+#define SB_PTHREAD_INTERNAL_ONCE(once_var) (once_var)
 #define SB_PTHREAD_INTERNAL_THREAD(thread) (thread)
 #define SB_PTHREAD_INTERNAL_THREAD_PTR(thread) \
   reinterpret_cast<pthread_t*>(&(thread))
@@ -49,4 +53,20 @@
   pthread_cond_t condition;
 } SbConditionVariablePrivate;
 
+// Wrapping pthread_mutex_t to add initialization state
+// which allows for lazy initialization to PTHREAD_MUTEX_INITIALIZER.
+// NOTE: The actual value of PTHREAD_MUTEX_INITIALIZER.
+typedef struct SbMutexPrivate {
+  InitializedState initialized_state;
+  pthread_mutex_t mutex;
+} SbMutexPrivate;
+
+// Wrapping pthread_once_t to add initialization state
+// which allows for lazy initialization to PTHREAD_ONCE_INIT.
+// NOTE: The actual value of PTHREAD_ONCE_INIT.
+typedef struct SbOnceControlPrivate {
+  InitializedState initialized_state;
+  pthread_once_t once;
+} SbOnceControlPrivate;
+
 #endif  // STARBOARD_SHARED_PTHREAD_TYPES_INTERNAL_H_
diff --git a/src/starboard/shared/pulse/pulse_audio_sink_type.cc b/src/starboard/shared/pulse/pulse_audio_sink_type.cc
index 346522c..92e763d 100644
--- a/src/starboard/shared/pulse/pulse_audio_sink_type.cc
+++ b/src/starboard/shared/pulse/pulse_audio_sink_type.cc
@@ -500,9 +500,27 @@
     pa_stream_flags_t flags,
     pa_stream_request_cb_t stream_request_cb,
     void* userdata) {
+  pa_channel_map channel_map = {sample_spec->channels};
+
+  if (sample_spec->channels == 6) {
+    // Assume the incoming layout is always "FL FR FC LFE BL BR".
+    channel_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+    channel_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+    channel_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
+    // Note that this should really be |PA_CHANNEL_POSITION_LFE|, but there is
+    // usually no lfe device on desktop so set it to rear center to make it
+    // audible.
+    channel_map.map[3] = PA_CHANNEL_POSITION_REAR_CENTER;
+    // Rear left and rear left are the same as back left and back right.
+    channel_map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
+    channel_map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
+  }
+
   ScopedLock lock(mutex_);
+
   pa_stream* stream =
-      pa_stream_new(context_, "cobalt_stream", sample_spec, NULL);
+      pa_stream_new(context_, "cobalt_stream", sample_spec,
+                    sample_spec->channels == 6 ? &channel_map : NULL);
   if (!stream) {
     SB_LOG(ERROR) << "Pulse audio error: cannot create stream.";
     return NULL;
diff --git a/src/starboard/shared/starboard/audio_sink/audio_sink_create.cc b/src/starboard/shared/starboard/audio_sink/audio_sink_create.cc
index eba72ef..bcddf3b 100644
--- a/src/starboard/shared/starboard/audio_sink/audio_sink_create.cc
+++ b/src/starboard/shared/starboard/audio_sink/audio_sink_create.cc
@@ -72,6 +72,7 @@
 
   auto audio_sink_type = SbAudioSinkPrivate::GetPreferredType();
   if (!audio_sink_type) {
+    SB_LOG(WARNING) << "Preferred Sink Type is invalid.";
     return kSbAudioSinkInvalid;
   }
   SbAudioSink audio_sink = audio_sink_type->Create(
@@ -88,6 +89,7 @@
   audio_sink_type->Destroy(audio_sink);
   auto fallback_audio_sink_type = SbAudioSinkPrivate::GetFallbackType();
   if (!fallback_audio_sink_type) {
+    SB_LOG(WARNING) << "Fallback Sink Type is invalid.";
     return kSbAudioSinkInvalid;
   }
   audio_sink = fallback_audio_sink_type->Create(
diff --git a/src/starboard/shared/starboard/configuration_constants_compatibility_defines.h b/src/starboard/shared/starboard/configuration_constants_compatibility_defines.h
index d0a4846..5af45c9 100644
--- a/src/starboard/shared/starboard/configuration_constants_compatibility_defines.h
+++ b/src/starboard/shared/starboard/configuration_constants_compatibility_defines.h
@@ -47,8 +47,6 @@
 
 #define kSbHasAsyncAudioFramesReporting SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING
 
-#define kSbHasAudiolessVideo SB_HAS_AUDIOLESS_VIDEO
-
 #define kSbHasMediaWebmVp9Support SB_HAS_MEDIA_WEBM_VP9_SUPPORT
 
 #define kSbHasThreadPrioritySupport SB_HAS_THREAD_PRIORITY_SUPPORT
@@ -83,6 +81,8 @@
 
 #define kSbPathSepString SB_PATH_SEP_STRING
 
+#define kSbPreferredRgbaByteOrder SB_PREFERRED_RGBA_BYTE_ORDER
+
 #define kSbUserMaxSignedIn SB_USER_MAX_SIGNED_IN
 
 #endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_create_transformer.cc b/src/starboard/shared/starboard/cryptography/cryptography_create_transformer.cc
index 4d453d3..9845a42 100644
--- a/src/starboard/shared/starboard/cryptography/cryptography_create_transformer.cc
+++ b/src/starboard/shared/starboard/cryptography/cryptography_create_transformer.cc
@@ -12,9 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "starboard/configuration.h"
+
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #include "starboard/common/log.h"
 #include "starboard/common/string.h"
-#include "starboard/configuration.h"
 #include "starboard/cryptography.h"
 #include "starboard/memory.h"
 #include "starboard/shared/starboard/cryptography/cryptography_internal.h"
@@ -119,3 +122,5 @@
 
   return transformer;
 }
+
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_destroy_transformer.cc b/src/starboard/shared/starboard/cryptography/cryptography_destroy_transformer.cc
index c4411ac..c3e68c5 100644
--- a/src/starboard/shared/starboard/cryptography/cryptography_destroy_transformer.cc
+++ b/src/starboard/shared/starboard/cryptography/cryptography_destroy_transformer.cc
@@ -13,6 +13,9 @@
 // limitations under the License.
 
 #include "starboard/configuration.h"
+
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #include "starboard/cryptography.h"
 #include "starboard/shared/starboard/cryptography/cryptography_internal.h"
 
@@ -23,3 +26,5 @@
 
   delete transformer;
 }
+
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_get_tag.cc b/src/starboard/shared/starboard/cryptography/cryptography_get_tag.cc
index e16d8c1..20dacd3 100644
--- a/src/starboard/shared/starboard/cryptography/cryptography_get_tag.cc
+++ b/src/starboard/shared/starboard/cryptography/cryptography_get_tag.cc
@@ -13,6 +13,9 @@
 // limitations under the License.
 
 #include "starboard/configuration.h"
+
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #include "starboard/cryptography.h"
 #include "starboard/shared/starboard/cryptography/cryptography_internal.h"
 #include "starboard/shared/starboard/cryptography/software_aes.h"
@@ -32,3 +35,5 @@
   AES_gcm128_tag(&transformer->gcm_context, out_tag, out_tag_size);
   return true;
 }
+
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_internal.h b/src/starboard/shared/starboard/cryptography/cryptography_internal.h
index a7b93ebd..4a5ffa2 100644
--- a/src/starboard/shared/starboard/cryptography/cryptography_internal.h
+++ b/src/starboard/shared/starboard/cryptography/cryptography_internal.h
@@ -15,6 +15,10 @@
 #ifndef STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_CRYPTOGRAPHY_INTERNAL_H_
 #define STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_CRYPTOGRAPHY_INTERNAL_H_
 
+#if SB_API_VERSION >= SB_CRYPTOAPI_DEPRECATED_VERSION
+#error "Starboard Crypto API is deprecated"
+#else
+
 #include "starboard/cryptography.h"
 
 #include "starboard/shared/internal_only.h"
@@ -48,4 +52,6 @@
   uint32_t counter;
 };
 
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #endif  // STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_CRYPTOGRAPHY_INTERNAL_H_
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_set_authenticated_data.cc b/src/starboard/shared/starboard/cryptography/cryptography_set_authenticated_data.cc
index 28deaca..7af5fce 100644
--- a/src/starboard/shared/starboard/cryptography/cryptography_set_authenticated_data.cc
+++ b/src/starboard/shared/starboard/cryptography/cryptography_set_authenticated_data.cc
@@ -13,6 +13,9 @@
 // limitations under the License.
 
 #include "starboard/configuration.h"
+
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #include "starboard/cryptography.h"
 #include "starboard/shared/starboard/cryptography/cryptography_internal.h"
 #include "starboard/shared/starboard/cryptography/software_aes.h"
@@ -32,3 +35,5 @@
   int result = AES_gcm128_aad(&transformer->gcm_context, data, data_size);
   return result == 1;
 }
+
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_set_initialization_vector.cc b/src/starboard/shared/starboard/cryptography/cryptography_set_initialization_vector.cc
index 64dc487..8c70ae0 100644
--- a/src/starboard/shared/starboard/cryptography/cryptography_set_initialization_vector.cc
+++ b/src/starboard/shared/starboard/cryptography/cryptography_set_initialization_vector.cc
@@ -12,11 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "starboard/configuration.h"
+
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #include "starboard/cryptography.h"
 
 #include <algorithm>
 
-#include "starboard/configuration.h"
 #include "starboard/memory.h"
 #include "starboard/shared/starboard/cryptography/cryptography_internal.h"
 #include "starboard/shared/starboard/cryptography/software_aes.h"
@@ -38,3 +41,5 @@
                           static_cast<int>(sizeof(transformer->ivec))));
   }
 }
+
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_transform.cc b/src/starboard/shared/starboard/cryptography/cryptography_transform.cc
index 92b6be3..8a84ee0 100644
--- a/src/starboard/shared/starboard/cryptography/cryptography_transform.cc
+++ b/src/starboard/shared/starboard/cryptography/cryptography_transform.cc
@@ -13,6 +13,9 @@
 // limitations under the License.
 
 #include "starboard/configuration.h"
+
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #include "starboard/cryptography.h"
 #include "starboard/shared/starboard/cryptography/cryptography_internal.h"
 #include "starboard/shared/starboard/cryptography/software_aes.h"
@@ -110,3 +113,5 @@
 
   return in_data_size;
 }
+
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
diff --git a/src/starboard/shared/starboard/cryptography/software_aes.cc b/src/starboard/shared/starboard/cryptography/software_aes.cc
index ec50a11..32793d4 100644
--- a/src/starboard/shared/starboard/cryptography/software_aes.cc
+++ b/src/starboard/shared/starboard/cryptography/software_aes.cc
@@ -60,6 +60,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "starboard/configuration.h"
+
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #include "starboard/shared/starboard/cryptography/software_aes.h"
 
 #include "starboard/common/byte_swap.h"
@@ -1568,3 +1572,5 @@
 }  // namespace starboard
 }  // namespace shared
 }  // namespace starboard
+
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
diff --git a/src/starboard/shared/starboard/cryptography/software_aes.h b/src/starboard/shared/starboard/cryptography/software_aes.h
index c429076..e05391f 100644
--- a/src/starboard/shared/starboard/cryptography/software_aes.h
+++ b/src/starboard/shared/starboard/cryptography/software_aes.h
@@ -63,6 +63,10 @@
 #ifndef STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_SOFTWARE_AES_H_
 #define STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_SOFTWARE_AES_H_
 
+#if SB_API_VERSION >= SB_CRYPTOAPI_DEPRECATED_VERSION
+#error "Starboard Crypto API is deprecated"
+#else
+
 #include "starboard/common/log.h"
 #include "starboard/configuration.h"
 #include "starboard/types.h"
@@ -212,4 +216,6 @@
 }  // namespace shared
 }  // namespace starboard
 
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #endif  // STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_SOFTWARE_AES_H_
diff --git a/src/starboard/shared/starboard/drm/drm_is_server_certificate_updatable.cc b/src/starboard/shared/starboard/drm/drm_is_server_certificate_updatable.cc
index b169c31..9c3d946 100644
--- a/src/starboard/shared/starboard/drm/drm_is_server_certificate_updatable.cc
+++ b/src/starboard/shared/starboard/drm/drm_is_server_certificate_updatable.cc
@@ -17,8 +17,6 @@
 #include "starboard/common/log.h"
 #include "starboard/shared/starboard/drm/drm_system_internal.h"
 
-#if SB_API_VERSION >= 10
-
 bool SbDrmIsServerCertificateUpdatable(SbDrmSystem drm_system) {
   if (!SbDrmSystemIsValid(drm_system)) {
     SB_DLOG(ERROR) << "Invalid DRM system.";
@@ -27,5 +25,3 @@
 
   return drm_system->IsServerCertificateUpdatable();
 }
-
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/drm/drm_system_internal.h b/src/starboard/shared/starboard/drm/drm_system_internal.h
index 5cf70e5..792159a 100644
--- a/src/starboard/shared/starboard/drm/drm_system_internal.h
+++ b/src/starboard/shared/starboard/drm/drm_system_internal.h
@@ -41,13 +41,11 @@
 
   virtual DecryptStatus Decrypt(InputBuffer* buffer) = 0;
 
-#if SB_API_VERSION >= 10
   virtual bool IsServerCertificateUpdatable() = 0;
 
   virtual void UpdateServerCertificate(int ticket,
                                        const void* certificate,
                                        int certificate_size) = 0;
-#endif  // SB_API_VERSION >= 10
 };
 
 #endif  // STARBOARD_SHARED_STARBOARD_DRM_DRM_SYSTEM_INTERNAL_H_
diff --git a/src/starboard/shared/starboard/drm/drm_update_server_certificate.cc b/src/starboard/shared/starboard/drm/drm_update_server_certificate.cc
index 2182f92..1c8f8e8 100644
--- a/src/starboard/shared/starboard/drm/drm_update_server_certificate.cc
+++ b/src/starboard/shared/starboard/drm/drm_update_server_certificate.cc
@@ -17,8 +17,6 @@
 #include "starboard/common/log.h"
 #include "starboard/shared/starboard/drm/drm_system_internal.h"
 
-#if SB_API_VERSION >= 10
-
 void SbDrmUpdateServerCertificate(SbDrmSystem drm_system,
                                   int ticket,
                                   const void* certificate,
@@ -35,5 +33,3 @@
 
   drm_system->UpdateServerCertificate(ticket, certificate, certificate_size);
 }
-
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/lazy_initialization_internal.h b/src/starboard/shared/starboard/lazy_initialization_internal.h
index 6580285..58d3b84 100644
--- a/src/starboard/shared/starboard/lazy_initialization_internal.h
+++ b/src/starboard/shared/starboard/lazy_initialization_internal.h
@@ -58,7 +58,8 @@
       SbThreadYield();
     } while (SbAtomicAcquire_Load(state) != INITIALIZED_STATE_INITIALIZED);
   } else {
-    SB_DCHECK(original == INITIALIZED_STATE_INITIALIZED);
+    SB_DCHECK(original == INITIALIZED_STATE_INITIALIZED)
+        << "Unexpected original=" << original;
   }
 
   return true;
diff --git a/src/starboard/shared/starboard/media/avc_util.cc b/src/starboard/shared/starboard/media/avc_util.cc
new file mode 100644
index 0000000..d5b9620
--- /dev/null
+++ b/src/starboard/shared/starboard/media/avc_util.cc
@@ -0,0 +1,219 @@
+// Copyright 2020 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/shared/starboard/media/avc_util.h"
+
+#include <type_traits>
+
+#include "starboard/memory.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+namespace {
+
+const uint8_t kAnnexBHeader[] = {0, 0, 0, 1};
+const auto kAnnexBHeaderSizeInBytes =
+    AvcParameterSets::kAnnexBHeaderSizeInBytes;
+
+bool StartsWithAnnexBHeader(const uint8_t* annex_b_data,
+                            size_t annex_b_data_size) {
+  static_assert(
+      sizeof(kAnnexBHeader) == AvcParameterSets::kAnnexBHeaderSizeInBytes,
+      "sizeof(kAnnexBHeader) doesn't match kAnnexBHeaderSizeInBytes");
+
+  if (annex_b_data_size < sizeof(kAnnexBHeader)) {
+    return false;
+  }
+  return SbMemoryCompare(annex_b_data, kAnnexBHeader, sizeof(kAnnexBHeader)) ==
+         0;
+}
+
+// UInt8Type can be "uint8_t", or "const uint8_t".
+template <typename UInt8Type>
+bool AdvanceToNextAnnexBHeader(UInt8Type** annex_b_data,
+                               size_t* annex_b_data_size) {
+  SB_DCHECK(annex_b_data);
+  SB_DCHECK(annex_b_data_size);
+
+  if (!StartsWithAnnexBHeader(*annex_b_data, *annex_b_data_size)) {
+    return false;
+  }
+
+  *annex_b_data += kAnnexBHeaderSizeInBytes;
+  *annex_b_data_size -= kAnnexBHeaderSizeInBytes;
+
+  while (*annex_b_data_size > 0) {
+    if (StartsWithAnnexBHeader(*annex_b_data, *annex_b_data_size)) {
+      return true;
+    }
+    ++*annex_b_data;
+    --*annex_b_data_size;
+  }
+  return true;
+}
+
+bool ExtractAnnexBNalu(const uint8_t** annex_b_data,
+                       size_t* annex_b_data_size,
+                       std::vector<uint8_t>* annex_b_nalu) {
+  SB_DCHECK(annex_b_data);
+  SB_DCHECK(annex_b_data_size);
+  SB_DCHECK(annex_b_nalu);
+
+  const uint8_t* saved_data = *annex_b_data;
+
+  if (!AdvanceToNextAnnexBHeader(annex_b_data, annex_b_data_size)) {
+    return false;
+  }
+
+  annex_b_nalu->assign(saved_data, *annex_b_data);
+  return true;
+}
+
+}  // namespace
+
+AvcParameterSets::AvcParameterSets(Format format,
+                                   const uint8_t* data,
+                                   size_t size)
+    : format_(format) {
+  SB_DCHECK(format == kAnnexB);
+
+  is_valid_ =
+      format == kAnnexB && (size == 0 || StartsWithAnnexBHeader(data, size));
+
+  if (!is_valid_) {
+    return;
+  }
+
+  if (size == 0) {
+    return;
+  }
+
+  std::vector<uint8_t> nalu;
+  while (size > kAnnexBHeaderSizeInBytes &&
+         ExtractAnnexBNalu(&data, &size, &nalu)) {
+    if (nalu[kAnnexBHeaderSizeInBytes] == kSpsStartCode) {
+      if (first_sps_index_ == -1) {
+        first_sps_index_ = static_cast<int>(parameter_sets_.size());
+      }
+      parameter_sets_.push_back(nalu);
+      combined_size_in_bytes_ += nalu.size();
+    } else if (nalu[kAnnexBHeaderSizeInBytes] == kPpsStartCode) {
+      if (first_pps_index_ == -1) {
+        first_pps_index_ = static_cast<int>(parameter_sets_.size());
+      }
+      parameter_sets_.push_back(nalu);
+      combined_size_in_bytes_ += nalu.size();
+    } else {
+      break;
+    }
+  }
+}
+
+AvcParameterSets AvcParameterSets::ConvertTo(Format new_format) const {
+  if (format_ == new_format) {
+    return *this;
+  }
+
+  SB_DCHECK(format_ == kAnnexB);
+  SB_DCHECK(new_format == kHeadless);
+
+  AvcParameterSets new_parameter_sets(*this);
+  new_parameter_sets.format_ = new_format;
+  if (!new_parameter_sets.is_valid()) {
+    return new_parameter_sets;
+  }
+  for (auto& parameter_set : new_parameter_sets.parameter_sets_) {
+    SB_DCHECK(parameter_set.size() >= kAnnexBHeaderSizeInBytes);
+    parameter_set.erase(parameter_set.begin(),
+                        parameter_set.begin() + kAnnexBHeaderSizeInBytes);
+    new_parameter_sets.combined_size_in_bytes_ -= kAnnexBHeaderSizeInBytes;
+  }
+
+  return new_parameter_sets;
+}
+
+bool AvcParameterSets::operator==(const AvcParameterSets& that) const {
+  if (is_valid() != that.is_valid()) {
+    return false;
+  }
+  if (!is_valid()) {
+    return true;
+  }
+
+  SB_DCHECK(format() == that.format());
+
+  if (parameter_sets_ == that.parameter_sets_) {
+    SB_DCHECK(first_sps_index_ == that.first_sps_index_);
+    SB_DCHECK(first_pps_index_ == that.first_pps_index_);
+    return true;
+  }
+  return false;
+}
+
+bool AvcParameterSets::operator!=(const AvcParameterSets& that) const {
+  return !(*this == that);
+}
+
+bool ConvertAnnexBToAvcc(const uint8_t* annex_b_source,
+                         size_t size,
+                         uint8_t* avcc_destination) {
+  if (size == 0) {
+    return true;
+  }
+
+  SB_DCHECK(annex_b_source);
+  SB_DCHECK(avcc_destination);
+
+  if (!StartsWithAnnexBHeader(annex_b_source, size)) {
+    return false;
+  }
+
+  auto annex_b_source_size = size;
+  // |avcc_destination_end| exists only for the purpose of validation.
+  const auto avcc_destination_end = avcc_destination + size;
+
+  const uint8_t* last_source = annex_b_source;
+
+  const auto kAvccLengthInBytes = kAnnexBHeaderSizeInBytes;
+
+  while (AdvanceToNextAnnexBHeader(&annex_b_source, &annex_b_source_size)) {
+    SB_DCHECK(annex_b_source - last_source >= kAnnexBHeaderSizeInBytes);
+    SB_DCHECK(avcc_destination < avcc_destination_end);
+
+    size_t payload_size =
+        annex_b_source - last_source - kAnnexBHeaderSizeInBytes;
+    avcc_destination[0] =
+        static_cast<uint8_t>((payload_size & 0xff000000) >> 24);
+    avcc_destination[1] = static_cast<uint8_t>((payload_size & 0xff0000) >> 16);
+    avcc_destination[2] = static_cast<uint8_t>((payload_size & 0xff00) >> 8);
+    avcc_destination[3] = static_cast<uint8_t>(payload_size & 0xff);
+    SbMemoryCopy(avcc_destination + kAvccLengthInBytes,
+                 last_source + kAnnexBHeaderSizeInBytes, payload_size);
+    avcc_destination += annex_b_source - last_source;
+    last_source = annex_b_source;
+  }
+
+  SB_DCHECK(annex_b_source_size == 0);
+  SB_DCHECK(avcc_destination == avcc_destination_end);
+
+  return true;
+}
+
+}  // namespace media
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/starboard/media/avc_util.h b/src/starboard/shared/starboard/media/avc_util.h
new file mode 100644
index 0000000..d603aac
--- /dev/null
+++ b/src/starboard/shared/starboard/media/avc_util.h
@@ -0,0 +1,110 @@
+// Copyright 2020 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 STARBOARD_SHARED_STARBOARD_MEDIA_AVC_UTIL_H_
+#define STARBOARD_SHARED_STARBOARD_MEDIA_AVC_UTIL_H_
+
+#include <vector>
+
+#include "starboard/common/log.h"
+#include "starboard/common/ref_counted.h"
+#include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/types.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+// Parse avc nalus produced by the Cobalt demuxer.
+// It makes the following assumptions:
+// 1. It won't be used frequently.  So it aimed to be easy to use, instead of
+//    being efficient.
+// 2. The data it processes is produced by Cobalt, and should be valid most of
+//    of the time.  The class won't crash on invalid input, but besides checking
+//    if the input starts with a nalu header, it doesn't do any further
+//    validations.  It is up to the decoder to detect invalid input.
+// 3. It assumes that multiple sps/pps nalus in different orders are different
+//    parameter sets.
+class AvcParameterSets {
+ public:
+  enum Format {
+    kAnnexB,
+    kHeadless,
+  };
+
+  static const size_t kAnnexBHeaderSizeInBytes = 4;
+  static const uint8_t kSpsStartCode = 0x67;
+  static const uint8_t kPpsStartCode = 0x68;
+
+  // Only |format == kAnnexB| is supported, which is checked in the ctor.
+  AvcParameterSets(Format format, const uint8_t* data, size_t size);
+
+  Format format() const { return format_; }
+  bool is_valid() const { return is_valid_; }
+  bool has_sps_and_pps() const {
+    return first_sps_index_ != -1 && first_pps_index_ != -1;
+  }
+
+  const std::vector<uint8_t>& first_sps() const {
+    SB_DCHECK(first_sps_index_ != -1);
+    return parameter_sets_[first_sps_index_];
+  }
+  const std::vector<uint8_t>& first_pps() const {
+    SB_DCHECK(first_pps_index_ != -1);
+    return parameter_sets_[first_pps_index_];
+  }
+
+  std::vector<const uint8_t*> GetAddresses() const {
+    std::vector<const uint8_t*> addresses;
+    for (auto& parameter_set : parameter_sets_) {
+      addresses.push_back(parameter_set.data());
+    }
+    return addresses;
+  }
+  std::vector<size_t> GetSizesInBytes() const {
+    std::vector<size_t> sizes_in_bytes;
+    for (auto& parameter_set : parameter_sets_) {
+      sizes_in_bytes.push_back(parameter_set.size());
+    }
+    return sizes_in_bytes;
+  }
+  size_t combined_size_in_bytes() const { return combined_size_in_bytes_; }
+
+  AvcParameterSets ConvertTo(Format new_format) const;
+
+  bool operator==(const AvcParameterSets& that) const;
+  bool operator!=(const AvcParameterSets& that) const;
+
+ private:
+  Format format_ = kAnnexB;
+  bool is_valid_ = false;
+  int first_sps_index_ = -1;
+  int first_pps_index_ = -1;
+  std::vector<std::vector<uint8_t>> parameter_sets_;
+  size_t combined_size_in_bytes_ = 0;
+};
+
+// The function will fail only when the input doesn't start with an Annex B
+// nalu header.
+bool ConvertAnnexBToAvcc(const uint8_t* annex_b_source,
+                         size_t size,
+                         uint8_t* avcc_destination);
+
+}  // namespace media
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_STARBOARD_MEDIA_AVC_UTIL_H_
diff --git a/src/starboard/shared/starboard/media/avc_util_test.cc b/src/starboard/shared/starboard/media/avc_util_test.cc
new file mode 100644
index 0000000..09345cb
--- /dev/null
+++ b/src/starboard/shared/starboard/media/avc_util_test.cc
@@ -0,0 +1,524 @@
+// Copyright 2020 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/shared/starboard/media/avc_util.h"
+
+#include <vector>
+
+#include "starboard/common/optional.h"
+#include "starboard/shared/starboard/player/filter/testing/test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+namespace {
+
+using ::starboard::shared::starboard::media::ConvertAnnexBToAvcc;
+
+const auto kAnnexB = AvcParameterSets::kAnnexB;
+const auto kHeadless = AvcParameterSets::kHeadless;
+const auto kAnnexBHeaderSizeInBytes =
+    AvcParameterSets::kAnnexBHeaderSizeInBytes;
+const uint8_t kSliceStartCode = 0x61;
+const uint8_t kIdrStartCode = 0x65;
+const uint8_t kSpsStartCode = AvcParameterSets::kSpsStartCode;
+const uint8_t kPpsStartCode = AvcParameterSets::kPpsStartCode;
+
+const std::vector<uint8_t> kRawSlice = {kSliceStartCode, 0, 0, 1, 0, 0, 0};
+const std::vector<uint8_t> kRawIdr = {kIdrStartCode, 1, 2, 3, 4};
+const std::vector<uint8_t> kRawSps = {kSpsStartCode, 10, 11};
+const std::vector<uint8_t> kRawPps = {kPpsStartCode, 20};
+
+const std::vector<uint8_t> kNaluHeaderOnlyInAnnexB = {0, 0, 0, 1};
+const std::vector<uint8_t> kSpsInAnnexB = {0, 0, 0, 1, kSpsStartCode, 10, 11};
+const std::vector<uint8_t> kPpsInAnnexB = {0, 0, 0, 1, kPpsStartCode, 20};
+const std::vector<uint8_t> kIdrInAnnexB = {0, 0, 0, 1, kIdrStartCode,
+                                           1, 2, 3, 4};
+const std::vector<uint8_t> kSliceInAnnexB = {0, 0, 0, 1, kSliceStartCode, 0, 0,
+                                             1, 0, 0, 0};
+
+std::vector<uint8_t> operator+(const std::vector<uint8_t>& left,
+                               const std::vector<uint8_t>& right) {
+  std::vector<uint8_t> result(left);
+  result.insert(result.end(), right.begin(), right.end());
+  return result;
+}
+
+std::vector<uint8_t> ToAnnexB(const std::vector<uint8_t>& nalu_body) {
+  return std::vector<uint8_t>({0, 0, 0, 1}) + nalu_body;
+}
+
+std::vector<uint8_t> ToAvcc(const std::vector<uint8_t>& nalu_body) {
+  std::vector<uint8_t> size(4);
+  size[0] = static_cast<uint8_t>((nalu_body.size() & 0xff000000) >> 24);
+  size[1] = static_cast<uint8_t>((nalu_body.size() & 0xff0000) >> 16);
+  size[2] = static_cast<uint8_t>((nalu_body.size() & 0xff00) >> 8);
+  size[3] = static_cast<uint8_t>(nalu_body.size() & 0xff);
+  return size + nalu_body;
+}
+
+// Return a nalu that is guaranteed to be different than the input parameter.
+std::vector<uint8_t> Mutate(const std::vector<uint8_t>& nalu_in_annex_b) {
+  return nalu_in_annex_b + std::vector<uint8_t>({123});
+}
+
+std::vector<uint8_t> ConvertAnnexBToAvcc(
+    const std::vector<uint8_t>& nalus_in_annex_b) {
+  std::vector<uint8_t> nalus_in_avcc(nalus_in_annex_b.size());
+  SB_CHECK(ConvertAnnexBToAvcc(nalus_in_annex_b.data(), nalus_in_annex_b.size(),
+                               nalus_in_avcc.data()));
+  return nalus_in_avcc;
+}
+
+void VerifyConvertTo(const AvcParameterSets& parameter_sets_in_annex_b) {
+  auto parameter_sets_headless = parameter_sets_in_annex_b.ConvertTo(kHeadless);
+
+  ASSERT_TRUE(parameter_sets_headless.is_valid());
+  ASSERT_EQ(parameter_sets_headless.format(), kHeadless);
+  ASSERT_EQ(parameter_sets_headless.has_sps_and_pps(),
+            parameter_sets_in_annex_b.has_sps_and_pps());
+  if (parameter_sets_in_annex_b.has_sps_and_pps()) {
+    ASSERT_EQ(ToAnnexB(parameter_sets_headless.first_sps()),
+              parameter_sets_in_annex_b.first_sps());
+    ASSERT_EQ(ToAnnexB(parameter_sets_headless.first_pps()),
+              parameter_sets_in_annex_b.first_pps());
+  }
+  ASSERT_EQ(parameter_sets_headless.GetAddresses().size(),
+            parameter_sets_in_annex_b.GetAddresses().size());
+  ASSERT_EQ(parameter_sets_headless.GetSizesInBytes().size(),
+            parameter_sets_in_annex_b.GetSizesInBytes().size());
+  for (size_t i = 0; i < parameter_sets_headless.GetAddresses().size(); ++i) {
+    auto nalu_headless =
+        std::vector<uint8_t>(parameter_sets_headless.GetAddresses()[i],
+                             parameter_sets_headless.GetAddresses()[i] +
+                                 parameter_sets_headless.GetSizesInBytes()[i]);
+    auto nalu_in_annex_b = std::vector<uint8_t>(
+        parameter_sets_in_annex_b.GetAddresses()[i],
+        parameter_sets_in_annex_b.GetAddresses()[i] +
+            parameter_sets_in_annex_b.GetSizesInBytes()[i]);
+    ASSERT_EQ(ToAnnexB(nalu_headless), nalu_in_annex_b);
+  }
+  ASSERT_EQ(parameter_sets_headless.combined_size_in_bytes() +
+                kAnnexBHeaderSizeInBytes *
+                    parameter_sets_headless.GetAddresses().size(),
+            parameter_sets_in_annex_b.combined_size_in_bytes());
+}
+
+void VerifyAnnexB(const std::vector<uint8_t>& nalus_in_annex_b,
+                  const std::vector<uint8_t>& first_sps_in_annex_b,
+                  const std::vector<uint8_t>& first_pps_in_annex_b,
+                  const std::vector<uint8_t>& parameter_sets_in_annex_b) {
+  AvcParameterSets parameter_sets(kAnnexB, nalus_in_annex_b.data(),
+                                  nalus_in_annex_b.size());
+
+  ASSERT_TRUE(parameter_sets.is_valid());
+  ASSERT_EQ(parameter_sets.format(), kAnnexB);
+  ASSERT_TRUE(parameter_sets.has_sps_and_pps());
+  ASSERT_EQ(parameter_sets.first_sps(), first_sps_in_annex_b);
+  ASSERT_EQ(parameter_sets.first_pps(), first_pps_in_annex_b);
+  ASSERT_EQ(parameter_sets.combined_size_in_bytes(),
+            parameter_sets_in_annex_b.size());
+
+  ASSERT_TRUE(parameter_sets == parameter_sets);
+  ASSERT_FALSE(parameter_sets != parameter_sets);
+
+  const auto& addresses = parameter_sets.GetAddresses();
+  const auto& sizes = parameter_sets.GetSizesInBytes();
+  ASSERT_EQ(addresses.size(), sizes.size());
+
+  std::vector<uint8_t> accumulated_parameter_sets;
+  for (size_t i = 0; i < addresses.size(); ++i) {
+    accumulated_parameter_sets =
+        accumulated_parameter_sets +
+        std::vector<uint8_t>(addresses[i], addresses[i] + sizes[i]);
+  }
+  ASSERT_EQ(accumulated_parameter_sets, parameter_sets_in_annex_b);
+
+  VerifyConvertTo(parameter_sets);
+}
+
+void VerifyAllEmpty(const std::vector<uint8_t>& nalus_in_annex_b) {
+  AvcParameterSets parameter_sets(kAnnexB, nalus_in_annex_b.data(),
+                                  nalus_in_annex_b.size());
+
+  ASSERT_EQ(parameter_sets.format(), kAnnexB);
+
+  for (int i = 0; i < 2; ++i) {
+    if (i == 1) {
+      parameter_sets = parameter_sets.ConvertTo(kHeadless);
+      ASSERT_EQ(parameter_sets.format(), kHeadless);
+    }
+    ASSERT_TRUE(parameter_sets.is_valid());
+    ASSERT_FALSE(parameter_sets.has_sps_and_pps());
+    ASSERT_TRUE(parameter_sets.GetAddresses().empty());
+    ASSERT_TRUE(parameter_sets.GetSizesInBytes().empty());
+    ASSERT_EQ(parameter_sets.combined_size_in_bytes(), 0);
+  }
+
+  VerifyConvertTo(parameter_sets);
+}
+
+bool HasEqualParameterSets(const std::vector<uint8_t>& nalus_in_annex_b_1,
+                           const std::vector<uint8_t>& nalus_in_annex_b_2) {
+  AvcParameterSets parameter_sets_1(kAnnexB, nalus_in_annex_b_1.data(),
+                                    nalus_in_annex_b_1.size());
+  AvcParameterSets parameter_sets_2(kAnnexB, nalus_in_annex_b_2.data(),
+                                    nalus_in_annex_b_2.size());
+
+  SB_CHECK((parameter_sets_1 == parameter_sets_2) !=
+           (parameter_sets_1 != parameter_sets_2));
+
+  return parameter_sets_1 == parameter_sets_2;
+}
+
+TEST(AvcParameterSetsTest, Ctor) {
+  AvcParameterSets parameter_sets_1(kAnnexB, nullptr, 0);
+  AvcParameterSets parameter_sets_2(kAnnexB, kSpsInAnnexB.data(),
+                                    kSpsInAnnexB.size());
+  AvcParameterSets parameter_sets_3(kAnnexB, kPpsInAnnexB.data(),
+                                    kPpsInAnnexB.size());
+  AvcParameterSets parameter_sets_4(kAnnexB, kIdrInAnnexB.data(),
+                                    kIdrInAnnexB.size());
+
+  auto nalus_in_annex_b = kSpsInAnnexB + kPpsInAnnexB + kIdrInAnnexB;
+  AvcParameterSets parameter_sets_5(kAnnexB, nalus_in_annex_b.data(),
+                                    nalus_in_annex_b.size());
+}
+
+TEST(AvcParameterSetsTest, SingleSpsAndPps) {
+  auto sps_before_pps = kSpsInAnnexB + kPpsInAnnexB + kIdrInAnnexB;
+  auto pps_before_sps = kPpsInAnnexB + kSpsInAnnexB + kIdrInAnnexB;
+
+  VerifyAnnexB(sps_before_pps, kSpsInAnnexB, kPpsInAnnexB,
+               kSpsInAnnexB + kPpsInAnnexB);
+  VerifyAnnexB(pps_before_sps, kSpsInAnnexB, kPpsInAnnexB,
+               kPpsInAnnexB + kSpsInAnnexB);
+
+  // Change sps and pps position are treated as unequal.
+  ASSERT_FALSE(HasEqualParameterSets(sps_before_pps, pps_before_sps));
+}
+
+TEST(AvcParameterSetsTest, MultipleSpsAndPps) {
+  for (int i = 0; i < 4; ++i) {
+    std::vector<uint8_t> parameter_sets_in_annex_b;
+    switch (i) {
+      case 0:
+        parameter_sets_in_annex_b = kSpsInAnnexB + Mutate(kSpsInAnnexB) +
+                                    kPpsInAnnexB + Mutate(kPpsInAnnexB);
+        break;
+      case 1:
+        parameter_sets_in_annex_b = kSpsInAnnexB + kPpsInAnnexB +
+                                    Mutate(kSpsInAnnexB) + Mutate(kPpsInAnnexB);
+        break;
+      case 2:
+        parameter_sets_in_annex_b = kPpsInAnnexB + kSpsInAnnexB +
+                                    Mutate(kSpsInAnnexB) + Mutate(kPpsInAnnexB);
+        break;
+      case 3:
+        parameter_sets_in_annex_b = kPpsInAnnexB + Mutate(kPpsInAnnexB) +
+                                    kSpsInAnnexB + Mutate(kSpsInAnnexB);
+        break;
+    }
+    auto nalus_in_annex_b =
+        parameter_sets_in_annex_b + kIdrInAnnexB + kSliceInAnnexB;
+
+    VerifyAnnexB(nalus_in_annex_b, kSpsInAnnexB, kPpsInAnnexB,
+                 parameter_sets_in_annex_b);
+
+    ASSERT_FALSE(HasEqualParameterSets(kSpsInAnnexB + kPpsInAnnexB,
+                                       parameter_sets_in_annex_b));
+  }
+}
+
+TEST(AvcParameterSetsTest, SpsWithoutPps) {
+  auto leading_sps_nalus = kSpsInAnnexB;
+  for (int i = 0; i < 5; ++i) {
+    auto nalus_in_annex_b = leading_sps_nalus + kIdrInAnnexB;
+
+    AvcParameterSets parameter_sets(kAnnexB, nalus_in_annex_b.data(),
+                                    nalus_in_annex_b.size());
+
+    ASSERT_TRUE(parameter_sets.is_valid());
+    ASSERT_FALSE(parameter_sets.has_sps_and_pps());
+    ASSERT_EQ(kSpsInAnnexB, parameter_sets.first_sps());
+    ASSERT_EQ(parameter_sets.combined_size_in_bytes(),
+              leading_sps_nalus.size());
+
+    ASSERT_EQ(parameter_sets.GetAddresses().size(), i + 1);
+    ASSERT_EQ(parameter_sets.GetSizesInBytes().size(), i + 1);
+    ASSERT_EQ(kSpsInAnnexB,
+              std::vector<uint8_t>(parameter_sets.GetAddresses()[0],
+                                   parameter_sets.GetAddresses()[0] +
+                                       parameter_sets.GetSizesInBytes()[0]));
+
+    leading_sps_nalus = leading_sps_nalus + Mutate(kSpsInAnnexB);
+  }
+}
+
+TEST(AvcParameterSetsTest, PpsWithoutSps) {
+  auto leading_pps_nalus = kPpsInAnnexB;
+  for (int i = 0; i < 5; ++i) {
+    auto nalus_in_annex_b = leading_pps_nalus + kIdrInAnnexB;
+
+    AvcParameterSets parameter_sets(kAnnexB, nalus_in_annex_b.data(),
+                                    nalus_in_annex_b.size());
+
+    ASSERT_TRUE(parameter_sets.is_valid());
+    ASSERT_FALSE(parameter_sets.has_sps_and_pps());
+    ASSERT_EQ(kPpsInAnnexB, parameter_sets.first_pps());
+    ASSERT_EQ(parameter_sets.combined_size_in_bytes(),
+              leading_pps_nalus.size());
+
+    ASSERT_EQ(parameter_sets.GetAddresses().size(), i + 1);
+    ASSERT_EQ(parameter_sets.GetSizesInBytes().size(), i + 1);
+    ASSERT_EQ(kPpsInAnnexB,
+              std::vector<uint8_t>(parameter_sets.GetAddresses()[0],
+                                   parameter_sets.GetAddresses()[0] +
+                                       parameter_sets.GetSizesInBytes()[0]));
+
+    leading_pps_nalus = leading_pps_nalus + Mutate(kPpsInAnnexB);
+  }
+}
+
+TEST(AvcParameterSetsTest, MultipleSpsAndPpsWithoutPayload) {
+  for (int i = 0; i < 4; ++i) {
+    std::vector<uint8_t> parameter_sets_in_annex_b;
+    switch (i) {
+      case 0:
+        parameter_sets_in_annex_b = kSpsInAnnexB + Mutate(kSpsInAnnexB) +
+                                    kPpsInAnnexB + Mutate(kPpsInAnnexB);
+        break;
+      case 1:
+        parameter_sets_in_annex_b = kSpsInAnnexB + kPpsInAnnexB +
+                                    Mutate(kSpsInAnnexB) + Mutate(kPpsInAnnexB);
+        break;
+      case 2:
+        parameter_sets_in_annex_b = kPpsInAnnexB + kSpsInAnnexB +
+                                    Mutate(kSpsInAnnexB) + Mutate(kPpsInAnnexB);
+        break;
+      case 3:
+        parameter_sets_in_annex_b = kPpsInAnnexB + Mutate(kPpsInAnnexB) +
+                                    kSpsInAnnexB + Mutate(kSpsInAnnexB);
+        break;
+    }
+
+    AvcParameterSets parameter_sets(kAnnexB, parameter_sets_in_annex_b.data(),
+                                    parameter_sets_in_annex_b.size());
+
+    VerifyAnnexB(parameter_sets_in_annex_b, kSpsInAnnexB, kPpsInAnnexB,
+                 parameter_sets_in_annex_b);
+  }
+}
+
+TEST(AvcParameterSetsTest, SpsAfterPayload) {
+  auto parameter_sets_in_annex_b = kSpsInAnnexB + kPpsInAnnexB;
+  auto nalus_in_annex_b =
+      parameter_sets_in_annex_b + kIdrInAnnexB + kSpsInAnnexB;
+
+  VerifyAnnexB(nalus_in_annex_b, kSpsInAnnexB, kPpsInAnnexB,
+               parameter_sets_in_annex_b);
+  ASSERT_TRUE(
+      HasEqualParameterSets(parameter_sets_in_annex_b, nalus_in_annex_b));
+}
+
+TEST(AvcParameterSetsTest, PpsAfterPayload) {
+  auto parameter_sets_in_annex_b = kSpsInAnnexB + kPpsInAnnexB;
+  auto nalus_in_annex_b =
+      parameter_sets_in_annex_b + kIdrInAnnexB + kPpsInAnnexB;
+
+  VerifyAnnexB(nalus_in_annex_b, kSpsInAnnexB, kPpsInAnnexB,
+               parameter_sets_in_annex_b);
+  ASSERT_TRUE(
+      HasEqualParameterSets(parameter_sets_in_annex_b, nalus_in_annex_b));
+}
+
+TEST(AvcParameterSetsTest, SpsAndPpsAfterPayloadWithoutSpsAndPps) {
+  auto parameter_sets_in_annex_b = kSpsInAnnexB + kPpsInAnnexB;
+  auto nalus_in_annex_b =
+      kIdrInAnnexB + kPpsInAnnexB + parameter_sets_in_annex_b;
+
+  VerifyAllEmpty(nalus_in_annex_b);
+}
+
+TEST(AvcParameterSetsTest, Nullptr) {
+  AvcParameterSets parameter_sets(kAnnexB, nullptr, 0);
+
+  ASSERT_TRUE(parameter_sets.is_valid());
+  ASSERT_EQ(parameter_sets.format(), kAnnexB);
+  ASSERT_FALSE(parameter_sets.has_sps_and_pps());
+  ASSERT_TRUE(parameter_sets.GetAddresses().empty());
+  ASSERT_TRUE(parameter_sets.GetSizesInBytes().empty());
+  ASSERT_EQ(parameter_sets.combined_size_in_bytes(), 0);
+}
+
+TEST(AvcParameterSetsTest, NaluHeaderWithoutType) {
+  {
+    AvcParameterSets parameter_sets(kAnnexB, kNaluHeaderOnlyInAnnexB.data(),
+                                    kNaluHeaderOnlyInAnnexB.size());
+
+    ASSERT_TRUE(parameter_sets.is_valid());
+    ASSERT_EQ(parameter_sets.format(), kAnnexB);
+    ASSERT_FALSE(parameter_sets.has_sps_and_pps());
+    ASSERT_TRUE(parameter_sets.GetAddresses().empty());
+    ASSERT_TRUE(parameter_sets.GetSizesInBytes().empty());
+    ASSERT_EQ(parameter_sets.combined_size_in_bytes(), 0);
+  }
+  for (int i = 0; i < 2; ++i) {
+    auto parameter_sets_in_annex_b = kSpsInAnnexB + kPpsInAnnexB;
+    std::vector<uint8_t> nalus_in_annex_b;
+    if (i == 0) {
+      nalus_in_annex_b = parameter_sets_in_annex_b + kNaluHeaderOnlyInAnnexB;
+    } else {
+      nalus_in_annex_b =
+          parameter_sets_in_annex_b + kIdrInAnnexB + kNaluHeaderOnlyInAnnexB;
+    }
+
+    VerifyAnnexB(nalus_in_annex_b, kSpsInAnnexB, kPpsInAnnexB,
+                 parameter_sets_in_annex_b);
+  }
+}
+
+TEST(AvcParameterSetsTest, InvalidNaluHeader) {
+  { VerifyAllEmpty(kNaluHeaderOnlyInAnnexB); }
+  {
+    auto parameter_sets_in_annex_b = kSpsInAnnexB + kPpsInAnnexB;
+    auto nalus_in_annex_b = parameter_sets_in_annex_b + kNaluHeaderOnlyInAnnexB;
+
+    VerifyAnnexB(nalus_in_annex_b, kSpsInAnnexB, kPpsInAnnexB,
+                 parameter_sets_in_annex_b);
+  }
+  {
+    auto parameter_sets_in_annex_b = kSpsInAnnexB + kPpsInAnnexB;
+    auto nalus_in_annex_b =
+        parameter_sets_in_annex_b + kIdrInAnnexB + kNaluHeaderOnlyInAnnexB;
+
+    VerifyAnnexB(nalus_in_annex_b, kSpsInAnnexB, kPpsInAnnexB,
+                 parameter_sets_in_annex_b);
+  }
+  {
+    auto parameter_sets_in_annex_b = kSpsInAnnexB + kPpsInAnnexB;
+    auto nalus_in_annex_b =
+        parameter_sets_in_annex_b + kIdrInAnnexB + kNaluHeaderOnlyInAnnexB;
+    nalus_in_annex_b.erase(nalus_in_annex_b.begin());  // One less 0
+
+    AvcParameterSets parameter_sets(kAnnexB, nalus_in_annex_b.data(),
+                                    nalus_in_annex_b.size());
+    ASSERT_FALSE(parameter_sets.is_valid());
+  }
+  {
+    auto parameter_sets_in_annex_b = kSpsInAnnexB + kPpsInAnnexB;
+    auto nalus_in_annex_b =
+        parameter_sets_in_annex_b + kIdrInAnnexB + kNaluHeaderOnlyInAnnexB;
+    nalus_in_annex_b.insert(nalus_in_annex_b.begin(), 0);  // One extra 0
+
+    AvcParameterSets parameter_sets(kAnnexB, nalus_in_annex_b.data(),
+                                    nalus_in_annex_b.size());
+    ASSERT_FALSE(parameter_sets.is_valid());
+  }
+}
+
+TEST(AvcParameterSetsTest, MultiNalusWithouSpsPps) {
+  std::vector<uint8_t> nalus_in_annex_b = kIdrInAnnexB + kSliceInAnnexB;
+
+  for (int i = 0; i < 3; ++i) {
+    VerifyAllEmpty(nalus_in_annex_b);
+
+    nalus_in_annex_b = nalus_in_annex_b + nalus_in_annex_b;
+
+    ASSERT_TRUE(
+        HasEqualParameterSets(nalus_in_annex_b, std::vector<uint8_t>()));
+  }
+}
+
+TEST(AvcParameterSetsTest, ConvertAnnexBToAvcc) {
+  {
+    std::vector<uint8_t> raw_nalus[] = {kRawSlice, kRawIdr, kRawSps, kRawPps};
+    std::vector<uint8_t> nalus_in_annex_b;
+    std::vector<uint8_t> nalus_in_avcc;
+
+    for (int i = 0; i < 20; ++i) {
+      nalus_in_annex_b =
+          nalus_in_annex_b + ToAnnexB(raw_nalus[i % SB_ARRAY_SIZE(raw_nalus)]);
+      nalus_in_avcc =
+          nalus_in_avcc + ToAvcc(raw_nalus[i % SB_ARRAY_SIZE(raw_nalus)]);
+
+      ASSERT_EQ(ConvertAnnexBToAvcc(nalus_in_annex_b), nalus_in_avcc);
+    }
+  }
+  {
+    std::vector<uint8_t> raw_nalus[] = {kRawSps, kRawPps, kRawSlice, kRawIdr};
+    std::vector<uint8_t> nalus_in_annex_b;
+    std::vector<uint8_t> nalus_in_avcc;
+
+    for (int i = 0; i < 20; ++i) {
+      nalus_in_annex_b =
+          nalus_in_annex_b + ToAnnexB(raw_nalus[i % SB_ARRAY_SIZE(raw_nalus)]);
+      nalus_in_avcc =
+          nalus_in_avcc + ToAvcc(raw_nalus[i % SB_ARRAY_SIZE(raw_nalus)]);
+
+      ASSERT_EQ(ConvertAnnexBToAvcc(nalus_in_annex_b), nalus_in_avcc);
+    }
+  }
+}
+
+TEST(AvcParameterSetsTest, ConvertAnnexBToAvccEmptyNalus) {
+  const std::vector<uint8_t> kEmpty;
+  const std::vector<uint8_t> kRawNalu = {1, 2, 3, 4, 5};
+
+  std::vector<uint8_t> nalus_in_annex_b;
+  std::vector<uint8_t> nalus_in_avcc;
+
+  for (int i = 0; i < 3; ++i) {
+    nalus_in_annex_b = nalus_in_annex_b + ToAnnexB(kEmpty);
+    nalus_in_avcc = nalus_in_avcc + ToAvcc(kEmpty);
+
+    ASSERT_EQ(ConvertAnnexBToAvcc(nalus_in_annex_b), nalus_in_avcc);
+  }
+
+  ASSERT_EQ(ConvertAnnexBToAvcc(ToAnnexB(kEmpty) + ToAnnexB(kRawNalu)),
+            ToAvcc(kEmpty) + ToAvcc(kRawNalu));
+  ASSERT_EQ(ConvertAnnexBToAvcc(ToAnnexB(kRawNalu) + ToAnnexB(kEmpty)),
+            ToAvcc(kRawNalu) + ToAvcc(kEmpty));
+}
+
+TEST(AvcParameterSetsTest, ConvertAnnexBToAvccInvalidNalus) {
+  {
+    auto parameter_sets_in_annex_b = kSpsInAnnexB + kPpsInAnnexB;
+    auto nalus_in_annex_b =
+        parameter_sets_in_annex_b + kIdrInAnnexB + kNaluHeaderOnlyInAnnexB;
+    nalus_in_annex_b.erase(nalus_in_annex_b.begin());  // One less 0
+    auto nalus_in_avcc = nalus_in_annex_b;
+    ASSERT_FALSE(ConvertAnnexBToAvcc(nalus_in_annex_b.data(),
+                                     nalus_in_annex_b.size(),
+                                     nalus_in_avcc.data()));
+  }
+  {
+    auto parameter_sets_in_annex_b = kSpsInAnnexB + kPpsInAnnexB;
+    auto nalus_in_annex_b =
+        parameter_sets_in_annex_b + kIdrInAnnexB + kNaluHeaderOnlyInAnnexB;
+    nalus_in_annex_b.insert(nalus_in_annex_b.begin(), 0);  // One extra 0
+    auto nalus_in_avcc = nalus_in_annex_b;
+    ASSERT_FALSE(ConvertAnnexBToAvcc(nalus_in_annex_b.data(),
+                                     nalus_in_annex_b.size(),
+                                     nalus_in_avcc.data()));
+  }
+}
+
+}  // namespace
+}  // namespace media
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/starboard/media/codec_util.cc b/src/starboard/shared/starboard/media/codec_util.cc
index ce45a9b..54c905f 100644
--- a/src/starboard/shared/starboard/media/codec_util.cc
+++ b/src/starboard/shared/starboard/media/codec_util.cc
@@ -576,6 +576,52 @@
 
 }  // namespace
 
+VideoConfig::VideoConfig(SbMediaVideoCodec video_codec,
+                         int width,
+                         int height,
+                         const uint8_t* data,
+                         size_t size)
+    : width_(width), height_(height) {
+  if (video_codec == kSbMediaVideoCodecVp9) {
+    video_codec_ = video_codec;
+  } else if (video_codec == kSbMediaVideoCodecH264) {
+    avc_parameter_sets_ =
+        AvcParameterSets(AvcParameterSets::kAnnexB, data, size);
+    if (avc_parameter_sets_->is_valid()) {
+      video_codec_ = video_codec;
+    }
+  } else {
+    SB_NOTREACHED();
+  }
+}
+
+#if SB_API_VERSION >= 11
+VideoConfig::VideoConfig(const SbMediaVideoSampleInfo& video_sample_info,
+                         const uint8_t* data,
+                         size_t size)
+    : VideoConfig(video_sample_info.codec,
+                  video_sample_info.frame_width,
+                  video_sample_info.frame_height,
+                  data,
+                  size) {
+  SB_DCHECK(video_sample_info.is_key_frame);
+}
+#endif  // SB_API_VERSION >= 11
+
+bool VideoConfig::operator==(const VideoConfig& that) const {
+  if (video_codec_ == kSbMediaVideoCodecNone &&
+      that.video_codec_ == kSbMediaVideoCodecNone) {
+    return true;
+  }
+  return video_codec_ == that.video_codec_ &&
+         avc_parameter_sets_ == that.avc_parameter_sets_ &&
+         width_ == that.width_ && height_ == that.height_;
+}
+
+bool VideoConfig::operator!=(const VideoConfig& that) const {
+  return !(*this == that);
+}
+
 SbMediaAudioCodec GetAudioCodecFromString(const char* codec) {
   if (SbStringCompare(codec, "mp4a.40.", 8) == 0) {
     return kSbMediaAudioCodecAac;
diff --git a/src/starboard/shared/starboard/media/codec_util.h b/src/starboard/shared/starboard/media/codec_util.h
index 3edd3d5..c2dc302 100644
--- a/src/starboard/shared/starboard/media/codec_util.h
+++ b/src/starboard/shared/starboard/media/codec_util.h
@@ -15,14 +15,56 @@
 #ifndef STARBOARD_SHARED_STARBOARD_MEDIA_CODEC_UTIL_H_
 #define STARBOARD_SHARED_STARBOARD_MEDIA_CODEC_UTIL_H_
 
+#include <vector>
+
+#include "starboard/common/optional.h"
 #include "starboard/media.h"
 #include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/media/avc_util.h"
 
 namespace starboard {
 namespace shared {
 namespace starboard {
 namespace media {
 
+// This class captures necessary information to describe a video config.  It can
+// be used to detect config change of video stream during the playback.
+// The class only supports h264 and vp9 for now, which is checked in its ctor.
+class VideoConfig {
+ public:
+  // |data| must point to the encoded data of a key frame.
+  VideoConfig(SbMediaVideoCodec video_codec,
+              int width,
+              int height,
+              const uint8_t* data,
+              size_t size);
+
+#if SB_API_VERSION >= 11
+  VideoConfig(const SbMediaVideoSampleInfo& video_sample_info,
+              const uint8_t* data,
+              size_t size);
+#endif  // SB_API_VERSION >= 11
+
+  bool is_valid() const { return video_codec_ != kSbMediaVideoCodecNone; }
+
+  const AvcParameterSets& avc_parameter_sets() const {
+    SB_DCHECK(is_valid());
+    SB_DCHECK(video_codec_ == kSbMediaVideoCodecH264);
+    SB_DCHECK(avc_parameter_sets_);
+    return avc_parameter_sets_.value();
+  }
+
+  bool operator==(const VideoConfig& that) const;
+  bool operator!=(const VideoConfig& that) const;
+
+ private:
+  SbMediaVideoCodec video_codec_ = kSbMediaVideoCodecNone;
+  int width_ = -1;
+  int height_ = -1;
+  // Only valid when |video_codec_| is |kSbMediaVideoCodecH264|.
+  optional<AvcParameterSets> avc_parameter_sets_;
+};
+
 SbMediaAudioCodec GetAudioCodecFromString(const char* codec);
 
 // This function parses the video codec string and returns a codec.  All fields
diff --git a/src/starboard/shared/starboard/media/codec_util_test.cc b/src/starboard/shared/starboard/media/codec_util_test.cc
index 3399504..df2ede3 100644
--- a/src/starboard/shared/starboard/media/codec_util_test.cc
+++ b/src/starboard/shared/starboard/media/codec_util_test.cc
@@ -14,6 +14,9 @@
 
 #include "starboard/shared/starboard/media/codec_util.h"
 
+#include <vector>
+
+#include "starboard/shared/starboard/media/avc_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace starboard {
@@ -22,7 +25,198 @@
 namespace media {
 namespace {
 
-class CodecUtilTest : public ::testing::Test {
+const auto kIdrStartCode = 0x65;
+const auto kSpsStartCode = AvcParameterSets::kSpsStartCode;
+const auto kPpsStartCode = AvcParameterSets::kPpsStartCode;
+const auto kAnnexB = AvcParameterSets::kAnnexB;
+
+const std::vector<uint8_t> kSpsInAnnexB = {0, 0, 0, 1, kSpsStartCode, 10, 11};
+const std::vector<uint8_t> kPpsInAnnexB = {0, 0, 0, 1, kPpsStartCode, 20};
+const std::vector<uint8_t> kIdrInAnnexB = {0, 0, 0, 1, kIdrStartCode,
+                                           1, 2, 3, 4};
+
+std::vector<uint8_t> operator+(const std::vector<uint8_t>& left,
+                               const std::vector<uint8_t>& right) {
+  std::vector<uint8_t> result(left);
+  result.insert(result.end(), right.begin(), right.end());
+  return result;
+}
+
+#if SB_API_VERSION >= 11
+TEST(VideoConfigTest, CtorWithSbMediaVideoSampleInfo) {
+  SbMediaVideoSampleInfo video_sample_info = {kSbMediaVideoCodecH264};
+  video_sample_info.is_key_frame = true;
+  video_sample_info.frame_width = 1920;
+  video_sample_info.frame_height = 1080;
+
+  std::vector<uint8_t> nalus_in_annex_b =
+      kSpsInAnnexB + kPpsInAnnexB + kIdrInAnnexB;
+
+  VideoConfig config_1(video_sample_info.codec, video_sample_info.frame_width,
+                       video_sample_info.frame_height, nalus_in_annex_b.data(),
+                       nalus_in_annex_b.size());
+  VideoConfig config_2(video_sample_info, nalus_in_annex_b.data(),
+                       nalus_in_annex_b.size());
+  ASSERT_TRUE(config_1 == config_2);
+}
+#endif  // SB_API_VERSION >= 11
+
+TEST(VideoConfigTest, IsValid) {
+  std::vector<uint8_t> nalus_in_annex_b =
+      kSpsInAnnexB + kPpsInAnnexB + kIdrInAnnexB;
+
+  {
+    VideoConfig config(kSbMediaVideoCodecH264, 1920, 1080, kPpsInAnnexB.data(),
+                       kPpsInAnnexB.size());
+    ASSERT_TRUE(config.is_valid());
+  }
+  {
+    VideoConfig config(kSbMediaVideoCodecH264, 1920, 1080, kIdrInAnnexB.data(),
+                       kIdrInAnnexB.size());
+    ASSERT_TRUE(config.is_valid());
+  }
+  {
+    VideoConfig config(kSbMediaVideoCodecH264, 1920, 1080,
+                       nalus_in_annex_b.data(), nalus_in_annex_b.size());
+    ASSERT_TRUE(config.is_valid());
+  }
+  {
+    // The implementation only fails when the format is avc and the input isn't
+    // empty and doesn't start with a nalu header.
+    VideoConfig config(kSbMediaVideoCodecH264, 1920, 1080,
+                       nalus_in_annex_b.data() + 1,
+                       nalus_in_annex_b.size() - 1);
+    ASSERT_FALSE(config.is_valid());
+  }
+  {
+    VideoConfig config(kSbMediaVideoCodecVp9, 1920, 1080, nullptr, 0);
+    ASSERT_TRUE(config.is_valid());
+  }
+}
+
+TEST(VideoConfigTest, SelfComparison) {
+  {
+    std::vector<uint8_t> nalus_in_annex_b =
+        kSpsInAnnexB + kPpsInAnnexB + kIdrInAnnexB;
+
+    VideoConfig config(kSbMediaVideoCodecH264, 640, 480,
+                       nalus_in_annex_b.data(), nalus_in_annex_b.size());
+    EXPECT_TRUE(config == config);
+    EXPECT_FALSE(config != config);
+  }
+  {
+    VideoConfig config(kSbMediaVideoCodecVp9, 640, 480, nullptr, 0);
+    EXPECT_TRUE(config == config);
+    EXPECT_FALSE(config != config);
+  }
+}
+
+TEST(VideoConfigTest, H264) {
+  std::vector<uint8_t> nalus_in_annex_b =
+      kSpsInAnnexB + kPpsInAnnexB + kIdrInAnnexB;
+
+  VideoConfig config(kSbMediaVideoCodecH264, 640, 480, nalus_in_annex_b.data(),
+                     nalus_in_annex_b.size());
+
+  // Different resolution, same parameter sets.
+  VideoConfig config_1(kSbMediaVideoCodecH264, 1920, 1080,
+                       nalus_in_annex_b.data(), nalus_in_annex_b.size());
+  EXPECT_TRUE(config != config_1);
+  EXPECT_TRUE(config.avc_parameter_sets() == config_1.avc_parameter_sets());
+  EXPECT_FALSE(config == config_1);
+
+  // Same resolution, different parameter sets.
+  nalus_in_annex_b =
+      kSpsInAnnexB + kPpsInAnnexB + std::vector<uint8_t>({99}) + kIdrInAnnexB;
+  VideoConfig config_2(kSbMediaVideoCodecH264, 640, 480,
+                       nalus_in_annex_b.data(), nalus_in_annex_b.size());
+  EXPECT_TRUE(config != config_2);
+  EXPECT_FALSE(config == config_2);
+
+  // Same resolution, same parameter sets, different idr data.
+  nalus_in_annex_b = kSpsInAnnexB + kPpsInAnnexB + kIdrInAnnexB;
+  nalus_in_annex_b.push_back(99);
+
+  VideoConfig config_3(kSbMediaVideoCodecH264, 640, 480,
+                       nalus_in_annex_b.data(), nalus_in_annex_b.size());
+  EXPECT_TRUE(config == config_3);
+  EXPECT_FALSE(config != config_3);
+}
+
+TEST(VideoConfigTest, H264MultiSpsPps) {
+  // Single sps and pps.
+  std::vector<uint8_t> nalus_in_annex_b =
+      kSpsInAnnexB + kPpsInAnnexB + kIdrInAnnexB;
+
+  VideoConfig config_single_sps_pps(kSbMediaVideoCodecH264, 640, 480,
+                                    nalus_in_annex_b.data(),
+                                    nalus_in_annex_b.size());
+
+  // Same resolution, multiple parameter sets.
+  nalus_in_annex_b =
+      kSpsInAnnexB + kSpsInAnnexB + kPpsInAnnexB + kPpsInAnnexB + kIdrInAnnexB;
+
+  VideoConfig config_dual_sps_pps(kSbMediaVideoCodecH264, 640, 480,
+                                  nalus_in_annex_b.data(),
+                                  nalus_in_annex_b.size());
+  EXPECT_TRUE(config_single_sps_pps != config_dual_sps_pps);
+  EXPECT_FALSE(config_single_sps_pps == config_dual_sps_pps);
+  EXPECT_TRUE(config_dual_sps_pps.avc_parameter_sets() ==
+              AvcParameterSets(kAnnexB, nalus_in_annex_b.data(),
+                               nalus_in_annex_b.size()));
+
+  // Same resolution, different parameter sets.
+  nalus_in_annex_b =
+      kSpsInAnnexB + kPpsInAnnexB + std::vector<uint8_t>({99}) + kIdrInAnnexB;
+  VideoConfig config_1(kSbMediaVideoCodecH264, 640, 480,
+                       nalus_in_annex_b.data(), nalus_in_annex_b.size());
+  EXPECT_TRUE(config_single_sps_pps != config_1);
+  EXPECT_FALSE(config_single_sps_pps == config_1);
+
+  // Same resolution, same parameter sets, different idr data.
+  nalus_in_annex_b = kSpsInAnnexB + kPpsInAnnexB + kIdrInAnnexB;
+  nalus_in_annex_b.push_back(99);
+
+  VideoConfig config_2(kSbMediaVideoCodecH264, 640, 480,
+                       nalus_in_annex_b.data(), nalus_in_annex_b.size());
+  EXPECT_TRUE(config_single_sps_pps == config_2);
+  EXPECT_FALSE(config_single_sps_pps != config_2);
+}
+
+TEST(VideoConfigTest, Vp9) {
+  // The class shouldn't look into vp9 bitstreams.
+  const uint8_t kInvalidData[] = {1, 7, 25};
+
+  VideoConfig config(kSbMediaVideoCodecVp9, 640, 480, kInvalidData,
+                     SB_ARRAY_SIZE(kInvalidData));
+
+  // Different resolution, same data.
+  VideoConfig config_1(kSbMediaVideoCodecVp9, 1920, 1080, kInvalidData,
+                       SB_ARRAY_SIZE(kInvalidData));
+  EXPECT_TRUE(config != config_1);
+  EXPECT_FALSE(config == config_1);
+
+  // Same resolution, different data (one less byte).
+  VideoConfig config_2(kSbMediaVideoCodecVp9, 640, 480, kInvalidData,
+                       SB_ARRAY_SIZE(kInvalidData) - 1);
+  EXPECT_TRUE(config == config_2);
+  EXPECT_FALSE(config != config_2);
+}
+
+TEST(VideoConfigTest, H264VsVp9) {
+  std::vector<uint8_t> nalus_in_annex_b =
+      kSpsInAnnexB + kPpsInAnnexB + kIdrInAnnexB;
+
+  VideoConfig config_h264(kSbMediaVideoCodecH264, 640, 480,
+                          nalus_in_annex_b.data(), nalus_in_annex_b.size());
+  VideoConfig config_vp9(kSbMediaVideoCodecVp9, 640, 480,
+                         nalus_in_annex_b.data(), nalus_in_annex_b.size());
+
+  EXPECT_TRUE(config_h264 != config_vp9);
+  EXPECT_FALSE(config_h264 == config_vp9);
+}
+
+class ParseVideoCodecTest : public ::testing::Test {
  protected:
   bool Parse(const char* codec_string) {
     return ParseVideoCodec(codec_string, &codec_, &profile_, &level_,
@@ -39,7 +233,7 @@
   SbMediaMatrixId matrix_id_;
 };
 
-TEST_F(CodecUtilTest, SimpleCodecs) {
+TEST_F(ParseVideoCodecTest, SimpleCodecs) {
   const char* kCodecStrings[] = {"vp8", "vp9"};
   const SbMediaVideoCodec kVideoCodecs[] = {kSbMediaVideoCodecVp8,
                                             kSbMediaVideoCodecVp9};
@@ -55,7 +249,7 @@
   }
 }
 
-TEST_F(CodecUtilTest, ShortFormAv1) {
+TEST_F(ParseVideoCodecTest, ShortFormAv1) {
   ASSERT_TRUE(Parse("av01.0.01M.08"));
 #if SB_API_VERSION < 11
   EXPECT_EQ(codec_, kSbMediaVideoCodecVp10);
@@ -70,7 +264,7 @@
   EXPECT_EQ(matrix_id_, kSbMediaMatrixIdBt709);
 }
 
-TEST_F(CodecUtilTest, LongFormAv1) {
+TEST_F(ParseVideoCodecTest, LongFormAv1) {
   ASSERT_TRUE(Parse("av01.0.04M.10.0.110.09.16.09.0"));
 #if SB_API_VERSION < 11
   EXPECT_EQ(codec_, kSbMediaVideoCodecVp10);
@@ -85,7 +279,7 @@
   EXPECT_EQ(matrix_id_, kSbMediaMatrixIdBt2020NonconstantLuminance);
 }
 
-TEST_F(CodecUtilTest, InvalidAv1) {
+TEST_F(ParseVideoCodecTest, InvalidAv1) {
   EXPECT_FALSE(Parse("av01.0.04M.10.0.110.9.16.9.0"));
   EXPECT_FALSE(Parse("av01.0.04M.10.0.110.09.16.09"));
   EXPECT_FALSE(Parse("av01.0.04M.10.0.110.09.16"));
@@ -101,7 +295,7 @@
   EXPECT_FALSE(Parse("av01.0.04M.10.0.110.09.16.09.2"));
 }
 
-TEST_F(CodecUtilTest, Avc) {
+TEST_F(ParseVideoCodecTest, Avc) {
   ASSERT_TRUE(Parse("avc1.640028"));
   EXPECT_EQ(codec_, kSbMediaVideoCodecH264);
   EXPECT_EQ(profile_, 100);
@@ -121,13 +315,13 @@
   EXPECT_EQ(matrix_id_, kSbMediaMatrixIdUnspecified);
 }
 
-TEST_F(CodecUtilTest, InvalidAvc) {
+TEST_F(ParseVideoCodecTest, InvalidAvc) {
   EXPECT_FALSE(Parse("avc1.64002"));
   EXPECT_FALSE(Parse("avc2.640028"));
   EXPECT_FALSE(Parse("avc3.640028.1"));
 }
 
-TEST_F(CodecUtilTest, H265) {
+TEST_F(ParseVideoCodecTest, H265) {
   ASSERT_TRUE(Parse("hvc1.1.2.L93.B0"));
   EXPECT_EQ(codec_, kSbMediaVideoCodecH265);
   EXPECT_EQ(profile_, 1);
@@ -145,7 +339,7 @@
   EXPECT_TRUE(Parse("hvc1.C1.ABCDEF01.H93.B0"));
 }
 
-TEST_F(CodecUtilTest, InvalidH265) {
+TEST_F(ParseVideoCodecTest, InvalidH265) {
   EXPECT_FALSE(Parse("hvc2.1.2.L93.B0"));
   EXPECT_FALSE(Parse("hvc1.D1.2.L93.B0"));
   EXPECT_FALSE(Parse("hvc1.A111.2.L93.B0"));
@@ -156,7 +350,7 @@
   EXPECT_FALSE(Parse("hvc1.1.2.L93.B0.B1.B2.B3.B4.B5.B6"));
 }
 
-TEST_F(CodecUtilTest, ShortFormVp9) {
+TEST_F(ParseVideoCodecTest, ShortFormVp9) {
   ASSERT_TRUE(Parse("vp09.00.41.08"));
   EXPECT_EQ(codec_, kSbMediaVideoCodecVp9);
   EXPECT_EQ(profile_, 0);
@@ -167,7 +361,7 @@
   EXPECT_EQ(matrix_id_, kSbMediaMatrixIdBt709);
 }
 
-TEST_F(CodecUtilTest, MediumFormVp9) {
+TEST_F(ParseVideoCodecTest, MediumFormVp9) {
   ASSERT_TRUE(Parse("vp09.02.10.10.01.09.16.09"));
   EXPECT_EQ(codec_, kSbMediaVideoCodecVp9);
   EXPECT_EQ(profile_, 2);
@@ -178,7 +372,7 @@
   EXPECT_EQ(matrix_id_, kSbMediaMatrixIdBt2020NonconstantLuminance);
 }
 
-TEST_F(CodecUtilTest, LongFormVp9) {
+TEST_F(ParseVideoCodecTest, LongFormVp9) {
   ASSERT_TRUE(Parse("vp09.02.10.10.01.09.16.09.01"));
   EXPECT_EQ(codec_, kSbMediaVideoCodecVp9);
   EXPECT_EQ(profile_, 2);
@@ -189,7 +383,7 @@
   EXPECT_EQ(matrix_id_, kSbMediaMatrixIdBt2020NonconstantLuminance);
 }
 
-TEST_F(CodecUtilTest, InvalidVp9) {
+TEST_F(ParseVideoCodecTest, InvalidVp9) {
   EXPECT_FALSE(Parse("vp09.02.10.10.01.9.16.9"));
   EXPECT_FALSE(Parse("vp09.02.10.10.01.09.16"));
   EXPECT_FALSE(Parse("vp09.02.10.10.01.09"));
diff --git a/src/starboard/shared/starboard/media/media.gyp b/src/starboard/shared/starboard/media/media.gyp
index fb4e361..50e97b5 100644
--- a/src/starboard/shared/starboard/media/media.gyp
+++ b/src/starboard/shared/starboard/media/media.gyp
@@ -19,6 +19,8 @@
       'target_name': 'media_util',
       'type': 'static_library',
       'sources': [
+        '<(DEPTH)/starboard/shared/starboard/media/avc_util.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/avc_util.h',
         '<(DEPTH)/starboard/shared/starboard/media/codec_util.cc',
         '<(DEPTH)/starboard/shared/starboard/media/codec_util.h',
         '<(DEPTH)/starboard/shared/starboard/media/media_util.cc',
diff --git a/src/starboard/shared/starboard/media/media_get_audio_buffer_budget.cc b/src/starboard/shared/starboard/media/media_get_audio_buffer_budget.cc
index 7f909ac..8a88034 100644
--- a/src/starboard/shared/starboard/media/media_get_audio_buffer_budget.cc
+++ b/src/starboard/shared/starboard/media/media_get_audio_buffer_budget.cc
@@ -14,23 +14,6 @@
 
 #include "starboard/media.h"
 
-#include "starboard/common/log.h"
-
-#if SB_API_VERSION >= 10
-// This is the legacy default value of the GYP variable.
-#define LEGACY_NON_VIDEO_BUDGET 5 * 1024 * 1024
-
 int SbMediaGetAudioBufferBudget() {
-#if defined(COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET) && \
-    COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET != LEGACY_NON_VIDEO_BUDGET
-  SB_DLOG(WARNING) << "COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET will be deprecated "
-                      "in a future Starboard version.";
-  // Use define forwarded from GYP variable.
-  return COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET;
-#else   // defined(COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET) &&
-  // COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET != LEGACY_NON_VIDEO_BUDGET
   return 5 * 1024 * 1024;
-#endif  // defined(COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET) &&
-        // COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET != LEGACY_NON_VIDEO_BUDGET
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/media/media_get_audio_output_count_stereo_only.cc b/src/starboard/shared/starboard/media/media_get_audio_output_count_stereo_only.cc
index c101880..7c908c8 100644
--- a/src/starboard/shared/starboard/media/media_get_audio_output_count_stereo_only.cc
+++ b/src/starboard/shared/starboard/media/media_get_audio_output_count_stereo_only.cc
@@ -16,15 +16,9 @@
 
 #include "starboard/media.h"
 
-#if SB_API_VERSION >= 10
 #error File media_get_audio_output_count_stereo_only.cc is deprecated, \
     consider using media_get_audio_output_count_single_audio_output.cc instead.
-#else  // SB_API_VERSION >= 10
-#pragma message(                                                            \
-    "File media_get_audio_output_count_stereo_only.cc will be deprecated, " \
-    "consider using media_get_audio_output_count_single_audio_output.cc "   \
-    "instead.")
-#endif  // SB_API_VERSION >= 10
+
 int SbMediaGetAudioOutputCount() {
   return 1;
 }
diff --git a/src/starboard/shared/starboard/media/media_get_buffer_alignment.cc b/src/starboard/shared/starboard/media/media_get_buffer_alignment.cc
index 067795a..9b0b88a 100644
--- a/src/starboard/shared/starboard/media/media_get_buffer_alignment.cc
+++ b/src/starboard/shared/starboard/media/media_get_buffer_alignment.cc
@@ -14,25 +14,7 @@
 
 #include "starboard/media.h"
 
-#include "starboard/common/log.h"
-
-#if SB_API_VERSION >= 10
-// This is the legacy default value of the GYP variable.
-#define LEGACY_BUFFER_ALIGNMENT 1
-
 int SbMediaGetBufferAlignment(SbMediaType type) {
   SB_UNREFERENCED_PARAMETER(type);
-#if defined(COBALT_MEDIA_BUFFER_ALIGNMENT) && \
-    COBALT_MEDIA_BUFFER_ALIGNMENT != LEGACY_BUFFER_ALIGNMENT
-#pragma message(                                                    \
-    "COBALT_MEDIA_BUFFER_ALIGNMENT will be deprecated in a future " \
-    "Starboard version.")
-  // Use define forwarded from GYP variable.
-  return COBALT_MEDIA_BUFFER_ALIGNMENT;
-#else   // defined(COBALT_MEDIA_BUFFER_ALIGNMENT && COBALT_MEDIA_BUFFER_ALIGNMENT
-  // != LEGACY_BUFFER_ALIGNMENT
   return 1;
-#endif  // defined(COBALT_MEDIA_BUFFER_ALIGNMENT &&
-        // COBALT_MEDIA_BUFFER_ALIGNMENT != LEGACY_BUFFER_ALIGNMENT
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/media/media_get_buffer_allocation_unit.cc b/src/starboard/shared/starboard/media/media_get_buffer_allocation_unit.cc
index d9d2909..78c7d30 100644
--- a/src/starboard/shared/starboard/media/media_get_buffer_allocation_unit.cc
+++ b/src/starboard/shared/starboard/media/media_get_buffer_allocation_unit.cc
@@ -14,21 +14,6 @@
 
 #include "starboard/media.h"
 
-#include "starboard/common/log.h"
-
-#if SB_API_VERSION >= 10
-// This is the legacy default value of the GYP variable.
-#define LEGACY_ALLOCATION_UNIT 1 * 1024 * 1024
-
 int SbMediaGetBufferAllocationUnit() {
-#if defined(COBALT_MEDIA_BUFFER_ALLOCATION_UNIT) && \
-    COBALT_MEDIA_BUFFER_ALLOCATION_UNIT != LEGACY_ALLOCATION_UNIT
-  SB_DLOG(WARNING) << "COBALT_MEDIA_BUFFER_ALLOCATION_UNIT will be deprecated "
-                      "in a future Starboard version.";
-  // Use define forwarded from GYP variable.
-  return COBALT_MEDIA_BUFFER_ALLOCATION_UNIT;
-#else   // defined(COBALT_MEDIA_BUFFER_ALLOCATION_UNIT
   return 4 * 1024 * 1024;
-#endif  // defined(COBALT_MEDIA_BUFFER_ALLOCATION_UNIT
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/media/media_get_buffer_garbage_collection_duration_threshold.cc b/src/starboard/shared/starboard/media/media_get_buffer_garbage_collection_duration_threshold.cc
index b9e30fd..a89285c 100644
--- a/src/starboard/shared/starboard/media/media_get_buffer_garbage_collection_duration_threshold.cc
+++ b/src/starboard/shared/starboard/media/media_get_buffer_garbage_collection_duration_threshold.cc
@@ -14,31 +14,6 @@
 
 #include "starboard/media.h"
 
-#include "starboard/common/log.h"
-
-#if SB_API_VERSION >= 10
-// This is the legacy default value of the GYP variable.
-#define LEGACY_GARBAGE_COLLECTION_DURATION 170
-
 SbTime SbMediaGetBufferGarbageCollectionDurationThreshold() {
-#if defined(                                                                 \
-    COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS) && \
-    COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS !=  \
-        LEGACY_GARBAGE_COLLECTION_DURATION
-// Use define forwarded from GYP variable.
-#pragma message(                                                            \
-    "COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS " \
-    "will be deprecated in a future Starboard version.")
-  return COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS *
-         kSbTimeSecond;
-#else   // defined(COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS)
-  // &&
-  // COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS !=
-  // LEGACY_GARBAGE_COLLECTION_DURATION
   return 170 * kSbTimeSecond;
-#endif  // defined(COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS)
-        // &&
-  // COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS !=
-  // LEGACY_GARBAGE_COLLECTION_DURATION
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/media/media_get_buffer_padding.cc b/src/starboard/shared/starboard/media/media_get_buffer_padding.cc
index 2665ea6..3384e9f 100644
--- a/src/starboard/shared/starboard/media/media_get_buffer_padding.cc
+++ b/src/starboard/shared/starboard/media/media_get_buffer_padding.cc
@@ -14,25 +14,7 @@
 
 #include "starboard/media.h"
 
-#include "starboard/common/log.h"
-
-#if SB_API_VERSION >= 10
-
-#define LEGACY_BUFFER_PADDING 0
-
 int SbMediaGetBufferPadding(SbMediaType type) {
   SB_UNREFERENCED_PARAMETER(type);
-#if defined(COBALT_MEDIA_BUFFER_PADDING) && \
-    COBALT_MEDIA_BUFFER_PADDING != LEGACY_BUFFER_PADDING
-#pragma message(                                                            \
-    "COBALT_MEDIA_BUFFER_PADDING will be deprecated in a future Starboard " \
-    "version.")
-  // Use define forwarded from GYP variable.
-  return COBALT_MEDIA_BUFFER_PADDING;
-#else   // defined(COBALT_MEDIA_BUFFER_PADDING) && COBALT_MEDIA_BUFFER_PADDING !=
-  // LEGACY_BUFFER_PADDING
   return 0;
-#endif  // defined(COBALT_MEDIA_BUFFER_PADDING) && COBALT_MEDIA_BUFFER_PADDING
-        // != LEGACY_BUFFER_PADDING
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/media/media_get_buffer_storage_type.cc b/src/starboard/shared/starboard/media/media_get_buffer_storage_type.cc
index c96b296..3d8dfcd 100644
--- a/src/starboard/shared/starboard/media/media_get_buffer_storage_type.cc
+++ b/src/starboard/shared/starboard/media/media_get_buffer_storage_type.cc
@@ -14,22 +14,6 @@
 
 #include "starboard/media.h"
 
-#include "starboard/common/log.h"
-
-#if SB_API_VERSION >= 10
-
 SbMediaBufferStorageType SbMediaGetBufferStorageType() {
-// Use define forwarded from GYP variable.
-#if defined(COBALT_MEDIA_BUFFER_STORAGE_TYPE_MEMORY)
-  // This is the legacy default value of the GYP variable, so don't warn.
   return kSbMediaBufferStorageTypeMemory;
-#elif defined(COBALT_MEDIA_BUFFER_STORAGE_TYPE_FILE)
-  SB_DLOG(WARNING) << "COBALT_MEDIA_BUFFER_STORAGE_TYPE_FILE will be "
-                      "deprecated in a future Starboard version.";
-  return kSbMediaBufferStorageTypeFile;
-#else   // defined(COBALT_MEDIA_BUFFER_STORAGE_TYPE_MEMORY)
-  // In the absence of other information, assume memory storage is the default.
-  return kSbMediaBufferStorageTypeMemory;
-#endif  // defined(COBALT_MEDIA_BUFFER_STORAGE_TYPE_MEMORY)
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/media/media_get_initial_buffer_capacity.cc b/src/starboard/shared/starboard/media/media_get_initial_buffer_capacity.cc
index 1947456..2e0e86e 100644
--- a/src/starboard/shared/starboard/media/media_get_initial_buffer_capacity.cc
+++ b/src/starboard/shared/starboard/media/media_get_initial_buffer_capacity.cc
@@ -14,24 +14,6 @@
 
 #include "starboard/media.h"
 
-#include "starboard/common/log.h"
-
-#if SB_API_VERSION >= 10
-// This is the legacy default value of the GYP variable.
-#define LEGACY_INITIAL_CAPACITY 21 * 1024 * 1024
-
 int SbMediaGetInitialBufferCapacity() {
-#if defined(COBALT_MEDIA_BUFFER_INITIAL_CAPACITY) && \
-    COBALT_MEDIA_BUFFER_INITIAL_CAPACITY != LEGACY_INITIAL_CAPACITY
-#pragma message(                                                           \
-    "COBALT_MEDIA_BUFFER_INITIAL_CAPACITY will be deprecated in a future " \
-    "Starboard version.")
-  // Use define forwarded from GYP variable.
-  return COBALT_MEDIA_BUFFER_INITIAL_CAPACITY;
-#else   // defined(COBALT_MEDIA_BUFFER_INITIAL_CAPACITY) &&
-  // COBALT_MEDIA_BUFFER_INITIAL_CAPACITY != LEGACY_INITIAL_CAPACITY
   return 21 * 1024 * 1024;
-#endif  // defined(COBALT_MEDIA_BUFFER_INITIAL_CAPACITY) &&
-        // COBALT_MEDIA_BUFFER_INITIAL_CAPACITY != LEGACY_INITIAL_CAPACITY
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/media/media_get_max_buffer_capacity.cc b/src/starboard/shared/starboard/media/media_get_max_buffer_capacity.cc
index 1410b43..f501f92 100644
--- a/src/starboard/shared/starboard/media/media_get_max_buffer_capacity.cc
+++ b/src/starboard/shared/starboard/media/media_get_max_buffer_capacity.cc
@@ -14,13 +14,6 @@
 
 #include "starboard/media.h"
 
-#include "starboard/common/log.h"
-
-#if SB_API_VERSION >= 10
-// These are the legacy default values of the GYP variables.
-#define LEGACY_MAX_CAPACITY_1080P 50 * 1024 * 1024
-#define LEGACY_MAX_CAPACITY_4K 140 * 1024 * 1024
-
 int SbMediaGetMaxBufferCapacity(SbMediaVideoCodec codec,
                                 int resolution_width,
                                 int resolution_height,
@@ -32,17 +25,7 @@
     // The maximum amount of memory that will be used to store media buffers
     // when video resolution is 1080p. If 0, then memory can grow without bound.
     // This must be larger than sum of 1080p video budget and non-video budget.
-#if defined(COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P) && \
-    COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P != LEGACY_MAX_CAPACITY_1080P
-    SB_DLOG(WARNING) << "COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P will be "
-                        "deprecated in a future Starboard version.";
-    // Use define forwarded from GYP variable.
-    return COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P;
-#else   // defined(COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P) &&
-    // COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P != LEGACY_MAX_CAPACITY_1080P
     return 50 * 1024 * 1024;
-#endif  // defined(COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P) &&
-        // COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P != LEGACY_MAX_CAPACITY_1080P
   }
 
   if (resolution_width <= 3840 && resolution_height <= 2160) {
@@ -51,17 +34,7 @@
       // when video resolution is 4k and bit per pixel is lower than 8. If 0,
       // then memory can grow without bound. This must be larger than sum of 4k
       // video budget and non-video budget.
-#if defined(COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K) && \
-    COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K != LEGACY_MAX_CAPACITY_4K
-      SB_DLOG(WARNING) << "COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K will be "
-                          "deprecated in a future Starboard version.";
-      // Use define forwarded from GYP variable.
-      return COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K;
-#else   // defined(COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K) &&
-      // COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K != LEGACY_MAX_CAPACITY_4K
       return 140 * 1024 * 1024;
-#endif  // defined(COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K) &&
-        // COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K != LEGACY_MAX_CAPACITY_4K
     } else {
       // The maximum amount of memory that will be used to store media buffers
       // when video resolution is 4k and bit per pixel is greater than 8. If 0,
@@ -76,4 +49,3 @@
   // must be larger than sum of 8k video budget and non-video budget.
   return 360 * 1024 * 1024;
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/media/media_get_progressive_buffer_budget.cc b/src/starboard/shared/starboard/media/media_get_progressive_buffer_budget.cc
index 35b68b9..2c80ec7 100644
--- a/src/starboard/shared/starboard/media/media_get_progressive_buffer_budget.cc
+++ b/src/starboard/shared/starboard/media/media_get_progressive_buffer_budget.cc
@@ -14,12 +14,6 @@
 
 #include "starboard/media.h"
 
-#include "starboard/common/log.h"
-
-#if SB_API_VERSION >= 10
-// This is the legacy default value of the GYP variable.
-#define LEGACY_PROGRESSIVE_BUDGET 12 * 1024 * 1024
-
 int SbMediaGetProgressiveBufferBudget(SbMediaVideoCodec codec,
                                       int resolution_width,
                                       int resolution_height,
@@ -28,16 +22,5 @@
   SB_UNREFERENCED_PARAMETER(resolution_width);
   SB_UNREFERENCED_PARAMETER(resolution_height);
   SB_UNREFERENCED_PARAMETER(bits_per_pixel);
-#if defined(COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET) && \
-    COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET != LEGACY_PROGRESSIVE_BUDGET
-  SB_DLOG(WARNING) << "COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET will be "
-                      "deprecated in a future Starboard version.";
-  // Use define forwarded from GYP variable.
-  return COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET;
-#else   // defined(COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET) &&
-  // COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET != LEGACY_PROGRESSIVE_BUDGET
   return 12 * 1024 * 1024;
-#endif  // defined(COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET) &&
-        // COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET != LEGACY_PROGRESSIVE_BUDGET
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/media/media_get_video_buffer_budget.cc b/src/starboard/shared/starboard/media/media_get_video_buffer_budget.cc
index bb8352e..339f204 100644
--- a/src/starboard/shared/starboard/media/media_get_video_buffer_budget.cc
+++ b/src/starboard/shared/starboard/media/media_get_video_buffer_budget.cc
@@ -14,13 +14,6 @@
 
 #include "starboard/media.h"
 
-#include "starboard/common/log.h"
-
-#if SB_API_VERSION >= 10
-// These are the legacy default values of the GYP variables.
-#define LEGACY_VIDEO_BUDGET_1080P 30 * 1024 * 1024
-#define LEGACY_VIDEO_BUDGET_4K 100 * 1024 * 1024
-
 int SbMediaGetVideoBufferBudget(SbMediaVideoCodec codec,
                                 int resolution_width,
                                 int resolution_height,
@@ -32,17 +25,7 @@
     // Specifies the maximum amount of memory used by video buffers of media
     // source before triggering a garbage collection when the video resolution
     // is lower than 1080p (1920x1080).
-#if defined(COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P) && \
-    COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P != LEGACY_VIDEO_BUDGET_1080P
-    SB_DLOG(WARNING) << "COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P will be "
-                        "deprecated in a future Starboard version.";
-    // Use define forwarded from GYP variable.
-    return COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P;
-#else   // defined(COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P) &&
-    // COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P != LEGACY_VIDEO_BUDGET_1080P
     return 30 * 1024 * 1024;
-#endif  // defined(COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P) &&
-        // COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P != LEGACY_VIDEO_BUDGET_1080P
   }
 
   if (resolution_width <= 3840 && resolution_height <= 2160) {
@@ -50,17 +33,7 @@
       // Specifies the maximum amount of memory used by video buffers of media
       // source before triggering a garbage collection when the video resolution
       // is lower than 4k (3840x2160) and bit per pixel is lower than 8.
-#if defined(COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K) && \
-    COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K != LEGACY_VIDEO_BUDGET_4K
-      SB_DLOG(WARNING) << "COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K will be "
-                          "deprecated in a future Starboard version.";
-      // Use define forwarded from GYP variable.
-      return COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K;
-#else   // defined(COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K) &&
-      // COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K != LEGACY_VIDEO_BUDGET_4K
       return 100 * 1024 * 1024;
-#endif  // defined(COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K) &&
-        // COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K != LEGACY_VIDEO_BUDGET_4K
     } else {
       // Specifies the maximum amount of memory used by video buffers of media
       // source before triggering a garbage collection when video resolution is
@@ -74,4 +47,3 @@
   // lower than 8k (7680x4320).
   return 300 * 1024 * 1024;
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc b/src/starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc
index 23937a7..ff2a9d3 100644
--- a/src/starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc
+++ b/src/starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc
@@ -18,7 +18,15 @@
 #include "starboard/configuration_constants.h"
 #include "starboard/media.h"
 
-bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec, int64_t bitrate) {
+bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                             const char* content_type,
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                             int64_t bitrate) {
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+  SB_UNREFERENCED_PARAMETER(content_type);
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+
   if (audio_codec == kSbMediaAudioCodecAac) {
     return bitrate <= kSbMediaMaxAudioBitrateInBitsPerSecond;
   }
diff --git a/src/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc b/src/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc
index 843ba3a..7bdcec8 100644
--- a/src/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc
+++ b/src/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc
@@ -18,7 +18,15 @@
 #include "starboard/configuration_constants.h"
 #include "starboard/media.h"
 
-bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec, int64_t bitrate) {
+bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                             const char* content_type,
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                             int64_t bitrate) {
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+  SB_UNREFERENCED_PARAMETER(content_type);
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+
   return audio_codec == kSbMediaAudioCodecAac &&
          bitrate <= kSbMediaMaxAudioBitrateInBitsPerSecond;
 }
diff --git a/src/starboard/shared/starboard/media/media_is_buffer_pool_allocate_on_demand.cc b/src/starboard/shared/starboard/media/media_is_buffer_pool_allocate_on_demand.cc
index 1caf8a0..229b4b3 100644
--- a/src/starboard/shared/starboard/media/media_is_buffer_pool_allocate_on_demand.cc
+++ b/src/starboard/shared/starboard/media/media_is_buffer_pool_allocate_on_demand.cc
@@ -14,25 +14,6 @@
 
 #include "starboard/media.h"
 
-#include "starboard/common/log.h"
-
-#if SB_API_VERSION >= 10
-// This is the legacy default value of the GYP variable.
-#define LEGACY_ALLOCATE_ON_DEMAND 1
-
 bool SbMediaIsBufferPoolAllocateOnDemand() {
-#if defined(COBALT_MEDIA_BUFFER_POOL_ALLOCATE_ON_DEMAND) && \
-    COBALT_MEDIA_BUFFER_POOL_ALLOCATE_ON_DEMAND != LEGACY_ALLOCATE_ON_DEMAND
-#pragma message(                                                           \
-    "COBALT_MEDIA_BUFFER_POOL_ALLOCATE_ON_DEMAND will be deprecated in a " \
-    "future Starboard version.")
-  return static_cast<bool>(COBALT_MEDIA_BUFFER_POOL_ALLOCATE_ON_DEMAND);
-#else   // defined(COBALT_MEDIA_BUFFER_POOL_ALLOCATE_ON_DEMAND) &&
-  // COBALT_MEDIA_BUFFER_POOL_ALLOCATE_ON_DEMAND !=
-  // LEGACY_ALLOCATE_ON_DEMAND
   return true;
-#endif  // defined(COBALT_MEDIA_BUFFER_POOL_ALLOCATE_ON_DEMAND) &&
-        // COBALT_MEDIA_BUFFER_POOL_ALLOCATE_ON_DEMAND !=
-        // LEGACY_ALLOCATE_ON_DEMAND
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/media/media_is_buffer_using_memory_pool.cc b/src/starboard/shared/starboard/media/media_is_buffer_using_memory_pool.cc
index dc65a41..db84689 100644
--- a/src/starboard/shared/starboard/media/media_is_buffer_using_memory_pool.cc
+++ b/src/starboard/shared/starboard/media/media_is_buffer_using_memory_pool.cc
@@ -14,8 +14,6 @@
 
 #include "starboard/media.h"
 
-#if SB_API_VERSION >= 10
 bool SbMediaIsBufferUsingMemoryPool() {
   return true;
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/media/media_support_internal.h b/src/starboard/shared/starboard/media/media_support_internal.h
index 5e14541..b106f0a 100644
--- a/src/starboard/shared/starboard/media/media_support_internal.h
+++ b/src/starboard/shared/starboard/media/media_support_internal.h
@@ -15,6 +15,7 @@
 #ifndef STARBOARD_SHARED_STARBOARD_MEDIA_MEDIA_SUPPORT_INTERNAL_H_
 #define STARBOARD_SHARED_STARBOARD_MEDIA_MEDIA_SUPPORT_INTERNAL_H_
 
+#include "starboard/configuration.h"
 #include "starboard/media.h"
 #include "starboard/shared/internal_only.h"
 
@@ -28,6 +29,9 @@
 // function returns |false|.
 //
 // |video_codec|: The video codec used in the media content.
+// |content_type|: The full content type passed to the corresponding dom
+//                 interface if there is any.  Otherwise it will be set to "".
+//                 It should never to set to NULL.
 // |profile|: The profile in the context of |video_codec|.  It should be set to
 //            -1 when it is unknown or not applicable.
 // |level|: The level in the context of |video_codec|.  It should be set to -1
@@ -54,6 +58,24 @@
 //        it indicates that the fps shouldn't be considered.
 // |decode_to_texture_required|: Whether or not the resulting video frames can
 //                               be decoded and used as textures by the GPU.
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+
+bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
+                             const char* content_type,
+                             int profile,
+                             int level,
+                             int bit_depth,
+                             SbMediaPrimaryId primary_id,
+                             SbMediaTransferId transfer_id,
+                             SbMediaMatrixId matrix_id,
+                             int frame_width,
+                             int frame_height,
+                             int64_t bitrate,
+                             int fps,
+                             bool decode_to_texture_required);
+
+#else  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+
 bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
 #if SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
                              int profile,
@@ -66,21 +88,32 @@
                              int frame_width,
                              int frame_height,
                              int64_t bitrate,
-                             int fps
-#if SB_API_VERSION >= 10
-                             ,
-                             bool decode_to_texture_required
-#endif  // SB_API_VERSION >= 10
-                             );
+                             int fps,
+                             bool decode_to_texture_required);
+
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
 
 // Indicates whether this platform supports |audio_codec| at |bitrate|.
 // If |audio_codec| is not supported under any condition, this function
 // returns |false|.
 //
 // |audio_codec|: The media's audio codec (|SbMediaAudioCodec|).
+// |content_type|: The full content type passed to the corresponding dom
+//                 interface if there is any.  Otherwise it will be set to "".
+//                 It should never to set to NULL.
 // |bitrate|: The media's bitrate.
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+
+bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
+                             const char* content_type,
+                             int64_t bitrate);
+
+#else  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+
 bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec, int64_t bitrate);
 
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+
 #if !SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
 // Indicates whether this platform supports |transfer_id| as a transfer
 // characteristics.  If |transfer_id| is not supported under any condition, this
diff --git a/src/starboard/shared/starboard/media/media_tests.gypi b/src/starboard/shared/starboard/media/media_tests.gypi
index 1d0583d..a07a89b 100644
--- a/src/starboard/shared/starboard/media/media_tests.gypi
+++ b/src/starboard/shared/starboard/media/media_tests.gypi
@@ -17,6 +17,7 @@
     # This will be included by 'starboard_platform_tests.gyp' so full path names
     # have to be used here.
     'media_tests_sources': [
+      '<(DEPTH)/starboard/shared/starboard/media/avc_util_test.cc',
       '<(DEPTH)/starboard/shared/starboard/media/codec_util_test.cc',
       '<(DEPTH)/starboard/shared/starboard/media/mime_type_test.cc',
     ],
diff --git a/src/starboard/shared/starboard/media/media_util.cc b/src/starboard/shared/starboard/media/media_util.cc
index 0efe732..d309add 100644
--- a/src/starboard/shared/starboard/media/media_util.cc
+++ b/src/starboard/shared/starboard/media/media_util.cc
@@ -60,7 +60,11 @@
 
   int bitrate = mime_type.GetParamIntValue("bitrate", kDefaultBitRate);
 
-  if (!SbMediaIsAudioSupported(audio_codec, bitrate)) {
+  if (!SbMediaIsAudioSupported(audio_codec,
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                               mime_type.raw_content_type().c_str(),
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                               bitrate)) {
     return false;
   }
 
@@ -100,10 +104,6 @@
                            const std::string& codec,
                            const char* key_system,
                            bool decode_to_texture_required) {
-#if SB_API_VERSION < 10
-  SB_UNREFERENCED_PARAMETER(decode_to_texture_required);
-#endif  // SB_API_VERSION < 10
-
   SbMediaVideoCodec video_codec;
   int profile = -1;
   int level = -1;
@@ -161,23 +161,18 @@
   int bitrate = mime_type.GetParamIntValue("bitrate", kDefaultBitRate);
 
 #if SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
-  if (!SbMediaIsVideoSupported(video_codec, profile, level, bit_depth,
-                               primary_id, transfer_id, matrix_id, width,
-                               height, bitrate, fps
-#if SB_API_VERSION >= 10
-                               ,
-                               decode_to_texture_required
-#endif  // SB_API_VERSION >= 10
-                               )) {
+  if (!SbMediaIsVideoSupported(video_codec,
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                               mime_type.raw_content_type().c_str(),
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                               profile, level, bit_depth, primary_id,
+                               transfer_id, matrix_id, width, height, bitrate,
+                               fps, decode_to_texture_required)) {
     return false;
   }
 #else  //  SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
-  if (!SbMediaIsVideoSupported(video_codec, width, height, bitrate, fps
-#if SB_API_VERSION >= 10
-                               ,
-                               decode_to_texture_required
-#endif  // SB_API_VERSION >= 10
-                               )) {
+  if (!SbMediaIsVideoSupported(video_codec, width, height, bitrate, fps,
+                               decode_to_texture_required)) {
     return false;
   }
 #endif  // SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
@@ -367,7 +362,6 @@
   }
 
   bool decode_to_texture_required = false;
-#if SB_API_VERSION >= 10
   std::string decode_to_texture_value =
       mime_type.GetParamStringValue("decode-to-texture", "false");
   if (decode_to_texture_value == "true") {
@@ -377,7 +371,6 @@
     // decode-to-texture, trivially reject.
     return kSbMediaSupportTypeNotSupported;
   }
-#endif  // SB_API_VERSION >= 10
 
   if (codecs.size() == 0) {
     // This is a progressive query.  We only support "video/mp4" in this case.
diff --git a/src/starboard/shared/starboard/media/mime_type.cc b/src/starboard/shared/starboard/media/mime_type.cc
index d1ce94e..f84439a 100644
--- a/src/starboard/shared/starboard/media/mime_type.cc
+++ b/src/starboard/shared/starboard/media/mime_type.cc
@@ -85,7 +85,8 @@
 
 const int MimeType::kInvalidParamIndex = -1;
 
-MimeType::MimeType(const std::string& content_type) : is_valid_(false) {
+MimeType::MimeType(const std::string& content_type)
+    : raw_content_type_(content_type), is_valid_(false) {
   Strings components = SplitAndTrim(content_type, ';');
 
   if (components.empty()) {
diff --git a/src/starboard/shared/starboard/media/mime_type.h b/src/starboard/shared/starboard/media/mime_type.h
index 9f69b0f..7c3d619 100644
--- a/src/starboard/shared/starboard/media/mime_type.h
+++ b/src/starboard/shared/starboard/media/mime_type.h
@@ -56,6 +56,7 @@
 
   explicit MimeType(const std::string& content_type);
 
+  const std::string& raw_content_type() const { return raw_content_type_; }
   bool is_valid() const { return is_valid_; }
 
   const std::string& type() const { return type_; }
@@ -90,6 +91,7 @@
 
   int GetParamIndexByName(const char* name) const;
 
+  const std::string raw_content_type_;
   bool is_valid_;
   std::string type_;
   std::string subtype_;
diff --git a/src/starboard/shared/starboard/media/mime_type_test.cc b/src/starboard/shared/starboard/media/mime_type_test.cc
index d051e39..7b29757 100644
--- a/src/starboard/shared/starboard/media/mime_type_test.cc
+++ b/src/starboard/shared/starboard/media/mime_type_test.cc
@@ -22,6 +22,19 @@
 namespace media {
 namespace {
 
+TEST(MimeTypeTest, RawContentType) {
+  {
+    const char kContentTypeWithSpace[] = " video/mp4; name0=123; name1=123.4 ";
+    MimeType mime_type(kContentTypeWithSpace);
+    EXPECT_EQ(mime_type.raw_content_type(), kContentTypeWithSpace);
+  }
+  {
+    const char kInvalidContentType[] = "video /mp4";
+    MimeType mime_type(kInvalidContentType);
+    EXPECT_EQ(mime_type.raw_content_type(), kInvalidContentType);
+  }
+}
+
 TEST(MimeTypeTest, EmptyString) {
   MimeType mime_type("");
   EXPECT_FALSE(mime_type.is_valid());
diff --git a/src/starboard/shared/starboard/player/decoded_audio_internal.cc b/src/starboard/shared/starboard/player/decoded_audio_internal.cc
index 1e5ac07..1e21a40 100644
--- a/src/starboard/shared/starboard/player/decoded_audio_internal.cc
+++ b/src/starboard/shared/starboard/player/decoded_audio_internal.cc
@@ -27,6 +27,8 @@
 
 namespace {
 
+using ::starboard::shared::starboard::media::GetBytesPerSample;
+
 void ConvertSample(const int16_t* source, float* destination) {
   *destination = static_cast<float>(*source) / 32768.f;
 }
@@ -59,13 +61,7 @@
       size_(size) {}
 
 int DecodedAudio::frames() const {
-  int bytes_per_sample;
-  if (sample_type_ == kSbMediaAudioSampleTypeInt16Deprecated) {
-    bytes_per_sample = 2;
-  } else {
-    SB_DCHECK(sample_type_ == kSbMediaAudioSampleTypeFloat32);
-    bytes_per_sample = 4;
-  }
+  int bytes_per_sample = GetBytesPerSample(sample_type_);
   SB_DCHECK(size_ % (bytes_per_sample * channels_) == 0);
   return static_cast<int>(size_ / bytes_per_sample / channels_);
 }
@@ -75,6 +71,44 @@
   size_ = new_size;
 }
 
+void DecodedAudio::AdjustForSeekTime(int samples_per_second,
+                                     SbTime seeking_to_time) {
+  SB_DCHECK(!is_end_of_stream());
+  SB_DCHECK(samples_per_second != 0);
+
+  int frames_to_remove =
+      (seeking_to_time - timestamp()) * samples_per_second / kSbTimeSecond;
+
+  if (samples_per_second == 0 || frames_to_remove < 0 ||
+      frames_to_remove >= frames()) {
+    SB_LOG(WARNING) << "AdjustForSeekTime failed for seeking_to_time at "
+                    << seeking_to_time << " for samples_per_second "
+                    << samples_per_second << ", and there are " << frames()
+                    << " frames in the DecodedAudio object.";
+    return;
+  }
+
+  auto bytes_per_sample = GetBytesPerSample(sample_type_);
+  auto bytes_per_frame = bytes_per_sample * channels();
+
+  if (storage_type_ == kSbMediaAudioFrameStorageTypeInterleaved) {
+    SbMemoryMove(buffer(), buffer() + bytes_per_frame * frames_to_remove,
+                 (frames() - frames_to_remove) * bytes_per_frame);
+  } else {
+    SB_DCHECK(storage_type_ == kSbMediaAudioFrameStorageTypePlanar);
+    const uint8_t* source_addr = buffer();
+    uint8_t* dest_addr = buffer();
+    for (int channel = 0; channel < channels(); ++channel) {
+      SbMemoryMove(dest_addr, source_addr + bytes_per_sample * frames_to_remove,
+                   (frames() - frames_to_remove) * bytes_per_sample);
+      source_addr += frames() * bytes_per_sample;
+      dest_addr += (frames() - frames_to_remove) * bytes_per_sample;
+    }
+  }
+  size_ = (frames() - frames_to_remove) * bytes_per_frame;
+  timestamp_ += frames_to_remove * kSbTimeSecond / samples_per_second;
+}
+
 void DecodedAudio::SwitchFormatTo(
     SbMediaAudioSampleType new_sample_type,
     SbMediaAudioFrameStorageType new_storage_type) {
diff --git a/src/starboard/shared/starboard/player/decoded_audio_internal.h b/src/starboard/shared/starboard/player/decoded_audio_internal.h
index 4afbc88..0ac7681 100644
--- a/src/starboard/shared/starboard/player/decoded_audio_internal.h
+++ b/src/starboard/shared/starboard/player/decoded_audio_internal.h
@@ -54,6 +54,10 @@
   void SwitchFormatTo(SbMediaAudioSampleType new_sample_type,
                       SbMediaAudioFrameStorageType new_storage_type);
   void ShrinkTo(size_t new_size);
+  // During seeking, the target time can be in the middle of the DecodedAudio
+  // object.  This function will adjust the object to the seek target time by
+  // removing the frames in the beginning that are before the seek target time.
+  void AdjustForSeekTime(int samples_per_second, SbTime seeking_to_time);
 
  private:
   void SwitchSampleTypeTo(SbMediaAudioSampleType new_sample_type);
diff --git a/src/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc b/src/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc
index 76973fd..ae3b448 100644
--- a/src/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc
@@ -188,11 +188,7 @@
       audio_decoder_creator_(input_audio_sample_info_, drm_system_);
 
   if (!audio_decoder_) {
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     error_cb_(kSbPlayerErrorDecode, "Decoder adapter cannot create decoder.");
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-    error_cb_();
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
     return;
   }
   audio_decoder_->Initialize(
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
index 74365bf..4a518cc 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Cobalt Authors. All Rights Reserved.
+// Copyright 2020 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.
@@ -15,31 +15,8 @@
 #ifndef STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_RENDERER_INTERNAL_H_
 #define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_RENDERER_INTERNAL_H_
 
-#include <functional>
-#include <vector>
-
-#include "starboard/atomic.h"
-#include "starboard/common/log.h"
-#include "starboard/common/mutex.h"
-#include "starboard/common/optional.h"
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/media.h"
-#include "starboard/shared/internal_only.h"
-#include "starboard/shared/starboard/player/decoded_audio_internal.h"
-#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
-#include "starboard/shared/starboard/player/filter/audio_frame_tracker.h"
-#include "starboard/shared/starboard/player/filter/audio_renderer_sink.h"
-#include "starboard/shared/starboard/player/filter/audio_resampler.h"
-#include "starboard/shared/starboard/player/filter/audio_time_stretcher.h"
-#include "starboard/shared/starboard/player/filter/media_time_provider.h"
+#include "starboard/shared/starboard/player/filter/common.h"
 #include "starboard/shared/starboard/player/input_buffer_internal.h"
-#include "starboard/shared/starboard/player/job_queue.h"
-#include "starboard/time.h"
-#include "starboard/types.h"
-
-// Uncomment the following statement to log the media time stats with deviation
-// when GetCurrentMediaTime() is called.
-// #define SB_LOG_MEDIA_TIME_STATS 1
 
 namespace starboard {
 namespace shared {
@@ -47,169 +24,22 @@
 namespace player {
 namespace filter {
 
-const int kFramesInBufferBeginUnderflow = 1024;
-
-// A class that sits in between the audio decoder, the audio sink and the
-// pipeline to coordinate data transfer between these parties.  It also serves
-// as the authority of playback time.
-class AudioRenderer : public MediaTimeProvider,
-                      private AudioRendererSink::RenderCallback,
-                      private JobQueue::JobOwner {
+class AudioRenderer {
  public:
-  // |max_cached_frames| is a soft limit for the max audio frames this class can
-  // cache so it can:
-  // 1. Avoid using too much memory.
-  // 2. Have the audio cache full to simulate the state that the renderer can no
-  //    longer accept more data.
-  // |min_frames_per_append| is the min number of frames that the audio renderer
-  // tries to append to the sink buffer at once.
-  AudioRenderer(scoped_ptr<AudioDecoder> decoder,
-                scoped_ptr<AudioRendererSink> audio_renderer_sink,
-                const SbMediaAudioSampleInfo& audio_sample_info,
-                int max_cached_frames,
-                int min_frames_per_append);
-  ~AudioRenderer();
+  virtual ~AudioRenderer() {}
 
-  void Initialize(const ErrorCB& error_cb,
-                  const PrerolledCB& prerolled_cb,
-                  const EndedCB& ended_cb);
-  void WriteSample(const scoped_refptr<InputBuffer>& input_buffer);
-  void WriteEndOfStream();
+  virtual void Initialize(const ErrorCB& error_cb,
+                          const PrerolledCB& prerolled_cb,
+                          const EndedCB& ended_cb) = 0;
+  virtual void WriteSample(const scoped_refptr<InputBuffer>& input_buffer) = 0;
+  virtual void WriteEndOfStream() = 0;
 
-  void SetVolume(double volume);
+  virtual void SetVolume(double volume) = 0;
 
   // TODO: Remove the eos state querying functions and their tests.
-  bool IsEndOfStreamWritten() const;
-  bool IsEndOfStreamPlayed() const;
-  bool CanAcceptMoreData() const;
-
-  // MediaTimeProvider methods
-  void Play() override;
-  void Pause() override;
-  void SetPlaybackRate(double playback_rate) override;
-  void Seek(SbTime seek_to_time) override;
-  SbTime GetCurrentMediaTime(bool* is_playing,
-                             bool* is_eos_played,
-                             bool* is_underflow) override;
-
- private:
-  enum EOSState {
-    kEOSNotReceived,
-    kEOSWrittenToDecoder,
-    kEOSDecoded,
-    kEOSSentToSink
-  };
-
-  const int max_cached_frames_;
-  const int min_frames_per_append_;
-  // |buffered_frames_to_start_| would be initialized in OnFirstOutput().
-  // Before it's initialized, set it to a large number.
-  int buffered_frames_to_start_ = 48 * 1024;
-
-  ErrorCB error_cb_;
-  PrerolledCB prerolled_cb_;
-  EndedCB ended_cb_;
-
-  Mutex mutex_;
-
-  bool paused_ = true;
-  bool consume_frames_called_ = false;
-  bool seeking_ = false;
-  SbTime seeking_to_time_ = 0;
-  SbTime last_media_time_ = 0;
-  AudioFrameTracker audio_frame_tracker_;
-  bool ended_cb_called_ = false;
-
-  int64_t total_frames_sent_to_sink_ = 0;
-  int64_t total_frames_consumed_by_sink_ = 0;
-  int32_t frames_consumed_by_sink_since_last_get_current_time_;
-
-  scoped_ptr<AudioDecoder> decoder_;
-
-  int64_t frames_consumed_set_at_;
-  double playback_rate_ = 1.0;
-
-  // AudioRendererSink methods
-  void GetSourceStatus(int* frames_in_buffer,
-                       int* offset_in_frames,
-                       bool* is_playing,
-                       bool* is_eos_reached) override;
-#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
-    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-  void ConsumeFrames(int frames_consumed, SbTime frames_consumed_at) override;
-#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
-        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-  void ConsumeFrames(int frames_consumed) override;
-#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
-        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-  void OnError(bool capability_changed) override;
-
-  void UpdateVariablesOnSinkThread_Locked(SbTime system_time_on_consume_frames);
-
-  void OnFirstOutput(const SbMediaAudioSampleType decoded_sample_type,
-                     const SbMediaAudioFrameStorageType decoded_storage_type,
-                     const int decoded_sample_rate);
-  bool IsEndOfStreamPlayed_Locked() const;
-
-  void OnDecoderConsumed();
-  void OnDecoderOutput();
-  void ProcessAudioData();
-  void FillResamplerAndTimeStretcher();
-  bool AppendAudioToFrameBuffer(bool* is_frame_buffer_full);
-
-  EOSState eos_state_ = kEOSNotReceived;
-  const int channels_;
-  const SbMediaAudioSampleType sink_sample_type_;
-  const int bytes_per_frame_;
-
-  scoped_ptr<AudioResampler> resampler_;
-  optional<int> decoder_sample_rate_;
-  AudioTimeStretcher time_stretcher_;
-
-  std::vector<uint8_t> frame_buffer_;
-  uint8_t* frame_buffers_[1];
-
-  int32_t pending_decoder_outputs_ = 0;
-
-  bool can_accept_more_data_ = true;
-  JobQueue::JobToken process_audio_data_job_token_;
-  std::function<void()> process_audio_data_job_;
-
-  // Our owner will attempt to seek to time 0 when playback begins.  In
-  // general, seeking could require a full reset of the underlying decoder on
-  // some platforms, so we make an effort to improve playback startup
-  // performance by keeping track of whether we already have a fresh decoder,
-  // and can thus avoid doing a full reset.
-  bool first_input_written_ = false;
-
-  scoped_ptr<AudioRendererSink> audio_renderer_sink_;
-  bool is_eos_reached_on_sink_thread_ = false;
-  bool is_playing_on_sink_thread_ = false;
-  int64_t frames_in_buffer_on_sink_thread_ = 0;
-  int64_t offset_in_frames_on_sink_thread_ = 0;
-  int64_t frames_consumed_on_sink_thread_ = 0;
-  SbTime frames_consumed_set_at_on_sink_thread_ = 0;
-  int64_t silence_frames_written_after_eos_on_sink_thread_ = 0;
-  int64_t silence_frames_consumed_on_sink_thread_ = 0;
-
-#if SB_LOG_MEDIA_TIME_STATS
-  SbTime system_and_media_time_offset_ = -1;
-  SbTime min_drift_ = kSbTimeMax;
-  SbTime max_drift_ = 0;
-  int64_t total_frames_consumed_ = 0;
-#endif  // SB_LOG_MEDIA_TIME_STATS
-
-  // Set to true when there are fewer than |kFramesInBufferBeginUnderflow|
-  // frames in buffer. Set to false when the queue is full or EOS.
-  bool underflow_ = false;
-
-#if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
-  static const int32_t kMaxSinkCallbacksBetweenCheck = 1024;
-  static const SbTime kCheckAudioSinkStatusInterval = kSbTimeSecond;
-  void CheckAudioSinkStatus();
-
-  atomic_int32_t sink_callbacks_since_last_check_;
-#endif  // SB_PLAYER_FILTER_ENABLE_STATE_CHECK
+  virtual bool IsEndOfStreamWritten() const = 0;
+  virtual bool IsEndOfStreamPlayed() const = 0;
+  virtual bool CanAcceptMoreData() const = 0;
 };
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_internal_impl.cc
similarity index 88%
rename from src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
rename to src/starboard/shared/starboard/player/filter/audio_renderer_internal_impl.cc
index dc013f1..08f306e 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal_impl.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/shared/starboard/player/filter/audio_renderer_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_renderer_internal_impl.h"
 
 #include <algorithm>
 
@@ -28,7 +28,7 @@
 namespace {
 
 // This class works only when the input format and output format are the same.
-// It allows for a simplified AudioRenderer implementation by always using a
+// It allows for a simplified AudioRendererImpl implementation by always using a
 // resampler.
 class IdentityAudioResampler : public AudioResampler {
  public:
@@ -49,8 +49,8 @@
   bool eos_reached_;
 };
 
-// AudioRenderer uses AudioTimeStretcher internally to adjust to playback rate.
-// So we try to use kSbMediaAudioSampleTypeFloat32 and only use
+// AudioRendererImpl uses AudioTimeStretcher internally to adjust to playback
+// rate. So we try to use kSbMediaAudioSampleTypeFloat32 and only use
 // kSbMediaAudioSampleTypeInt16Deprecated when float32 is not supported.  To use
 // kSbMediaAudioSampleTypeFloat32 will cause an extra conversion from float32 to
 // int16 before the samples are sent to the audio sink.
@@ -64,11 +64,12 @@
 
 }  // namespace
 
-AudioRenderer::AudioRenderer(scoped_ptr<AudioDecoder> decoder,
-                             scoped_ptr<AudioRendererSink> audio_renderer_sink,
-                             const SbMediaAudioSampleInfo& audio_sample_info,
-                             int max_cached_frames,
-                             int min_frames_per_append)
+AudioRendererImpl::AudioRendererImpl(
+    scoped_ptr<AudioDecoder> decoder,
+    scoped_ptr<AudioRendererSink> audio_renderer_sink,
+    const SbMediaAudioSampleInfo& audio_sample_info,
+    int max_cached_frames,
+    int min_frames_per_append)
     : max_cached_frames_(max_cached_frames),
       min_frames_per_append_(min_frames_per_append),
       channels_(audio_sample_info.number_of_channels),
@@ -78,10 +79,10 @@
       frames_consumed_set_at_(SbTimeGetMonotonicNow()),
       decoder_(decoder.Pass()),
       process_audio_data_job_(
-          std::bind(&AudioRenderer::ProcessAudioData, this)),
+          std::bind(&AudioRendererImpl::ProcessAudioData, this)),
       audio_renderer_sink_(audio_renderer_sink.Pass()) {
-  SB_DLOG(INFO) << "Creating AudioRenderer with " << channels_ << " channels, "
-                << bytes_per_frame_ << " bytes per frame, "
+  SB_DLOG(INFO) << "Creating AudioRendererImpl with " << channels_
+                << " channels, " << bytes_per_frame_ << " bytes per frame, "
                 << max_cached_frames_ << " max cached frames, and "
                 << min_frames_per_append_ << " min frames per append.";
   SB_DCHECK(decoder_ != NULL);
@@ -91,22 +92,22 @@
   frame_buffers_[0] = &frame_buffer_[0];
 
 #if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
-  Schedule(std::bind(&AudioRenderer::CheckAudioSinkStatus, this),
+  Schedule(std::bind(&AudioRendererImpl::CheckAudioSinkStatus, this),
            kCheckAudioSinkStatusInterval);
 #endif  // SB_PLAYER_FILTER_ENABLE_STATE_CHECK
 }
 
-AudioRenderer::~AudioRenderer() {
-  SB_DLOG(INFO) << "Destroying AudioRenderer with " << channels_
+AudioRendererImpl::~AudioRendererImpl() {
+  SB_DLOG(INFO) << "Destroying AudioRendererImpl with " << channels_
                 << " channels, " << bytes_per_frame_ << " bytes per frame, "
                 << max_cached_frames_ << " max cached frames, and "
                 << min_frames_per_append_ << " min frames per append.";
   SB_DCHECK(BelongsToCurrentThread());
 }
 
-void AudioRenderer::Initialize(const ErrorCB& error_cb,
-                               const PrerolledCB& prerolled_cb,
-                               const EndedCB& ended_cb) {
+void AudioRendererImpl::Initialize(const ErrorCB& error_cb,
+                                   const PrerolledCB& prerolled_cb,
+                                   const EndedCB& ended_cb) {
   SB_DCHECK(error_cb);
   SB_DCHECK(prerolled_cb);
   SB_DCHECK(ended_cb);
@@ -118,11 +119,11 @@
   prerolled_cb_ = prerolled_cb;
   ended_cb_ = ended_cb;
 
-  decoder_->Initialize(std::bind(&AudioRenderer::OnDecoderOutput, this),
+  decoder_->Initialize(std::bind(&AudioRendererImpl::OnDecoderOutput, this),
                        error_cb);
 }
 
-void AudioRenderer::WriteSample(
+void AudioRendererImpl::WriteSample(
     const scoped_refptr<InputBuffer>& input_buffer) {
   SB_DCHECK(BelongsToCurrentThread());
   SB_DCHECK(input_buffer);
@@ -137,11 +138,11 @@
   can_accept_more_data_ = false;
 
   decoder_->Decode(input_buffer,
-                   std::bind(&AudioRenderer::OnDecoderConsumed, this));
+                   std::bind(&AudioRendererImpl::OnDecoderConsumed, this));
   first_input_written_ = true;
 }
 
-void AudioRenderer::WriteEndOfStream() {
+void AudioRendererImpl::WriteEndOfStream() {
   SB_DCHECK(BelongsToCurrentThread());
   // TODO: Check |can_accept_more_data_| and make WriteEndOfStream() depend on
   // CanAcceptMoreData() or callback.
@@ -160,28 +161,28 @@
   first_input_written_ = true;
 }
 
-void AudioRenderer::SetVolume(double volume) {
+void AudioRendererImpl::SetVolume(double volume) {
   SB_DCHECK(BelongsToCurrentThread());
   audio_renderer_sink_->SetVolume(volume);
 }
 
-bool AudioRenderer::IsEndOfStreamWritten() const {
+bool AudioRendererImpl::IsEndOfStreamWritten() const {
   SB_DCHECK(BelongsToCurrentThread());
   return eos_state_ >= kEOSWrittenToDecoder;
 }
 
-bool AudioRenderer::IsEndOfStreamPlayed() const {
+bool AudioRendererImpl::IsEndOfStreamPlayed() const {
   ScopedLock lock(mutex_);
   return IsEndOfStreamPlayed_Locked();
 }
 
-bool AudioRenderer::CanAcceptMoreData() const {
+bool AudioRendererImpl::CanAcceptMoreData() const {
   SB_DCHECK(BelongsToCurrentThread());
   return eos_state_ == kEOSNotReceived && can_accept_more_data_ &&
          (!decoder_sample_rate_ || !time_stretcher_.IsQueueFull());
 }
 
-void AudioRenderer::Play() {
+void AudioRendererImpl::Play() {
   SB_DCHECK(BelongsToCurrentThread());
 
   ScopedLock lock(mutex_);
@@ -189,14 +190,14 @@
   consume_frames_called_ = false;
 }
 
-void AudioRenderer::Pause() {
+void AudioRendererImpl::Pause() {
   SB_DCHECK(BelongsToCurrentThread());
 
   ScopedLock lock(mutex_);
   paused_ = true;
 }
 
-void AudioRenderer::SetPlaybackRate(double playback_rate) {
+void AudioRendererImpl::SetPlaybackRate(double playback_rate) {
   SB_DCHECK(BelongsToCurrentThread());
 
   ScopedLock lock(mutex_);
@@ -221,7 +222,7 @@
   }
 }
 
-void AudioRenderer::Seek(SbTime seek_to_time) {
+void AudioRendererImpl::Seek(SbTime seek_to_time) {
   SB_DCHECK(BelongsToCurrentThread());
   SB_DCHECK(seek_to_time >= 0);
 
@@ -271,14 +272,14 @@
   CancelPendingJobs();
 
 #if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
-  Schedule(std::bind(&AudioRenderer::CheckAudioSinkStatus, this),
+  Schedule(std::bind(&AudioRendererImpl::CheckAudioSinkStatus, this),
            kCheckAudioSinkStatusInterval);
 #endif  // SB_PLAYER_FILTER_ENABLE_STATE_CHECK
 }
 
-SbTime AudioRenderer::GetCurrentMediaTime(bool* is_playing,
-                                          bool* is_eos_played,
-                                          bool* is_underflow) {
+SbTime AudioRendererImpl::GetCurrentMediaTime(bool* is_playing,
+                                              bool* is_eos_played,
+                                              bool* is_underflow) {
   SB_DCHECK(is_playing);
   SB_DCHECK(is_eos_played);
   SB_DCHECK(is_underflow);
@@ -360,10 +361,10 @@
   return media_time;
 }
 
-void AudioRenderer::GetSourceStatus(int* frames_in_buffer,
-                                    int* offset_in_frames,
-                                    bool* is_playing,
-                                    bool* is_eos_reached) {
+void AudioRendererImpl::GetSourceStatus(int* frames_in_buffer,
+                                        int* offset_in_frames,
+                                        bool* is_playing,
+                                        bool* is_eos_reached) {
 #if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
   sink_callbacks_since_last_check_.increment();
 #endif  // SB_PLAYER_FILTER_ENABLE_STATE_CHECK
@@ -404,14 +405,14 @@
   }
 }
 
-void AudioRenderer::ConsumeFrames(int frames_consumed
+void AudioRendererImpl::ConsumeFrames(int frames_consumed
 #if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
     SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-                                  ,
-                                  SbTime frames_consumed_at
+                                      ,
+                                      SbTime frames_consumed_at
 #endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
         // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-                                  ) {
+) {
 #if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
   sink_callbacks_since_last_check_.increment();
 #endif  // SB_PLAYER_FILTER_ENABLE_STATE_CHECK
@@ -447,7 +448,7 @@
   }
 }
 
-void AudioRenderer::OnError(bool capability_changed) {
+void AudioRendererImpl::OnError(bool capability_changed) {
   SB_DCHECK(error_cb_);
   if (capability_changed) {
     error_cb_(kSbPlayerErrorCapabilityChanged, "failed to start audio sink");
@@ -459,7 +460,7 @@
   }
 }
 
-void AudioRenderer::UpdateVariablesOnSinkThread_Locked(
+void AudioRendererImpl::UpdateVariablesOnSinkThread_Locked(
     SbTime system_time_on_consume_frames) {
   mutex_.DCheckAcquired();
 
@@ -505,7 +506,7 @@
   }
 }
 
-void AudioRenderer::OnFirstOutput(
+void AudioRendererImpl::OnFirstOutput(
     const SbMediaAudioSampleType decoded_sample_type,
     const SbMediaAudioFrameStorageType decoded_storage_type,
     const int decoded_sample_rate) {
@@ -540,22 +541,18 @@
       reinterpret_cast<SbAudioSinkFrameBuffers>(frame_buffers_),
       max_cached_frames_, this);
   if (!audio_renderer_sink_->HasStarted()) {
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     SB_LOG(ERROR) << "Failed to start audio sink.";
     error_cb_(kSbPlayerErrorDecode, "failed to start audio sink");
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-    error_cb_();
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
   }
 }
 
-bool AudioRenderer::IsEndOfStreamPlayed_Locked() const {
+bool AudioRendererImpl::IsEndOfStreamPlayed_Locked() const {
   mutex_.DCheckAcquired();
   return eos_state_ >= kEOSSentToSink &&
          total_frames_sent_to_sink_ == total_frames_consumed_by_sink_;
 }
 
-void AudioRenderer::OnDecoderConsumed() {
+void AudioRendererImpl::OnDecoderConsumed() {
   SB_DCHECK(BelongsToCurrentThread());
 
   // TODO: Unify EOS and non EOS request once WriteEndOfStream() depends on
@@ -567,7 +564,7 @@
   }
 }
 
-void AudioRenderer::OnDecoderOutput() {
+void AudioRendererImpl::OnDecoderOutput() {
   SB_DCHECK(BelongsToCurrentThread());
 
   ++pending_decoder_outputs_;
@@ -580,7 +577,7 @@
   ProcessAudioData();
 }
 
-void AudioRenderer::ProcessAudioData() {
+void AudioRendererImpl::ProcessAudioData() {
   SB_DCHECK(BelongsToCurrentThread());
 
   process_audio_data_job_token_.ResetToInvalid();
@@ -609,6 +606,10 @@
         decoder_->Read(&decoded_audio_sample_rate);
     SB_DCHECK(decoded_audio);
     if (!audio_renderer_sink_->HasStarted()) {
+      if (!decoded_audio->is_end_of_stream()) {
+        decoded_audio->AdjustForSeekTime(decoded_audio_sample_rate,
+                                         seeking_to_time_);
+      }
       OnFirstOutput(decoded_audio->sample_type(), decoded_audio->storage_type(),
                     decoded_audio_sample_rate);
     }
@@ -674,7 +675,7 @@
   }
 }
 
-bool AudioRenderer::AppendAudioToFrameBuffer(bool* is_frame_buffer_full) {
+bool AudioRendererImpl::AppendAudioToFrameBuffer(bool* is_frame_buffer_full) {
   SB_DCHECK(BelongsToCurrentThread());
   SB_DCHECK(is_frame_buffer_full);
 
@@ -742,7 +743,7 @@
 }
 
 #if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
-void AudioRenderer::CheckAudioSinkStatus() {
+void AudioRendererImpl::CheckAudioSinkStatus() {
   SB_DCHECK(BelongsToCurrentThread());
 
   // Check if sink callbacks are called too frequently.
@@ -771,7 +772,7 @@
                      << sink_callbacks_since_last_check
                      << " callbacks since last check.";
   }
-  Schedule(std::bind(&AudioRenderer::CheckAudioSinkStatus, this),
+  Schedule(std::bind(&AudioRendererImpl::CheckAudioSinkStatus, this),
            kCheckAudioSinkStatusInterval);
 }
 #endif  // SB_PLAYER_FILTER_ENABLE_STATE_CHECK
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal_impl.h b/src/starboard/shared/starboard/player/filter/audio_renderer_internal_impl.h
new file mode 100644
index 0000000..e984ec2
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal_impl.h
@@ -0,0 +1,223 @@
+// Copyright 2016 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 STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_RENDERER_INTERNAL_IMPL_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_RENDERER_INTERNAL_IMPL_H_
+
+#include <functional>
+#include <vector>
+
+#include "starboard/atomic.h"
+#include "starboard/common/log.h"
+#include "starboard/common/mutex.h"
+#include "starboard/common/optional.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_frame_tracker.h"
+#include "starboard/shared/starboard/player/filter/audio_renderer_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_renderer_sink.h"
+#include "starboard/shared/starboard/player/filter/audio_resampler.h"
+#include "starboard/shared/starboard/player/filter/audio_time_stretcher.h"
+#include "starboard/shared/starboard/player/filter/media_time_provider.h"
+#include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/shared/starboard/player/job_queue.h"
+#include "starboard/time.h"
+#include "starboard/types.h"
+
+// Uncomment the following statement to log the media time stats with deviation
+// when GetCurrentMediaTime() is called.
+// #define SB_LOG_MEDIA_TIME_STATS 1
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+
+const int kFramesInBufferBeginUnderflow = 1024;
+
+// A class that sits in between the audio decoder, the audio sink and the
+// pipeline to coordinate data transfer between these parties.  It also serves
+// as the authority of playback time.
+class AudioRendererImpl : public AudioRenderer,
+                          public MediaTimeProvider,
+                          private AudioRendererSink::RenderCallback,
+                          private JobQueue::JobOwner {
+ public:
+  // |max_cached_frames| is a soft limit for the max audio frames this class can
+  // cache so it can:
+  // 1. Avoid using too much memory.
+  // 2. Have the audio cache full to simulate the state that the renderer can no
+  //    longer accept more data.
+  // |min_frames_per_append| is the min number of frames that the audio renderer
+  // tries to append to the sink buffer at once.
+  AudioRendererImpl(scoped_ptr<AudioDecoder> decoder,
+                    scoped_ptr<AudioRendererSink> audio_renderer_sink,
+                    const SbMediaAudioSampleInfo& audio_sample_info,
+                    int max_cached_frames,
+                    int min_frames_per_append);
+  ~AudioRendererImpl() override;
+
+  void Initialize(const ErrorCB& error_cb,
+                  const PrerolledCB& prerolled_cb,
+                  const EndedCB& ended_cb) override;
+  void WriteSample(const scoped_refptr<InputBuffer>& input_buffer) override;
+  void WriteEndOfStream() override;
+
+  void SetVolume(double volume) override;
+
+  // TODO: Remove the eos state querying functions and their tests.
+  bool IsEndOfStreamWritten() const override;
+  bool IsEndOfStreamPlayed() const override;
+  bool CanAcceptMoreData() const override;
+
+  // MediaTimeProvider methods
+  void Play() override;
+  void Pause() override;
+  void SetPlaybackRate(double playback_rate) override;
+  void Seek(SbTime seek_to_time) override;
+  SbTime GetCurrentMediaTime(bool* is_playing,
+                             bool* is_eos_played,
+                             bool* is_underflow) override;
+
+ private:
+  enum EOSState {
+    kEOSNotReceived,
+    kEOSWrittenToDecoder,
+    kEOSDecoded,
+    kEOSSentToSink
+  };
+
+  const int max_cached_frames_;
+  const int min_frames_per_append_;
+  // |buffered_frames_to_start_| would be initialized in OnFirstOutput().
+  // Before it's initialized, set it to a large number.
+  int buffered_frames_to_start_ = 48 * 1024;
+
+  ErrorCB error_cb_;
+  PrerolledCB prerolled_cb_;
+  EndedCB ended_cb_;
+
+  Mutex mutex_;
+
+  bool paused_ = true;
+  bool consume_frames_called_ = false;
+  bool seeking_ = false;
+  SbTime seeking_to_time_ = 0;
+  SbTime last_media_time_ = 0;
+  AudioFrameTracker audio_frame_tracker_;
+  bool ended_cb_called_ = false;
+
+  int64_t total_frames_sent_to_sink_ = 0;
+  int64_t total_frames_consumed_by_sink_ = 0;
+  int32_t frames_consumed_by_sink_since_last_get_current_time_;
+
+  scoped_ptr<AudioDecoder> decoder_;
+
+  int64_t frames_consumed_set_at_;
+  double playback_rate_ = 1.0;
+
+  // AudioRendererSink methods
+  void GetSourceStatus(int* frames_in_buffer,
+                       int* offset_in_frames,
+                       bool* is_playing,
+                       bool* is_eos_reached) override;
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+  void ConsumeFrames(int frames_consumed, SbTime frames_consumed_at) override;
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+  void ConsumeFrames(int frames_consumed) override;
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+  void OnError(bool capability_changed) override;
+
+  void UpdateVariablesOnSinkThread_Locked(SbTime system_time_on_consume_frames);
+
+  void OnFirstOutput(const SbMediaAudioSampleType decoded_sample_type,
+                     const SbMediaAudioFrameStorageType decoded_storage_type,
+                     const int decoded_sample_rate);
+  bool IsEndOfStreamPlayed_Locked() const;
+
+  void OnDecoderConsumed();
+  void OnDecoderOutput();
+  void ProcessAudioData();
+  void FillResamplerAndTimeStretcher();
+  bool AppendAudioToFrameBuffer(bool* is_frame_buffer_full);
+
+  EOSState eos_state_ = kEOSNotReceived;
+  const int channels_;
+  const SbMediaAudioSampleType sink_sample_type_;
+  const int bytes_per_frame_;
+
+  scoped_ptr<AudioResampler> resampler_;
+  optional<int> decoder_sample_rate_;
+  AudioTimeStretcher time_stretcher_;
+
+  std::vector<uint8_t> frame_buffer_;
+  uint8_t* frame_buffers_[1];
+
+  int32_t pending_decoder_outputs_ = 0;
+
+  bool can_accept_more_data_ = true;
+  JobQueue::JobToken process_audio_data_job_token_;
+  std::function<void()> process_audio_data_job_;
+
+  // Our owner will attempt to seek to time 0 when playback begins.  In
+  // general, seeking could require a full reset of the underlying decoder on
+  // some platforms, so we make an effort to improve playback startup
+  // performance by keeping track of whether we already have a fresh decoder,
+  // and can thus avoid doing a full reset.
+  bool first_input_written_ = false;
+
+  scoped_ptr<AudioRendererSink> audio_renderer_sink_;
+  bool is_eos_reached_on_sink_thread_ = false;
+  bool is_playing_on_sink_thread_ = false;
+  int64_t frames_in_buffer_on_sink_thread_ = 0;
+  int64_t offset_in_frames_on_sink_thread_ = 0;
+  int64_t frames_consumed_on_sink_thread_ = 0;
+  SbTime frames_consumed_set_at_on_sink_thread_ = 0;
+  int64_t silence_frames_written_after_eos_on_sink_thread_ = 0;
+  int64_t silence_frames_consumed_on_sink_thread_ = 0;
+
+#if SB_LOG_MEDIA_TIME_STATS
+  SbTime system_and_media_time_offset_ = -1;
+  SbTime min_drift_ = kSbTimeMax;
+  SbTime max_drift_ = 0;
+  int64_t total_frames_consumed_ = 0;
+#endif  // SB_LOG_MEDIA_TIME_STATS
+
+  // Set to true when there are fewer than |kFramesInBufferBeginUnderflow|
+  // frames in buffer. Set to false when the queue is full or EOS.
+  bool underflow_ = false;
+
+#if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
+  static const int32_t kMaxSinkCallbacksBetweenCheck = 1024;
+  static const SbTime kCheckAudioSinkStatusInterval = kSbTimeSecond;
+  void CheckAudioSinkStatus();
+
+  atomic_int32_t sink_callbacks_since_last_check_;
+#endif  // SB_PLAYER_FILTER_ENABLE_STATE_CHECK
+};
+
+}  // namespace filter
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_RENDERER_INTERNAL_IMPL_H_
diff --git a/src/starboard/shared/starboard/player/filter/common.h b/src/starboard/shared/starboard/player/filter/common.h
index abfac92..b487763 100644
--- a/src/starboard/shared/starboard/player/filter/common.h
+++ b/src/starboard/shared/starboard/player/filter/common.h
@@ -27,13 +27,9 @@
 namespace player {
 namespace filter {
 
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
 typedef std::function<void(SbPlayerError error,
                            const std::string& error_message)>
     ErrorCB;
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-typedef std::function<void()> ErrorCB;
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 typedef std::function<void()> PrerolledCB;
 typedef std::function<void()> EndedCB;
 
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
index 0d51a92..6c9ff3e 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
@@ -125,11 +125,7 @@
   update_media_info_cb_ = update_media_info_cb;
   get_player_state_cb_ = get_player_state_cb;
   update_player_state_cb_ = update_player_state_cb;
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
   update_player_error_cb_ = update_player_error_cb;
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-  SB_DCHECK(!update_player_error_cb);
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 
   scoped_ptr<PlayerComponents::Factory> factory =
       PlayerComponents::Factory::Create();
@@ -185,11 +181,7 @@
     SB_LOG(INFO) << "Initialize audio renderer with volume " << volume_;
 
     audio_renderer_->Initialize(
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
         std::bind(&FilterBasedPlayerWorkerHandler::OnError, this, _1, _2),
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-        std::bind(&FilterBasedPlayerWorkerHandler::OnError, this),
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
         std::bind(&FilterBasedPlayerWorkerHandler::OnPrerolled, this,
                   kSbMediaTypeAudio),
         std::bind(&FilterBasedPlayerWorkerHandler::OnEnded, this,
@@ -202,11 +194,7 @@
     SB_LOG(INFO) << "Initialize video renderer.";
 
     video_renderer_->Initialize(
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
         std::bind(&FilterBasedPlayerWorkerHandler::OnError, this, _1, _2),
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-        std::bind(&FilterBasedPlayerWorkerHandler::OnError, this),
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
         std::bind(&FilterBasedPlayerWorkerHandler::OnPrerolled, this,
                   kSbMediaTypeVideo),
         std::bind(&FilterBasedPlayerWorkerHandler::OnEnded, this,
@@ -408,12 +396,19 @@
     const PlayerWorker::Bounds& bounds) {
   SB_DCHECK(BelongsToCurrentThread());
 
-  SB_LOG(INFO) << "Set bounds to "
-               << "x: " << bounds.x << ", y: " << bounds.y
-               << ", width: " << bounds.width << ", height: " << bounds.height
-               << ", z_index: " << bounds.z_index;
-
   if (SbMemoryCompare(&bounds_, &bounds, sizeof(bounds_)) != 0) {
+    // |z_index| is changed quite frequently.  Assign |z_index| first, so we
+    // only log when the other members of |bounds| have been changed to avoid
+    // spamming the log.
+    bounds_.z_index = bounds.z_index;
+    bool bounds_changed =
+        SbMemoryCompare(&bounds_, &bounds, sizeof(bounds_)) != 0;
+    SB_LOG_IF(INFO, bounds_changed)
+        << "Set bounds to "
+        << "x: " << bounds.x << ", y: " << bounds.y
+        << ", width: " << bounds.width << ", height: " << bounds.height
+        << ", z_index: " << bounds.z_index;
+
     bounds_ = bounds;
     if (video_renderer_) {
       // TODO: Force a frame update
@@ -425,7 +420,6 @@
   return true;
 }
 
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
 void FilterBasedPlayerWorkerHandler::OnError(SbPlayerError error,
                                              const std::string& error_message) {
   if (!BelongsToCurrentThread()) {
@@ -440,16 +434,6 @@
                                        : error_message);
   }
 }
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-void FilterBasedPlayerWorkerHandler::OnError() {
-  if (!BelongsToCurrentThread()) {
-    Schedule(std::bind(&FilterBasedPlayerWorkerHandler::OnError, this));
-    return;
-  }
-
-  update_player_state_cb_(kSbPlayerStateError);
-}
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 
 void FilterBasedPlayerWorkerHandler::OnPrerolled(SbMediaType media_type) {
   if (!BelongsToCurrentThread()) {
@@ -473,6 +457,10 @@
   if ((!audio_renderer_ || audio_prerolled_) &&
       (!video_renderer_ || video_prerolled_)) {
     update_player_state_cb_(kSbPlayerStatePresenting);
+    // The call is required to improve the calculation of media time in
+    // PlayerInternal, because it updates the system monotonic time used as the
+    // base of media time extrapolation.
+    Update();
     if (!paused_) {
       media_time_provider_->Play();
     }
@@ -547,6 +535,9 @@
 }
 
 SbDecodeTarget FilterBasedPlayerWorkerHandler::GetCurrentDecodeTarget() {
+  if (output_mode_ != kSbPlayerOutputModeDecodeToTexture) {
+    return kSbDecodeTargetInvalid;
+  }
   SbDecodeTarget decode_target = kSbDecodeTargetInvalid;
   if (player_components_existence_mutex_.AcquireTry()) {
     if (video_renderer_) {
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
index 5529860..f0082b0 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
@@ -75,11 +75,7 @@
   void Stop() override;
 
   void Update();
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
   void OnError(SbPlayerError error, const std::string& error_message);
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-  void OnError();
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
   void OnPrerolled(SbMediaType media_type);
   void OnEnded(SbMediaType media_type);
 
diff --git a/src/starboard/shared/starboard/player/filter/player_components.cc b/src/starboard/shared/starboard/player/filter/player_components.cc
index 288d905..1bee234 100644
--- a/src/starboard/shared/starboard/player/filter/player_components.cc
+++ b/src/starboard/shared/starboard/player/filter/player_components.cc
@@ -18,12 +18,14 @@
 #include "starboard/shared/starboard/application.h"
 #include "starboard/shared/starboard/command_line.h"
 #include "starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_renderer_internal_impl.h"
 #include "starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h"
 #include "starboard/shared/starboard/player/filter/media_time_provider_impl.h"
 #include "starboard/shared/starboard/player/filter/punchout_video_renderer_sink.h"
 #include "starboard/shared/starboard/player/filter/stub_audio_decoder.h"
 #include "starboard/shared/starboard/player/filter/stub_video_decoder.h"
 #include "starboard/shared/starboard/player/filter/video_render_algorithm_impl.h"
+#include "starboard/shared/starboard/player/filter/video_renderer_internal_impl.h"
 
 namespace starboard {
 namespace shared {
@@ -45,8 +47,8 @@
 class PlayerComponentsImpl : public PlayerComponents {
  public:
   PlayerComponentsImpl(scoped_ptr<MediaTimeProviderImpl> media_time_provider,
-                       scoped_ptr<AudioRenderer> audio_renderer,
-                       scoped_ptr<VideoRenderer> video_renderer)
+                       scoped_ptr<AudioRendererImpl> audio_renderer,
+                       scoped_ptr<VideoRendererImpl> video_renderer)
       : media_time_provider_(media_time_provider.Pass()),
         audio_renderer_(audio_renderer.Pass()),
         video_renderer_(video_renderer.Pass()) {
@@ -65,8 +67,8 @@
  private:
   // |media_time_provider_| will only be used when |audio_renderer_| is nullptr.
   scoped_ptr<MediaTimeProviderImpl> media_time_provider_;
-  scoped_ptr<AudioRenderer> audio_renderer_;
-  scoped_ptr<VideoRenderer> video_renderer_;
+  scoped_ptr<AudioRendererImpl> audio_renderer_;
+  scoped_ptr<VideoRendererImpl> video_renderer_;
 };
 
 }  // namespace
@@ -215,8 +217,8 @@
   }
 
   scoped_ptr<MediaTimeProviderImpl> media_time_provider_impl;
-  scoped_ptr<AudioRenderer> audio_renderer;
-  scoped_ptr<VideoRenderer> video_renderer;
+  scoped_ptr<AudioRendererImpl> audio_renderer;
+  scoped_ptr<VideoRendererImpl> video_renderer;
 
   if (creation_parameters.audio_codec() != kSbMediaAudioCodecNone) {
     SB_DCHECK(audio_decoder);
@@ -227,9 +229,9 @@
                            &min_frames_per_append);
 
     audio_renderer.reset(
-        new AudioRenderer(audio_decoder.Pass(), audio_renderer_sink.Pass(),
-                          creation_parameters.audio_sample_info(),
-                          max_cached_frames, min_frames_per_append));
+        new AudioRendererImpl(audio_decoder.Pass(), audio_renderer_sink.Pass(),
+                              creation_parameters.audio_sample_info(),
+                              max_cached_frames, min_frames_per_append));
   }
 
   if (creation_parameters.video_codec() != kSbMediaVideoCodecNone) {
@@ -245,9 +247,9 @@
               new MonotonicSystemTimeProviderImpl)));
       media_time_provider = media_time_provider_impl.get();
     }
-    video_renderer.reset(
-        new VideoRenderer(video_decoder.Pass(), media_time_provider,
-                          video_render_algorithm.Pass(), video_renderer_sink));
+    video_renderer.reset(new VideoRendererImpl(
+        video_decoder.Pass(), media_time_provider,
+        video_render_algorithm.Pass(), video_renderer_sink));
   }
 
   SB_DCHECK(audio_renderer || video_renderer);
diff --git a/src/starboard/shared/starboard/player/filter/player_components.h b/src/starboard/shared/starboard/player/filter/player_components.h
index c41837c..567c4a1 100644
--- a/src/starboard/shared/starboard/player/filter/player_components.h
+++ b/src/starboard/shared/starboard/player/filter/player_components.h
@@ -114,11 +114,9 @@
 #endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 
       SbPlayer player() const {
-        SB_DCHECK(video_codec_ != kSbMediaVideoCodecNone);
         return player_;
       }
       SbPlayerOutputMode output_mode() const {
-        SB_DCHECK(video_codec_ != kSbMediaVideoCodecNone);
         return output_mode_;
       }
       SbDecodeTargetGraphicsContextProvider*
diff --git a/src/starboard/shared/starboard/player/filter/player_filter.gypi b/src/starboard/shared/starboard/player/filter/player_filter.gypi
index 02024e1..2d79207 100644
--- a/src/starboard/shared/starboard/player/filter/player_filter.gypi
+++ b/src/starboard/shared/starboard/player/filter/player_filter.gypi
@@ -21,8 +21,9 @@
       '<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h',
       '<(DEPTH)/starboard/shared/starboard/player/filter/audio_frame_tracker.cc',
       '<(DEPTH)/starboard/shared/starboard/player/filter/audio_frame_tracker.h',
-      '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc',
       '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h',
+      '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal_impl.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal_impl.h',
       '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink.h',
       '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc',
       '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h',
@@ -63,8 +64,9 @@
       '<(DEPTH)/starboard/shared/starboard/player/filter/video_render_algorithm.h',
       '<(DEPTH)/starboard/shared/starboard/player/filter/video_render_algorithm_impl.cc',
       '<(DEPTH)/starboard/shared/starboard/player/filter/video_render_algorithm_impl.h',
-      '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.cc',
       '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h',
+      '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal_impl.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal_impl.h',
       '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_sink.h',
       '<(DEPTH)/starboard/shared/starboard/player/filter/wsola_internal.cc',
       '<(DEPTH)/starboard/shared/starboard/player/filter/wsola_internal.h',
diff --git a/src/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc b/src/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc
index 4c9481d..9da268a 100644
--- a/src/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc
@@ -13,8 +13,10 @@
 // limitations under the License.
 
 #include <cmath>
+#include <deque>
 #include <functional>
 #include <numeric>
+#include <queue>
 
 #include "starboard/common/mutex.h"
 #include "starboard/common/scoped_ptr.h"
@@ -23,13 +25,14 @@
 #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
 #include "starboard/shared/starboard/player/filter/player_components.h"
 #include "starboard/shared/starboard/player/filter/stub_player_components_factory.h"
+#include "starboard/shared/starboard/player/filter/testing/test_util.h"
+#include "starboard/shared/starboard/player/job_queue.h"
 #include "starboard/shared/starboard/player/video_dmp_reader.h"
 #include "starboard/thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 // TODO: Implement AudioDecoderMock and refactor the test accordingly.
-#if SB_HAS(PLAYER_FILTER_TESTS) && \
-    SB_API_VERSION >= 11
+#if SB_API_VERSION >= 11
 
 namespace starboard {
 namespace shared {
@@ -48,25 +51,18 @@
 
 const SbTimeMonotonic kWaitForNextEventTimeOut = 5 * kSbTimeSecond;
 
-void DeallocateSampleFunc(SbPlayer player,
-                          void* context,
-                          const void* sample_buffer) {
-  SB_UNREFERENCED_PARAMETER(player);
-  SB_UNREFERENCED_PARAMETER(context);
-  SB_UNREFERENCED_PARAMETER(sample_buffer);
-}
-
 scoped_refptr<InputBuffer> GetAudioInputBuffer(const VideoDmpReader& dmp_reader,
                                                size_t index) {
   auto player_sample_info =
       dmp_reader.GetPlayerSampleInfo(kSbMediaTypeAudio, index);
 #if SB_API_VERSION >= 11
-  return new InputBuffer(DeallocateSampleFunc, NULL, NULL, player_sample_info);
+  return new InputBuffer(StubDeallocateSampleFunc, NULL, NULL,
+                         player_sample_info);
 #else   // SB_API_VERSION >= 11
   SbMediaAudioSampleInfo audio_sample_info =
       dmp_reader.GetAudioSampleInfo(index);
-  return new InputBuffer(kSbMediaTypeAudio, DeallocateSampleFunc, NULL, NULL,
-                         player_sample_info, &audio_sample_info);
+  return new InputBuffer(kSbMediaTypeAudio, StubDeallocateSampleFunc, NULL,
+                         NULL, player_sample_info, &audio_sample_info);
 #endif  // SB_API_VERSION >= 11
 }
 
@@ -125,22 +121,12 @@
       ASSERT_GT(dmp_reader->number_of_audio_buffers(), 0);
     }
 
-    PlayerComponents::Factory::CreationParameters creation_parameters(
-        dmp_readers_[0]->audio_codec(), dmp_readers_[0]->audio_sample_info());
-
     scoped_ptr<AudioRendererSink> audio_renderer_sink;
-    scoped_ptr<PlayerComponents::Factory> factory;
-    if (using_stub_decoder_) {
-      factory = StubPlayerComponentsFactory::Create();
-    } else {
-      factory = PlayerComponents::Factory::Create();
-    }
-    std::string error_message;
-    ASSERT_TRUE(factory->CreateSubComponents(
-        creation_parameters, &audio_decoder_, &audio_renderer_sink, nullptr,
-        nullptr, nullptr, &error_message));
+    ASSERT_TRUE(CreateAudioComponents(using_stub_decoder_,
+                                      dmp_readers_[0]->audio_codec(),
+                                      dmp_readers_[0]->audio_sample_info(),
+                                      &audio_decoder_, &audio_renderer_sink));
     ASSERT_TRUE(audio_decoder_);
-
     audio_decoder_->Initialize(
         std::bind(&AdaptiveAudioDecoderTest::OnOutput, this),
         std::bind(&AdaptiveAudioDecoderTest::OnError, this));
@@ -372,38 +358,14 @@
 }
 
 vector<vector<const char*>> GetSupportedTests() {
-  // beneath_the_canopy_aac_stereo.dmp
-  //   codec: kSbMediaAudioCodecAac
-  //   sampling rate: 44.1k
-  //   frames per AU: 1024
-  // beneath_the_canopy_opus_stereo.dmp
-  //   codec: kSbMediaAudioCodecOpus
-  //   sampling rate: 48.0k
-  //   frames per AU: 960
-  const char* kFilenames[] = {"beneath_the_canopy_aac_stereo.dmp",
-                              "beneath_the_canopy_aac_5_1.dmp",
-                              "beneath_the_canopy_aac_mono.dmp",
-                              "beneath_the_canopy_opus_5_1.dmp",
-                              "beneath_the_canopy_opus_stereo.dmp",
-                              "beneath_the_canopy_opus_mono.dmp",
-                              "sintel_329_ec3.dmp",
-                              "sintel_381_ac3.dmp"};
-
   static vector<vector<const char*>> test_params;
 
   if (!test_params.empty()) {
     return test_params;
   }
 
-  vector<const char*> supported_files;
-  for (auto filename : kFilenames) {
-    VideoDmpReader dmp_reader(ResolveTestFileName(filename).c_str());
-    SB_DCHECK(dmp_reader.number_of_audio_buffers() > 0);
-    if (SbMediaIsAudioSupported(dmp_reader.audio_codec(),
-                                dmp_reader.audio_bitrate())) {
-      supported_files.push_back(filename);
-    }
-  }
+  vector<const char*> supported_files = GetSupportedAudioTestFiles(false);
+
   // Generate test cases. For example, we have |supported_files| [A, B, C].
   // Add tests A->A, A->B, A->C, B->A, B->B, B->C, C->A, C->B and C->C.
   for (int i = 0; i < supported_files.size(); i++) {
@@ -440,5 +402,4 @@
 }  // namespace shared
 }  // namespace starboard
 
-#endif  // SB_HAS(PLAYER_FILTER_TESTS) &&
-        // SB_API_VERSION >= 11
+#endif  // SB_API_VERSION >= 11
diff --git a/src/starboard/shared/starboard/player/filter/testing/audio_channel_layout_mixer_test.cc b/src/starboard/shared/starboard/player/filter/testing/audio_channel_layout_mixer_test.cc
index 2ca3a1c..4dad168 100644
--- a/src/starboard/shared/starboard/player/filter/testing/audio_channel_layout_mixer_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/audio_channel_layout_mixer_test.cc
@@ -22,8 +22,6 @@
 #include "starboard/shared/starboard/player/filter/audio_channel_layout_mixer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if SB_HAS(PLAYER_FILTER_TESTS)
-
 namespace starboard {
 namespace shared {
 namespace starboard {
@@ -343,5 +341,3 @@
 }  // namespace starboard
 }  // namespace shared
 }  // namespace starboard
-
-#endif  // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/filter/testing/audio_decoder_benchmark.cc b/src/starboard/shared/starboard/player/filter/testing/audio_decoder_benchmark.cc
new file mode 100644
index 0000000..f9f57f9
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/testing/audio_decoder_benchmark.cc
@@ -0,0 +1,157 @@
+// Copyright 2020 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/shared/starboard/player/filter/audio_decoder_internal.h"
+
+#include <algorithm>
+
+#include "starboard/common/log.h"
+#include "starboard/media.h"
+#include "starboard/player.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+#include "starboard/shared/starboard/player/filter/testing/test_util.h"
+#include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/shared/starboard/player/job_queue.h"
+#include "starboard/shared/starboard/player/video_dmp_reader.h"
+#include "third_party/google_benchmark/include/benchmark/benchmark.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+namespace testing {
+namespace {
+
+using video_dmp::VideoDmpReader;
+
+const size_t kMaxNumberOfInputs = 256;
+
+class AudioDecoderHelper {
+ public:
+  explicit AudioDecoderHelper(const char* filename)
+      : dmp_reader_(ResolveTestFileName(filename).c_str()),
+        number_of_inputs_(std::min(dmp_reader_.number_of_audio_buffers(),
+                                   kMaxNumberOfInputs)) {
+    const bool kUseStubDecoder = false;
+    SB_CHECK(number_of_inputs_ > 0);
+    SB_CHECK(CreateAudioComponents(kUseStubDecoder, dmp_reader_.audio_codec(),
+                                   dmp_reader_.audio_sample_info(),
+                                   &audio_decoder_, &audio_renderer_sink_));
+    SB_CHECK(audio_decoder_);
+    audio_decoder_->Initialize(std::bind(&AudioDecoderHelper::OnOutput, this),
+                               std::bind(&AudioDecoderHelper::OnError, this));
+  }
+
+  size_t number_of_inputs() const { return number_of_inputs_; }
+
+  void DecodeAll() {
+    SB_CHECK(current_input_buffer_index_ == 0);
+    OnConsumed();  // Kick off the first Decode() call
+    // Note that we deliberately don't add any time out to the loop, to ensure
+    // that the benchmark is accurate.
+    while (!end_of_stream_decoded_) {
+      job_queue_.RunUntilIdle();
+    }
+  }
+
+ private:
+  scoped_refptr<InputBuffer> GetAudioInputBuffer(size_t index) {
+    auto player_sample_info =
+        dmp_reader_.GetPlayerSampleInfo(kSbMediaTypeAudio, index);
+#if SB_API_VERSION >= 11
+    return new InputBuffer(StubDeallocateSampleFunc, NULL, NULL,
+                           player_sample_info);
+#else   // SB_API_VERSION >= 11
+    SbMediaAudioSampleInfo audio_sample_info =
+        dmp_reader_.GetAudioSampleInfo(index);
+    return new InputBuffer(kSbMediaTypeAudio, StubDeallocateSampleFunc, NULL,
+                           NULL, player_sample_info, &audio_sample_info);
+#endif  // SB_API_VERSION >= 11
+  }
+
+  void OnOutput() {
+    if (!job_queue_.BelongsToCurrentThread()) {
+      job_queue_.Schedule(std::bind(&AudioDecoderHelper::OnOutput, this));
+      return;
+    }
+    int decoded_sample_rate;
+    auto decoded_audio = audio_decoder_->Read(&decoded_sample_rate);
+    end_of_stream_decoded_ = decoded_audio->is_end_of_stream();
+  }
+
+  void OnError() { SB_NOTREACHED(); }
+
+  void OnConsumed() {
+    if (!job_queue_.BelongsToCurrentThread()) {
+      job_queue_.Schedule(std::bind(&AudioDecoderHelper::OnConsumed, this));
+      return;
+    }
+    if (current_input_buffer_index_ < number_of_inputs_) {
+      audio_decoder_->Decode(GetAudioInputBuffer(current_input_buffer_index_),
+                             std::bind(&AudioDecoderHelper::OnConsumed, this));
+      ++current_input_buffer_index_;
+    } else {
+      SB_CHECK(current_input_buffer_index_ == number_of_inputs_);
+      audio_decoder_->WriteEndOfStream();
+      // Increment so we can know if WriteEndOfStream() is called twice.
+      ++current_input_buffer_index_;
+    }
+  }
+
+  VideoDmpReader dmp_reader_;
+  JobQueue job_queue_;
+
+  const size_t number_of_inputs_;
+  size_t current_input_buffer_index_ = 0;
+  bool end_of_stream_decoded_ = false;
+
+  scoped_ptr<AudioDecoder> audio_decoder_;
+  scoped_ptr<AudioRendererSink> audio_renderer_sink_;
+};
+
+}  // namespace
+
+void RunBenchmark(::benchmark::State& state, const char* filename) {
+  size_t number_of_inputs = 0;
+  for (auto _ : state) {
+    state.PauseTiming();
+    AudioDecoderHelper helper(filename);
+    number_of_inputs = helper.number_of_inputs();
+    state.ResumeTiming();
+    helper.DecodeAll();
+  }
+  state.SetItemsProcessed(state.iterations() * number_of_inputs);
+}
+
+}  // namespace testing
+}  // namespace filter
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+// This function has to reside in the global namespace for BENCHMARK_CAPTURE to
+// pick it up.
+void BM_AudioDecoder(::benchmark::State& state, const char* filename) {
+  starboard::shared::starboard::player::filter::testing::RunBenchmark(state,
+                                                                      filename);
+}
+
+BENCHMARK_CAPTURE(BM_AudioDecoder,
+                  aac_stereo,
+                  "beneath_the_canopy_aac_stereo.dmp");
+BENCHMARK_CAPTURE(BM_AudioDecoder,
+                  opus_stereo,
+                  "beneath_the_canopy_opus_stereo.dmp");
diff --git a/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc b/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
index 225d9a3..b2e1392 100644
--- a/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
@@ -29,13 +29,12 @@
 #include "starboard/shared/starboard/player/decoded_audio_internal.h"
 #include "starboard/shared/starboard/player/filter/player_components.h"
 #include "starboard/shared/starboard/player/filter/stub_player_components_factory.h"
+#include "starboard/shared/starboard/player/filter/testing/test_util.h"
+#include "starboard/shared/starboard/player/job_queue.h"
 #include "starboard/shared/starboard/player/video_dmp_reader.h"
 #include "starboard/thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if SB_HAS(PLAYER_FILTER_TESTS)
-// TODO: Write test for HE-AAC
-
 namespace starboard {
 namespace shared {
 namespace starboard {
@@ -51,35 +50,6 @@
 
 const SbTimeMonotonic kWaitForNextEventTimeOut = 5 * kSbTimeSecond;
 
-std::string GetTestInputDirectory() {
-  const size_t kPathSize = kSbFileMaxPath + 1;
-
-  std::vector<char> content_path(kPathSize);
-  SB_CHECK(SbSystemGetPath(kSbSystemPathContentDirectory, content_path.data(),
-                           kPathSize));
-  std::string directory_path =
-      std::string(content_path.data()) + kSbFileSepChar + "test" +
-      kSbFileSepChar + "starboard" + kSbFileSepChar + "shared" +
-      kSbFileSepChar + "starboard" + kSbFileSepChar + "player" +
-      kSbFileSepChar + "testdata";
-
-  SB_CHECK(SbDirectoryCanOpen(directory_path.c_str()))
-      << "Cannot open directory " << directory_path;
-  return directory_path;
-}
-
-void DeallocateSampleFunc(SbPlayer player,
-                          void* context,
-                          const void* sample_buffer) {
-  SB_UNREFERENCED_PARAMETER(player);
-  SB_UNREFERENCED_PARAMETER(context);
-  SB_UNREFERENCED_PARAMETER(sample_buffer);
-}
-
-std::string ResolveTestFileName(const char* filename) {
-  return GetTestInputDirectory() + kSbFileSepChar + filename;
-}
-
 class AudioDecoderTest
     : public ::testing::TestWithParam<std::tuple<const char*, bool> > {
  public:
@@ -105,31 +75,12 @@
                         const SbMediaAudioSampleInfo& audio_sample_info,
                         scoped_ptr<AudioDecoder>* audio_decoder,
                         scoped_ptr<AudioRendererSink>* audio_renderer_sink) {
-    ASSERT_TRUE(audio_decoder);
-    ASSERT_TRUE(audio_renderer_sink);
-
-    audio_renderer_sink->reset();
-    audio_decoder->reset();
-
-    PlayerComponents::Factory::CreationParameters creation_parameters(
-        codec, audio_sample_info);
-
-    scoped_ptr<PlayerComponents::Factory> factory;
-    if (using_stub_decoder_) {
-      factory = StubPlayerComponentsFactory::Create();
-    } else {
-      factory = PlayerComponents::Factory::Create();
-    }
-    std::string error_message;
-    if (factory->CreateSubComponents(creation_parameters, audio_decoder,
-                                     audio_renderer_sink, nullptr, nullptr,
-                                     nullptr, &error_message)) {
+    if (CreateAudioComponents(using_stub_decoder_, codec, audio_sample_info,
+                              audio_decoder, audio_renderer_sink)) {
       SB_CHECK(*audio_decoder);
       (*audio_decoder)
           ->Initialize(std::bind(&AudioDecoderTest::OnOutput, this),
                        std::bind(&AudioDecoderTest::OnError, this));
-    } else {
-      audio_decoder->reset();
     }
   }
 
@@ -349,13 +300,13 @@
     auto player_sample_info =
         dmp_reader_.GetPlayerSampleInfo(kSbMediaTypeAudio, index);
 #if SB_API_VERSION >= 11
-    auto input_buffer = new InputBuffer(DeallocateSampleFunc, nullptr, nullptr,
-                                        player_sample_info);
+    auto input_buffer = new InputBuffer(StubDeallocateSampleFunc, nullptr,
+                                        nullptr, player_sample_info);
 #else   // SB_API_VERSION >= 11
     SbMediaAudioSampleInfo audio_sample_info =
         dmp_reader_.GetAudioSampleInfo(index);
     auto input_buffer =
-        new InputBuffer(kSbMediaTypeAudio, DeallocateSampleFunc, nullptr,
+        new InputBuffer(kSbMediaTypeAudio, StubDeallocateSampleFunc, nullptr,
                         nullptr, player_sample_info, &audio_sample_info);
 #endif  // SB_API_VERSION >= 11
     auto iter = invalid_inputs_.find(index);
@@ -658,6 +609,7 @@
   SbTime duration = kSbTimeSecond / 2;
   SbMediaSetAudioWriteDuration(duration);
 
+  SbTime start = SbTimeGetMonotonicNow();
   int start_index = 0;
   Event event;
   while (true) {
@@ -684,43 +636,21 @@
   }
   WriteEndOfStream();
   ASSERT_NO_FATAL_FAILURE(DrainOutputs());
+  SbTime elapsed = SbTimeGetMonotonicNow() - start;
+  SB_LOG(INFO) << "Decoding " << dmp_reader_.number_of_audio_buffers() << " au "
+               << " of " << media::GetCodecName(dmp_reader_.audio_codec())
+               << " takes " << elapsed << " microseconds.";
+
   ASSERT_TRUE(last_decoded_audio_);
   ASSERT_NO_FATAL_FAILURE(AssertInvalidOutputFormat());
 }
 
 #endif  // SB_API_VERSION >= 11
 
-std::vector<const char*> GetSupportedTests() {
-  const char* kFilenames[] = {"beneath_the_canopy_aac_5_1.dmp",
-                              "beneath_the_canopy_aac_stereo.dmp",
-                              "beneath_the_canopy_opus_5_1.dmp",
-                              "beneath_the_canopy_opus_stereo.dmp",
-                              "heaac.dmp",
-                              "sintel_329_ec3.dmp",
-                              "sintel_381_ac3.dmp"};
-
-  static std::vector<const char*> test_params;
-
-  if (!test_params.empty()) {
-    return test_params;
-  }
-
-  for (auto filename : kFilenames) {
-    VideoDmpReader dmp_reader(ResolveTestFileName(filename).c_str());
-    SB_DCHECK(dmp_reader.number_of_audio_buffers() > 0);
-    if (SbMediaIsAudioSupported(dmp_reader.audio_codec(),
-                                dmp_reader.audio_bitrate())) {
-      test_params.push_back(filename);
-    }
-  }
-
-  SB_DCHECK(!test_params.empty());
-  return test_params;
-}
-
 INSTANTIATE_TEST_CASE_P(AudioDecoderTests,
                         AudioDecoderTest,
-                        Combine(ValuesIn(GetSupportedTests()), Bool()));
+                        Combine(ValuesIn(GetSupportedAudioTestFiles(true)),
+                                Bool()));
 
 }  // namespace
 }  // namespace testing
@@ -729,4 +659,3 @@
 }  // namespace starboard
 }  // namespace shared
 }  // namespace starboard
-#endif  // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc b/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
index 575d755..d3250c1 100644
--- a/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/shared/starboard/player/filter/audio_renderer_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_renderer_internal_impl.h"
 
 #include <functional>
 #include <set>
@@ -22,13 +22,13 @@
 #include "starboard/media.h"
 #include "starboard/memory.h"
 #include "starboard/shared/starboard/media/media_util.h"
+#include "starboard/shared/starboard/player/filter/audio_renderer_sink.h"
 #include "starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h"
 #include "starboard/shared/starboard/player/filter/mock_audio_decoder.h"
 #include "starboard/shared/starboard/player/filter/mock_audio_renderer_sink.h"
 #include "starboard/thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if SB_HAS(PLAYER_FILTER_TESTS)
 namespace starboard {
 namespace shared {
 namespace starboard {
@@ -113,7 +113,7 @@
 
     const int kMaxCachedFrames = 256 * 1024;
     const int kMaxFramesPerAppend = 16384;
-    audio_renderer_.reset(new AudioRenderer(
+    audio_renderer_.reset(new AudioRendererImpl(
         make_scoped_ptr<AudioDecoder>(audio_decoder_),
         make_scoped_ptr<AudioRendererSink>(audio_renderer_sink_),
         GetDefaultAudioSampleInfo(), kMaxCachedFrames, kMaxFramesPerAppend));
@@ -127,14 +127,14 @@
   // until the renderer reaches its preroll threshold.
   // Once the renderer is "full", an EndOfStream is written.
   // Returns the number of frames written.
-  int FillRendererWithDecodedAudioAndWriteEOS() {
+  int FillRendererWithDecodedAudioAndWriteEOS(SbTime start_timestamp) {
     const int kFramesPerBuffer = 1024;
 
     int frames_written = 0;
 
     while (!prerolled_) {
-      SbTime timestamp =
-          frames_written * kSbTimeSecond / kDefaultSamplesPerSecond;
+      SbTime timestamp = start_timestamp + frames_written * kSbTimeSecond /
+                                               kDefaultSamplesPerSecond;
       scoped_refptr<InputBuffer> input_buffer = CreateInputBuffer(timestamp);
       WriteSample(input_buffer);
       CallConsumedCB();
@@ -236,7 +236,7 @@
   AudioDecoder::ConsumedCB consumed_cb_;
   bool prerolled_ = true;
 
-  scoped_ptr<AudioRenderer> audio_renderer_;
+  scoped_ptr<AudioRendererImpl> audio_renderer_;
   MockAudioDecoder* audio_decoder_;
   MockAudioRendererSink* audio_renderer_sink_;
   AudioRendererSink::RenderCallback* renderer_callback_;
@@ -327,7 +327,7 @@
 
   Seek(0);
 
-  int frames_written = FillRendererWithDecodedAudioAndWriteEOS();
+  int frames_written = FillRendererWithDecodedAudioAndWriteEOS(0);
 
   bool is_playing = true;
   bool is_eos_played = true;
@@ -421,7 +421,7 @@
 
   Seek(0);
 
-  int frames_written = FillRendererWithDecodedAudioAndWriteEOS();
+  int frames_written = FillRendererWithDecodedAudioAndWriteEOS(0);
   bool is_playing = false;
   bool is_eos_played = true;
   bool is_underflow = true;
@@ -453,7 +453,8 @@
 
   // Consume frames in two batches, so we can test if |GetCurrentMediaTime()|
   // is incrementing in an expected manner.
-  const int frames_to_consume = std::min(frames_written, frames_in_buffer) / 2;
+  const int frames_to_consume =
+      std::min(frames_written / kPlaybackRate, frames_in_buffer) / 2;
   SbTime new_media_time;
 
   EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed());
@@ -501,7 +502,7 @@
 
   audio_renderer_->Play();
 
-  int frames_written = FillRendererWithDecodedAudioAndWriteEOS();
+  int frames_written = FillRendererWithDecodedAudioAndWriteEOS(0);
 
   SendDecoderOutput(new DecodedAudio);
 
@@ -862,7 +863,7 @@
 
   Seek(0);
 
-  int frames_written = FillRendererWithDecodedAudioAndWriteEOS();
+  int frames_written = FillRendererWithDecodedAudioAndWriteEOS(0);
 
   bool is_playing;
   bool is_eos_played;
@@ -907,7 +908,7 @@
   EXPECT_GE(new_media_time, media_time);
   Seek(seek_time);
 
-  frames_written += FillRendererWithDecodedAudioAndWriteEOS();
+  frames_written += FillRendererWithDecodedAudioAndWriteEOS(seek_time);
 
   EXPECT_GE(audio_renderer_->GetCurrentMediaTime(&is_playing, &is_eos_played,
                                                  &is_underflow),
@@ -946,4 +947,3 @@
 }  // namespace starboard
 }  // namespace shared
 }  // namespace starboard
-#endif  // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/filter/testing/file_cache_reader_test.cc b/src/starboard/shared/starboard/player/filter/testing/file_cache_reader_test.cc
index 323d0bb..8506b7a 100644
--- a/src/starboard/shared/starboard/player/filter/testing/file_cache_reader_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/file_cache_reader_test.cc
@@ -21,6 +21,7 @@
 #include "starboard/configuration_constants.h"
 #include "starboard/file.h"
 #include "starboard/memory.h"
+#include "starboard/shared/starboard/player/filter/testing/test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace starboard {
@@ -33,27 +34,6 @@
 
 const char kTestFilename[] = "beneath_the_canopy_aac_stereo.dmp";
 
-std::string GetTestInputDirectory() {
-  using std::string;
-  const size_t kPathSize = kSbFileMaxPath + 1;
-
-  std::vector<char> content_path(kPathSize);
-  SB_CHECK(SbSystemGetPath(kSbSystemPathContentDirectory, content_path.data(),
-                           kPathSize));
-  string directory_path =
-      string(content_path.data()) + kSbFileSepChar + "test" + kSbFileSepChar +
-      "starboard" + kSbFileSepChar + "shared" + kSbFileSepChar + "starboard" +
-      kSbFileSepChar + "player" + kSbFileSepChar + "testdata";
-
-  SB_CHECK(SbDirectoryCanOpen(directory_path.c_str()))
-      << "Cannot open directory " << directory_path;
-  return directory_path;
-}
-
-std::string ResolveTestFileName(const char* filename) {
-  return GetTestInputDirectory() + kSbFileSepChar + filename;
-}
-
 class FileCacheReaderTest : public ::testing::Test {
  public:
   FileCacheReaderTest()
diff --git a/src/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc b/src/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc
index 8cf4555..2c89e47 100644
--- a/src/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc
@@ -20,7 +20,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if SB_HAS(PLAYER_FILTER_TESTS)
 namespace starboard {
 namespace shared {
 namespace starboard {
@@ -250,4 +249,3 @@
 }  // namespace starboard
 }  // namespace shared
 }  // namespace starboard
-#endif  // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/filter/testing/player_filter_tests.gyp b/src/starboard/shared/starboard/player/filter/testing/player_filter_tests.gyp
index 5a81320..507bc5f 100644
--- a/src/starboard/shared/starboard/player/filter/testing/player_filter_tests.gyp
+++ b/src/starboard/shared/starboard/player/filter/testing/player_filter_tests.gyp
@@ -25,6 +25,8 @@
         '<(DEPTH)/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc',
         '<(DEPTH)/starboard/shared/starboard/player/filter/testing/file_cache_reader_test.cc',
         '<(DEPTH)/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/filter/testing/test_util.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/filter/testing/test_util.h',
         '<(DEPTH)/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc',
         '<(DEPTH)/starboard/shared/starboard/player/filter/testing/video_frame_cadence_pattern_generator_test.cc',
         '<(DEPTH)/starboard/shared/starboard/player/filter/testing/video_frame_rate_estimator_test.cc',
@@ -64,5 +66,45 @@
       },
       'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
+    {
+      'target_name': 'player_filter_benchmarks',
+      'type': '<(final_executable_type)',
+      'conditions': [
+        ['gl_type != "none"', {
+          'dependencies': [
+            # This is needed because VideoDecoderTest depends on
+            # FakeGraphicsContextProvider which depends on EGL and GLES.
+            '<(DEPTH)/starboard/egl_and_gles/egl_and_gles.gyp:egl_and_gles',
+          ],
+        }],
+      ],
+      'defines': [
+        # This allows the benchmarks to include internal only header files.
+        'STARBOARD_IMPLEMENTATION',
+      ],
+      'sources': [
+        '<(DEPTH)/starboard/shared/starboard/player/filter/testing/audio_decoder_benchmark.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/filter/testing/test_util.cc',
+        '<(DEPTH)/starboard/common/benchmark_main.cc',
+      ],
+      'dependencies': [
+        '<@(cobalt_platform_dependencies)',
+        '<(DEPTH)/starboard/shared/starboard/player/player.gyp:video_dmp',
+        '<(DEPTH)/starboard/shared/starboard/player/player.gyp:player_copy_test_data',
+        '<(DEPTH)/starboard/starboard.gyp:starboard',
+        '<(DEPTH)/third_party/google_benchmark/google_benchmark.gyp:google_benchmark',
+      ],
+    },
+    {
+      'target_name': 'player_filter_benchmarks_deploy',
+      'type': 'none',
+      'dependencies': [
+        'player_filter_benchmarks',
+      ],
+      'variables': {
+        'executable_name': 'player_filter_benchmarks',
+      },
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+    },
   ],
 }
diff --git a/src/starboard/shared/starboard/player/filter/testing/test_util.cc b/src/starboard/shared/starboard/player/filter/testing/test_util.cc
new file mode 100644
index 0000000..0d4181e
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/testing/test_util.cc
@@ -0,0 +1,217 @@
+// Copyright 2020 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/shared/starboard/player/filter/testing/test_util.h"
+
+#include "starboard/common/log.h"
+#include "starboard/directory.h"
+#include "starboard/shared/starboard/media/media_support_internal.h"
+#include "starboard/shared/starboard/player/filter/player_components.h"
+#include "starboard/shared/starboard/player/filter/stub_player_components_factory.h"
+#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
+#include "starboard/shared/starboard/player/video_dmp_reader.h"
+#include "starboard/string.h"
+#include "starboard/system.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+namespace testing {
+namespace {
+
+using video_dmp::VideoDmpReader;
+
+std::string GetTestInputDirectory() {
+  const size_t kPathSize = kSbFileMaxPath + 1;
+
+  std::vector<char> content_path(kPathSize);
+  SB_CHECK(SbSystemGetPath(kSbSystemPathContentDirectory, content_path.data(),
+                           kPathSize));
+  std::string directory_path = std::string(content_path.data()) +
+                               kSbFileSepChar + "test" + kSbFileSepChar +
+                               "starboard" + kSbFileSepChar + "shared" +
+                               kSbFileSepChar + "starboard" + kSbFileSepChar +
+                               "player" + kSbFileSepChar + "testdata";
+
+  SB_CHECK(SbDirectoryCanOpen(directory_path.c_str()))
+      << "Cannot open directory " << directory_path;
+  return directory_path;
+}
+
+}  // namespace
+
+void StubDeallocateSampleFunc(SbPlayer player,
+                              void* context,
+                              const void* sample_buffer) {
+  SB_UNREFERENCED_PARAMETER(player);
+  SB_UNREFERENCED_PARAMETER(context);
+  SB_UNREFERENCED_PARAMETER(sample_buffer);
+}
+
+std::string ResolveTestFileName(const char* filename) {
+  return GetTestInputDirectory() + kSbFileSepChar + filename;
+}
+
+std::vector<const char*> GetSupportedAudioTestFiles(bool include_heaac) {
+  // beneath_the_canopy_aac_stereo.dmp
+  //   codec: kSbMediaAudioCodecAac
+  //   sampling rate: 44.1k
+  //   frames per AU: 1024
+  // beneath_the_canopy_opus_stereo.dmp
+  //   codec: kSbMediaAudioCodecOpus
+  //   sampling rate: 48.0k
+  //   frames per AU: 960
+
+  // IMPORTANT: do not change the order of test files unless necessary. If you
+  // must change the order of these tests please make sure you update the test
+  // filters for every platform, just in case a particular test case should be
+  // disabled.
+
+  const char* kFilenames[] = {"beneath_the_canopy_aac_stereo.dmp",
+                              "beneath_the_canopy_aac_5_1.dmp",
+                              "beneath_the_canopy_aac_mono.dmp",
+                              "beneath_the_canopy_opus_5_1.dmp",
+                              "beneath_the_canopy_opus_stereo.dmp",
+                              "beneath_the_canopy_opus_mono.dmp",
+                              "sintel_329_ec3.dmp",
+                              "sintel_381_ac3.dmp",
+                              "heaac.dmp"};
+
+  static std::vector<const char*> test_params_with_heaac;
+  static std::vector<const char*> test_params_without_heaac;
+
+  std::vector<const char*>& test_params =
+      include_heaac ? test_params_with_heaac : test_params_without_heaac;
+
+  if (!test_params.empty()) {
+    return test_params;
+  }
+
+  for (auto filename : kFilenames) {
+    VideoDmpReader dmp_reader(ResolveTestFileName(filename).c_str());
+    SB_DCHECK(dmp_reader.number_of_audio_buffers() > 0);
+    if (SbMediaIsAudioSupported(dmp_reader.audio_codec(),
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                                "",  // content_type
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                                dmp_reader.audio_bitrate())) {
+      test_params_with_heaac.push_back(filename);
+      if (SbStringFindString(filename, "heaac") == nullptr) {
+        test_params_without_heaac.push_back(filename);
+      }
+    }
+  }
+
+  SB_DCHECK(!test_params.empty());
+  return test_params;
+}
+
+std::vector<VideoTestParam> GetSupportedVideoTests() {
+  SbPlayerOutputMode kOutputModes[] = {kSbPlayerOutputModeDecodeToTexture,
+                                       kSbPlayerOutputModePunchOut};
+
+  // IMPORTANT: do not change the order of test files unless necessary. If you
+  // must change the order of these tests please make sure you update the test
+  // filters for every platform, just in case a particular test case should be
+  // disabled.
+
+  const char* kFilenames[] = {"beneath_the_canopy_137_avc.dmp",
+                              "beneath_the_canopy_248_vp9.dmp",
+                              "sintel_399_av1.dmp",
+                              "black_test_avc_1080p_30to60_fps.dmp"};
+
+  static std::vector<VideoTestParam> test_params;
+
+  if (!test_params.empty()) {
+    return test_params;
+  }
+
+  for (auto filename : kFilenames) {
+    VideoDmpReader dmp_reader(ResolveTestFileName(filename).c_str());
+    SB_DCHECK(dmp_reader.number_of_video_buffers() > 0);
+
+    for (auto output_mode : kOutputModes) {
+      if (!VideoDecoder::OutputModeSupported(
+              output_mode, dmp_reader.video_codec(), kSbDrmSystemInvalid)) {
+        continue;
+      }
+
+      const auto& video_sample_info =
+          dmp_reader.GetPlayerSampleInfo(kSbMediaTypeVideo, 0)
+              .video_sample_info;
+
+      if (SbMediaIsVideoSupported(
+              dmp_reader.video_codec(),
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+              "",  // content_type
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+#if SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
+              -1, -1, 8, kSbMediaPrimaryIdUnspecified,
+              kSbMediaTransferIdUnspecified, kSbMediaMatrixIdUnspecified,
+#endif  // SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
+#if SB_API_VERSION >= 11
+              video_sample_info.frame_width, video_sample_info.frame_height,
+#else   // SB_API_VERSION >= 11
+              video_sample_info->frame_width, video_sample_info->frame_height,
+#endif  // SB_API_VERSION >= 11
+              dmp_reader.video_bitrate(), dmp_reader.video_fps(), false)) {
+        test_params.push_back({filename, output_mode});
+      }
+    }
+  }
+
+  SB_DCHECK(!test_params.empty());
+  return test_params;
+}
+
+bool CreateAudioComponents(bool using_stub_decoder,
+                           SbMediaAudioCodec codec,
+                           const SbMediaAudioSampleInfo& audio_sample_info,
+                           scoped_ptr<AudioDecoder>* audio_decoder,
+                           scoped_ptr<AudioRendererSink>* audio_renderer_sink) {
+  SB_CHECK(audio_decoder);
+  SB_CHECK(audio_renderer_sink);
+
+  audio_renderer_sink->reset();
+  audio_decoder->reset();
+
+  PlayerComponents::Factory::CreationParameters creation_parameters(
+      codec, audio_sample_info);
+
+  scoped_ptr<PlayerComponents::Factory> factory;
+  if (using_stub_decoder) {
+    factory = StubPlayerComponentsFactory::Create();
+  } else {
+    factory = PlayerComponents::Factory::Create();
+  }
+  std::string error_message;
+  if (factory->CreateSubComponents(creation_parameters, audio_decoder,
+                                   audio_renderer_sink, nullptr, nullptr,
+                                   nullptr, &error_message)) {
+    SB_CHECK(*audio_decoder);
+    return true;
+  }
+  audio_renderer_sink->reset();
+  audio_decoder->reset();
+  return false;
+}
+
+}  // namespace testing
+}  // namespace filter
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/starboard/player/filter/testing/test_util.h b/src/starboard/shared/starboard/player/filter/testing/test_util.h
new file mode 100644
index 0000000..7002bf5
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/testing/test_util.h
@@ -0,0 +1,60 @@
+// Copyright 2020 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 STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_TESTING_TEST_UTIL_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_TESTING_TEST_UTIL_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "starboard/player.h"
+
+#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_renderer_sink.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+namespace testing {
+
+typedef std::pair<const char*, SbPlayerOutputMode> VideoTestParam;
+
+// The function doesn't free the buffer, it assumes that the lifetime of the
+// buffer is actually managed by other code.  It can be used in the places where
+// SbPlayerDeallocateSampleFunc is expected.
+void StubDeallocateSampleFunc(SbPlayer player,
+                              void* context,
+                              const void* sample_buffer);
+
+std::string ResolveTestFileName(const char* filename);
+std::vector<const char*> GetSupportedAudioTestFiles(bool include_heaac);
+std::vector<VideoTestParam> GetSupportedVideoTests();
+
+bool CreateAudioComponents(bool using_stub_decoder,
+                           SbMediaAudioCodec codec,
+                           const SbMediaAudioSampleInfo& audio_sample_info,
+                           scoped_ptr<AudioDecoder>* audio_decoder,
+                           scoped_ptr<AudioRendererSink>* audio_renderer_sink);
+
+}  // namespace testing
+}  // namespace filter
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_TESTING_TEST_UTIL_H_
diff --git a/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc b/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
index 4b39170..8a51890 100644
--- a/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
@@ -28,9 +28,9 @@
 #include "starboard/drm.h"
 #include "starboard/media.h"
 #include "starboard/memory.h"
-#include "starboard/shared/starboard/media/media_support_internal.h"
 #include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/filter/stub_player_components_factory.h"
+#include "starboard/shared/starboard/player/filter/testing/test_util.h"
 #include "starboard/shared/starboard/player/job_queue.h"
 #include "starboard/shared/starboard/player/video_dmp_reader.h"
 #include "starboard/testing/fake_graphics_context_provider.h"
@@ -38,7 +38,6 @@
 #include "starboard/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if SB_HAS(PLAYER_FILTER_TESTS)
 // This has to be defined in the global namespace as its instance will be used
 // as SbPlayer.
 struct SbPlayerPrivate {};
@@ -62,41 +61,8 @@
 using ::testing::ValuesIn;
 using video_dmp::VideoDmpReader;
 
-struct TestParam {
-  SbPlayerOutputMode output_mode;
-  const char* filename;
-};
-
 const SbTimeMonotonic kDefaultWaitForNextEventTimeOut = 5 * kSbTimeSecond;
 
-std::string GetTestInputDirectory() {
-  const size_t kPathSize = kSbFileMaxPath + 1;
-
-  std::vector<char> content_path(kPathSize);
-  EXPECT_TRUE(SbSystemGetPath(kSbSystemPathContentDirectory,
-                              content_path.data(), kPathSize));
-  std::string directory_path =
-      std::string(content_path.data()) + kSbFileSepChar + "test" +
-      kSbFileSepChar + "starboard" + kSbFileSepChar + "shared" +
-      kSbFileSepChar + "starboard" + kSbFileSepChar + "player" +
-      kSbFileSepChar + "testdata";
-
-  SB_CHECK(SbDirectoryCanOpen(directory_path.c_str())) << directory_path;
-  return directory_path;
-}
-
-void DeallocateSampleFunc(SbPlayer player,
-                          void* context,
-                          const void* sample_buffer) {
-  SB_UNREFERENCED_PARAMETER(player);
-  SB_UNREFERENCED_PARAMETER(context);
-  SB_UNREFERENCED_PARAMETER(sample_buffer);
-}
-
-std::string ResolveTestFileName(const char* filename) {
-  return GetTestInputDirectory() + kSbFileSepChar + filename;
-}
-
 AssertionResult AlmostEqualTime(SbTime time1, SbTime time2) {
   const SbTime kEpsilon = kSbTimeSecond / 1000;
   SbTime diff = time1 - time2;
@@ -129,11 +95,11 @@
 #endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 
 class VideoDecoderTest
-    : public ::testing::TestWithParam<std::tuple<TestParam, bool>> {
+    : public ::testing::TestWithParam<std::tuple<VideoTestParam, bool>> {
  public:
   VideoDecoderTest()
-      : test_filename_(std::get<0>(GetParam()).filename),
-        output_mode_(std::get<0>(GetParam()).output_mode),
+      : test_filename_(std::get<0>(GetParam()).first),
+        output_mode_(std::get<0>(GetParam()).second),
         using_stub_decoder_(std::get<1>(GetParam())),
         dmp_reader_(ResolveTestFileName(test_filename_).c_str()) {
     SB_LOG(INFO) << "Testing " << test_filename_ << ", output mode "
@@ -454,11 +420,12 @@
     auto video_sample_info =
         dmp_reader_.GetPlayerSampleInfo(kSbMediaTypeVideo, index);
 #if SB_API_VERSION >= 11
-    auto input_buffer =
-        new InputBuffer(DeallocateSampleFunc, NULL, NULL, video_sample_info);
+    auto input_buffer = new InputBuffer(StubDeallocateSampleFunc, NULL, NULL,
+                                        video_sample_info);
 #else   // SB_API_VERSION >= 11
-    auto input_buffer = new InputBuffer(kSbMediaTypeVideo, DeallocateSampleFunc,
-                                        NULL, NULL, video_sample_info, NULL);
+    auto input_buffer =
+        new InputBuffer(kSbMediaTypeVideo, StubDeallocateSampleFunc, NULL, NULL,
+                        video_sample_info, NULL);
 #endif  // SB_API_VERSION >= 11
     auto iter = invalid_inputs_.find(index);
     if (iter != invalid_inputs_.end()) {
@@ -758,10 +725,11 @@
 }
 
 TEST_P(VideoDecoderTest, ResetAfterInput) {
-  const size_t kMaxInputToWrite = 10;
+  const size_t max_inputs_to_write =
+      std::min<size_t>(dmp_reader_.number_of_video_buffers(), 10);
   bool error_occurred = false;
   WriteMultipleInputs(
-      0, kMaxInputToWrite, [&](const Event& event, bool* continue_process) {
+      0, max_inputs_to_write, [&](const Event& event, bool* continue_process) {
         if (event.status == kTimeout || event.status == kError) {
           error_occurred = true;
           *continue_process = false;
@@ -775,7 +743,9 @@
 }
 
 TEST_P(VideoDecoderTest, MultipleResets) {
-  for (int max_inputs = 1; max_inputs < 10; ++max_inputs) {
+  const size_t max_inputs_to_write =
+      std::min<size_t>(dmp_reader_.number_of_video_buffers(), 10);
+  for (int max_inputs = 1; max_inputs < max_inputs_to_write; ++max_inputs) {
     bool error_occurred = false;
     WriteMultipleInputs(
         0, max_inputs, [&](const Event& event, bool* continue_process) {
@@ -912,63 +882,9 @@
   ASSERT_FALSE(error_occurred);
 }
 
-std::vector<TestParam> GetSupportedTests() {
-  SbPlayerOutputMode kOutputModes[] = {kSbPlayerOutputModeDecodeToTexture,
-                                       kSbPlayerOutputModePunchOut};
-
-  const char* kFilenames[] = {"beneath_the_canopy_137_avc.dmp",
-                              "beneath_the_canopy_248_vp9.dmp",
-                              "sintel_399_av1.dmp"};
-
-  static std::vector<TestParam> test_params;
-
-  if (!test_params.empty()) {
-    return test_params;
-  }
-
-  for (auto filename : kFilenames) {
-    VideoDmpReader dmp_reader(ResolveTestFileName(filename).c_str());
-    SB_DCHECK(dmp_reader.number_of_video_buffers() > 0);
-
-    for (auto output_mode : kOutputModes) {
-      if (!VideoDecoder::OutputModeSupported(
-              output_mode, dmp_reader.video_codec(), kSbDrmSystemInvalid)) {
-        continue;
-      }
-
-      const auto& video_sample_info =
-          dmp_reader.GetPlayerSampleInfo(kSbMediaTypeVideo, 0)
-              .video_sample_info;
-
-      if (SbMediaIsVideoSupported(
-              dmp_reader.video_codec(),
-#if SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
-              -1, -1, 8, kSbMediaPrimaryIdUnspecified,
-              kSbMediaTransferIdUnspecified, kSbMediaMatrixIdUnspecified,
-#endif  // SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
-#if SB_API_VERSION >= 11
-              video_sample_info.frame_width, video_sample_info.frame_height,
-#else   // SB_API_VERSION >= 11
-              video_sample_info->frame_width, video_sample_info->frame_height,
-#endif  // SB_API_VERSION >= 11
-              dmp_reader.video_bitrate(), dmp_reader.video_fps()
-#if SB_API_VERSION >= 10
-                                              ,
-              false
-#endif  // SB_API_VERSION >= 10
-              )) {
-        test_params.push_back({output_mode, filename});
-      }
-    }
-  }
-
-  SB_DCHECK(!test_params.empty());
-  return test_params;
-}
-
 INSTANTIATE_TEST_CASE_P(VideoDecoderTests,
                         VideoDecoderTest,
-                        Combine(ValuesIn(GetSupportedTests()), Bool()));
+                        Combine(ValuesIn(GetSupportedVideoTests()), Bool()));
 
 }  // namespace
 }  // namespace testing
@@ -977,4 +893,3 @@
 }  // namespace starboard
 }  // namespace shared
 }  // namespace starboard
-#endif  // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/filter/testing/video_frame_cadence_pattern_generator_test.cc b/src/starboard/shared/starboard/player/filter/testing/video_frame_cadence_pattern_generator_test.cc
index 54af4be..23b2339 100644
--- a/src/starboard/shared/starboard/player/filter/testing/video_frame_cadence_pattern_generator_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/video_frame_cadence_pattern_generator_test.cc
@@ -16,8 +16,6 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if SB_HAS(PLAYER_FILTER_TESTS)
-
 namespace starboard {
 namespace shared {
 namespace starboard {
@@ -113,5 +111,3 @@
 }  // namespace starboard
 }  // namespace shared
 }  // namespace starboard
-
-#endif  // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/filter/testing/video_frame_rate_estimator_test.cc b/src/starboard/shared/starboard/player/filter/testing/video_frame_rate_estimator_test.cc
index 52ca795..9eb1a92 100644
--- a/src/starboard/shared/starboard/player/filter/testing/video_frame_rate_estimator_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/video_frame_rate_estimator_test.cc
@@ -23,8 +23,6 @@
 #include "starboard/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if SB_HAS(PLAYER_FILTER_TESTS)
-
 namespace starboard {
 namespace shared {
 namespace starboard {
@@ -243,5 +241,3 @@
 }  // namespace starboard
 }  // namespace shared
 }  // namespace starboard
-
-#endif  // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/filter/tools/audio_dmp_player.cc b/src/starboard/shared/starboard/player/filter/tools/audio_dmp_player.cc
index 4fcab39..36cfe40 100644
--- a/src/starboard/shared/starboard/player/filter/tools/audio_dmp_player.cc
+++ b/src/starboard/shared/starboard/player/filter/tools/audio_dmp_player.cc
@@ -27,8 +27,6 @@
 #include "starboard/shared/starboard/player/video_dmp_reader.h"
 #include "starboard/system.h"
 
-#if SB_HAS(PLAYER_FILTER_TESTS)
-
 namespace {
 
 using starboard::shared::starboard::player::video_dmp::VideoDmpReader;
@@ -121,7 +119,7 @@
 
 void PrerolledCB() {
   SB_LOG(INFO) << "Playback started.";
-  s_player_components->GetAudioRenderer()->Play();
+  s_player_components->GetMediaTimeProvider()->Play();
 }
 
 void EndedCB() {
@@ -151,9 +149,9 @@
 
   s_player_components->GetAudioRenderer()->Initialize(
       std::bind(ErrorCB, _1, _2), std::bind(PrerolledCB), std::bind(EndedCB));
-  s_player_components->GetAudioRenderer()->SetPlaybackRate(1.0);
+  s_player_components->GetMediaTimeProvider()->SetPlaybackRate(1.0);
   s_player_components->GetAudioRenderer()->SetVolume(1.0);
-  s_player_components->GetAudioRenderer()->Seek(0);
+  s_player_components->GetMediaTimeProvider()->Seek(0);
   s_job_thread->job_queue()->Schedule(std::bind(OnTimer));
 }
 
@@ -188,20 +186,3 @@
       break;
   }
 }
-
-#else  // SB_HAS(PLAYER_FILTER_TESTS)
-
-void SbEventHandle(const SbEvent* event) {
-  switch (event->type) {
-    case kSbEventTypeStart:
-      SB_LOG(INFO) << "\"audio_dmp_player\" is only support in SB_API_VERSION"
-                   << " 10 or later, or when SB_HAS_PLAYER_FILTER_TESTS is"
-                   << " defined.";
-      SbSystemRequestStop(0);
-      break;
-    default:
-      break;
-  }
-}
-
-#endif  // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/filter/video_render_algorithm_impl.cc b/src/starboard/shared/starboard/player/filter/video_render_algorithm_impl.cc
index 4877292..78b6486 100644
--- a/src/starboard/shared/starboard/player/filter/video_render_algorithm_impl.cc
+++ b/src/starboard/shared/starboard/player/filter/video_render_algorithm_impl.cc
@@ -114,7 +114,7 @@
 
   if (is_audio_eos_played) {
     while (frames->size() > 1) {
-      frames->pop_back();
+      frames->pop_front();
     }
   }
 
@@ -170,13 +170,6 @@
 
   while (frames->size() > 1 && !frames->front()->is_end_of_stream() &&
          frames->front()->timestamp() < media_time) {
-    frame_rate_estimate_.Update(*frames);
-    auto frame_rate = frame_rate_estimate_.frame_rate();
-    SB_DCHECK(frame_rate != VideoFrameRateEstimator::kInvalidFrameRate);
-    cadence_pattern_generator_.UpdateRefreshRateAndMaybeReset(refresh_rate);
-    cadence_pattern_generator_.UpdateFrameRate(frame_rate);
-    SB_DCHECK(cadence_pattern_generator_.has_cadence());
-
     auto second_iter = frames->begin();
     ++second_iter;
 
@@ -184,6 +177,13 @@
       break;
     }
 
+    frame_rate_estimate_.Update(*frames);
+    auto frame_rate = frame_rate_estimate_.frame_rate();
+    SB_DCHECK(frame_rate != VideoFrameRateEstimator::kInvalidFrameRate);
+    cadence_pattern_generator_.UpdateRefreshRateAndMaybeReset(refresh_rate);
+    cadence_pattern_generator_.UpdateFrameRate(frame_rate);
+    SB_DCHECK(cadence_pattern_generator_.has_cadence());
+
     auto frame_duration =
         static_cast<SbTime>(kSbTimeSecond / refresh_rate);
 
@@ -245,7 +245,23 @@
 
   if (is_audio_eos_played) {
     while (frames->size() > 1) {
-      frames->pop_back();
+      frames->pop_front();
+    }
+  }
+
+  if (frames->size() == 2) {
+    // When there are only two frames and the second one is end of stream, we
+    // want to advance to it explicitly if the current media time is later than
+    // the timestamp of the first frame, and the first frame has been displayed.
+    // This ensures that the video can be properly ended when there is no audio
+    // stream, where |is_audio_eos_played| will never be true.
+    auto second_iter = frames->begin();
+    ++second_iter;
+
+    if ((*second_iter)->is_end_of_stream() &&
+        media_time >= frames->front()->timestamp() &&
+        current_frame_rendered_times_ > 0) {
+      frames->pop_front();
     }
   }
 
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_internal.h b/src/starboard/shared/starboard/player/filter/video_renderer_internal.h
index 0e1c85f..471dfc7 100644
--- a/src/starboard/shared/starboard/player/filter/video_renderer_internal.h
+++ b/src/starboard/shared/starboard/player/filter/video_renderer_internal.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Cobalt Authors. All Rights Reserved.
+// Copyright 2020 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.
@@ -15,25 +15,11 @@
 #ifndef STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_VIDEO_RENDERER_INTERNAL_H_
 #define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_VIDEO_RENDERER_INTERNAL_H_
 
-#include <list>
-
-#include "starboard/atomic.h"
-#include "starboard/common/log.h"
-#include "starboard/common/mutex.h"
-#include "starboard/common/optional.h"
 #include "starboard/common/ref_counted.h"
 #include "starboard/common/scoped_ptr.h"
-#include "starboard/media.h"
 #include "starboard/shared/internal_only.h"
 #include "starboard/shared/starboard/player/filter/common.h"
-#include "starboard/shared/starboard/player/filter/media_time_provider.h"
-#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
-#include "starboard/shared/starboard/player/filter/video_frame_internal.h"
-#include "starboard/shared/starboard/player/filter/video_render_algorithm.h"
-#include "starboard/shared/starboard/player/filter/video_renderer_internal.h"
-#include "starboard/shared/starboard/player/filter/video_renderer_sink.h"
 #include "starboard/shared/starboard/player/input_buffer_internal.h"
-#include "starboard/shared/starboard/player/job_queue.h"
 #include "starboard/time.h"
 
 namespace starboard {
@@ -42,110 +28,28 @@
 namespace player {
 namespace filter {
 
-// A class that sits in between the video decoder, the video sink and the
-// pipeline to coordinate data transfer between these parties.
-class VideoRenderer : JobQueue::JobOwner {
+class VideoRenderer {
  public:
   // All of the functions are called on the PlayerWorker thread unless marked
   // otherwise.
-  VideoRenderer(scoped_ptr<VideoDecoder> decoder,
-                MediaTimeProvider* media_time_provider,
-                scoped_ptr<VideoRenderAlgorithm> algorithm,
-                scoped_refptr<VideoRendererSink> sink);
-  ~VideoRenderer();
+  virtual ~VideoRenderer() {}
 
-  void Initialize(const ErrorCB& error_cb,
-                  const PrerolledCB& prerolled_cb,
-                  const EndedCB& ended_cb);
-  int GetDroppedFrames() const { return algorithm_->GetDroppedFrames(); }
+  virtual void Initialize(const ErrorCB& error_cb,
+                          const PrerolledCB& prerolled_cb,
+                          const EndedCB& ended_cb) = 0;
+  virtual int GetDroppedFrames() const = 0;
 
-  void WriteSample(const scoped_refptr<InputBuffer>& input_buffer);
-  void WriteEndOfStream();
+  virtual void WriteSample(const scoped_refptr<InputBuffer>& input_buffer) = 0;
+  virtual void WriteEndOfStream() = 0;
 
-  void Seek(SbTime seek_to_time);
+  virtual void Seek(SbTime seek_to_time) = 0;
 
-  bool IsEndOfStreamWritten() const { return end_of_stream_written_.load(); }
-  bool CanAcceptMoreData() const;
+  virtual bool IsEndOfStreamWritten() const = 0;
+  virtual bool CanAcceptMoreData() const = 0;
 
   // Both of the following two functions can be called on any threads.
-  void SetBounds(int z_index, int x, int y, int width, int height);
-  SbDecodeTarget GetCurrentDecodeTarget();
-
- private:
-  typedef std::list<scoped_refptr<VideoFrame>> Frames;
-
-  // Both of the following two functions can be called on any threads.
-  void OnDecoderStatus(VideoDecoder::Status status,
-                       const scoped_refptr<VideoFrame>& frame);
-  void Render(VideoRendererSink::DrawFrameCB draw_frame_cb);
-  void OnSeekTimeout();
-
-  MediaTimeProvider* const media_time_provider_;
-  scoped_ptr<VideoRenderAlgorithm> algorithm_;
-  scoped_refptr<VideoRendererSink> sink_;
-  scoped_ptr<VideoDecoder> decoder_;
-
-  PrerolledCB prerolled_cb_;
-  EndedCB ended_cb_;
-
-  SbTimeMonotonic absolute_time_of_first_input_ = 0;
-  // Our owner will attempt to seek to time 0 when playback begins.  In
-  // general, seeking could require a full reset of the underlying decoder on
-  // some platforms, so we make an effort to improve playback startup
-  // performance by keeping track of whether we already have a fresh decoder,
-  // and can thus avoid doing a full reset.
-  bool first_input_written_ = false;
-  atomic_bool end_of_stream_written_;
-  atomic_bool ended_cb_called_;
-
-  atomic_bool need_more_input_;
-  atomic_bool seeking_;
-  SbTime seeking_to_time_ = 0;
-
-  // |number_of_frames_| = decoder_frames_.size() + sink_frames_.size()
-  atomic_int32_t number_of_frames_;
-  // |sink_frames_| is locked inside VideoRenderer::Render() when calling
-  // algorithm_->Render().  So OnDecoderStatus() won't try to lock and append
-  // the decoded frames to |sink_frames_| directly to avoid being blocked.  It
-  // will append newly decoded frames to |decoder_frames_| instead.  Note that
-  // both |decoder_frames_| and |sink_frames_| can be used on multiple threads.
-  // When they are being modified at the same time, |decoder_frames_mutex_|
-  // should always be locked before |sink_frames_mutex_| to avoid deadlock.
-  Mutex decoder_frames_mutex_;
-  Frames decoder_frames_;
-  Mutex sink_frames_mutex_;
-  Frames sink_frames_;
-
-#if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
-  enum BufferingState {
-    kWaitForBuffer,
-    kWaitForConsumption,
-  };
-
-  static const SbTimeMonotonic kCheckBufferingStateInterval = kSbTimeSecond;
-  static const SbTimeMonotonic kDelayBeforeWarning = 2 * kSbTimeSecond;
-  static const SbTimeMonotonic kMinLagWarningInterval = 10 * kSbTimeSecond;
-
-  static const SbTimeMonotonic kMaxRenderIntervalBeforeWarning =
-      66 * kSbTimeMillisecond;
-  static const SbTimeMonotonic kMaxGetCurrentDecodeTargetDuration =
-      16 * kSbTimeMillisecond;
-
-  void CheckBufferingState();
-  void CheckForFrameLag(SbTime last_decoded_frame_timestamp);
-
-  volatile BufferingState buffering_state_ = kWaitForBuffer;
-  volatile SbTimeMonotonic last_buffering_state_update_ = 0;
-  volatile SbTimeMonotonic last_output_ = 0;
-  mutable volatile SbTime last_can_accept_more_data = 0;
-  atomic_bool end_of_stream_decoded_;
-
-  SbTimeMonotonic time_of_last_lag_warning_;
-
-  SbTimeMonotonic time_of_last_render_call_ = -1;
-
-  SbTimeMonotonic first_input_written_at_ = 0;
-#endif  // SB_PLAYER_FILTER_ENABLE_STATE_CHECK
+  virtual void SetBounds(int z_index, int x, int y, int width, int height) = 0;
+  virtual SbDecodeTarget GetCurrentDecodeTarget() = 0;
 };
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc b/src/starboard/shared/starboard/player/filter/video_renderer_internal_impl.cc
similarity index 86%
rename from src/starboard/shared/starboard/player/filter/video_renderer_internal.cc
rename to src/starboard/shared/starboard/player/filter/video_renderer_internal_impl.cc
index 0eae09d..14b442c 100644
--- a/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/video_renderer_internal_impl.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/shared/starboard/player/filter/video_renderer_internal.h"
+#include "starboard/shared/starboard/player/filter/video_renderer_internal_impl.h"
 
 #include <algorithm>
 #include <functional>
@@ -32,10 +32,10 @@
 
 }  // namespace
 
-VideoRenderer::VideoRenderer(scoped_ptr<VideoDecoder> decoder,
-                             MediaTimeProvider* media_time_provider,
-                             scoped_ptr<VideoRenderAlgorithm> algorithm,
-                             scoped_refptr<VideoRendererSink> sink)
+VideoRendererImpl::VideoRendererImpl(scoped_ptr<VideoDecoder> decoder,
+                                     MediaTimeProvider* media_time_provider,
+                                     scoped_ptr<VideoRenderAlgorithm> algorithm,
+                                     scoped_refptr<VideoRendererSink> sink)
     : media_time_provider_(media_time_provider),
       algorithm_(algorithm.Pass()),
       sink_(sink),
@@ -57,13 +57,13 @@
   last_buffering_state_update_ = SbTimeGetMonotonicNow();
   last_output_ = last_buffering_state_update_;
   last_can_accept_more_data = last_buffering_state_update_;
-  Schedule(std::bind(&VideoRenderer::CheckBufferingState, this),
+  Schedule(std::bind(&VideoRendererImpl::CheckBufferingState, this),
            kCheckBufferingStateInterval);
   time_of_last_lag_warning_ = SbTimeGetMonotonicNow() - kMinLagWarningInterval;
 #endif  // SB_PLAYER_FILTER_ENABLE_STATE_CHECK
 }
 
-VideoRenderer::~VideoRenderer() {
+VideoRendererImpl::~VideoRendererImpl() {
   SB_DCHECK(BelongsToCurrentThread());
 
   sink_ = NULL;
@@ -82,9 +82,9 @@
   decoder_.reset();
 }
 
-void VideoRenderer::Initialize(const ErrorCB& error_cb,
-                               const PrerolledCB& prerolled_cb,
-                               const EndedCB& ended_cb) {
+void VideoRendererImpl::Initialize(const ErrorCB& error_cb,
+                                   const PrerolledCB& prerolled_cb,
+                                   const EndedCB& ended_cb) {
   SB_DCHECK(BelongsToCurrentThread());
   SB_DCHECK(prerolled_cb);
   SB_DCHECK(ended_cb);
@@ -94,14 +94,14 @@
   prerolled_cb_ = prerolled_cb;
   ended_cb_ = ended_cb;
 
-  decoder_->Initialize(std::bind(&VideoRenderer::OnDecoderStatus, this, _1, _2),
-                       error_cb);
+  decoder_->Initialize(
+      std::bind(&VideoRendererImpl::OnDecoderStatus, this, _1, _2), error_cb);
   if (sink_) {
-    sink_->SetRenderCB(std::bind(&VideoRenderer::Render, this, _1));
+    sink_->SetRenderCB(std::bind(&VideoRendererImpl::Render, this, _1));
   }
 }
 
-void VideoRenderer::WriteSample(
+void VideoRendererImpl::WriteSample(
     const scoped_refptr<InputBuffer>& input_buffer) {
   SB_DCHECK(BelongsToCurrentThread());
   SB_DCHECK(input_buffer);
@@ -131,7 +131,7 @@
   decoder_->WriteInputBuffer(input_buffer);
 }
 
-void VideoRenderer::WriteEndOfStream() {
+void VideoRendererImpl::WriteEndOfStream() {
   SB_DCHECK(BelongsToCurrentThread());
 
   SB_LOG_IF(WARNING, end_of_stream_written_.load())
@@ -148,7 +148,7 @@
   decoder_->WriteEndOfStream();
 }
 
-void VideoRenderer::Seek(SbTime seek_to_time) {
+void VideoRendererImpl::Seek(SbTime seek_to_time) {
   SB_DCHECK(BelongsToCurrentThread());
   SB_DCHECK(seek_to_time >= 0);
 
@@ -169,7 +169,8 @@
 
   auto preroll_timeout = decoder_->GetPrerollTimeout();
   if (preroll_timeout != kSbTimeMax) {
-    Schedule(std::bind(&VideoRenderer::OnSeekTimeout, this), preroll_timeout);
+    Schedule(std::bind(&VideoRendererImpl::OnSeekTimeout, this),
+             preroll_timeout);
   }
 
   ScopedLock scoped_lock_decoder_frames(decoder_frames_mutex_);
@@ -186,7 +187,7 @@
   algorithm_->Reset();  // This is also guarded by sink_frames_mutex_.
 }
 
-bool VideoRenderer::CanAcceptMoreData() const {
+bool VideoRendererImpl::CanAcceptMoreData() const {
   SB_DCHECK(BelongsToCurrentThread());
   bool can_accept_more_data =
       number_of_frames_.load() <
@@ -200,17 +201,17 @@
   return can_accept_more_data;
 }
 
-void VideoRenderer::SetBounds(int z_index,
-                              int x,
-                              int y,
-                              int width,
-                              int height) {
+void VideoRendererImpl::SetBounds(int z_index,
+                                  int x,
+                                  int y,
+                                  int width,
+                                  int height) {
   if (sink_) {
     sink_->SetBounds(z_index, x, y, width, height);
   }
 }
 
-SbDecodeTarget VideoRenderer::GetCurrentDecodeTarget() {
+SbDecodeTarget VideoRendererImpl::GetCurrentDecodeTarget() {
   // FilterBasedPlayerWorkerHandler::Stop() ensures that this function won't be
   // called right before VideoRenderer dtor is called and |decoder_| is set to
   // NULL inside the dtor.
@@ -231,15 +232,16 @@
 #if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
   auto end = SbTimeGetMonotonicNow();
   if (end - start > kMaxGetCurrentDecodeTargetDuration) {
-    SB_LOG(WARNING) << "VideoRenderer::GetCurrentDecodeTarget() takes "
+    SB_LOG(WARNING) << "VideoRendererImpl::GetCurrentDecodeTarget() takes "
                     << end - start << " microseconds.";
   }
 #endif  // SB_PLAYER_FILTER_ENABLE_STATE_CHECK
   return decode_target;
 }
 
-void VideoRenderer::OnDecoderStatus(VideoDecoder::Status status,
-                                    const scoped_refptr<VideoFrame>& frame) {
+void VideoRendererImpl::OnDecoderStatus(
+    VideoDecoder::Status status,
+    const scoped_refptr<VideoFrame>& frame) {
   if (status == VideoDecoder::kReleaseAllFrames) {
     ScopedLock scoped_lock_decoder_frames(decoder_frames_mutex_);
     ScopedLock scoped_lock_sink_frames(sink_frames_mutex_);
@@ -304,7 +306,7 @@
   need_more_input_.store(status == VideoDecoder::kNeedMoreInput);
 }
 
-void VideoRenderer::Render(VideoRendererSink::DrawFrameCB draw_frame_cb) {
+void VideoRendererImpl::Render(VideoRendererSink::DrawFrameCB draw_frame_cb) {
 #if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
   auto now = SbTimeGetMonotonicNow();
   if (time_of_last_render_call_ != -1) {
@@ -351,20 +353,20 @@
 #endif  // SB_PLAYER_FILTER_ENABLE_STATE_CHECK
 }
 
-void VideoRenderer::OnSeekTimeout() {
+void VideoRendererImpl::OnSeekTimeout() {
   SB_DCHECK(BelongsToCurrentThread());
   if (number_of_frames_.load() > 0) {
     if (seeking_.exchange(false)) {
       Schedule(prerolled_cb_);
     }
   } else if (seeking_.load()) {
-    Schedule(std::bind(&VideoRenderer::OnSeekTimeout, this),
+    Schedule(std::bind(&VideoRendererImpl::OnSeekTimeout, this),
              kSeekTimeoutRetryInterval);
   }
 }
 
 #if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
-void VideoRenderer::CheckBufferingState() {
+void VideoRendererImpl::CheckBufferingState() {
   if (end_of_stream_decoded_.load()) {
     return;
   }
@@ -392,11 +394,11 @@
   auto elasped = now - last_output_;
   SB_LOG_IF(ERROR, elasped > kDelayBeforeWarning)
       << "Haven't received any output for " << elasped << " microseconds.";
-  Schedule(std::bind(&VideoRenderer::CheckBufferingState, this),
+  Schedule(std::bind(&VideoRendererImpl::CheckBufferingState, this),
            kCheckBufferingStateInterval);
 }
 
-void VideoRenderer::CheckForFrameLag(SbTime last_decoded_frame_timestamp) {
+void VideoRendererImpl::CheckForFrameLag(SbTime last_decoded_frame_timestamp) {
   SbTimeMonotonic now = SbTimeGetMonotonicNow();
   // Limit check frequency to minimize call to GetCurrentMediaTime().
   if (now - time_of_last_lag_warning_ < kMinLagWarningInterval) {
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_internal_impl.h b/src/starboard/shared/starboard/player/filter/video_renderer_internal_impl.h
new file mode 100644
index 0000000..d8c0648
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/video_renderer_internal_impl.h
@@ -0,0 +1,161 @@
+// Copyright 2016 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 STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_VIDEO_RENDERER_INTERNAL_IMPL_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_VIDEO_RENDERER_INTERNAL_IMPL_H_
+
+#include <list>
+
+#include "starboard/atomic.h"
+#include "starboard/common/log.h"
+#include "starboard/common/mutex.h"
+#include "starboard/common/optional.h"
+#include "starboard/common/ref_counted.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/filter/common.h"
+#include "starboard/shared/starboard/player/filter/media_time_provider.h"
+#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
+#include "starboard/shared/starboard/player/filter/video_frame_internal.h"
+#include "starboard/shared/starboard/player/filter/video_render_algorithm.h"
+#include "starboard/shared/starboard/player/filter/video_renderer_internal.h"
+#include "starboard/shared/starboard/player/filter/video_renderer_sink.h"
+#include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/shared/starboard/player/job_queue.h"
+#include "starboard/time.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+
+// A class that sits in between the video decoder, the video sink and the
+// pipeline to coordinate data transfer between these parties.
+class VideoRendererImpl : public VideoRenderer, private JobQueue::JobOwner {
+ public:
+  // All of the functions are called on the PlayerWorker thread unless marked
+  // otherwise.
+  VideoRendererImpl(scoped_ptr<VideoDecoder> decoder,
+                    MediaTimeProvider* media_time_provider,
+                    scoped_ptr<VideoRenderAlgorithm> algorithm,
+                    scoped_refptr<VideoRendererSink> sink);
+  ~VideoRendererImpl() override;
+
+  void Initialize(const ErrorCB& error_cb,
+                  const PrerolledCB& prerolled_cb,
+                  const EndedCB& ended_cb) override;
+  int GetDroppedFrames() const override {
+    return algorithm_->GetDroppedFrames();
+  }
+
+  void WriteSample(const scoped_refptr<InputBuffer>& input_buffer) override;
+  void WriteEndOfStream() override;
+
+  void Seek(SbTime seek_to_time) override;
+
+  bool IsEndOfStreamWritten() const override {
+    return end_of_stream_written_.load();
+  }
+  bool CanAcceptMoreData() const override;
+
+  // Both of the following two functions can be called on any threads.
+  void SetBounds(int z_index, int x, int y, int width, int height) override;
+  SbDecodeTarget GetCurrentDecodeTarget() override;
+
+ private:
+  typedef std::list<scoped_refptr<VideoFrame>> Frames;
+
+  // Both of the following two functions can be called on any threads.
+  void OnDecoderStatus(VideoDecoder::Status status,
+                       const scoped_refptr<VideoFrame>& frame);
+  void Render(VideoRendererSink::DrawFrameCB draw_frame_cb);
+  void OnSeekTimeout();
+
+  MediaTimeProvider* const media_time_provider_;
+  scoped_ptr<VideoRenderAlgorithm> algorithm_;
+  scoped_refptr<VideoRendererSink> sink_;
+  scoped_ptr<VideoDecoder> decoder_;
+
+  PrerolledCB prerolled_cb_;
+  EndedCB ended_cb_;
+
+  SbTimeMonotonic absolute_time_of_first_input_ = 0;
+  // Our owner will attempt to seek to time 0 when playback begins.  In
+  // general, seeking could require a full reset of the underlying decoder on
+  // some platforms, so we make an effort to improve playback startup
+  // performance by keeping track of whether we already have a fresh decoder,
+  // and can thus avoid doing a full reset.
+  bool first_input_written_ = false;
+  atomic_bool end_of_stream_written_;
+  atomic_bool ended_cb_called_;
+
+  atomic_bool need_more_input_;
+  atomic_bool seeking_;
+  SbTime seeking_to_time_ = 0;
+
+  // |number_of_frames_| = decoder_frames_.size() + sink_frames_.size()
+  atomic_int32_t number_of_frames_;
+  // |sink_frames_| is locked inside VideoRenderer::Render() when calling
+  // algorithm_->Render().  So OnDecoderStatus() won't try to lock and append
+  // the decoded frames to |sink_frames_| directly to avoid being blocked.  It
+  // will append newly decoded frames to |decoder_frames_| instead.  Note that
+  // both |decoder_frames_| and |sink_frames_| can be used on multiple threads.
+  // When they are being modified at the same time, |decoder_frames_mutex_|
+  // should always be locked before |sink_frames_mutex_| to avoid deadlock.
+  Mutex decoder_frames_mutex_;
+  Frames decoder_frames_;
+  Mutex sink_frames_mutex_;
+  Frames sink_frames_;
+
+#if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
+  enum BufferingState {
+    kWaitForBuffer,
+    kWaitForConsumption,
+  };
+
+  static const SbTimeMonotonic kCheckBufferingStateInterval = kSbTimeSecond;
+  static const SbTimeMonotonic kDelayBeforeWarning = 2 * kSbTimeSecond;
+  static const SbTimeMonotonic kMinLagWarningInterval = 10 * kSbTimeSecond;
+
+  static const SbTimeMonotonic kMaxRenderIntervalBeforeWarning =
+      66 * kSbTimeMillisecond;
+  static const SbTimeMonotonic kMaxGetCurrentDecodeTargetDuration =
+      16 * kSbTimeMillisecond;
+
+  void CheckBufferingState();
+  void CheckForFrameLag(SbTime last_decoded_frame_timestamp);
+
+  volatile BufferingState buffering_state_ = kWaitForBuffer;
+  volatile SbTimeMonotonic last_buffering_state_update_ = 0;
+  volatile SbTimeMonotonic last_output_ = 0;
+  mutable volatile SbTime last_can_accept_more_data = 0;
+  atomic_bool end_of_stream_decoded_;
+
+  SbTimeMonotonic time_of_last_lag_warning_;
+
+  SbTimeMonotonic time_of_last_render_call_ = -1;
+
+  SbTimeMonotonic first_input_written_at_ = 0;
+#endif  // SB_PLAYER_FILTER_ENABLE_STATE_CHECK
+};
+
+}  // namespace filter
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_VIDEO_RENDERER_INTERNAL_IMPL_H_
diff --git a/src/starboard/shared/starboard/player/player_create.cc b/src/starboard/shared/starboard/player/player_create.cc
index f124449..e34b530 100644
--- a/src/starboard/shared/starboard/player/player_create.cc
+++ b/src/starboard/shared/starboard/player/player_create.cc
@@ -22,9 +22,9 @@
 #include "starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 #include "starboard/shared/starboard/player/player_worker.h"
-#if SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER
 #include "starboard/shared/starboard/player/video_dmp_writer.h"
-#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
+#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER
 
 using starboard::shared::media_session::
     UpdateActiveSessionPlatformPlaybackState;
@@ -81,9 +81,9 @@
 
   SbMediaAudioCodec audio_codec = creation_param->audio_sample_info.codec;
   SbMediaVideoCodec video_codec = creation_param->video_sample_info.codec;
-#if SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER
   SbDrmSystem drm_system = creation_param->drm_system;
-#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
+#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER
   const SbMediaAudioSampleInfo* audio_sample_info =
       &creation_param->audio_sample_info;
   const auto output_mode = creation_param->output_mode;
@@ -93,9 +93,6 @@
 SbPlayer SbPlayerCreate(SbWindow window,
                         SbMediaVideoCodec video_codec,
                         SbMediaAudioCodec audio_codec,
-#if SB_API_VERSION < 10
-                        SbMediaTime duration_pts,
-#endif  // SB_API_VERSION < 10
                         SbDrmSystem drm_system,
                         const SbMediaAudioSampleInfo* audio_sample_info,
 #if SB_API_VERSION >= 11
@@ -104,9 +101,7 @@
                         SbPlayerDeallocateSampleFunc sample_deallocate_func,
                         SbPlayerDecoderStatusFunc decoder_status_func,
                         SbPlayerStatusFunc player_status_func,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
                         SbPlayerErrorFunc player_error_func,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
                         void* context,
                         SbPlayerOutputMode output_mode,
                         SbDecodeTargetGraphicsContextProvider* provider) {
@@ -116,9 +111,6 @@
 #if SB_API_VERSION >= 11
   SB_UNREFERENCED_PARAMETER(max_video_capabilities);
 #endif  // SB_API_VERSION >= 11
-#if SB_API_VERSION < 10
-  SB_UNREFERENCED_PARAMETER(duration_pts);
-#endif  // SB_API_VERSION < 10
 #if SB_API_VERSION >= 11
   if (audio_sample_info) {
     SB_DCHECK(audio_sample_info->codec == audio_codec);
@@ -126,16 +118,18 @@
 #endif  // SB_API_VERSION >= 11
 
   if (!sample_deallocate_func || !decoder_status_func || !player_status_func
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
       || !player_error_func
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
       ) {
     return kSbPlayerInvalid;
   }
 
   const int64_t kDefaultBitRate = 0;
   if (audio_codec != kSbMediaAudioCodecNone &&
-      !SbMediaIsAudioSupported(audio_codec, kDefaultBitRate)) {
+      !SbMediaIsAudioSupported(audio_codec,
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                               audio_mime,
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                               kDefaultBitRate)) {
     SB_LOG(ERROR) << "Unsupported audio codec " << audio_codec;
     return kSbPlayerInvalid;
   }
@@ -147,20 +141,19 @@
   const int kDefaultFrameHeight = 0;
   const int kDefaultFrameRate = 0;
   if (video_codec != kSbMediaVideoCodecNone &&
-      !SbMediaIsVideoSupported(video_codec,
+      !SbMediaIsVideoSupported(
+          video_codec,
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+          video_mime,
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
 #if SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
-                               kDefaultProfile, kDefaultLevel,
-                               kDefaultColorDepth, kSbMediaPrimaryIdUnspecified,
-                               kSbMediaTransferIdUnspecified,
-                               kSbMediaMatrixIdUnspecified,
+          kDefaultProfile, kDefaultLevel, kDefaultColorDepth,
+          kSbMediaPrimaryIdUnspecified, kSbMediaTransferIdUnspecified,
+          kSbMediaMatrixIdUnspecified,
 #endif  // SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
-                               kDefaultFrameWidth, kDefaultFrameHeight,
-                               kDefaultBitRate, kDefaultFrameRate
-#if SB_API_VERSION >= 10
-                               ,
-                               output_mode == kSbPlayerOutputModeDecodeToTexture
-#endif
-                               )) {
+          kDefaultFrameWidth, kDefaultFrameHeight, kDefaultBitRate,
+          kDefaultFrameRate,
+          output_mode == kSbPlayerOutputModeDecodeToTexture)) {
     SB_LOG(ERROR) << "Unsupported video codec " << video_codec;
     return kSbPlayerInvalid;
   }
@@ -206,16 +199,14 @@
   SbPlayer player = SbPlayerPrivate::CreateInstance(
       audio_codec, video_codec, audio_sample_info, sample_deallocate_func,
       decoder_status_func, player_status_func,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
       player_error_func,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
       context, handler.Pass());
 
-#if SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER
   using ::starboard::shared::starboard::player::video_dmp::VideoDmpWriter;
   VideoDmpWriter::OnPlayerCreate(player, audio_codec, video_codec, drm_system,
                                  audio_sample_info);
-#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
+#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER
 
   return player;
 }
diff --git a/src/starboard/shared/starboard/player/player_destroy.cc b/src/starboard/shared/starboard/player/player_destroy.cc
index f7e2660..7e75eb0 100644
--- a/src/starboard/shared/starboard/player/player_destroy.cc
+++ b/src/starboard/shared/starboard/player/player_destroy.cc
@@ -16,9 +16,9 @@
 
 #include "starboard/shared/media_session/playback_state.h"
 #include "starboard/shared/starboard/player/player_internal.h"
-#if SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER
 #include "starboard/shared/starboard/player/video_dmp_writer.h"
-#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
+#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER
 
 using starboard::shared::media_session::kNone;
 using starboard::shared::media_session::
@@ -30,10 +30,10 @@
   }
   UpdateActiveSessionPlatformPlaybackState(kNone);
 
-#if SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER
   using ::starboard::shared::starboard::player::video_dmp::VideoDmpWriter;
   VideoDmpWriter::OnPlayerDestroy(player);
-#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
+#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER
 
   delete player;
 }
diff --git a/src/starboard/shared/starboard/player/player_get_info.cc b/src/starboard/shared/starboard/player/player_get_info.cc
deleted file mode 100644
index 76677da..0000000
--- a/src/starboard/shared/starboard/player/player_get_info.cc
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2016 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/player.h"
-
-#include "starboard/common/log.h"
-#include "starboard/shared/starboard/player/player_internal.h"
-
-#if SB_API_VERSION < 10
-void SbPlayerGetInfo(SbPlayer player, SbPlayerInfo* out_player_info) {
-  if (!SbPlayerIsValid(player)) {
-    SB_DLOG(WARNING) << "player is invalid.";
-    return;
-  }
-
-  if (out_player_info == NULL) {
-    SB_DLOG(WARNING) << "out_player_info is NULL.";
-    return;
-  }
-
-  player->GetInfo(out_player_info);
-}
-#endif  // SB_API_VERSION < 10
diff --git a/src/starboard/shared/starboard/player/player_get_info2.cc b/src/starboard/shared/starboard/player/player_get_info2.cc
index d73f921..528b124 100644
--- a/src/starboard/shared/starboard/player/player_get_info2.cc
+++ b/src/starboard/shared/starboard/player/player_get_info2.cc
@@ -17,7 +17,6 @@
 #include "starboard/common/log.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 
-#if SB_API_VERSION >= 10
 void SbPlayerGetInfo2(SbPlayer player, SbPlayerInfo2* out_player_info) {
   if (!SbPlayerIsValid(player)) {
     SB_DLOG(WARNING) << "player is invalid.";
@@ -31,4 +30,3 @@
 
   player->GetInfo(out_player_info);
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/player/player_get_maximum_number_of_samples_per_write.cc b/src/starboard/shared/starboard/player/player_get_maximum_number_of_samples_per_write.cc
index f053425..f704486 100644
--- a/src/starboard/shared/starboard/player/player_get_maximum_number_of_samples_per_write.cc
+++ b/src/starboard/shared/starboard/player/player_get_maximum_number_of_samples_per_write.cc
@@ -14,9 +14,7 @@
 
 #include "starboard/player.h"
 
-#if SB_API_VERSION >= 10
 int SbPlayerGetMaximumNumberOfSamplesPerWrite(SbPlayer /*player*/,
                                               SbMediaType /*sample_type*/) {
   return 1;
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/player/player_internal.cc b/src/starboard/shared/starboard/player/player_internal.cc
index 924de76..c273223 100644
--- a/src/starboard/shared/starboard/player/player_internal.cc
+++ b/src/starboard/shared/starboard/player/player_internal.cc
@@ -17,9 +17,9 @@
 #include <functional>
 
 #include "starboard/common/log.h"
-#if SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER
 #include "starboard/shared/starboard/player/video_dmp_writer.h"
-#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
+#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER
 
 namespace {
 
@@ -47,9 +47,7 @@
     SbPlayerDeallocateSampleFunc sample_deallocate_func,
     SbPlayerDecoderStatusFunc decoder_status_func,
     SbPlayerStatusFunc player_status_func,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     SbPlayerErrorFunc player_error_func,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
     void* context,
     starboard::scoped_ptr<PlayerWorker::Handler> player_worker_handler)
     : sample_deallocate_func_(sample_deallocate_func),
@@ -64,9 +62,7 @@
       audio_codec, video_codec, player_worker_handler.Pass(),
       std::bind(&SbPlayerPrivate::UpdateMediaInfo, this, _1, _2, _3, _4),
       decoder_status_func, player_status_func,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
       player_error_func,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
       this, context));
 
   ++number_of_players_;
@@ -82,17 +78,13 @@
     SbPlayerDeallocateSampleFunc sample_deallocate_func,
     SbPlayerDecoderStatusFunc decoder_status_func,
     SbPlayerStatusFunc player_status_func,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     SbPlayerErrorFunc player_error_func,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
     void* context,
     starboard::scoped_ptr<PlayerWorker::Handler> player_worker_handler) {
   SbPlayerPrivate* ret = new SbPlayerPrivate(
       audio_codec, video_codec, audio_sample_info, sample_deallocate_func,
       decoder_status_func, player_status_func,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
       player_error_func,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
       context, player_worker_handler.Pass());
 
   if (ret && ret->worker_) {
@@ -123,10 +115,10 @@
   }
   starboard::scoped_refptr<InputBuffer> input_buffer =
       new InputBuffer(sample_deallocate_func_, this, context_, sample_info);
-#if SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER
   using ::starboard::shared::starboard::player::video_dmp::VideoDmpWriter;
   VideoDmpWriter::OnPlayerWriteSample(this, input_buffer);
-#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
+#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER
   worker_->WriteSample(input_buffer);
 }
 #else  // SB_API_VERSION >= 11
@@ -140,10 +132,10 @@
   starboard::scoped_refptr<InputBuffer> input_buffer =
       new InputBuffer(sample_type, sample_deallocate_func_, this, context_,
                       sample_info, &audio_sample_info_);
-#if SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER
   using ::starboard::shared::starboard::player::video_dmp::VideoDmpWriter;
   VideoDmpWriter::OnPlayerWriteSample(this, input_buffer);
-#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
+#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER
   worker_->WriteSample(input_buffer);
 }
 #endif  // SB_API_VERSION >= 11
@@ -162,23 +154,10 @@
   // TODO: Wait until a frame is rendered with the updated bounds.
 }
 
-#if SB_API_VERSION < 10
-void SbPlayerPrivate::GetInfo(SbPlayerInfo* out_player_info) {
-#else   // SB_API_VERSION < 10
 void SbPlayerPrivate::GetInfo(SbPlayerInfo2* out_player_info) {
-#endif  // SB_API_VERSION < 10
   SB_DCHECK(out_player_info != NULL);
 
   starboard::ScopedLock lock(mutex_);
-#if SB_API_VERSION < 10
-  out_player_info->duration_pts = SB_PLAYER_NO_DURATION;
-  if (is_paused_ || underflow_) {
-    out_player_info->current_media_pts = SB_TIME_TO_SB_MEDIA_TIME(media_time_);
-  } else {
-    out_player_info->current_media_pts = SB_TIME_TO_SB_MEDIA_TIME(
-        GetMediaTime(media_time_, media_time_updated_at_, playback_rate_));
-  }
-#else   // SB_API_VERSION < 10
   out_player_info->duration = SB_PLAYER_NO_DURATION;
   if (is_paused_ || underflow_) {
     out_player_info->current_media_timestamp = media_time_;
@@ -186,7 +165,6 @@
     out_player_info->current_media_timestamp =
         GetMediaTime(media_time_, media_time_updated_at_, playback_rate_);
   }
-#endif  // SB_API_VERSION < 10
 
   out_player_info->frame_width = frame_width_;
   out_player_info->frame_height = frame_height_;
diff --git a/src/starboard/shared/starboard/player/player_internal.h b/src/starboard/shared/starboard/player/player_internal.h
index d030869..da69e23 100644
--- a/src/starboard/shared/starboard/player/player_internal.h
+++ b/src/starboard/shared/starboard/player/player_internal.h
@@ -37,9 +37,7 @@
       SbPlayerDeallocateSampleFunc sample_deallocate_func,
       SbPlayerDecoderStatusFunc decoder_status_func,
       SbPlayerStatusFunc player_status_func,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
       SbPlayerErrorFunc player_error_func,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
       void* context,
       starboard::scoped_ptr<PlayerWorker::Handler> player_worker_handler);
 
@@ -53,11 +51,7 @@
   void WriteEndOfStream(SbMediaType stream_type);
   void SetBounds(int z_index, int x, int y, int width, int height);
 
-#if SB_API_VERSION < 10
-  void GetInfo(SbPlayerInfo* out_player_info);
-#else   // SB_API_VERSION < 10
   void GetInfo(SbPlayerInfo2* out_player_info);
-#endif  // SB_API_VERSION < 10
   void SetPause(bool pause);
   void SetPlaybackRate(double playback_rate);
   void SetVolume(double volume);
@@ -70,8 +64,6 @@
                   << number_of_players_ << " players.";
   }
 
-  static int number_of_players() { return number_of_players_; }
-
  private:
   SbPlayerPrivate(
       SbMediaAudioCodec audio_codec,
@@ -80,9 +72,7 @@
       SbPlayerDeallocateSampleFunc sample_deallocate_func,
       SbPlayerDecoderStatusFunc decoder_status_func,
       SbPlayerStatusFunc player_status_func,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
       SbPlayerErrorFunc player_error_func,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
       void* context,
       starboard::scoped_ptr<PlayerWorker::Handler> player_worker_handler);
 
diff --git a/src/starboard/shared/starboard/player/player_seek.cc b/src/starboard/shared/starboard/player/player_seek.cc
deleted file mode 100644
index b2ae194..0000000
--- a/src/starboard/shared/starboard/player/player_seek.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2016 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/player.h"
-
-#include "starboard/common/log.h"
-#include "starboard/shared/starboard/player/player_internal.h"
-
-#if SB_API_VERSION < 10
-void SbPlayerSeek(SbPlayer player, SbMediaTime seek_to_pts, int ticket) {
-  if (!SbPlayerIsValid(player)) {
-    SB_DLOG(WARNING) << "player is invalid.";
-    return;
-  }
-
-  player->Seek(SB_MEDIA_TIME_TO_SB_TIME(seek_to_pts), ticket);
-}
-#endif  // SB_API_VERSION < 10
diff --git a/src/starboard/shared/starboard/player/player_seek2.cc b/src/starboard/shared/starboard/player/player_seek2.cc
index 2605d99..f34ae55 100644
--- a/src/starboard/shared/starboard/player/player_seek2.cc
+++ b/src/starboard/shared/starboard/player/player_seek2.cc
@@ -17,7 +17,6 @@
 #include "starboard/common/log.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 
-#if SB_API_VERSION >= 10
 void SbPlayerSeek2(SbPlayer player, SbTime seek_to_timestamp, int ticket) {
   if (!SbPlayerIsValid(player)) {
     SB_DLOG(WARNING) << "player is invalid.";
@@ -26,4 +25,3 @@
 
   player->Seek(seek_to_timestamp, ticket);
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/player/player_worker.cc b/src/starboard/shared/starboard/player/player_worker.cc
index 4d1ab24..07a3f8e 100644
--- a/src/starboard/shared/starboard/player/player_worker.cc
+++ b/src/starboard/shared/starboard/player/player_worker.cc
@@ -65,18 +65,14 @@
     UpdateMediaInfoCB update_media_info_cb,
     SbPlayerDecoderStatusFunc decoder_status_func,
     SbPlayerStatusFunc player_status_func,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     SbPlayerErrorFunc player_error_func,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
     SbPlayer player,
     void* context) {
 
   PlayerWorker* ret = new PlayerWorker(audio_codec, video_codec, handler.Pass(),
                                        update_media_info_cb,
                                        decoder_status_func, player_status_func,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
                                        player_error_func,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
                                        player, context);
 
   if (ret && SbThreadIsValid(ret->thread_)) {
@@ -106,9 +102,7 @@
                            UpdateMediaInfoCB update_media_info_cb,
                            SbPlayerDecoderStatusFunc decoder_status_func,
                            SbPlayerStatusFunc player_status_func,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
                            SbPlayerErrorFunc player_error_func,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
                            SbPlayer player,
                            void* context)
     : thread_(kSbThreadInvalid),
@@ -118,9 +112,7 @@
       update_media_info_cb_(update_media_info_cb),
       decoder_status_func_(decoder_status_func),
       player_status_func_(player_status_func),
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
       player_error_func_(player_error_func),
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
       player_(player),
       context_(context),
       ticket_(SB_PLAYER_INITIAL_TICKET),
@@ -152,18 +144,10 @@
 }
 
 void PlayerWorker::UpdatePlayerState(SbPlayerState player_state) {
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
   if (error_occurred_) {
     SB_LOG(WARNING) << "Player state is updated after an error.";
     return;
   }
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-  SB_DCHECK(error_occurred_ == (player_state == kSbPlayerStateError))
-      << "Player state error if and only if error occurred.";
-  if (error_occurred_ && (player_state != kSbPlayerStateError)) {
-    return;
-  }
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
   player_state_ = player_state;
 
   if (!player_status_func_) {
@@ -173,7 +157,6 @@
   player_status_func_(player_, context_, player_state_, ticket_);
 }
 
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
 void PlayerWorker::UpdatePlayerError(SbPlayerError error,
                                      const std::string& error_message) {
   error_occurred_ = true;
@@ -186,13 +169,6 @@
 
   player_error_func_(player_, context_, error, error_message.c_str());
 }
-#else  // SB_HAS(PLAYER_ERROR_MESSAGE)
-void PlayerWorker::UpdatePlayerError(const std::string& message) {
-  SB_LOG(WARNING) << "encountered player error: " << message;
-
-  UpdatePlayerState(kSbPlayerStateError);
-}
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 
 // static
 void* PlayerWorker::ThreadEntryPoint(void* context) {
@@ -220,10 +196,8 @@
   SB_DCHECK(job_queue_->BelongsToCurrentThread());
 
   Handler::UpdatePlayerErrorCB update_player_error_cb;
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
   update_player_error_cb =
       std::bind(&PlayerWorker::UpdatePlayerError, this, _1, _2);
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
   std::string error_message;
   if (handler_->Init(
           player_, std::bind(&PlayerWorker::UpdateMediaInfo, this, _1, _2, _3),
@@ -232,14 +206,9 @@
           update_player_error_cb, &error_message)) {
     UpdatePlayerState(kSbPlayerStateInitialized);
   } else {
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     UpdatePlayerError(
         kSbPlayerErrorDecode,
         "Failed to initialize PlayerWorker with error " + error_message);
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-    UpdatePlayerError("Failed to initialize PlayerWorker with error " +
-                      error_message);
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
   }
 }
 
@@ -260,11 +229,7 @@
   pending_video_buffer_ = NULL;
 
   if (!handler_->Seek(seek_to_time, ticket)) {
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     UpdatePlayerError(kSbPlayerErrorDecode, "Failed seek.");
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-    UpdatePlayerError("Failed seek.");
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
     return;
   }
 
@@ -306,11 +271,7 @@
   bool written;
   bool result = handler_->WriteSample(input_buffer, &written);
   if (!result) {
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     UpdatePlayerError(kSbPlayerErrorDecode, "Failed to write sample.");
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-    UpdatePlayerError("Failed to write sample.");
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
     return;
   }
   if (written) {
@@ -370,22 +331,14 @@
   }
 
   if (!handler_->WriteEndOfStream(sample_type)) {
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     UpdatePlayerError(kSbPlayerErrorDecode, "Failed to write end of stream.");
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-    UpdatePlayerError("Failed to write end of stream.");
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
   }
 }
 
 void PlayerWorker::DoSetBounds(Bounds bounds) {
   SB_DCHECK(job_queue_->BelongsToCurrentThread());
   if (!handler_->SetBounds(bounds)) {
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     UpdatePlayerError(kSbPlayerErrorDecode, "Failed to set bounds");
-#else  // SB_HAS(PLAYER_ERROR_MESSAGE)
-    UpdatePlayerError("Failed to set bounds");
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
   }
 }
 
@@ -393,11 +346,7 @@
   SB_DCHECK(job_queue_->BelongsToCurrentThread());
 
   if (!handler_->SetPause(pause)) {
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     UpdatePlayerError(kSbPlayerErrorDecode, "Failed to set pause.");
-#else  // SB_HAS(PLAYER_ERROR_MESSAGE)
-    UpdatePlayerError("Failed to set pause.");
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
   }
 }
 
@@ -405,11 +354,7 @@
   SB_DCHECK(job_queue_->BelongsToCurrentThread());
 
   if (!handler_->SetPlaybackRate(playback_rate)) {
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     UpdatePlayerError(kSbPlayerErrorDecode, "Failed to set playback rate.");
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-    UpdatePlayerError("Failed to set playback rate.");
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
   }
 }
 
diff --git a/src/starboard/shared/starboard/player/player_worker.h b/src/starboard/shared/starboard/player/player_worker.h
index 49d7042..936756e 100644
--- a/src/starboard/shared/starboard/player/player_worker.h
+++ b/src/starboard/shared/starboard/player/player_worker.h
@@ -65,13 +65,9 @@
         UpdateMediaInfoCB;
     typedef std::function<SbPlayerState()> GetPlayerStateCB;
     typedef std::function<void(SbPlayerState player_state)> UpdatePlayerStateCB;
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     typedef std::function<void(SbPlayerError error,
                                const std::string& error_message)>
         UpdatePlayerErrorCB;
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-    typedef std::function<void()> UpdatePlayerErrorCB;
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
     virtual ~Handler() {}
 
     // All the following functions return false to signal a fatal error.  The
@@ -107,9 +103,7 @@
       UpdateMediaInfoCB update_media_info_cb,
       SbPlayerDecoderStatusFunc decoder_status_func,
       SbPlayerStatusFunc player_status_func,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
       SbPlayerErrorFunc player_error_func,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
       SbPlayer player,
       void* context);
 
@@ -170,9 +164,7 @@
                UpdateMediaInfoCB update_media_info_cb,
                SbPlayerDecoderStatusFunc decoder_status_func,
                SbPlayerStatusFunc player_status_func,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
                SbPlayerErrorFunc player_error_func,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
                SbPlayer player,
                void* context);
 
@@ -180,11 +172,7 @@
 
   SbPlayerState player_state() const { return player_state_; }
   void UpdatePlayerState(SbPlayerState player_state);
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
   void UpdatePlayerError(SbPlayerError error, const std::string& message);
-#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
-  void UpdatePlayerError(const std::string& message);
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 
   static void* ThreadEntryPoint(void* context);
   void RunLoop();
@@ -211,9 +199,7 @@
 
   SbPlayerDecoderStatusFunc decoder_status_func_;
   SbPlayerStatusFunc player_status_func_;
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
   SbPlayerErrorFunc player_error_func_;
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
   bool error_occurred_ = false;
   SbPlayer player_;
   void* context_;
diff --git a/src/starboard/shared/starboard/player/player_write_sample.cc b/src/starboard/shared/starboard/player/player_write_sample.cc
deleted file mode 100644
index ca67484..0000000
--- a/src/starboard/shared/starboard/player/player_write_sample.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2016 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/player.h"
-
-#include "starboard/common/log.h"
-#include "starboard/shared/starboard/player/player_internal.h"
-
-#if SB_API_VERSION < 10
-void SbPlayerWriteSample(SbPlayer player,
-                         SbMediaType sample_type,
-                         const void* const* sample_buffers,
-                         const int* sample_buffer_sizes,
-                         int number_of_sample_buffers,
-                         SbMediaTime sample_pts,
-                         const SbMediaVideoSampleInfo* video_sample_info,
-                         const SbDrmSampleInfo* sample_drm_info) {
-  if (!SbPlayerIsValid(player)) {
-    SB_DLOG(WARNING) << "player is invalid.";
-    return;
-  }
-
-  if (number_of_sample_buffers < 1) {
-    SB_DLOG(WARNING) << "SbPlayerWriteSample() doesn't support"
-                     << " |number_of_sample_buffers| less than one.";
-    return;
-  }
-
-  SB_DCHECK(number_of_sample_buffers == 1);
-
-  if (number_of_sample_buffers > 1) {
-    SB_DLOG(WARNING) << "SbPlayerWriteSample() doesn't support"
-                     << " |number_of_sample_buffers| greater than one.";
-    return;
-  }
-
-  if (sample_buffers == NULL) {
-    SB_DLOG(WARNING) << "|sample_buffers| cannot be NULL";
-    return;
-  }
-
-  if (sample_buffer_sizes == NULL) {
-    SB_DLOG(WARNING) << "|sample_buffer_sizes| cannot be NULL";
-    return;
-  }
-
-  SbTime sample_timestamp = SB_MEDIA_TIME_TO_SB_TIME(sample_pts);
-
-  SbPlayerSampleInfo sample_info = {
-      sample_buffers[0], sample_buffer_sizes[0], sample_timestamp,
-      video_sample_info ? video_sample_info : NULL,
-      sample_drm_info ? sample_drm_info : NULL};
-  player->WriteSample(sample_type, sample_info);
-}
-#endif  // SB_API_VERSION < 10
diff --git a/src/starboard/shared/starboard/player/player_write_sample2.cc b/src/starboard/shared/starboard/player/player_write_sample2.cc
index 9bb9f3e..bec8b26 100644
--- a/src/starboard/shared/starboard/player/player_write_sample2.cc
+++ b/src/starboard/shared/starboard/player/player_write_sample2.cc
@@ -17,7 +17,6 @@
 #include "starboard/common/log.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 
-#if SB_API_VERSION >= 10
 void SbPlayerWriteSample2(SbPlayer player,
                           SbMediaType sample_type,
                           const SbPlayerSampleInfo* sample_infos,
@@ -35,4 +34,3 @@
   player->WriteSample(sample_type, *sample_infos);
 #endif  // SB_API_VERSION >= 11
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/player/testdata/black_test_avc_1080p_30to60_fps.dmp.sha1 b/src/starboard/shared/starboard/player/testdata/black_test_avc_1080p_30to60_fps.dmp.sha1
new file mode 100644
index 0000000..e70a0d0
--- /dev/null
+++ b/src/starboard/shared/starboard/player/testdata/black_test_avc_1080p_30to60_fps.dmp.sha1
@@ -0,0 +1 @@
+eba2d4b5be46a273811f4fcbe9c3bae72bd65b37
\ No newline at end of file
diff --git a/src/starboard/shared/starboard/player/video_dmp_common.cc b/src/starboard/shared/starboard/player/video_dmp_common.cc
index 35b8e07..b7de2de 100644
--- a/src/starboard/shared/starboard/player/video_dmp_common.cc
+++ b/src/starboard/shared/starboard/player/video_dmp_common.cc
@@ -16,7 +16,6 @@
 
 #include <limits>
 
-#if SB_HAS(PLAYER_FILTER_TESTS)
 namespace starboard {
 namespace shared {
 namespace starboard {
@@ -289,4 +288,3 @@
 }  // namespace starboard
 }  // namespace shared
 }  // namespace starboard
-#endif  // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/video_dmp_common.h b/src/starboard/shared/starboard/player/video_dmp_common.h
index d1aa8fd..1353c6b 100644
--- a/src/starboard/shared/starboard/player/video_dmp_common.h
+++ b/src/starboard/shared/starboard/player/video_dmp_common.h
@@ -24,7 +24,6 @@
 #include "starboard/memory.h"
 #include "starboard/shared/internal_only.h"
 
-#if SB_HAS(PLAYER_FILTER_TESTS)
 namespace starboard {
 namespace shared {
 namespace starboard {
@@ -174,5 +173,4 @@
 }  // namespace shared
 }  // namespace starboard
 
-#endif  // SB_HAS(PLAYER_FILTER_TESTS)
 #endif  // STARBOARD_SHARED_STARBOARD_PLAYER_VIDEO_DMP_COMMON_H_
diff --git a/src/starboard/shared/starboard/player/video_dmp_reader.cc b/src/starboard/shared/starboard/player/video_dmp_reader.cc
index e4f1107..e8d3306 100644
--- a/src/starboard/shared/starboard/player/video_dmp_reader.cc
+++ b/src/starboard/shared/starboard/player/video_dmp_reader.cc
@@ -19,7 +19,6 @@
 
 #include "starboard/shared/starboard/player/file_cache_reader.h"
 
-#if SB_HAS(PLAYER_FILTER_TESTS)
 namespace starboard {
 namespace shared {
 namespace starboard {
@@ -262,4 +261,3 @@
 }  // namespace starboard
 }  // namespace shared
 }  // namespace starboard
-#endif  // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/video_dmp_reader.h b/src/starboard/shared/starboard/player/video_dmp_reader.h
index 563e03a..7333b8f 100644
--- a/src/starboard/shared/starboard/player/video_dmp_reader.h
+++ b/src/starboard/shared/starboard/player/video_dmp_reader.h
@@ -25,7 +25,6 @@
 #include "starboard/shared/internal_only.h"
 #include "starboard/shared/starboard/player/video_dmp_common.h"
 
-#if SB_HAS(PLAYER_FILTER_TESTS)
 namespace starboard {
 namespace shared {
 namespace starboard {
@@ -141,5 +140,4 @@
 }  // namespace shared
 }  // namespace starboard
 
-#endif  // SB_HAS(PLAYER_FILTER_TESTS)
 #endif  // STARBOARD_SHARED_STARBOARD_PLAYER_VIDEO_DMP_READER_H_
diff --git a/src/starboard/shared/starboard/player/video_dmp_writer.cc b/src/starboard/shared/starboard/player/video_dmp_writer.cc
index 4119604..22c89f4 100644
--- a/src/starboard/shared/starboard/player/video_dmp_writer.cc
+++ b/src/starboard/shared/starboard/player/video_dmp_writer.cc
@@ -24,7 +24,6 @@
 #include "starboard/once.h"
 #include "starboard/shared/starboard/application.h"
 
-#if SB_HAS(PLAYER_FILTER_TESTS)
 namespace starboard {
 namespace shared {
 namespace starboard {
@@ -211,4 +210,3 @@
 }  // namespace starboard
 }  // namespace shared
 }  // namespace starboard
-#endif  // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/video_dmp_writer.h b/src/starboard/shared/starboard/player/video_dmp_writer.h
index 3255b68..fef0e10 100644
--- a/src/starboard/shared/starboard/player/video_dmp_writer.h
+++ b/src/starboard/shared/starboard/player/video_dmp_writer.h
@@ -22,7 +22,6 @@
 #include "starboard/shared/starboard/player/input_buffer_internal.h"
 #include "starboard/shared/starboard/player/video_dmp_common.h"
 
-#if SB_HAS(PLAYER_FILTER_TESTS)
 namespace starboard {
 namespace shared {
 namespace starboard {
@@ -67,5 +66,4 @@
 }  // namespace shared
 }  // namespace starboard
 
-#endif  // SB_HAS(PLAYER_FILTER_TESTS)
 #endif  // STARBOARD_SHARED_STARBOARD_PLAYER_VIDEO_DMP_WRITER_H_
diff --git a/src/starboard/shared/starboard/system_supports_resume.cc b/src/starboard/shared/starboard/system_supports_resume.cc
index d0dc340..822f960 100644
--- a/src/starboard/shared/starboard/system_supports_resume.cc
+++ b/src/starboard/shared/starboard/system_supports_resume.cc
@@ -14,10 +14,6 @@
 
 #include "starboard/system.h"
 
-#if SB_API_VERSION >= 10
-
 bool SbSystemSupportsResume() {
   return true;
 }
-
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/stub/atomic_public.h b/src/starboard/shared/stub/atomic_public.h
index ab97c05..68b5857 100644
--- a/src/starboard/shared/stub/atomic_public.h
+++ b/src/starboard/shared/stub/atomic_public.h
@@ -110,7 +110,6 @@
   return 0;
 }
 
-#if SB_API_VERSION >= 10
 SB_C_FORCE_INLINE SbAtomic8
 SbAtomicRelease_CompareAndSwap8(volatile SbAtomic8* ptr,
                                SbAtomic8 old_value,
@@ -132,7 +131,6 @@
   SB_UNREFERENCED_PARAMETER(ptr);
   return 0;
 }
-#endif  // SB_API_VERSION >= 10
 
 // 64-bit atomic operations (only available on 64-bit processors).
 #if SB_HAS(64_BIT_ATOMICS)
diff --git a/src/starboard/shared/stub/cryptography_create_transformer.cc b/src/starboard/shared/stub/cryptography_create_transformer.cc
index a0e64ab..ee81941 100644
--- a/src/starboard/shared/stub/cryptography_create_transformer.cc
+++ b/src/starboard/shared/stub/cryptography_create_transformer.cc
@@ -13,6 +13,9 @@
 // limitations under the License.
 
 #include "starboard/configuration.h"
+
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #include "starboard/cryptography.h"
 
 SbCryptographyTransformer SbCryptographyCreateTransformer(
@@ -34,3 +37,5 @@
   SB_UNREFERENCED_PARAMETER(key_size);
   return kSbCryptographyInvalidTransformer;
 }
+
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
diff --git a/src/starboard/shared/stub/cryptography_destroy_transformer.cc b/src/starboard/shared/stub/cryptography_destroy_transformer.cc
index bfc9bea..79680c5 100644
--- a/src/starboard/shared/stub/cryptography_destroy_transformer.cc
+++ b/src/starboard/shared/stub/cryptography_destroy_transformer.cc
@@ -13,8 +13,13 @@
 // limitations under the License.
 
 #include "starboard/configuration.h"
+
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #include "starboard/cryptography.h"
 
 void SbCryptographyDestroyTransformer(SbCryptographyTransformer transformer) {
   SB_UNREFERENCED_PARAMETER(transformer);
 }
+
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
diff --git a/src/starboard/shared/stub/cryptography_get_tag.cc b/src/starboard/shared/stub/cryptography_get_tag.cc
index 13fb677..ff75ba3 100644
--- a/src/starboard/shared/stub/cryptography_get_tag.cc
+++ b/src/starboard/shared/stub/cryptography_get_tag.cc
@@ -13,6 +13,9 @@
 // limitations under the License.
 
 #include "starboard/configuration.h"
+
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #include "starboard/cryptography.h"
 
 bool SbCryptographyGetTag(
@@ -24,3 +27,5 @@
   SB_UNREFERENCED_PARAMETER(out_tag_size);
   return false;
 }
+
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
diff --git a/src/starboard/shared/stub/cryptography_set_authenticated_data.cc b/src/starboard/shared/stub/cryptography_set_authenticated_data.cc
index e47ebe7..b6df57e 100644
--- a/src/starboard/shared/stub/cryptography_set_authenticated_data.cc
+++ b/src/starboard/shared/stub/cryptography_set_authenticated_data.cc
@@ -13,6 +13,9 @@
 // limitations under the License.
 
 #include "starboard/configuration.h"
+
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #include "starboard/cryptography.h"
 
 bool SbCryptographySetAuthenticatedData(
@@ -24,3 +27,5 @@
   SB_UNREFERENCED_PARAMETER(data_size);
   return false;
 }
+
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
diff --git a/src/starboard/shared/stub/cryptography_set_initialization_vector.cc b/src/starboard/shared/stub/cryptography_set_initialization_vector.cc
index 36a7bfd..94c2485 100644
--- a/src/starboard/shared/stub/cryptography_set_initialization_vector.cc
+++ b/src/starboard/shared/stub/cryptography_set_initialization_vector.cc
@@ -13,6 +13,9 @@
 // limitations under the License.
 
 #include "starboard/configuration.h"
+
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #include "starboard/cryptography.h"
 
 void SbCryptographySetInitializationVector(
@@ -23,3 +26,5 @@
   SB_UNREFERENCED_PARAMETER(initialization_vector);
   SB_UNREFERENCED_PARAMETER(initialization_vector_size);
 }
+
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
diff --git a/src/starboard/shared/stub/cryptography_transform.cc b/src/starboard/shared/stub/cryptography_transform.cc
index a96e40a..b6266e2 100644
--- a/src/starboard/shared/stub/cryptography_transform.cc
+++ b/src/starboard/shared/stub/cryptography_transform.cc
@@ -13,6 +13,9 @@
 // limitations under the License.
 
 #include "starboard/configuration.h"
+
+#if SB_API_VERSION < SB_CRYPTOAPI_DEPRECATED_VERSION
+
 #include "starboard/cryptography.h"
 
 int SbCryptographyTransform(SbCryptographyTransformer transformer,
@@ -25,3 +28,5 @@
   SB_UNREFERENCED_PARAMETER(out_data);
   return 0;
 }
+
+#endif  // SB_CRYPTOAPI_DEPRECATED_VERSION
diff --git a/src/starboard/shared/stub/drm_create_system.cc b/src/starboard/shared/stub/drm_create_system.cc
index 5d8628f..67cdc93 100644
--- a/src/starboard/shared/stub/drm_create_system.cc
+++ b/src/starboard/shared/stub/drm_create_system.cc
@@ -14,8 +14,6 @@
 
 #include "starboard/drm.h"
 
-#if SB_API_VERSION >= 10
-
 SbDrmSystem SbDrmCreateSystem(
     const char* key_system,
     void* context,
@@ -33,39 +31,3 @@
   SB_UNREFERENCED_PARAMETER(session_closed_callback);
   return kSbDrmSystemInvalid;
 }
-
-#elif SB_HAS(DRM_SESSION_CLOSED)
-
-SbDrmSystem SbDrmCreateSystem(
-    const char* key_system,
-    void* context,
-    SbDrmSessionUpdateRequestFunc update_request_callback,
-    SbDrmSessionUpdatedFunc session_updated_callback,
-    SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback,
-    SbDrmSessionClosedFunc session_closed_callback) {
-  SB_UNREFERENCED_PARAMETER(context);
-  SB_UNREFERENCED_PARAMETER(key_system);
-  SB_UNREFERENCED_PARAMETER(update_request_callback);
-  SB_UNREFERENCED_PARAMETER(session_updated_callback);
-  SB_UNREFERENCED_PARAMETER(key_statuses_changed_callback);
-  SB_UNREFERENCED_PARAMETER(session_closed_callback);
-  return kSbDrmSystemInvalid;
-}
-
-#else  // SB_HAS(DRM_SESSION_CLOSED)
-
-SbDrmSystem SbDrmCreateSystem(
-    const char* key_system,
-    void* context,
-    SbDrmSessionUpdateRequestFunc update_request_callback,
-    SbDrmSessionUpdatedFunc session_updated_callback,
-    SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback) {
-  SB_UNREFERENCED_PARAMETER(context);
-  SB_UNREFERENCED_PARAMETER(key_system);
-  SB_UNREFERENCED_PARAMETER(update_request_callback);
-  SB_UNREFERENCED_PARAMETER(session_updated_callback);
-  SB_UNREFERENCED_PARAMETER(key_statuses_changed_callback);
-  return kSbDrmSystemInvalid;
-}
-
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
diff --git a/src/starboard/shared/stub/drm_is_server_certificate_updatable.cc b/src/starboard/shared/stub/drm_is_server_certificate_updatable.cc
index 5e17c55..8169bd6 100644
--- a/src/starboard/shared/stub/drm_is_server_certificate_updatable.cc
+++ b/src/starboard/shared/stub/drm_is_server_certificate_updatable.cc
@@ -14,12 +14,8 @@
 
 #include "starboard/drm.h"
 
-#if SB_API_VERSION >= 10
-
 bool SbDrmIsServerCertificateUpdatable(SbDrmSystem drm_system) {
   SB_UNREFERENCED_PARAMETER(drm_system);
 
   return false;
 }
-
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/stub/drm_update_server_certificate.cc b/src/starboard/shared/stub/drm_update_server_certificate.cc
index 3d25cd8..635b6ef 100644
--- a/src/starboard/shared/stub/drm_update_server_certificate.cc
+++ b/src/starboard/shared/stub/drm_update_server_certificate.cc
@@ -14,8 +14,6 @@
 
 #include "starboard/drm.h"
 
-#if SB_API_VERSION >= 10
-
 void SbDrmUpdateServerCertificate(SbDrmSystem drm_system,
                                   int ticket,
                                   const void* certificate,
@@ -25,5 +23,3 @@
   SB_UNREFERENCED_PARAMETER(certificate);
   SB_UNREFERENCED_PARAMETER(certificate_size);
 }
-
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/stub/media_is_audio_supported.cc b/src/starboard/shared/stub/media_is_audio_supported.cc
index c8a199b..c180d4f 100644
--- a/src/starboard/shared/stub/media_is_audio_supported.cc
+++ b/src/starboard/shared/stub/media_is_audio_supported.cc
@@ -17,6 +17,9 @@
 #include "starboard/media.h"
 
 bool SbMediaIsAudioSupported(SbMediaAudioCodec /*audio_codec*/,
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                             const char* /*content_type*/,
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
                              int64_t /*bitrate*/) {
   return false;
 }
diff --git a/src/starboard/shared/stub/media_is_video_supported.cc b/src/starboard/shared/stub/media_is_video_supported.cc
index 4ceead5..eaeb8ad 100644
--- a/src/starboard/shared/stub/media_is_video_supported.cc
+++ b/src/starboard/shared/stub/media_is_video_supported.cc
@@ -17,6 +17,9 @@
 #include "starboard/media.h"
 
 bool SbMediaIsVideoSupported(SbMediaVideoCodec /*video_codec*/,
+#if SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
+                             const char* /*content_type*/,
+#endif  // SB_API_VERSION >= SB_MEDIA_SUPPORT_QUERY_WITH_CONTENT_TYPE_VERSION
 #if SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
                              int /*profile*/,
                              int /*level*/,
@@ -28,11 +31,7 @@
                              int /*frame_width*/,
                              int /*frame_height*/,
                              int64_t /*bitrate*/,
-                             int /*fps*/
-#if SB_API_VERSION >= 10
-                             ,
-                             bool /*decode_to_texture_required*/
-#endif  // SB_API_VERSION >= 10
-                             ) {
+                             int /*fps*/,
+                             bool /*decode_to_texture_required*/) {
   return false;
 }
diff --git a/src/starboard/shared/stub/memory_protect.cc b/src/starboard/shared/stub/memory_protect.cc
index 036b8fb..99ca1e9 100644
--- a/src/starboard/shared/stub/memory_protect.cc
+++ b/src/starboard/shared/stub/memory_protect.cc
@@ -15,10 +15,8 @@
 #include "starboard/configuration.h"
 #include "starboard/memory.h"
 
-#if SB_API_VERSION >= 10
 bool SbMemoryProtect(void* /*virtual_address*/,
                      int64_t /*size_bytes*/,
                      int /*flags*/) {
   return false;
 }
-#endif
diff --git a/src/starboard/shared/stub/player_create.cc b/src/starboard/shared/stub/player_create.cc
index ff11851..a5a905f 100644
--- a/src/starboard/shared/stub/player_create.cc
+++ b/src/starboard/shared/stub/player_create.cc
@@ -35,9 +35,6 @@
 SbPlayer SbPlayerCreate(SbWindow /*window*/,
                         SbMediaVideoCodec /*video_codec*/,
                         SbMediaAudioCodec /*audio_codec*/,
-#if SB_API_VERSION < 10
-                        SbMediaTime /*duration_pts*/,
-#endif  // SB_API_VERSION < 10
                         SbDrmSystem /*drm_system*/,
                         const SbMediaAudioSampleInfo* /*audio_sample_info*/,
 #if SB_API_VERSION >= 11
diff --git a/src/starboard/shared/stub/player_get_info.cc b/src/starboard/shared/stub/player_get_info.cc
deleted file mode 100644
index 6f07066..0000000
--- a/src/starboard/shared/stub/player_get_info.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2016 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/player.h"
-
-#if SB_API_VERSION < 10
-void SbPlayerGetInfo(SbPlayer /*player*/, SbPlayerInfo* /*out_player_info*/) {}
-#endif  // SB_API_VERSION < 10
diff --git a/src/starboard/shared/stub/player_get_info2.cc b/src/starboard/shared/stub/player_get_info2.cc
index 77c3603..4b443c1 100644
--- a/src/starboard/shared/stub/player_get_info2.cc
+++ b/src/starboard/shared/stub/player_get_info2.cc
@@ -14,7 +14,5 @@
 
 #include "starboard/player.h"
 
-#if SB_API_VERSION >= 10
 void SbPlayerGetInfo2(SbPlayer /*player*/, SbPlayerInfo2* /*out_player_info*/) {
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/stub/player_get_maximum_number_of_samples_per_write.cc b/src/starboard/shared/stub/player_get_maximum_number_of_samples_per_write.cc
index adb568e..ebba599 100644
--- a/src/starboard/shared/stub/player_get_maximum_number_of_samples_per_write.cc
+++ b/src/starboard/shared/stub/player_get_maximum_number_of_samples_per_write.cc
@@ -14,9 +14,7 @@
 
 #include "starboard/player.h"
 
-#if SB_API_VERSION >= 10
 int SbPlayerGetMaximumNumberOfSamplesPerWrite(SbPlayer /*player*/,
                                               SbMediaType /*sample_type*/) {
   return 0;
 }
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/stub/player_seek.cc b/src/starboard/shared/stub/player_seek.cc
deleted file mode 100644
index c09a56a..0000000
--- a/src/starboard/shared/stub/player_seek.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2016 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/player.h"
-
-#if SB_API_VERSION < 10
-void SbPlayerSeek(SbPlayer /*player*/,
-                  SbMediaTime /*seek_to_pts*/,
-                  int /*ticket*/) {}
-#endif  // SB_API_VERSION < 10
diff --git a/src/starboard/shared/stub/player_seek2.cc b/src/starboard/shared/stub/player_seek2.cc
index be6cb32..23e4b82 100644
--- a/src/starboard/shared/stub/player_seek2.cc
+++ b/src/starboard/shared/stub/player_seek2.cc
@@ -14,8 +14,6 @@
 
 #include "starboard/player.h"
 
-#if SB_API_VERSION >= 10
 void SbPlayerSeek2(SbPlayer /*player*/,
                    SbTime /*seek_to_timestamp*/,
                    int /*ticket*/) {}
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/stub/player_write_sample.cc b/src/starboard/shared/stub/player_write_sample.cc
deleted file mode 100644
index 0fb7059..0000000
--- a/src/starboard/shared/stub/player_write_sample.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2016 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/player.h"
-
-#if SB_API_VERSION < 10
-void SbPlayerWriteSample(SbPlayer /*player*/,
-                         SbMediaType /*sample_type*/,
-                         const void* const* /*sample_buffers*/,
-                         const int* /*sample_buffer_sizes*/,
-                         int /*number_of_sample_buffers*/,
-                         SbMediaTime /*sample_pts*/,
-                         const SbMediaVideoSampleInfo* /*video_sample_info*/,
-                         const SbDrmSampleInfo* /*sample_drm_info*/) {
-}
-#endif  // SB_API_VERSION < 10
diff --git a/src/starboard/shared/stub/player_write_sample2.cc b/src/starboard/shared/stub/player_write_sample2.cc
index e5bdc7f..4ed00c4 100644
--- a/src/starboard/shared/stub/player_write_sample2.cc
+++ b/src/starboard/shared/stub/player_write_sample2.cc
@@ -14,9 +14,7 @@
 
 #include "starboard/player.h"
 
-#if SB_API_VERSION >= 10
 void SbPlayerWriteSample2(SbPlayer /*player*/,
                           SbMediaType /*sample_type*/,
                           const SbPlayerSampleInfo* /*sample_infos*/,
                           int /*number_of_sample_infos*/) {}
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/stub/system_supports_resume.cc b/src/starboard/shared/stub/system_supports_resume.cc
index d0dc340..822f960 100644
--- a/src/starboard/shared/stub/system_supports_resume.cc
+++ b/src/starboard/shared/stub/system_supports_resume.cc
@@ -14,10 +14,6 @@
 
 #include "starboard/system.h"
 
-#if SB_API_VERSION >= 10
-
 bool SbSystemSupportsResume() {
   return true;
 }
-
-#endif  // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/wayland/dev_input.cc b/src/starboard/shared/wayland/dev_input.cc
index 3cd3493..cc5a6b1 100644
--- a/src/starboard/shared/wayland/dev_input.cc
+++ b/src/starboard/shared/wayland/dev_input.cc
@@ -524,9 +524,7 @@
 void DevInput::CreateKey(int key, int state, bool is_repeat) {
   SbInputData* data = new SbInputData();
   SbMemorySet(data, 0, sizeof(*data));
-#if SB_API_VERSION >= 10
   data->timestamp = SbTimeGetMonotonicNow();
-#endif  // SB_API_VERSION >= 10
   data->window = window_;
   data->type = (state == 0 ? kSbInputEventTypeUnpress : kSbInputEventTypePress);
   data->device_type = kSbInputDeviceTypeRemote;
diff --git a/src/starboard/shared/widevine/drm_create_system.cc b/src/starboard/shared/widevine/drm_create_system.cc
index 595cf4d..0ceebdb 100644
--- a/src/starboard/shared/widevine/drm_create_system.cc
+++ b/src/starboard/shared/widevine/drm_create_system.cc
@@ -31,13 +31,9 @@
     void* context,
     SbDrmSessionUpdateRequestFunc update_request_callback,
     SbDrmSessionUpdatedFunc session_updated_callback,
-    SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback
-#if SB_API_VERSION >= 10
-    ,
+    SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback,
     SbDrmServerCertificateUpdatedFunc server_certificate_updated_callback,
-    SbDrmSessionClosedFunc session_closed_callback
-#endif  // SB_API_VERSION >= 10
-    ) {
+    SbDrmSessionClosedFunc session_closed_callback) {
   using starboard::shared::widevine::DrmSystemWidevine;
   if (!update_request_callback || !session_updated_callback) {
     return kSbDrmSystemInvalid;
@@ -45,29 +41,17 @@
   if (!key_statuses_changed_callback) {
     return kSbDrmSystemInvalid;
   }
-#if SB_API_VERSION >= 10
   if (!server_certificate_updated_callback || !session_closed_callback) {
     return kSbDrmSystemInvalid;
   }
-#endif  // SB_API_VERSION >= 10
   if (!DrmSystemWidevine::IsKeySystemSupported()) {
     SB_DLOG(WARNING) << "Invalid key system " << key_system;
     return kSbDrmSystemInvalid;
   }
   SB_LOG(ERROR) << "|company_name| and |model_name| are set to \"www\", "
                 << "premium content playback resolution may be limited.";
-  return new DrmSystemWidevine(context, update_request_callback,
-                               session_updated_callback
-                               ,
-                               key_statuses_changed_callback
-#if SB_API_VERSION >= 10
-                               ,
-                               server_certificate_updated_callback
-#endif  // SB_API_VERSION >= 10
-#if SB_HAS(DRM_SESSION_CLOSED)
-                               ,
-                               session_closed_callback
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
-                               ,
-                               kCompanyName, kModelName);
+  return new DrmSystemWidevine(
+      context, update_request_callback, session_updated_callback,
+      key_statuses_changed_callback, server_certificate_updated_callback,
+      session_closed_callback, kCompanyName, kModelName);
 }
diff --git a/src/starboard/shared/widevine/drm_system_widevine.cc b/src/starboard/shared/widevine/drm_system_widevine.cc
index 398b806..fb6b48e 100644
--- a/src/starboard/shared/widevine/drm_system_widevine.cc
+++ b/src/starboard/shared/widevine/drm_system_widevine.cc
@@ -204,28 +204,17 @@
     void* context,
     SbDrmSessionUpdateRequestFunc session_update_request_callback,
     SbDrmSessionUpdatedFunc session_updated_callback,
-    SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback
-#if SB_API_VERSION >= 10
-    ,
-    SbDrmServerCertificateUpdatedFunc server_certificate_updated_callback
-#endif  // SB_API_VERSION >= 10
-#if SB_HAS(DRM_SESSION_CLOSED)
-    ,
-    SbDrmSessionClosedFunc session_closed_callback
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
-    ,
+    SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback,
+    SbDrmServerCertificateUpdatedFunc server_certificate_updated_callback,
+    SbDrmSessionClosedFunc session_closed_callback,
     const std::string& company_name,
     const std::string& model_name)
     : context_(context),
       session_update_request_callback_(session_update_request_callback),
       session_updated_callback_(session_updated_callback),
       key_statuses_changed_callback_(key_statuses_changed_callback),
-#if SB_API_VERSION >= 10
       server_certificate_updated_callback_(server_certificate_updated_callback),
-#endif  // SB_API_VERSION >= 10
-#if SB_HAS(DRM_SESSION_CLOSED)
       session_closed_callback_(session_closed_callback),
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
       ticket_thread_id_(SbThreadGetId()) {
   SB_DCHECK(!company_name.empty());
   SB_DCHECK(!model_name.empty());
@@ -245,11 +234,7 @@
 #endif  // !defined(COBALT_BUILD_TYPE_GOLD)
 
   EnsureWidevineCdmIsInitialized(company_name, model_name);
-#if SB_API_VERSION >= 10
   const bool kEnablePrivacyMode = true;
-#else   // SB_API_VERSION >= 10
-  const bool kEnablePrivacyMode = false;
-#endif  // SB_API_VERSION >= 10
   cdm_.reset(wv3cdm::create(this, NULL, kEnablePrivacyMode));
   SB_DCHECK(cdm_);
 
@@ -332,14 +317,9 @@
     first_update_session_received_.store(true);
   }
   SB_DLOG(INFO) << "Update keys status " << status;
-#if SB_API_VERSION >= 10
   session_updated_callback_(this, context_, ticket,
                             CdmStatusToSbDrmStatus(status), "",
                             sb_drm_session_id, sb_drm_session_id_size);
-#else   // SB_API_VERSION >= 10
-  session_updated_callback_(this, context_, ticket, sb_drm_session_id,
-                            sb_drm_session_id_size, status == wv3cdm::kSuccess);
-#endif  // SB_API_VERSION >= 10
 
   // It is possible that |key| actually contains a server certificate, in such
   // case try to process the pending GenerateSessionUpdateRequest() calls.
@@ -355,13 +335,10 @@
   if (succeeded) {
     cdm_->close(wvcdm_session_id);
   }
-#if SB_HAS(DRM_SESSION_CLOSED)
   session_closed_callback_(this, context_, sb_drm_session_id,
                            sb_drm_session_id_size);
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
 }
 
-#if SB_API_VERSION >= 10
 void DrmSystemWidevine::UpdateServerCertificate(int ticket,
                                                 const void* certificate,
                                                 int certificate_size) {
@@ -375,7 +352,6 @@
   server_certificate_updated_callback_(this, context_, ticket,
                                        CdmStatusToSbDrmStatus(status), "");
 }
-#endif  // SB_API_VERSION >= 10
 
 void IncrementIv(uint8_t* iv, size_t block_count) {
   if (0 == block_count)
@@ -578,15 +554,14 @@
               kSbDrmTicketInvalid);
 
     SB_DLOG(ERROR) << "GenerateKeyRequest status " << status;
-// Send an empty request to signal an error.
-#if SB_API_VERSION >= 10
+    const char* session_id =
+        SbDrmTicketIsValid(ticket) ? NULL : kFirstSbDrmSessionId;
+    int session_id_size =
+        session_id ? static_cast<int>(SbStringGetLength(session_id)) : 0;
     session_update_request_callback_(
         this, context_, ticket, CdmStatusToSbDrmStatus(status),
-        kSbDrmSessionRequestTypeLicenseRequest, "", NULL, 0, NULL, 0, NULL);
-#else   // SB_API_VERSION >= 10
-    session_update_request_callback_(this, context_, ticket, NULL, 0, NULL, 0,
-                                     NULL);
-#endif  // SB_API_VERSION >= 10
+        kSbDrmSessionRequestTypeLicenseRequest, "", session_id, session_id_size,
+        NULL, 0, NULL);
   }
 
   // When |status| is |kDeferred|, it indicates that the cdm requires
@@ -699,12 +674,10 @@
 
 std::string DrmSystemWidevine::WvdmSessionIdToSbDrmSessionId(
     const std::string& wvcdm_session_id) {
-#if SB_API_VERSION >= 10
   SB_DCHECK(wvcdm_session_id != kFirstSbDrmSessionId);
   if (wvcdm_session_id == first_wvcdm_session_id_) {
     return kFirstSbDrmSessionId;
   }
-#endif  // SB_API_VERSION >= 10
   return wvcdm_session_id;
 }
 
@@ -715,12 +688,10 @@
   SB_DCHECK(wvcdm_session_id);
   const std::string str_sb_drm_session_id(
       static_cast<const char*>(sb_drm_session_id), sb_drm_session_id_size);
-#if SB_API_VERSION >= 10
   if (str_sb_drm_session_id == kFirstSbDrmSessionId) {
     *wvcdm_session_id = first_wvcdm_session_id_;
     return !first_wvcdm_session_id_.empty();
   }
-#endif  // SB_API_VERSION >= 10
   *wvcdm_session_id = str_sb_drm_session_id;
   return true;
 }
@@ -737,14 +708,9 @@
                              kFirstSbDrmSessionId, message);
   } else {
 // Signals failure by sending NULL as the session id.
-#if SB_API_VERSION >= 10
     session_update_request_callback_(
         this, context_, ticket, CdmStatusToSbDrmStatus(status),
         kSbDrmSessionRequestTypeLicenseRequest, "", NULL, 0, NULL, 0, NULL);
-#else   // SB_API_VERSION >= 10
-    session_update_request_callback_(this, context_, ticket, NULL, 0, NULL, 0,
-                                     NULL);
-#endif  // SB_API_VERSION >= 10
   }
 }
 
@@ -783,8 +749,6 @@
     const std::string& message) {
   int ticket = GetAndResetTicket(sb_drm_session_id);
 
-#if SB_API_VERSION >= 10
-
 #if !defined(COBALT_BUILD_TYPE_GOLD)
   if (number_of_session_updates_sent_ > maximum_number_of_session_updates_) {
     SB_LOG(INFO) << "Number of drm sessions exceeds maximum allowed session"
@@ -802,12 +766,6 @@
       this, context_, ticket, kSbDrmStatusSuccess, type, "",
       sb_drm_session_id.c_str(), static_cast<int>(sb_drm_session_id.size()),
       message.c_str(), static_cast<int>(message.size()), NULL);
-#else   // SB_API_VERSION >= 10
-  session_update_request_callback_(
-      this, context_, ticket, sb_drm_session_id.c_str(),
-      static_cast<int>(sb_drm_session_id.size()), message.c_str(),
-      static_cast<int>(message.size()), NULL);
-#endif  // SB_API_VERSION >= 10
 }
 
 }  // namespace widevine
diff --git a/src/starboard/shared/widevine/drm_system_widevine.h b/src/starboard/shared/widevine/drm_system_widevine.h
index fb7bce7..c1a66a9 100644
--- a/src/starboard/shared/widevine/drm_system_widevine.h
+++ b/src/starboard/shared/widevine/drm_system_widevine.h
@@ -45,16 +45,9 @@
       void* context,
       SbDrmSessionUpdateRequestFunc update_request_callback,
       SbDrmSessionUpdatedFunc session_updated_callback,
-      SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback
-#if SB_API_VERSION >= 10
-      ,
-      SbDrmServerCertificateUpdatedFunc server_certificate_updated_callback
-#endif  // SB_API_VERSION >= 10
-#if SB_HAS(DRM_SESSION_CLOSED)
-      ,
-      SbDrmSessionClosedFunc session_closed_callback
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
-      ,
+      SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback,
+      SbDrmServerCertificateUpdatedFunc server_certificate_updated_callback,
+      SbDrmSessionClosedFunc session_closed_callback,
       const std::string& company_name,
       const std::string& model_name);
 
@@ -80,7 +73,6 @@
 
   DecryptStatus Decrypt(InputBuffer* buffer) override;
 
-#if SB_API_VERSION >= 10
   bool IsServerCertificateUpdatable() override { return true; }
 
   // This function is called by the app to explicitly set the server
@@ -92,7 +84,6 @@
   void UpdateServerCertificate(int ticket,
                                const void* certificate,
                                int certificate_size) override;
-#endif  // SB_API_VERSION >= 10
 
  private:
   // Stores the data necessary to call GenerateSessionUpdateRequestInternal().
@@ -164,12 +155,8 @@
   const SbDrmSessionUpdateRequestFunc session_update_request_callback_;
   const SbDrmSessionUpdatedFunc session_updated_callback_;
   const SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback_;
-#if SB_API_VERSION >= 10
   const SbDrmServerCertificateUpdatedFunc server_certificate_updated_callback_;
-#endif  // SB_API_VERSION >= 10
-#if SB_HAS(DRM_SESSION_CLOSED)
   const SbDrmSessionClosedFunc session_closed_callback_;
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
 
   // Store a map from session id generated by the cdm to its associated ticket
   // id.  The ticket is a unique id passed to GenerateSessionUpdateRequest() to
@@ -194,11 +181,8 @@
   std::string first_wvcdm_session_id_;
 
   scoped_ptr<::widevine::Cdm> cdm_;
-#if SB_API_VERSION >= 10
+
   bool is_server_certificate_set_ = false;
-#else   // SB_API_VERSION >= 10
-  bool is_server_certificate_set_ = true;
-#endif  // SB_API_VERSION >= 10
 
   volatile bool quitting_ = false;
 
diff --git a/src/starboard/shared/x11/application_x11.cc b/src/starboard/shared/x11/application_x11.cc
index 42376d4..07cfc1f 100644
--- a/src/starboard/shared/x11/application_x11.cc
+++ b/src/starboard/shared/x11/application_x11.cc
@@ -1144,9 +1144,7 @@
 
   scoped_ptr<SbInputData> data(new SbInputData());
   SbMemorySet(data.get(), 0, sizeof(*data));
-#if SB_API_VERSION >= 10
   data->timestamp = SbTimeGetMonotonicNow();
-#endif  // SB_API_VERSION >= 10
   data->window = windows_[0];
   SB_DCHECK(SbWindowIsValid(data->window));
   data->type = paste_buffer_key_release_pending_ ? kSbInputEventTypeUnpress
@@ -1212,9 +1210,7 @@
 
       scoped_ptr<SbInputData> data(new SbInputData());
       SbMemorySet(data.get(), 0, sizeof(*data));
-#if SB_API_VERSION >= 10
       data->timestamp = SbTimeGetMonotonicNow();
-#endif  // SB_API_VERSION >= 10
       data->window = FindWindow(x_key_event->window);
       SB_DCHECK(SbWindowIsValid(data->window));
       data->type = x_event->type == KeyPress ? kSbInputEventTypePress
@@ -1240,9 +1236,7 @@
       }
       scoped_ptr<SbInputData> data(new SbInputData());
       SbMemorySet(data.get(), 0, sizeof(*data));
-#if SB_API_VERSION >= 10
       data->timestamp = SbTimeGetMonotonicNow();
-#endif  // SB_API_VERSION >= 10
       data->window = FindWindow(x_button_event->window);
       SB_DCHECK(SbWindowIsValid(data->window));
       data->key = XButtonEventToSbKey(x_button_event);
@@ -1267,9 +1261,7 @@
       XMotionEvent* x_motion_event = reinterpret_cast<XMotionEvent*>(x_event);
       scoped_ptr<SbInputData> data(new SbInputData());
       SbMemorySet(data.get(), 0, sizeof(*data));
-#if SB_API_VERSION >= 10
       data->timestamp = SbTimeGetMonotonicNow();
-#endif  // SB_API_VERSION >= 10
       data->window = FindWindow(x_motion_event->window);
       SB_DCHECK(SbWindowIsValid(data->window));
       data->pressure = NAN;
diff --git a/src/starboard/starboard.syms b/src/starboard/starboard.syms
index a83a7ec..a684024 100644
--- a/src/starboard/starboard.syms
+++ b/src/starboard/starboard.syms
@@ -1,3 +1,4 @@
 {
   Sb*;
+  kSb*;
 };
diff --git a/src/starboard/starboard_all.gyp b/src/starboard/starboard_all.gyp
index caf7e36..a8ab024 100644
--- a/src/starboard/starboard_all.gyp
+++ b/src/starboard/starboard_all.gyp
@@ -64,6 +64,7 @@
         '<(DEPTH)/starboard/examples/blitter/blitter.gyp:*',
         '<(DEPTH)/starboard/examples/window/window.gyp:*',
         '<(DEPTH)/starboard/nplb/blitter_pixel_tests/blitter_pixel_tests.gyp:*',
+        '<(DEPTH)/starboard/nplb/nplb_evergreen_compat_tests/nplb_evergreen_compat_tests.gyp:*',
         '<(DEPTH)/starboard/nplb/nplb.gyp:*',
         '<(DEPTH)/starboard/starboard.gyp:*',
         '<(DEPTH)/starboard/tools/tools.gyp:*',
@@ -85,7 +86,7 @@
         }],
         ['sb_filter_based_player==1', {
           'dependencies': [
-            '<(DEPTH)/starboard/shared/starboard/player/filter/testing/player_filter_tests.gyp:*',
+            '<(DEPTH)/starboard/shared/starboard/player/filter/testing/player_filter_tests.gyp:player_filter_tests_deploy',
             '<(DEPTH)/starboard/shared/starboard/player/filter/tools/tools.gyp:*',
           ],
         }],
diff --git a/src/starboard/stub/BUILD.gn b/src/starboard/stub/BUILD.gn
index 32a6ec3..adda976 100644
--- a/src/starboard/stub/BUILD.gn
+++ b/src/starboard/stub/BUILD.gn
@@ -279,15 +279,12 @@
     "//starboard/shared/stub/player_create.cc",
     "//starboard/shared/stub/player_destroy.cc",
     "//starboard/shared/stub/player_get_current_frame.cc",
-    "//starboard/shared/stub/player_get_info.cc",
     "//starboard/shared/stub/player_get_preferred_output_mode.cc",
     "//starboard/shared/stub/player_output_mode_supported.cc",
-    "//starboard/shared/stub/player_seek.cc",
     "//starboard/shared/stub/player_set_bounds.cc",
     "//starboard/shared/stub/player_set_playback_rate.cc",
     "//starboard/shared/stub/player_set_volume.cc",
     "//starboard/shared/stub/player_write_end_of_stream.cc",
-    "//starboard/shared/stub/player_write_sample.cc",
     "//starboard/shared/stub/socket_accept.cc",
     "//starboard/shared/stub/socket_bind.cc",
     "//starboard/shared/stub/socket_clear_last_error.cc",
diff --git a/src/starboard/stub/configuration_constants.cc b/src/starboard/stub/configuration_constants.cc
index cb8fbfe..ae91a87 100644
--- a/src/starboard/stub/configuration_constants.cc
+++ b/src/starboard/stub/configuration_constants.cc
@@ -55,9 +55,6 @@
 // Specifies whether this platform updates audio frames asynchronously.
 const bool kSbHasAsyncAudioFramesReporting = false;
 
-// Allow playing audioless video.
-const bool kSbHasAudiolessVideo = true;
-
 // Specifies whether this platform has webm/vp9 support.  This should be set to
 // non-zero on platforms with webm/vp9 support.
 const bool kSbHasMediaWebmVp9Support = false;
@@ -140,5 +137,10 @@
 // The string form of SB_PATH_SEP_CHAR.
 const char* kSbPathSepString = ":";
 
+// Specifies the preferred byte order of color channels in a pixel. Refer to
+// starboard/configuration.h for the possible values. EGL/GLES platforms should
+// generally prefer a byte order of RGBA, regardless of endianness.
+const int kSbPreferredRgbaByteOrder = SB_PREFERRED_RGBA_BYTE_ORDER_RGBA;
+
 // The maximum number of users that can be signed in at the same time.
 const uint32_t kSbUserMaxSignedIn = 1;
diff --git a/src/starboard/stub/configuration_public.h b/src/starboard/stub/configuration_public.h
index df4f29e..c339a86 100644
--- a/src/starboard/stub/configuration_public.h
+++ b/src/starboard/stub/configuration_public.h
@@ -204,11 +204,6 @@
 // API. The basic requirement is a scaled, clipped, alpha-blended blit.
 #define SB_HAS_BLITTER 0
 
-// Specifies the preferred byte order of color channels in a pixel. Refer to
-// starboard/configuration.h for the possible values. EGL/GLES platforms should
-// generally prefer a byte order of RGBA, regardless of endianness.
-#define SB_PREFERRED_RGBA_BYTE_ORDER SB_PREFERRED_RGBA_BYTE_ORDER_RGBA
-
 // Indicates whether or not the given platform supports bilinear filtering.
 // This can be checked to enable/disable renderer tests that verify that this is
 // working properly.
@@ -220,6 +215,10 @@
 
 #define SB_HAS_VIRTUAL_REALITY 1
 
+#ifndef COBALT_FORCE_STUB_RASTERIZER
+#define COBALT_FORCE_STUB_RASTERIZER 1
+#endif
+
 // --- I/O Configuration -----------------------------------------------------
 
 // Whether the current platform implements the on screen keyboard interface.
diff --git a/src/starboard/stub/gyp_configuration.gypi b/src/starboard/stub/gyp_configuration.gypi
index 1692859..88ea471 100644
--- a/src/starboard/stub/gyp_configuration.gypi
+++ b/src/starboard/stub/gyp_configuration.gypi
@@ -16,7 +16,6 @@
     # Stub does not use a filter-based player.
     'sb_filter_based_player': 0,
 
-    'target_arch': 'x64',
     'target_os': 'linux',
 
     # No GL drivers available.
diff --git a/src/starboard/stub/stub_sources.gypi b/src/starboard/stub/stub_sources.gypi
index 431269a..58b5737 100644
--- a/src/starboard/stub/stub_sources.gypi
+++ b/src/starboard/stub/stub_sources.gypi
@@ -150,18 +150,15 @@
         '<(DEPTH)/starboard/shared/stub/player_create.cc',
         '<(DEPTH)/starboard/shared/stub/player_destroy.cc',
         '<(DEPTH)/starboard/shared/stub/player_get_current_frame.cc',
-        '<(DEPTH)/starboard/shared/stub/player_get_info.cc',
         '<(DEPTH)/starboard/shared/stub/player_get_info2.cc',
         '<(DEPTH)/starboard/shared/stub/player_get_maximum_number_of_samples_per_write.cc',
         '<(DEPTH)/starboard/shared/stub/player_get_preferred_output_mode.cc',
         '<(DEPTH)/starboard/shared/stub/player_output_mode_supported.cc',
-        '<(DEPTH)/starboard/shared/stub/player_seek.cc',
         '<(DEPTH)/starboard/shared/stub/player_seek2.cc',
         '<(DEPTH)/starboard/shared/stub/player_set_bounds.cc',
         '<(DEPTH)/starboard/shared/stub/player_set_playback_rate.cc',
         '<(DEPTH)/starboard/shared/stub/player_set_volume.cc',
         '<(DEPTH)/starboard/shared/stub/player_write_end_of_stream.cc',
-        '<(DEPTH)/starboard/shared/stub/player_write_sample.cc',
         '<(DEPTH)/starboard/shared/stub/player_write_sample2.cc',
         '<(DEPTH)/starboard/shared/stub/socket_accept.cc',
         '<(DEPTH)/starboard/shared/stub/socket_bind.cc',
diff --git a/src/starboard/system.h b/src/starboard/system.h
index 544abf2..cc09fb5 100644
--- a/src/starboard/system.h
+++ b/src/starboard/system.h
@@ -55,12 +55,6 @@
   // usable by Starboard applications.
   kSbSystemPathFontConfigurationDirectory,
 
-#if SB_API_VERSION < 10
-  // Deprecated and unused. Tests looking for static data should instead look
-  // in the 'test' subdirectory of kSbSystemPathContentDirectory.
-  kSbSystemPathSourceDirectory,
-#endif  // SB_API_VERSION < 10
-
   // Path to a directory where temporary files can be written.
   kSbSystemPathTempDirectory,
 
@@ -130,21 +124,14 @@
   // User-Agent, say.
   kSbSystemPropertyPlatformName,
 
-#if SB_API_VERSION < 10
-  // A universally-unique ID for the current user.
-  kSbSystemPropertyPlatformUuid,
-#endif  // SB_API_VERSION < 10
-
   // The Google Speech API key. The platform manufacturer is responsible
   // for registering a Google Speech API key for their products. In the API
   // Console (http://developers.google.com/console), you can enable the
   // Speech APIs and generate a Speech API key.
   kSbSystemPropertySpeechApiKey,
 
-#if SB_API_VERSION >= 5
   // A field that, if available, is appended to the user agent
   kSbSystemPropertyUserAgentAuxField,
-#endif  // SB_API_VERSION >= 5
 } SbSystemPropertyId;
 
 // Enumeration of device types.
@@ -203,14 +190,12 @@
   // call.
   kSbSystemCapabilityCanQueryGPUMemoryStats,
 
-#if SB_API_VERSION >= 10
   // Whether this system sets the |timestamp| field of SbInputData. If the
   // system does not set this field, then it will automatically be set; however,
   // the relative time between input events likely will not be preserved, so
   // time-related calculations (e.g. velocity for move events) will be
   // incorrect.
   kSbSystemCapabilitySetsInputTimestamp,
-#endif
 
   // ATTENTION: Do not add more to this enum. Instead add an "IsSupported"
   // function in the relevant module.
@@ -394,7 +379,6 @@
 // |out_path|: The platform-defined system path specified by |path_id|.
 // |path_length|: The length of the system path.
 
-// presubmit: allow sb_export mismatch
 SB_EXPORT bool SbSystemGetPath(SbSystemPathId path_id,
                                char* out_path,
                                int path_length);
@@ -594,7 +578,6 @@
 // from any thread and must be idempotent.
 SB_EXPORT void SbSystemHideSplashScreen();
 
-#if SB_API_VERSION >= 10
 // Returns false if the platform doesn't need resume after suspend support. In
 // such case Cobalt will free up the resource it retains for resume after
 // suspend.
@@ -603,7 +586,6 @@
 // The return value of this function cannot change over the life time of the
 // application.
 SB_EXPORT bool SbSystemSupportsResume();
-#endif  // SB_API_VERSION >= 10
 
 #if SB_API_VERSION >= 11
 // Returns pointer to a constant global struct implementing the extension named
diff --git a/src/starboard/testing/fake_graphics_context_provider.cc b/src/starboard/testing/fake_graphics_context_provider.cc
index 50ce274..5a851cf 100644
--- a/src/starboard/testing/fake_graphics_context_provider.cc
+++ b/src/starboard/testing/fake_graphics_context_provider.cc
@@ -237,7 +237,7 @@
   EGLint context_attrib_list[] = {
       EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE,
   };
-#if defined(GLES3_SUPPORTED)
+#if SB_API_VERSION < SB_GLES3_DEPRECATED_VERSION && defined(GLES3_SUPPORTED)
   // Attempt to create an OpenGL ES 3.0 context.
   context_ = EGL_CALL_SIMPLE(eglCreateContext(
       display_, config, EGL_NO_CONTEXT, context_attrib_list));
diff --git a/src/starboard/tools/media/create_sha1_from_dmp_files.py b/src/starboard/tools/media/create_sha1_from_dmp_files.py
index cafb926..456d077 100644
--- a/src/starboard/tools/media/create_sha1_from_dmp_files.py
+++ b/src/starboard/tools/media/create_sha1_from_dmp_files.py
@@ -31,10 +31,9 @@
 
 def main(argv):
   dmp_dir = argv[1]
-  sha1 = hashlib.sha1()
 
-  dmp_sha1_dir = os.path.join(dmp_dir, 'dmp_sha1_files')
-  gs_dir = os.path.join(dmp_dir, 'gs_files')
+  dmp_sha1_dir = os.path.join(dmp_dir, "dmp_sha1_files")
+  gs_dir = os.path.join(dmp_dir, "gs_files")
 
   if not os.path.exists(dmp_sha1_dir):
     os.mkdir(dmp_sha1_dir)
@@ -43,25 +42,33 @@
 
   for filename in os.listdir(dmp_dir):
     _, ext = os.path.splitext(filename)
-    if ext == '.dmp':
+    if ext == ".dmp":
+      print "*"
+      print "File: " + filename
       # Compute the sha1 of the file
       filepath = os.path.join(dmp_dir, filename)
-      with open(filepath, 'rb') as filehandle:
+      with open(filepath, "rb") as filehandle:
+        sha1 = hashlib.sha1()
         buf = filehandle.read(65536)
         while buf:
           sha1.update(buf)
           buf = filehandle.read(65536)
         sha1sum = sha1.hexdigest()
 
+        print "SHA1: " + sha1sum
+
         # Rename the original DMP to its |SHA1| with no extension.
-        shutil.copyfile(filepath, os.path.join(gs_dir, sha1sum))
+        gs_out = os.path.join(gs_dir, sha1sum)
+        shutil.copyfile(filepath, gs_out)
+        print "Copied \'" + filepath + "\' => \'" + gs_out + "\'"
 
         # Create a new file with the |SHA1| of the DMP as its contents.
-        dmp_sha1_filename = os.path.join(dmp_sha1_dir, filename + '.sha1')
-        with open(dmp_sha1_filename, 'w') as dmp_sha1_file:
+        dmp_sha1_filename = os.path.join(dmp_sha1_dir, filename + ".sha1")
+        with open(dmp_sha1_filename, "w") as dmp_sha1_file:
           dmp_sha1_file.write(sha1sum)
           dmp_sha1_file.close()
+          print "Created \'" + dmp_sha1_filename + "\'"
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
   sys.exit(main(sys.argv))
diff --git a/src/starboard/tools/testing/test_runner.py b/src/starboard/tools/testing/test_runner.py
index 512734d..9948bfe 100755
--- a/src/starboard/tools/testing/test_runner.py
+++ b/src/starboard/tools/testing/test_runner.py
@@ -239,6 +239,7 @@
       self._loader_platform_config = build.GetPlatformConfig(loader_platform)
     self._app_config = self._platform_config.GetApplicationConfiguration(
         application_name)
+    self.application_name = application_name
     self.dry_run = dry_run
     self.xml_output_dir = xml_output_dir
     self.log_xml_results = log_xml_results
@@ -335,11 +336,21 @@
 
   def _GetTestFilters(self):
     filters = self._platform_config.GetTestFilters()
-    if not filters:
-      filters = []
     app_filters = self._app_config.GetTestFilters()
     if app_filters:
       filters.extend(app_filters)
+    # Regardless of our own platform, if we are Evergreen we also need to
+    # filter the tests that are filtered by the underlying platform. For
+    # example, the 'evergreen-arm-hardfp' needs to filter the 'raspi-2'
+    # filtered tests when it is running on a Raspberry Pi 2.
+    if self.loader_platform and self.loader_config:
+      loader_platform_config = build.GetPlatformConfig(self.loader_platform)
+      loader_app_config = loader_platform_config.GetApplicationConfiguration(
+          self.application_name)
+      for filter in (loader_platform_config.GetTestFilters() +
+                     loader_app_config.GetTestFilters()):
+        if filter not in filters:
+          filters.append(filter)
     return filters
 
   def _GetAllTestEnvVariables(self):
diff --git a/src/starboard/tools/toolchain/bash.py b/src/starboard/tools/toolchain/bash.py
index 6f03589..1cd17c6 100644
--- a/src/starboard/tools/toolchain/bash.py
+++ b/src/starboard/tools/toolchain/bash.py
@@ -32,6 +32,8 @@
     # whitelist common OK ones and quote anything else.
     if re.match(r'^[a-zA-Z0-9_=.,\\/+-]+$', argument):
       return argument  # No quoting necessary.
+    if re.match(r'^--gcc-toolchain', argument):
+      return argument.replace("'", "'\"'\"'")  # Do not surround with quotes
     return "'" + argument.replace("'", "'\"'\"'") + "'"
 
   def Join(self, command):
diff --git a/src/starboard/tools/toolchain/clangxx.py b/src/starboard/tools/toolchain/clangxx.py
index 22ba859..a1b6562 100644
--- a/src/starboard/tools/toolchain/clangxx.py
+++ b/src/starboard/tools/toolchain/clangxx.py
@@ -56,10 +56,16 @@
 
   def __init__(self, **kwargs):
     super(ExecutableLinker, self).__init__(**kwargs)
+    # Groups archives to be searched until all references are resolved.
+    self._write_group = kwargs.get('write_group', False)
 
   def GetCommand(self, path, extra_flags, flags, shell):
     del shell  # Not used.
-    return '{0} {1} {2} @$rspfile -o $out'.format(path, extra_flags, flags)
+    if self._write_group:
+      return ('{0} {1} {2} -Wl,--start-group @$rspfile -Wl,--end-group -o $out'
+              .format(path, extra_flags, flags))
+    else:
+      return '{0} {1} {2} @$rspfile -o $out'.format(path, extra_flags, flags)
 
 
 class SharedLibraryLinker(DynamicLinkerBase, abstract.SharedLibraryLinker):